commit ea7fa10403088cbd4f44a1ccc6da1f79209ae4dc Author: Kevin Thomas Date: Wed Oct 7 07:22:43 2026 -0400 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..b105d83 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +*.c linguist-detectable=true +*.cmake linguist-detectable=false +*.py linguist-detectable=false +*.txt linguist-detectable=false +*.json linguist-detectable=false +*.ps1 linguist-detectable=false + +# Line endings: store LF in the repository; check out LF everywhere except the +# Windows PowerShell scripts, which stay CRLF. Prevents CRLF<->LF churn. +* text=auto eol=lf +*.ps1 text eol=crlf + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6993dfc --- /dev/null +++ b/.gitignore @@ -0,0 +1,83 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb +*.rep +*.gpr + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# Build output +build/ +target/ + +# Node.js and PDF generation script +node_modules/ +package-lock.json +package.json +svg-to-pdf.ps1 + +# Operating System Files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Logs written by the Binary Ninja console helpers (build / flash / OpenOCD) +openocd.log +flash.log +build.log +build-flash.log + +# Nested repositories +eCTF/ + diff --git a/0x0001_hello-world/.gitignore b/0x0001_hello-world/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0001_hello-world/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0001_hello-world/.vscode/c_cpp_properties.json b/0x0001_hello-world/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/0x0001_hello-world/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0001_hello-world/.vscode/cmake-kits.json b/0x0001_hello-world/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0001_hello-world/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0001_hello-world/.vscode/extensions.json b/0x0001_hello-world/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0001_hello-world/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0001_hello-world/.vscode/launch.json b/0x0001_hello-world/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0001_hello-world/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0001_hello-world/.vscode/settings.json b/0x0001_hello-world/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/0x0001_hello-world/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/0x0001_hello-world/.vscode/tasks.json b/0x0001_hello-world/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x0001_hello-world/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0001_hello-world/0x0001_hello-world.c b/0x0001_hello-world/0x0001_hello-world.c new file mode 100644 index 0000000..7f7b062 --- /dev/null +++ b/0x0001_hello-world/0x0001_hello-world.c @@ -0,0 +1,46 @@ +/** + * @file 0x0001_hello-world.c + * @brief Hello World: print a greeting over UART in an infinite loop + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates the minimal "hello, world" program on the Raspberry Pi Pico 2. + * Initializes stdio over UART and prints a greeting in an infinite loop. + * + * Wiring: + * No external wiring required (USB serial). + */ + +#include +#include "pico/stdlib.h" + +int main(void) { + stdio_init_all(); + while (true) { + printf("hello, world\r\n"); + } +} diff --git a/0x0001_hello-world/CMakeLists.txt b/0x0001_hello-world/CMakeLists.txt new file mode 100644 index 0000000..31d8fc5 --- /dev/null +++ b/0x0001_hello-world/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0001_hello-world C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0001_hello-world 0x0001_hello-world.c ) + +pico_set_program_name(0x0001_hello-world "0x0001_hello-world") +pico_set_program_version(0x0001_hello-world "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0001_hello-world 1) +pico_enable_stdio_usb(0x0001_hello-world 0) + +# Add the standard library to the build +target_link_libraries(0x0001_hello-world + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0001_hello-world PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0001_hello-world) + diff --git a/0x0001_hello-world/pico_sdk_import.cmake b/0x0001_hello-world/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0001_hello-world/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0001a_stack/.gitignore b/0x0001a_stack/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0001a_stack/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0001a_stack/.vscode/c_cpp_properties.json b/0x0001a_stack/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..5a108f1 --- /dev/null +++ b/0x0001a_stack/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.3.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.3.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/15_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0001a_stack/.vscode/cmake-kits.json b/0x0001a_stack/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0001a_stack/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0001a_stack/.vscode/extensions.json b/0x0001a_stack/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0001a_stack/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0001a_stack/.vscode/launch.json b/0x0001a_stack/.vscode/launch.json new file mode 100644 index 0000000..7078fb1 --- /dev/null +++ b/0x0001a_stack/.vscode/launch.json @@ -0,0 +1,52 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "debuggerArgs": ["-ex", "set debug-file-directory /debug"], + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.3.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "debuggerArgs": ["-ex", "set debug-file-directory /debug"], + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.3.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + } + ] +} diff --git a/0x0001a_stack/.vscode/settings.json b/0x0001a_stack/.vscode/settings.json new file mode 100644 index 0000000..ecbf7dc --- /dev/null +++ b/0x0001a_stack/.vscode/settings.json @@ -0,0 +1,46 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + }, + "variant": { + "statusBarVisibility": "hidden" + }, + "buildTarget": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v4.3.4/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.3.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/15_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/15_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.3.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v4.3.4/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.13.2;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.3.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/15_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/15_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.3.0/picotool:${env:HOME}/.pico-sdk/cmake/v4.3.4/bin:${env:HOME}/.pico-sdk/ninja/v1.13.2:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.3.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/15_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/15_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.3.0/picotool:${env:HOME}/.pico-sdk/cmake/v4.3.4/bin:${env:HOME}/.pico-sdk/ninja/v1.13.2:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v4.3.4/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.13.2/ninja" +} diff --git a/0x0001a_stack/.vscode/tasks.json b/0x0001a_stack/.vscode/tasks.json new file mode 100644 index 0000000..60f30f6 --- /dev/null +++ b/0x0001a_stack/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.13.2/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.13.2/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.3.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.3.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + } + ] +} diff --git a/0x0001a_stack/0x0001a_stack.c b/0x0001a_stack/0x0001a_stack.c new file mode 100644 index 0000000..5e1945d --- /dev/null +++ b/0x0001a_stack/0x0001a_stack.c @@ -0,0 +1,23 @@ +#include +#include "pico/stdlib.h" + + +int main() +{ + stdio_init_all(); + + while (true) { + __asm volatile( + // Save a low register and the link register. + "push {r4, lr}\n" + // Intentionally unordered: the encoded list is still r2, r3, r6. + "push {r3, r2, r6}\n" + // Thumb PUSH cannot encode high registers r8-r12. + "stmdb sp!, {r9, r10}\n" + // Restore each group in reverse order to return SP to its start. + "ldmia sp!, {r9, r10}\n" + "pop {r2, r3, r6}\n" + "pop {r4, lr}\n" + ::: "memory"); + } +} diff --git a/0x0001a_stack/CMakeLists.txt b/0x0001a_stack/CMakeLists.txt new file mode 100644 index 0000000..c1b84e7 --- /dev/null +++ b/0x0001a_stack/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.3.0) +set(toolchainVersion 15_2_Rel1) +set(picotoolVersion 2.3.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0001a_stack C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0001a_stack 0x0001a_stack.c ) + +pico_set_program_name(0x0001a_stack "0x0001a_stack") +pico_set_program_version(0x0001a_stack "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0001a_stack 1) +pico_enable_stdio_usb(0x0001a_stack 0) + +# Add the standard library to the build +target_link_libraries(0x0001a_stack + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0001a_stack PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0001a_stack) + diff --git a/0x0001a_stack/pico_sdk_import.cmake b/0x0001a_stack/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0001a_stack/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0001b_ctf/.gitignore b/0x0001b_ctf/.gitignore new file mode 100644 index 0000000..97b60a5 --- /dev/null +++ b/0x0001b_ctf/.gitignore @@ -0,0 +1,4 @@ +build +!.vscode/* +!build-ctf/0x0001b_ctf.bin +!CTF-01.bin diff --git a/0x0001b_ctf/.vscode/c_cpp_properties.json b/0x0001b_ctf/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..613c996 --- /dev/null +++ b/0x0001b_ctf/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.3.1/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.3.1/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/15_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0001b_ctf/.vscode/cmake-kits.json b/0x0001b_ctf/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0001b_ctf/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0001b_ctf/.vscode/extensions.json b/0x0001b_ctf/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0001b_ctf/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0001b_ctf/.vscode/launch.json b/0x0001b_ctf/.vscode/launch.json new file mode 100644 index 0000000..451b846 --- /dev/null +++ b/0x0001b_ctf/.vscode/launch.json @@ -0,0 +1,52 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "debuggerArgs": ["-ex", "set debug-file-directory /debug"], + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.3.1/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "debuggerArgs": ["-ex", "set debug-file-directory /debug"], + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.3.1/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + } + ] +} diff --git a/0x0001b_ctf/.vscode/settings.json b/0x0001b_ctf/.vscode/settings.json new file mode 100644 index 0000000..5b883ba --- /dev/null +++ b/0x0001b_ctf/.vscode/settings.json @@ -0,0 +1,46 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + }, + "variant": { + "statusBarVisibility": "hidden" + }, + "buildTarget": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v4.3.4/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.3.1", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/15_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/15_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.3.1/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v4.3.4/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.13.2;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.3.1", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/15_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/15_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.3.1/picotool:${env:HOME}/.pico-sdk/cmake/v4.3.4/bin:${env:HOME}/.pico-sdk/ninja/v1.13.2:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.3.1", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/15_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/15_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.3.1/picotool:${env:HOME}/.pico-sdk/cmake/v4.3.4/bin:${env:HOME}/.pico-sdk/ninja/v1.13.2:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v4.3.4/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.13.2/ninja" +} diff --git a/0x0001b_ctf/.vscode/tasks.json b/0x0001b_ctf/.vscode/tasks.json new file mode 100644 index 0000000..2f07d94 --- /dev/null +++ b/0x0001b_ctf/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.13.2/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.13.2/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.3.1/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.3.1/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + } + ] +} diff --git a/0x0001b_ctf/CMakeLists.txt b/0x0001b_ctf/CMakeLists.txt new file mode 100644 index 0000000..cd4bff3 --- /dev/null +++ b/0x0001b_ctf/CMakeLists.txt @@ -0,0 +1,91 @@ +# MIT License +# +# Copyright (c) 2026 Kevin Thomas +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Author: Kevin Thomas +# Email: kevin@mytechnotalent.com +# GitHub: https://github.com/mytechnotalent +# File: CMakeLists.txt +# Desc: Configures the RP2350 Pico SDK project for the Operation Black Start +# CTF relay firmware. +# Created: 2026 + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.3.1) +set(toolchainVersion 15_2_Rel1) +set(picotoolVersion 2.3.1) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0001b_ctf C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable with modular sources in src/ +add_executable(0x0001b_ctf + src/main.c + src/grid.c + src/console.c +) + +pico_set_program_name(0x0001b_ctf "0x0001b_ctf") +pico_set_program_version(0x0001b_ctf "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0001b_ctf 1) +pico_enable_stdio_usb(0x0001b_ctf 0) +target_compile_definitions(0x0001b_ctf PRIVATE + PICO_DEFAULT_UART_BAUD_RATE=115200 +) + +# Add the standard library to the build +target_link_libraries(0x0001b_ctf + pico_stdlib +) + +# Add the standard include files to the build +target_include_directories(0x0001b_ctf PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/include +) + +pico_add_extra_outputs(0x0001b_ctf) diff --git a/0x0001b_ctf/CTF-01-I.md b/0x0001b_ctf/CTF-01-I.md new file mode 100644 index 0000000..dbda501 --- /dev/null +++ b/0x0001b_ctf/CTF-01-I.md @@ -0,0 +1,442 @@ +# Operation Black Start - Student Instructions + +**⚠ WORLDGRID EMERGENCY INCIDENT ⚠** + +``` ++----------------------------------------------------------------------------------------+ +| | +| ██████╗ ██╗ █████╗ ██████╗██╗ ██╗███████╗████████╗ █████╗ ██████╗ ████████╗ | +| ██╔══██╗██║ ██╔══██╗██╔════╝██║ ██╔╝██╔════╝╚══██╔══╝██╔══██╗██╔══██╗╚══██╔══╝ | +| ██████╔╝██║ ███████║██║ █████╔╝ ███████╗ ██║ ███████║██████╔╝ ██║ | +| ██╔══██╗██║ ██╔══██║██║ ██╔═██╗ ╚════██║ ██║ ██╔══██║██╔══██╗ ██║ | +| ██████╔╝███████╗██║ ██║╚██████╗██║ ██╗███████╗ ██║ ██║ ██║██║ ██║ ██║ | +| ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝██║ ██║╚══════╝ ╚═╝ ╚═╝ ╚═╝██║ ██║ ██║ | +| | +| | +| O P E R A T I O N B L A C K S T A R T | +| | +| *** PRIORITY RED *** | +| | ++----------------------------------------------------------------------------------------+ +``` + +--- + +## Project Overview + +WorldGrid Compact's emergency firmware build for its GRID-7 relay fleet +shipped a miscompiled safety threshold and a hardcoded false status string, +so deployed relays report a false-safe "STABLE" status while the frozen +frequency deviation reading of 0.87 Hz is nearly 50% beyond the 0.60 Hz +engineering limit. The source was overwritten by the next build and cannot +be recovered, so the only surviving evidence is the exact miscompiled +training image. Students reverse engineer `CTF-01.bin` with Ghidra, locate +and patch both defects directly in the binary, export a corrected image, +flash it to a Pico 2, and prove the corrected behavior with GDB and a UART +console. + +--- + +## Scenario Briefing + +### Background + +**WorldGrid Compact** is the emergency interconnection standard shared by +three allied national grid operators. When any member's primary SCADA +network goes dark, a fleet of small embedded relay nodes - call sign +**GRID-7** - is the only thing standing between an orderly recovery and an +uncontrolled cascade. Each relay node watches the last known grid frequency +deviation, decides whether conditions are safe, and either **holds** the +automatic black-start dispatch or **authorizes** it. + +At 03:11 UTC, a coordinated cyberattack severed the primary SCADA uplink +across the GRID-7 corridor and the WATER-3 aqueduct pumping stations that +depend on it. With the network coordination center offline and three +continents' worth of hospitals, rail systems, and water treatment plants +running on backup power, WorldGrid's engineering team did the only thing +they could: they rushed an emergency firmware build for the relay fleet and +pushed it within **eleven minutes** of the attack being detected. + +### The Disaster + +The engineer who built that emergency image, **Dr. Elias Renner**, has not +slept in thirty-one hours. He compiled the fix, ran a five-second bench +test, and shipped it - because the alternative was leaving the relay fleet +completely blind. It appears to work. The relay boots. It prints a status +report. It reports **GRID STATUS: STABLE** and **DISPATCH PATH: +AUTHORIZED**. + +There is a problem: the frozen frequency reading latched at the moment +communications were cut shows a deviation of **0.87 Hz** - nearly *50% +beyond* WorldGrid's hard engineering limit of **0.60 Hz**. A deviation this +large, if trusted, means the grid is nowhere near stable enough for an +automatic black-start dispatch. If the fleet authorizes dispatch on a false +"STABLE" reading, cascading generator trips will follow within minutes, +and GRID-7 and WATER-3 will go dark for the second time - this time with no +backup plan. + +**Dr. Renner's rushed build has a bug. Multiple relay nodes are already +reporting the same false-safe status. Nobody has found where in the +compiled firmware the error lives, because the source code used for that +emergency compile was overwritten by the next build fifteen minutes later +and cannot be recovered.** + +### The Only Surviving Evidence + +One relay node - the training/verification unit - still holds the exact +miscompiled image that shipped to the fleet. This binary, and this binary +alone, is the only remaining copy of the emergency build. There is no +source code. There is no build log. There is only the compiled image, a +UART cable, and whatever a skilled embedded reverse engineer can prove by +reading machine code. + +### The Human Stakes + +| Consequence if the false "STABLE" reading is trusted | Scale | +|---|---| +| Hospitals on generator backup past their fuel reserve | 214 facilities | +| Water treatment and pumping stations losing pressure | 3 aqueduct systems | +| Rail corridors stranded mid-route | 6 national rail networks | +| Estimated population affected by cascading failure | 40+ million people | + +**The options are:** + +1. ❌ **Trust the fleet's reported status** - dispatch fires on a false + reading, cascading failure follows within the hour. +2. ❌ **Shut the entire relay fleet down** - buys time, but leaves 40 + million people with no automated recovery path at all. +3. **REVERSE ENGINEER THE EMERGENCY BUILD** - find the exact + miscompiled bytes, patch them, verify the corrected image on real + hardware, and hand the fix to the field team so the *rest of the fleet* + can be safely repatched before the next attempt. + +### THE SHORTAGE + +For years, the world treated embedded systems as invisible infrastructure. +The engineers who could read a vector table, decode a Thumb branch, or +patch a miscompiled constant directly in a stripped binary were never +numerous enough. Tonight almost all of them are already in the field +chasing other failures. **You are the reserve team.** + +You were called in because you can do something Dr. Renner's exhausted +team cannot do right now: read what the processor is actually doing, with +no source code, no time for a rewrite, and no room for a guess. + +> **⏰ TIME PRESSURE:** The field team is standing by to push your verified +> patch to the rest of the GRID-7 fleet. Every relay node still reporting +> a false "STABLE" status is one dispatch cycle away from disaster. + +> **AUTHORIZED LAB ONLY:** This challenge uses a supplied Pico 2 training +> relay and its exact miscompiled firmware image. Do not connect this +> exercise to a public network, an operational grid, a water utility, or +> any device you do not own or have explicit written authorization to test. + +--- + +## Learning Objectives + +- Decode the RP2350 / ARM Cortex-M33 vector table and identify the reset + handler and initial stack pointer. +- Trace the bootrom-to-reset handoff and translate Thumb reset-vector + addresses into real function entry points. +- Locate a miscompiled boundary comparison and reason about the correct + immediate value the compiler should have encoded. +- Patch compare instructions and a status string directly in a raw binary + with Ghidra. +- Recover a hidden quarantined dispatch frame from the compiled image. +- Export and UF2-convert a corrected image, then verify the corrected + behavior on real hardware with GDB and a UART console. + +--- + +## What This Project Tests + +| Week | Concepts Tested | +|------|-----------------| +| 1 | RP2350 architecture, ARM Cortex-M33 registers, stack, flash/RAM, Thumb assembly, Ghidra static analysis | +| 2 | GDB connection, breakpoints, disassembly, register and memory inspection, UART observation | +| 3 | Bootrom handoff, vector table, reset handler, startup code, XIP, Thumb-bit addressing | + +--- + +## Part 1: Understanding the System + +### GRID-7 Relay Hardware + +| Component | Connection | Purpose | +|-----------|------------|---------| +| Raspberry Pi Pico 2 | RP2350 | Runs the miscompiled emergency firmware | +| UART TX | GPIO 0 | Relay telemetry output | +| UART RX | GPIO 1 | Reserved (no command parser is implemented) | +| SWD debug interface | Supplied probe | Authorized GDB inspection | + +No LED, relay output, sensor, display, or other peripheral is part of this +CTF. Every graded finding lives in flash (`.rodata`/`.text`) or SRAM, and is +reachable with only the Weeks 1-3 toolset: Ghidra, GDB, and a UART monitor. + +### UART Configuration + +- Baud: `115200` +- Data: `8 bits` +- Parity: `none` +- Stop: `1` +- Logic: `3.3 V` + +### Normal (Intended) Behavior + +The relay should latch the frozen deviation reading, compare it against the +**real** WorldGrid safety limit of **60** (0.60 Hz, encoded as an integer +`x100`), and report honestly: + +``` ++-----------------------------------------------------------------+ +| Intended Relay Behavior | +| | +| 1. Boot and initialize UART | +| 2. Print the boot identity and a signal-quality banner | +| 3. Compare the frozen 87 (0.87 Hz) reading against the 60 | +| (0.60 Hz) safety limit | +| 4. 87 exceeds 60, so the grid is NOT stable | +| 5. Report GRID STATUS: CRITICAL and DISPATCH PATH: HELD | +| 6. Repeat the report once per second until conditions change | ++-----------------------------------------------------------------+ +``` + +### Observed (Buggy) Behavior - What You Will See When You First Flash `CTF-01.uf2` + +```text +GLOBAL EMBEDDED RESPONSE NETWORK +BLACK START WINDOW: 27 MINUTES +UART0 115200 8N1 | AUTHORIZED LAB CONSOLE +SIGNAL: NORMAL +RESPONSE> GRID STATUS: STABLE +DISPATCH PATH: AUTHORIZED +LAST FRAME: QUARANTINED +RESPONSE> +``` + +> **Terminal Timing Note:** The first four lines (`GLOBAL EMBEDDED...` through +> `SIGNAL: NORMAL`) represent the **initial boot banner**, emitted once during +> startup. If your serial terminal (PuTTY) connects after the board has +> booted, you will observe the continuous 1-second status stream (`GRID +> STATUS...` and `DISPATCH PATH...`). To view the boot banner in your terminal, +> reset the Pico (pulse `RUN` to `GND`) while PuTTY is actively connected. + +This is exactly what Dr. Renner's team is seeing on the deployed fleet. It +is wrong, and it is wrong in **two independent ways** inside the compiled +binary. Do not assume the first readable sentence is the full truth - +treat every printed line as evidence to be checked against the machine +code, not as a fact on its own. + +--- + +## Part 2: The Firmware + +You do not have the source code. It was overwritten fifteen minutes after +the emergency build shipped. You have only the compiled image. Your job is +to reverse engineer it with Ghidra, locate the defects, and patch the +binary directly - exactly the way Dr. Renner's field team will need to +patch the rest of the deployed fleet. + +### What The Firmware Does + +1. Initializes UART0 and stdio. +2. Reads a frozen grid-frequency-deviation reading that was latched in + memory before communications were severed. +3. Compares that reading against a compiled-in safety threshold - **twice**, + once for each independent status line it reports. +4. Prints a boot banner containing an unconditional signal-quality line. +5. Enters an infinite loop printing the grid classification and dispatch + decision once per second. + +### Bug Summary - What You Are Graded On + +| Bug # | Category | Severity | Description | Hint | +|-------|----------|----------|--------------|------| +| **Bug #1** | Miscompiled safety constant | **CRITICAL** | The safety threshold used to classify the frozen reading was compiled far too permissive. It is used **twice** - once for the operator-facing status and once for the automated dispatch decision - and **both** copies must be corrected. | The real WorldGrid safety limit is 60 (0.60 Hz). Search for the wrong immediate value used in the comparison. | +| **Bug #2** | Hardcoded string literal | **HIGH** | The boot banner unconditionally prints a signal-quality word that does not reflect the actual reading, regardless of what the relay later reports. | The correct word describes the true state of a 0.87 Hz deviation against a 0.60 Hz limit - not "NORMAL". | + +**Important:** The replacement text for Bug #2 **must be the same length** +as the original - patching a shorter or longer string will corrupt +adjacent flash data. + +### A Third Finding - Not a Bug, a Recovery Task + +Somewhere in this image is the **quarantined black-start authorization +frame** - the exact frame the relay is supposed to transmit to the +regional dispatcher once a human operator confirms it is safe to proceed. +It is never printed by the firmware. Recovering it (without patching +anything) is required evidence for your final report. + +--- + +## Part 3: Your Assignment + +Whenever a task asks you to **Document** or **answer**, write your answers +in a single file named `CTF-01-Answers.md`. + +### Task 1: Setup and Initial Analysis + +1. Create a new Ghidra project named `Black_Start_Investigation`. +2. Import `CTF-01.bin`. +3. Configure the language as **ARM Cortex 32-bit, little endian**. +4. Set the base address to `0x10000000`. +5. Run auto-analysis. + +**Document:** +- A screenshot of the Ghidra **Import Results** or **Program Information** + window showing the project name, processor settings, and base address. +- The address of `main()`. +- The address of the recurring status loop (the branch target that repeats + once per second). +- The vector-table base, the initial stack pointer, and the reset-handler + pointer as stored (note its Thumb bit) versus the actual instruction + address. + +### Task 2: Find and Patch Bug #1 - The Miscalibrated Safety Threshold + +1. Find **both** locations where the frozen reading is compared against + the miscompiled safety constant. +2. Document the exact address, the original instruction, and the original + immediate value at each location. +3. Determine the correct immediate value. **Caution:** the compiler may + not have encoded the raw threshold you expect - a strict "less than" + comparison against an unsigned value is often optimized into a + "less-or-equal" comparison against one less than the threshold. Show + your reasoning. +4. Patch **both** locations in Ghidra using the **Bytes Window** workflow: + > **Critical ARM Thumb-2 Patching Note:** In ARM Cortex-M, compare instructions that directly precede conditional execution blocks (`ite hi`) must **not** be patched using the right-click *Patch Instruction* dialog. Ghidra's automatic re-disassembler encounters an internal context conflict with the subsequent `ite hi` instruction, which collapses Thumb decoding and swallows Compare Site B (`0x1000020A`). + > + > To patch cleanly without breaking downstream disassembly, use the **Bytes Window**: + > 1. Ensure the Bytes window is open (**Window** -> **Bytes: CTF-01.bin**). + > 2. In the Bytes window toolbar, click the **pencil icon** (**Toggle Edit Mode**). + > 3. In the Listing window, click on address `0x100001FC` (Compare Site A) and press **`C`** (**Clear Code Bytes**). The instruction temporarily clears into raw bytes (`5E 2B`). + > 4. In the Bytes window, locate offset `100001fc`, click on `5E`, and change it to **`3B`**. + > 5. Click back in the Listing window on address `0x100001FC` and press **`D`** (**Disassemble**). The instruction immediately disassembles cleanly as `cmp r3, #0x3b`. + > 6. Notice that Compare Site B at `0x1000020A` remains completely intact and visible! Repeat the exact same steps at `0x1000020A`: click `0x1000020A` in the Listing, press **`C`**, change `5E` to **`3B`** in the Bytes window, click back in the Listing, and press **`D`**. + +**Questions to answer:** +- Why must both locations be patched? What happens if you only patch one? +- Why is a false "STABLE" classification on an 0.87 Hz reading dangerous + for an automated black-start dispatch? + +### Task 3: Find and Patch Bug #2 - The False Signal Banner + +1. Find the boot-banner string that unconditionally reports the wrong + signal quality. +2. Document its address and the exact bytes that must change. +3. Patch the string, preserving its exact length. + +**Questions to answer:** +- Document the original vs. patched bytes, character by character. +- Why is a hardcoded, unconditional status word more dangerous than one + that is at least computed from a (miscalibrated) reading? + +### Task 4: Recover the Quarantined Dispatch Frame + +1. Use Ghidra's Defined Strings (or a raw string search) to locate the + hidden black-start authorization frame. +2. Document its address and explain why it is never transmitted by the + current firmware. +3. Do **not** attempt to patch this value - it is evidence, not a bug. + +### Task 5: Export and Verify + +1. Export your patched binary as `CTF-01_fixed.bin`. +2. Convert it to UF2 format for the RP2350: + ```bash + python uf2conv.py CTF-01_fixed.bin --base 0x10000000 --family 0xe48bff59 --output CTF-01_fixed.uf2 + ``` +3. Flash `CTF-01_fixed.uf2` to your Pico 2 and capture the corrected UART + output. +4. Confirm that the corrected image now reports **GRID STATUS: CRITICAL**, + **DISPATCH PATH: HELD**, and the corrected signal-quality word - an + honest, safe report instead of a false "all clear." +5. Build a summary table of every patch: address, original bytes, patched + bytes, and a one-line description. + +### Task 6: Written Reflection (short answers, 150 words or less each) + +1. Why is "the build was rushed under emergency pressure" not an + acceptable excuse for shipping a firmware defect that could trigger a + cascading grid failure? +2. Name one concrete engineering practice (code review, static analysis, + hardware-in-the-loop test, etc.) that would have caught **each** of the + two graded bugs before this image ever reached the fleet. + +--- + +## How To Breadboard + +- Pico 2 **GPIO 0 / UART TX** -> USB-UART adapter **RX** +- Pico 2 **GPIO 1 / UART RX** -> USB-UART adapter **TX** +- Pico 2 **GND** -> USB-UART adapter **GND** +- Use **3.3 V logic only**. Never connect a 5 V line to a Pico GPIO. +- Connect the supplied SWD probe according to its documented pinout. + +The supplied image is `CTF-01.bin` (for Ghidra analysis) and `CTF-01.uf2` +(for flashing). If your instructor supplies different filenames, record the +actual filenames in your report. + +--- + +## Memory Map Reference + +| Region | Address | Purpose | +|--------|---------|---------| +| Bootrom | `0x00000000` | Immutable boot code | +| Flash/XIP | `0x10000000` | Vector table, code, constants, strings | +| SRAM | `0x20000000` | Stack and writable state | + +--- + +## Submission Format + +Submit a folder containing: + +- `CTF-01-Answers.md`; +- screenshots or terminal transcripts; +- `CTF-01_fixed.bin` and `CTF-01_fixed.uf2`; +- the original image hash. + +--- + +## Success Criteria + +You complete the challenge when you can prove all of the following: + +- You can explain how the RP2350 reaches the relay's code from reset. +- You can locate and patch both copies of the miscalibrated threshold. +- You can locate and patch the false signal-quality string without + corrupting adjacent data. +- You can export, convert, and flash a corrected image. +- You can prove on real hardware that the corrected image reports the + true, dangerous state instead of the false "all clear." +- You can recover the quarantined dispatch frame as evidence. + +--- + +## Academic Integrity + +By submitting this CTF work, you certify that: + +1. You used only the supplied training relay, image, and lab interface. +2. You did not connect the challenge to a public network, an operational + grid, a water utility, or any third-party device. +3. You understand that embedded reverse engineering and binary patching + require explicit authorization in any real-world context. +4. You will report any discovered weakness responsibly to the course + instructor. + +The world is short on people who can do this work. Treat that +responsibility seriously: verify before you patch, patch before you trust, +and never confuse a clean-looking status line with a safe system. + +--- + +## Reference Material + +- ARM Cortex-M33 Technical Reference Manual +- RP2350 datasheet +- GDB documentation +- Ghidra documentation: [https://ghidra-sre.org/](https://ghidra-sre.org/) diff --git a/0x0001b_ctf/CTF-01-I.pdf b/0x0001b_ctf/CTF-01-I.pdf new file mode 100644 index 0000000..1ed54e3 Binary files /dev/null and b/0x0001b_ctf/CTF-01-I.pdf differ diff --git a/0x0001b_ctf/CTF-01-R.md b/0x0001b_ctf/CTF-01-R.md new file mode 100644 index 0000000..9dbec48 --- /dev/null +++ b/0x0001b_ctf/CTF-01-R.md @@ -0,0 +1,237 @@ +# Operation Black Start - Requirements & Grading Criteria + +``` ++----------------------------------------------------------------------------------------+ +| | +| ██████╗ ██╗ █████╗ ██████╗██╗ ██╗███████╗████████╗ █████╗ ██████╗ ████████╗ | +| ██╔══██╗██║ ██╔══██╗██╔════╝██║ ██╔╝██╔════╝╚══██╔══╝██╔══██╗██╔══██╗╚══██╔══╝ | +| ██████╔╝██║ ███████║██║ █████╔╝ ███████╗ ██║ ███████║██████╔╝ ██║ | +| ██╔══██╗██║ ██╔══██║██║ ██╔═██╗ ╚════██║ ██║ ██╔══██║██╔══██╗ ██║ | +| ██████╔╝███████╗██║ ██║╚██████╗██║ ██╗███████╗ ██║ ██║ ██║██║ ██║ ██║ | +| ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝██║ ██║╚══════╝ ╚═╝ ╚═╝ ╚═╝██║ ██║ ██║ | +| | +| | +| O P E R A T I O N B L A C K S T A R T | +| | +| REQUIREMENTS & GRADING CRITERIA | +| | ++----------------------------------------------------------------------------------------+ +``` + +--- + +## Project Overview + +Students are the reverse-engineering reserve team called in after WorldGrid +Compact's emergency firmware build shipped a miscompiled safety threshold and +a false status string to its GRID-7 relay fleet. Students reverse engineer +`CTF-01.bin` with Ghidra, locate two real defects, patch them in the binary, +export a corrected image, flash it to real hardware, and prove the corrected +behavior with the debugger and the console. + +The challenge is separate from all FINAL projects and contains no FINAL-project +answer, constant, address, bug, or patch. + +--- + +## Learning Objectives + +- Decode an ARM Cortex-M33 vector table and identify the reset handler and + initial stack pointer. +- Translate Thumb reset-vector addresses into real function entry points. +- Locate a miscompiled boundary comparison and reason about its immediate + value. +- Patch compare instructions and a status string in a raw binary with Ghidra. +- Export and UF2-convert a corrected image, then verify it on real hardware. +- Capture derived state with GDB and read UART console output. + +Students must use only Weeks 1-3 concepts: ARM registers and stack behavior, +UART output, GDB, Ghidra static analysis and binary patching, vector tables, +reset startup, XIP, and Thumb addressing. + +--- + +## Deliverables Checklist + +| # | Deliverable | Format | Criterion | +|---|-------------|--------|-----------| +| 1 | Ghidra project screenshot (project name, processor, base address) | PNG/JPG | 1.1 | +| 2 | `main()` and status-loop addresses | Inside `CTF-01-Answers.md` | 1.2 | +| 3 | Vector table base, initial SP, reset pointer | Inside `CTF-01-Answers.md` | 1.3 | +| 4 | Thumb bit explanation | Inside `CTF-01-Answers.md` | 1.4 | +| 5 | Bug #1 evidence and patches (both compare sites) | Inside `CTF-01-Answers.md` | 2.1-2.6 | +| 6 | Bug #2 evidence and patch (six characters) | Inside `CTF-01-Answers.md` | 3.1-3.4 | +| 7 | Recovered dispatch frame and address | Inside `CTF-01-Answers.md` | 4.1-4.2 | +| 8 | `CTF-01_fixed.bin` | BIN file | 5.1 | +| 9 | `CTF-01_fixed.uf2` | UF2 file | 5.2 | +| 10 | Corrected console transcript | Inside `CTF-01-Answers.md` | 5.3 | +| 11 | Summary table of all patches | Inside `CTF-01-Answers.md` | 5.4 | +| 12 | Written reflection | Inside `CTF-01-Answers.md` | 6.1-6.2 | + +--- + +## Required Tools and Equipment + +| Tool | Purpose | +|------|---------| +| Raspberry Pi Pico 2 | Isolated target | +| 3.3 V USB-UART adapter | UART capture on GPIO 0 (TX) / GPIO 1 (RX) | +| Serial monitor | Observe output | +| Ghidra | Static analysis and binary patching | +| Python (`uf2conv.py`) | UF2 conversion | +| `CTF-01.bin` and `CTF-01.uf2` | Supplied artifacts | + +UART settings: **115200 baud, 8 data bits, no parity, 1 stop bit**. + +--- + +## Artifact Identity + +The instructor-issued artifact hashes are: + +```text +CTF-01.bin 425591AC17FF4C22206286EE6F40B06A89523483804850A41854A9B6F89D7B70 +CTF-01.uf2 B1552EE3BB5763D96C65D8DF1C86984EE842EF1A823FDF5D94132B1E10FF9D16 +``` + +--- + +## Grading Rubric - Detailed Breakdown + +### Task 1: Setup and Initial Analysis (15 points) + +| Criterion | Points | Full credit | Partial credit | No credit | +|-----------|--------|-------------|----------------|-----------| +| Criterion 1.1: Ghidra Project Setup | 3 | Correct project name, `ARM:LE:32:Cortex` (ARM Cortex 32-bit little endian), base `0x10000000` | One item off | Not set up | +| Criterion 1.2: main() and Status-Loop Addresses | 4 | Both addresses correct | One correct | Neither found | +| Criterion 1.3: Vector Table Decoding | 4 | Correct base, initial SP, reset pointer | One missing | Not found | +| Criterion 1.4: Thumb Addressing | 4 | Correctly clears bit 0 and identifies `main()` | General explanation | Incorrect | + +### Task 2: Find and Patch Bug #1: The Miscalibrated Safety Threshold (30 points) + +| Criterion | Points | Full credit | Partial credit | No credit | +|-----------|--------|-------------|----------------|-----------| +| Criterion 2.1: Locate Compare Site A | 5 | Correct address and original bytes | Address off | Not found | +| Criterion 2.2: Locate Compare Site B | 5 | Correct address and original bytes | Address off | Not found | +| Criterion 2.3: Correct Immediate-Value Reasoning | 8 | Explains the `<` to `<=` transform and gives `0x3B` | Correct value, no reasoning | Wrong value | +| Criterion 2.4: Patch Compare Site A | 4 | Byte change verified | Wrong byte | Not patched | +| Criterion 2.5: Patch Compare Site B | 4 | Byte change verified | Wrong byte | Not patched | +| Criterion 2.6: Explain Why Both Sites Must Be Patched | 4 | Clear explanation of the two independent comparisons | Vague | Missing | + +### Task 3: Find and Patch Bug #2: The False Signal Banner (20 points) + +| Criterion | Points | Full credit | Partial credit | No credit | +|-----------|--------|-------------|----------------|-----------| +| Criterion 3.1: Locate the Banner String | 5 | Correct address | Approximate | Not found | +| Criterion 3.2: Patch Six Characters | 8 | All six bytes changed, length preserved | Correct text, wrong bytes documented | Wrong length | +| Criterion 3.3: Character-by-Character Documentation | 4 | Original vs patched byte for all six characters | Partial | Missing | +| Criterion 3.4: Explain the Danger of a Hardcoded Status Word | 3 | Clear, specific reasoning | Generic | Missing | + +### Task 4: Recover the Quarantined Dispatch Frame (10 points) + +| Criterion | Points | Full credit | Partial credit | No credit | +|-----------|--------|-------------|----------------|-----------| +| Criterion 4.1: Recover the Dispatch Frame | 6 | Correct address and full text | Partial text | Not found | +| Criterion 4.2: Explain Why It Is Never Transmitted | 4 | Clear static-analysis explanation | Vague | Missing | + +### Task 5: Export and Verify (20 points) + +| Criterion | Points | Full credit | Partial credit | No credit | +|-----------|--------|-------------|----------------|-----------| +| Criterion 5.1: Export CTF-01_fixed.bin | 4 | Valid patched binary | Corrupted | Not submitted | +| Criterion 5.2: Convert to CTF-01_fixed.uf2 | 4 | Correct base and family flags | Wrong flags | Not submitted | +| Criterion 5.3: Hardware Verification | 8 | Corrected console output confirmed (CRITICAL/HELD in 1s stream; DANGER at boot/Ghidra) | Some lines corrected | No verification | +| Criterion 5.4: Summary Table of All Patches | 4 | Complete address and before/after table | Missing entries | No table | + +### Task 6: Written Reflection (5 points) + +| Criterion | Points | Full credit | Partial credit | No credit | +|-----------|--------|-------------|----------------|-----------| +| Criterion 6.1: "Rushed Build" Is Not an Excuse | 2 | Specific, grounded reasoning | Generic | Missing | +| Criterion 6.2: One Engineering Practice per Bug | 3 | Concrete practice for each bug | One bug only | Missing | + +--- + +## Common Pitfalls + +| Pitfall | Consequence | Avoidance | +|---------|-------------|-----------| +| Patching only one threshold site | One status line still lies | Patch both `0x100001FC` and `0x1000020A` | +| Assuming the immediate equals the limit | Off-by-one, wrong boundary | Use `0x3B` (59), not `0x3C` (60) | +| Using Patch Instruction before IT block | Re-disassembler context conflict swallows Site B | In Listing press `C` -> edit byte in Bytes window (pencil) -> press `D` | +| Missing boot banner in serial terminal | PuTTY misses one-time 5ms boot banner | Pulse RUN to GND while connected to capture | +| Replacing a string with a different length | Corrupts adjacent flash | `NORMAL` and `DANGER` are both 6 bytes | +| Treating an odd vector address as invalid | Thumb analysis fails | Clear bit 0 | +| Modifying the quarantined dispatch frame | Destroys evidence | Recover it, do not patch it | + +--- + +## How To Breadboard + +- **Raspberry Pi Pico 2** powered over USB. +- **3.3 V USB-UART adapter**: + - Adapter RX to Pico GP0 (UART0 TX) + - Adapter TX to Pico GP1 (UART0 RX) + - Adapter GND to Pico GND + - Do not connect the adapter VCC while the Pico is USB powered. +- **Serial monitor:** 115200 baud, 8 data bits, no parity, 1 stop bit. +- No other peripherals are required; all evidence is obtained from the console. + +--- + +## Memory Map Reference + +| Region | Address | Purpose | +|--------|---------|---------| +| Bootrom | `0x00000000` | Immutable boot code | +| Flash/XIP | `0x10000000` | Vector table, code, constants, strings | +| SRAM | `0x20000000` | Stack and writable state | + +--- + +## Deadline & Submission + +- Create a folder containing the Ghidra screenshot, `CTF-01_fixed.bin`, and + `CTF-01_fixed.uf2`. +- Write all written answers in a single file named `CTF-01-Answers.md` inside that + folder. +- ZIP the folder as `lastname-firstname-CTF-01.zip`. +- Submit the ZIP before the posted deadline; late submissions lose 10 percent + per day. + +--- + +## Grade Scale + +| Grade | Percentage | Points | +|-------|------------|--------| +| A+ | 97-100% | 97-100 | +| A | 93-96% | 93-96 | +| A- | 90-92% | 90-92 | +| B+ | 87-89% | 87-89 | +| B | 84-86% | 84-86 | +| B- | 80-83% | 80-83 | +| C | 70-79% | 70-79 | +| F | 0-69% | 0-69 | + +--- + +## Academic Integrity + +Use only the supplied Pico 2 and firmware. Do not connect the exercise to an +operational grid, water plant, public network, military system, or third-party +device. This is a controlled, isolated educational exercise. All analysis and +patches must be your own work; sharing binaries, addresses, or answers is a +violation of the academic integrity policy. + +--- + +## Reference Material + +| Topic | Reference | +|-------|-----------| +| ARM Cortex-M33 registers and stack | Week 1 | +| UART output and console capture | Week 2 | +| Vector tables, reset startup, and XIP | Week 2 | +| Ghidra static analysis and binary patching | Week 3 | +| Thumb addressing | Week 3 | diff --git a/0x0001b_ctf/CTF-01-R.pdf b/0x0001b_ctf/CTF-01-R.pdf new file mode 100644 index 0000000..01ca3d8 Binary files /dev/null and b/0x0001b_ctf/CTF-01-R.pdf differ diff --git a/0x0001b_ctf/CTF-01-S.md b/0x0001b_ctf/CTF-01-S.md new file mode 100644 index 0000000..6d19d05 --- /dev/null +++ b/0x0001b_ctf/CTF-01-S.md @@ -0,0 +1,467 @@ +# Operation Black Start - Instructor Solution Key + +``` ++----------------------------------------------------------------------------------------+ +| | +| ██████╗ ██╗ █████╗ ██████╗██╗ ██╗███████╗████████╗ █████╗ ██████╗ ████████╗ | +| ██╔══██╗██║ ██╔══██╗██╔════╝██║ ██╔╝██╔════╝╚══██╔══╝██╔══██╗██╔══██╗╚══██╔══╝ | +| ██████╔╝██║ ███████║██║ █████╔╝ ███████╗ ██║ ███████║██████╔╝ ██║ | +| ██╔══██╗██║ ██╔══██║██║ ██╔═██╗ ╚════██║ ██║ ██╔══██║██╔══██╗ ██║ | +| ██████╔╝███████╗██║ ██║╚██████╗██║ ██╗███████╗ ██║ ██║ ██║██║ ██║ ██║ | +| ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝██║ ██║╚══════╝ ╚═╝ ╚═╝ ╚═╝██║ ██║ ██║ | +| | +| | +| O P E R A T I O N B L A C K S T A R T | +| | +| *** INSTRUCTOR SOLUTION KEY - RESTRICTED *** | +| | ++----------------------------------------------------------------------------------------+ +``` + +> The task and criterion headings in this key are word-for-word identical to +> `CTF-01-R.md`, so a student can match each criterion one-to-one. + +--- + +## Artifact Identity + +| Artifact | Value | +|----------|-------| +| Student image | `CTF-01.bin` | +| Flash image | `CTF-01.uf2` | +| Target | Raspberry Pi Pico 2 / RP2350 ARM Cortex-M33 | +| Image base | `0x10000000` | +| Console | UART0, GPIO 0 TX / GPIO 1 RX, 115200 8N1 | + +```text +CTF-01.bin 425591AC17FF4C22206286EE6F40B06A89523483804850A41854A9B6F89D7B70 +CTF-01.uf2 B1552EE3BB5763D96C65D8DF1C86984EE842EF1A823FDF5D94132B1E10FF9D16 +``` + +Proof tool: `python3 scripts/verify_ctf.py` returns `11/11 checks passed` against +`CTF-01.bin`. + +--- + +## Task 1: Setup and Initial Analysis (15 points) + +### Solution + +**Criterion 1.1: Ghidra Project Setup (3 points).** Import `CTF-01.bin` as +`Raw Binary`, language `ARM:LE:32:Cortex`, base address `0x10000000`, then run +auto-analysis. + +**Criterion 1.2: main() and Status-Loop Addresses (4 points).** + +| Element | Address | +|---------|---------| +| `main()` | `0x100001E0` | +| Recurring status loop start | `0x10000234` | +| Loop back-edge (`b.n 0x10000234`) | `0x10000266` | + +The back-edge instruction at `0x10000266` is `b.n 0x10000234`, encoded as bytes +`E5 E7`. + +**Criterion 1.3: Vector Table Decoding (4 points).** +First 32 bytes of `CTF-01.bin`: + +```text +00 20 08 20 5B 01 00 10 1B 01 00 10 1D 01 00 10 +11 01 00 10 11 01 00 10 11 01 00 10 11 01 00 10 +``` + +| Evidence | Answer | +|----------|--------| +| Vector table base | `0x10000000` | +| Initial SP | `0x20082000` | +| Reset pointer (as stored) | `0x1000015B` | +| Reset instruction address | `0x1000015A` | + +**Criterion 1.4: Thumb Addressing (4 points).** +The stored reset pointer `0x1000015B` has bit 0 set to 1, which selects Thumb +mode. Clearing bit 0 (`0x1000015B & ~1`) gives the real instruction address +`0x1000015A`. The equally odd interrupt vectors (`0x1000011B`, `0x1000011D`, +`0x10000111`) are handled the same way. + +### Grading Rubric (1-to-1 Mapping) + +| Criterion | Points | Full Credit (Answer Key) | Partial Credit | No Credit | +|-----------|--------|--------------------------|----------------|-----------| +| Criterion 1.1: Ghidra Project Setup | 3 | Correct project name, `ARM:LE:32:Cortex` (ARM Cortex 32-bit little endian), base `0x10000000` | One item off | Not set up | +| Criterion 1.2: main() and Status-Loop Addresses | 4 | Both addresses correct | One correct | Neither found | +| Criterion 1.3: Vector Table Decoding | 4 | Correct base, initial SP, reset pointer | One missing | Not found | +| Criterion 1.4: Thumb Addressing | 4 | Correctly clears bit 0 and identifies `main()` | General explanation | Incorrect | + +### Instructor Notes & Assembly + +- Confirm the Ghidra import used `Raw Binary`, `ARM:LE:32:Cortex`, base + `0x10000000`, and that auto-analysis completed before any address was read. +- Verify `main()` is `0x100001E0`, the loop head is `0x10000234`, and the + back-edge bytes `E5 E7` sit at `0x10000266`. +- The reset vector is stored as `0x1000015B`; the real Thumb entry is + `0x1000015A`. Do not accept the un-cleared `0x1000015B` as an instruction + address. + +--- + +## Task 2: Find and Patch Bug #1: The Miscalibrated Safety Threshold (30 points) + +### Solution + +`grid_deviation` is `volatile`, so the compiler emits two independent compares: +one for the operator status and one for the automated dispatch decision. + +**Criterion 2.1: Locate Compare Site A (5 points).** + +```text +100001fc: 2b5e cmp r3, #94 @ 0x5e +``` + +| Item | Value | +|------|-------| +| Address | `0x100001FC` | +| File offset | `0x1FC` | +| Original bytes | `5E 2B` | +| Original instruction | `cmp r3, #94` | + +**Criterion 2.2: Locate Compare Site B (5 points).** + +```text +1000020a: 2b5e cmp r3, #94 @ 0x5e +``` + +| Item | Value | +|------|-------| +| Address | `0x1000020A` | +| File offset | `0x20A` | +| Original bytes | `5E 2B` | +| Original instruction | `cmp r3, #94` | + +**Criterion 2.3: Correct Immediate-Value Reasoning (8 points).** +The source constant is `SAFE_THRESHOLD = 95` and the test is `x < 95`. For an +unsigned value, `x < 95` is exactly `x <= 94`, which the compiler emits as +`cmp r3, #94` plus `ite hi`. The correct engineering limit is `60`, so the test +is `x < 60`, which is `x <= 59`. The correct patched immediate is therefore +**`0x3B` (59)**, not `0x3C` (60). Patching to `0x3C` would wrongly accept a +reading of exactly 60. + +**Criterion 2.4: Patch Compare Site A (4 points).** + +| Address | File Offset | Original | Patched | Before | After | +|---------|-------------|----------|---------|--------|-------| +| `0x100001FC` | `0x1FC` | `5E 2B` | `3B 2B` | `cmp r3, #94` | `cmp r3, #59` | + +**Criterion 2.5: Patch Compare Site B (4 points).** + +| Address | File Offset | Original | Patched | Before | After | +|---------|-------------|----------|---------|--------|-------| +| `0x1000020A` | `0x20A` | `5E 2B` | `3B 2B` | `cmp r3, #94` | `cmp r3, #59` | + +**Criterion 2.6: Explain Why Both Sites Must Be Patched (4 points).** +`operator_state` (`0x20000844`) and `dispatch_state` (`0x20000834`) are each +computed from their own read of `grid_deviation`. Patching only site A fixes the +displayed text while the automated dispatch at site B still authorizes on the +dangerous reading. Frozen reading `87`: `87 <= 94` is true (STABLE/AUTHORIZED, +wrong); `87 <= 59` is false (CRITICAL/HELD, correct). + +### Grading Rubric (1-to-1 Mapping) + +| Criterion | Points | Full Credit (Answer Key) | Partial Credit | No Credit | +|-----------|--------|--------------------------|----------------|-----------| +| Criterion 2.1: Locate Compare Site A | 5 | Correct address and original bytes | Address off | Not found | +| Criterion 2.2: Locate Compare Site B | 5 | Correct address and original bytes | Address off | Not found | +| Criterion 2.3: Correct Immediate-Value Reasoning | 8 | Explains the `<` to `<=` transform and gives `0x3B` | Correct value, no reasoning | Wrong value | +| Criterion 2.4: Patch Compare Site A | 4 | Byte change verified | Wrong byte | Not patched | +| Criterion 2.5: Patch Compare Site B | 4 | Byte change verified | Wrong byte | Not patched | +| Criterion 2.6: Explain Why Both Sites Must Be Patched | 4 | Clear explanation of the two independent comparisons | Vague | Missing | + +### Instructor Notes & Assembly + +- Both sites must be patched: `0x100001FC` for operator status and + `0x1000020A` for the automated dispatch decision. +- The correct immediate is `0x3B` (59), not `0x3C` (60); the source test is a + strict `<`, compiled as `<= 94`. +- Verify the byte changes on hardware: after patching, `operator_state` + (`0x20000844`) and `dispatch_state` (`0x20000834`) read `0` and `0`. +- **Ghidra ARM/Thumb Context Note:** In raw `.bin` files, patching an instruction + that precedes an `IT` block (`ite hi`) using the GUI *Patch Instruction* action + triggers Ghidra's `ReDisassembleCommand`. The re-disassembler encounters an + internal context register conflict when trying to re-declare the `ITBlock` + context over existing instructions, collapsing Thumb decoding into 32-bit ARM + mode and swallowing Site B (`0x1000020A`). Students must patch using the Bytes + window workflow (Clear `C` -> edit byte `5E` -> `3B` in Bytes window with pencil + icon -> Disassemble `D`) to keep Site B visible and cleanly aligned. + +--- + +## Task 3: Find and Patch Bug #2: The False Signal Banner (20 points) + +### Solution + +**Criterion 3.1: Locate the Banner String (5 points).** + +| String | Address | +|--------|---------| +| `"SIGNAL: NORMAL\r"` | `0x10003678` | +| `"NORMAL"` substring to patch | `0x10003680` | + +The string is loaded at call site `0x10000224` (`ldr r0, [pc, #92]` -> +`0x10003678`) and printed by `bl __wrap_puts` at `0x10000226`. It is printed +once at boot and never recomputed. + +**Criterion 3.2: Patch Six Characters (8 points).** +`NORMAL` and `DANGER` are both six ASCII characters, so the patch preserves the +string length. + +| Address Range | Original Bytes | Patched Bytes | +|---------------|----------------|---------------| +| `0x10003680` - `0x10003685` | `4E 4F 52 4D 41 4C` | `44 41 4E 47 45 52` | + +**Criterion 3.3: Character-by-Character Documentation (4 points).** + +| Address | Original Char | Original Byte | Patched Char | Patched Byte | +|---------|---------------|---------------|--------------|--------------| +| `0x10003680` | N | `4E` | D | `44` | +| `0x10003681` | O | `4F` | A | `41` | +| `0x10003682` | R | `52` | N | `4E` | +| `0x10003683` | M | `4D` | G | `47` | +| `0x10003684` | A | `41` | E | `45` | +| `0x10003685` | L | `4C` | R | `52` | + +**Criterion 3.4: Explain the Danger of a Hardcoded Status Word (3 points).** +The banner never consults the reading, so it reports a healthy line even while +the frozen reading is dangerous. Operators trust supervisory banners, so a +hardcoded `NORMAL` masks the hazard and prevents manual intervention. + +### Grading Rubric (1-to-1 Mapping) + +| Criterion | Points | Full Credit (Answer Key) | Partial Credit | No Credit | +|-----------|--------|--------------------------|----------------|-----------| +| Criterion 3.1: Locate the Banner String | 5 | Correct address | Approximate | Not found | +| Criterion 3.2: Patch Six Characters | 8 | All six bytes changed, length preserved | Correct text, wrong bytes documented | Wrong length | +| Criterion 3.3: Character-by-Character Documentation | 4 | Original vs patched byte for all six characters | Partial | Missing | +| Criterion 3.4: Explain the Danger of a Hardcoded Status Word | 3 | Clear, specific reasoning | Generic | Missing | + +### Instructor Notes & Assembly + +- `NORMAL` and `DANGER` are both six characters; the patch must not change the + string length or overwrite adjacent flash. +- The banner is printed once at boot and never recomputed, so it is a separate + defect from the threshold and must be graded independently. +- Confirm the patch covers `0x10003680` through `0x10003685` exactly. + +--- + +## Task 4: Recover the Quarantined Dispatch Frame (10 points) + +### Solution + +**Criterion 4.1: Recover the Dispatch Frame (6 points).** +Address `0x100037A0` in flash `.rodata`: + +```text +WORLDGRID:BLACKSTART:GRID-7:WATER-3 +``` + +**Criterion 4.2: Explain Why It Is Never Transmitted (4 points).** +`retain_dispatch_frame()` reads only the first character into a `volatile` local +so the linker keeps the string, but the pointer is never passed to any print or +UART routine. The frame is evidence only and must not be patched. + +### Grading Rubric (1-to-1 Mapping) + +| Criterion | Points | Full Credit (Answer Key) | Partial Credit | No Credit | +|-----------|--------|--------------------------|----------------|-----------| +| Criterion 4.1: Recover the Dispatch Frame | 6 | Correct address and full text | Partial text | Not found | +| Criterion 4.2: Explain Why It Is Never Transmitted | 4 | Clear static-analysis explanation | Vague | Missing | + +### Instructor Notes & Assembly + +- Accept the full string `WORLDGRID:BLACKSTART:GRID-7:WATER-3` at `0x100037A0` + in flash `.rodata`. +- The frame is evidence only. Penalize any submission that patches or rewrites + it instead of recovering it. + +--- + +## Task 5: Export and Verify (20 points) + +### Solution + +**Criterion 5.1: Export CTF-01_fixed.bin (4 points).** +Export the patched program from Ghidra (`File -> Export Program...`, `Binary +Format`) as `CTF-01_fixed.bin`. + +**Criterion 5.2: Convert to CTF-01_fixed.uf2 (4 points).** + +```bash +python uf2conv.py CTF-01_fixed.bin --base 0x10000000 --family 0xe48bff59 --output CTF-01_fixed.uf2 +``` + +**Criterion 5.3: Hardware Verification (8 points).** + +Before patching: + +```text +GLOBAL EMBEDDED RESPONSE NETWORK +BLACK START WINDOW: 27 MINUTES +UART0 115200 8N1 | AUTHORIZED LAB CONSOLE +SIGNAL: NORMAL +RESPONSE> GRID STATUS: STABLE +DISPATCH PATH: AUTHORIZED +LAST FRAME: QUARANTINED +RESPONSE> +``` + +After all three patches: + +```text +GLOBAL EMBEDDED RESPONSE NETWORK +BLACK START WINDOW: 27 MINUTES +UART0 115200 8N1 | AUTHORIZED LAB CONSOLE +SIGNAL: DANGER +RESPONSE> GRID STATUS: CRITICAL +DISPATCH PATH: HELD +LAST FRAME: QUARANTINED +RESPONSE> +``` + +If the console is not wired, the same proof is read over SWD: after reset, +`operator_state` (`0x20000844`) and `dispatch_state` (`0x20000834`) are `1` and +`1` for the shipped image, and `0` and `0` for the patched image. This has been +confirmed on real hardware. + +**Criterion 5.4: Summary Table of All Patches (4 points).** + +| # | What | Address(es) | Original | Patched | +|---|------|-------------|----------|---------| +| 1a | Operator threshold | `0x100001FC` | `5E 2B` | `3B 2B` | +| 1b | Dispatch threshold | `0x1000020A` | `5E 2B` | `3B 2B` | +| 2 | Signal banner | `0x10003680`-`0x10003685` | `4E 4F 52 4D 41 4C` | `44 41 4E 47 45 52` | + +Total: **8 bytes actually change** (one immediate byte at each compare site and +six string bytes). + +### Grading Rubric (1-to-1 Mapping) + +| Criterion | Points | Full Credit (Answer Key) | Partial Credit | No Credit | +|-----------|--------|--------------------------|----------------|-----------| +| Criterion 5.1: Export CTF-01_fixed.bin | 4 | Valid patched binary | Corrupted | Not submitted | +| Criterion 5.2: Convert to CTF-01_fixed.uf2 | 4 | Correct base and family flags | Wrong flags | Not submitted | +| Criterion 5.3: Hardware Verification | 8 | Corrected console output confirmed on hardware | Some lines corrected | No verification | +| Criterion 5.4: Summary Table of All Patches | 4 | Complete address and before/after table | Missing entries | No table | + +### Instructor Notes & Assembly + +- Verify the exported image with `python3 scripts/verify_ctf.py`; the shipped + check expects `11/11 checks passed` against `CTF-01.bin`. +- Confirm the UF2 conversion used base `0x10000000` and family `0xe48bff59`. +- **Serial Terminal Timing:** Note that `print_boot_banner()` (`SIGNAL: DANGER`) + fires within the first 5 milliseconds of boot. In normal lab usage, PuTTY + attaches after boot and will display the continuous 1-second status loop + (`GRID STATUS: CRITICAL`, `DISPATCH PATH: HELD`). To see `SIGNAL: DANGER`, + the student must pulse `RUN` to `GND` while PuTTY is open, or demonstrate + the string change at `0x10003680` via Ghidra static analysis. +- If no console is available, accept the SWD capture showing `operator_state` + and `dispatch_state` at `0` and `0` on the patched image. + +--- + +## Task 6: Written Reflection (5 points) + +### Solution + +**Criterion 6.1: "Rushed Build" Is Not an Excuse (2 points).** +The missed review step is exactly what shipped the false-safe report; pressure +explains why the safeguard was skipped, not why it should be skipped. + +**Criterion 6.2: One Engineering Practice per Bug (3 points).** +- Bug #1 (duplicated threshold): a unit test or static-analysis rule that + requires every comparison against `SAFE_THRESHOLD` to use one shared source of + truth. +- Bug #2 (hardcoded banner): a hardware-in-the-loop smoke test that checks the + boot banner against the latched reading. + +### Grading Rubric (1-to-1 Mapping) + +| Criterion | Points | Full Credit (Answer Key) | Partial Credit | No Credit | +|-----------|--------|--------------------------|----------------|-----------| +| Criterion 6.1: "Rushed Build" Is Not an Excuse | 2 | Specific, grounded reasoning | Generic | Missing | +| Criterion 6.2: One Engineering Practice per Bug | 3 | Concrete practice for each bug | One bug only | Missing | + +### Instructor Notes & Assembly + +- Grade the specificity of the reasoning, not the length of the prose. +- Require one concrete engineering practice for each of the two bugs. + +--- + +## How To Breadboard + +- **Raspberry Pi Pico 2** powered over USB. +- **3.3 V USB-UART adapter**: + - Adapter RX to Pico GP0 (UART0 TX) + - Adapter TX to Pico GP1 (UART0 RX) + - Adapter GND to Pico GND + - Do not connect the adapter VCC while the Pico is USB powered. +- **Serial monitor:** 115200 baud, 8 data bits, no parity, 1 stop bit. +- No other peripherals are required; all evidence is obtained from the console. + +--- + +## Complete Grading Summary + +| Task | Title | Points | +|------|-------|--------| +| Task 1 | Setup and Initial Analysis | 15 | +| Task 2 | Find and Patch Bug #1: The Miscalibrated Safety Threshold | 30 | +| Task 3 | Find and Patch Bug #2: The False Signal Banner | 20 | +| Task 4 | Recover the Quarantined Dispatch Frame | 10 | +| Task 5 | Export and Verify | 20 | +| Task 6 | Written Reflection | 5 | +| **TOTAL** | | **100** | + +--- + +## Instructor Notes + +Safety: Use only the supplied Pico 2, 3.3 V UART adapter, and firmware. Never +connect the exercise to an operational grid, water plant, public network, +military system, or third-party device. + +### Common Student Mistakes + +- Patching only one threshold site (`0x100001FC` or `0x1000020A`), which leaves + one status line lying. +- Assuming the immediate equals the limit, producing an off-by-one boundary; + the correct byte is `0x3B` (59), not `0x3C` (60). +- Replacing the banner string with a different length, corrupting adjacent + flash; `NORMAL` and `DANGER` are both 6 bytes. +- Treating the odd vector address `0x1000015B` as invalid instead of clearing + bit 0 to get `0x1000015A`. +- Modifying the quarantined dispatch frame at `0x100037A0`, which destroys + evidence. + +### Partial Credit Guidelines + +- Award partial credit for one correct threshold site out of two, or for a + correct immediate value without the `<` to `<=` reasoning. +- Award partial credit for a correct banner text with incorrectly documented + bytes, or for partial character-by-character documentation. +- Accept an SWD read of `operator_state` and `dispatch_state` as equivalent + proof when no UART console is available. +- Award no credit for patches that change string length or overwrite adjacent + flash. + +--- + +## Appendix: Expected Binary Diff + +| # | What | File Offset(s) | Address(es) | Original Bytes | Patched Bytes | +|---|------|----------------|-------------|----------------|---------------| +| 1a | Operator threshold | `0x1FC` | `0x100001FC` | `5E 2B` | `3B 2B` | +| 1b | Dispatch threshold | `0x20A` | `0x1000020A` | `5E 2B` | `3B 2B` | +| 2 | Signal banner | `0x3680` - `0x3685` | `0x10003680` - `0x10003685` | `4E 4F 52 4D 41 4C` | `44 41 4E 47 45 52` | + +Total: **8 bytes actually change** (one immediate byte at each compare site and +six string bytes). diff --git a/0x0001b_ctf/CTF-01-S.pdf b/0x0001b_ctf/CTF-01-S.pdf new file mode 100644 index 0000000..1180fbe Binary files /dev/null and b/0x0001b_ctf/CTF-01-S.pdf differ diff --git a/0x0001b_ctf/CTF-01.bin b/0x0001b_ctf/CTF-01.bin new file mode 100644 index 0000000..2a1de88 Binary files /dev/null and b/0x0001b_ctf/CTF-01.bin differ diff --git a/0x0001b_ctf/CTF-01.uf2 b/0x0001b_ctf/CTF-01.uf2 new file mode 100644 index 0000000..e0313c1 Binary files /dev/null and b/0x0001b_ctf/CTF-01.uf2 differ diff --git a/0x0001b_ctf/CTF-01_fixed.bin b/0x0001b_ctf/CTF-01_fixed.bin new file mode 100644 index 0000000..a9d582c Binary files /dev/null and b/0x0001b_ctf/CTF-01_fixed.bin differ diff --git a/0x0001b_ctf/CTF-01_fixed.uf2 b/0x0001b_ctf/CTF-01_fixed.uf2 new file mode 100644 index 0000000..7384d9a Binary files /dev/null and b/0x0001b_ctf/CTF-01_fixed.uf2 differ diff --git a/0x0001b_ctf/include/console.h b/0x0001b_ctf/include/console.h new file mode 100644 index 0000000..b5a6502 --- /dev/null +++ b/0x0001b_ctf/include/console.h @@ -0,0 +1,72 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: console.h +// Desc: Declares the UART console banner and status reporting interface +// for Operation Black Start. +// Created: 2026 + +#ifndef CONSOLE_H +#define CONSOLE_H + +#include "grid.h" +#include + +/** + * @brief Print the response controller's boot identity and unconditional signal line. + * + * Emits the boot banner, operation window, serial configuration banner, + * unconditional signal quality line, and the interactive command prompt. + * + * @param None. + * @return None. + */ +static inline void print_boot_banner(void) +{ + printf("GLOBAL EMBEDDED RESPONSE NETWORK\r\n"); + printf("BLACK START WINDOW: 27 MINUTES\r\n"); + printf("UART0 115200 8N1 | AUTHORIZED LAB CONSOLE\r\n"); + printf("SIGNAL: NORMAL\r\n"); + printf("RESPONSE> "); +} + +/** + * @brief Print the recurring grid classification and dispatch authorization report. + * + * Evaluates the current operator state and dispatch state flags and transmits + * the formatted status report over UART0 with the quarantined frame notification. + * + * @param None. + * @return None. + */ +static inline void print_status(void) +{ + printf("GRID STATUS: %s\r\n", operator_state ? "STABLE" : "CRITICAL"); + printf("DISPATCH PATH: %s\r\n", dispatch_state ? "AUTHORIZED" : "HELD"); + printf("LAST FRAME: QUARANTINED\r\n"); + printf("RESPONSE> "); +} + +#endif // CONSOLE_H diff --git a/0x0001b_ctf/include/grid.h b/0x0001b_ctf/include/grid.h new file mode 100644 index 0000000..c1444c8 --- /dev/null +++ b/0x0001b_ctf/include/grid.h @@ -0,0 +1,85 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: grid.h +// Desc: Declares the grid telemetry and safety evaluation interface for +// Operation Black Start. +// Created: 2026 + +#ifndef GRID_H +#define GRID_H + +#include + +// Safety margin (Hz deviation x100) below which grid frequency is tolerable. +// BUG: emergency build miscompiled this far too permissive; correct value is 60. +#define SAFE_THRESHOLD 95 + +// Frozen grid frequency deviation reading latched when comms were severed. +extern volatile uint32_t grid_deviation; + +// Operator-facing classification of the frozen reading (drives GRID STATUS). +extern volatile uint32_t operator_state; + +// Automated dispatch authorization decision (drives DISPATCH PATH). +extern volatile uint32_t dispatch_state; + +// Quarantined black-start authorization frame, retained in flash, never sent. +static volatile const char dispatch_frame[] = + "WORLDGRID:BLACKSTART:GRID-7:WATER-3"; + +static inline void retain_dispatch_frame(void) +{ + volatile char frame_marker = dispatch_frame[0]; + (void)frame_marker; +} + +static inline void evaluate_grid(void) +{ + operator_state = (grid_deviation < SAFE_THRESHOLD) ? 1 : 0; + dispatch_state = (grid_deviation < SAFE_THRESHOLD) ? 1 : 0; +} + +/** + * @brief Anchor the hidden dispatch frame in flash without transmitting it. + * + * Reads the first character of the quarantined black-start authorization + * frame into a volatile local marker to prevent the compiler from optimizing + * out the string literal from .rodata flash storage. + * + * @param None. + * @return None. + */ +/** + * @brief Classify the frozen telemetry reading against the compiled threshold. + * + * Evaluates the frozen grid deviation reading against SAFE_THRESHOLD twice, + * once for the operator-facing status line and once for the automated + * dispatch decision, mirroring the duplicated immediate comparison site. + * + * @param None. + * @return None. + */ +#endif // GRID_H diff --git a/0x0001b_ctf/pico_sdk_import.cmake b/0x0001b_ctf/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0001b_ctf/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0001b_ctf/scripts/verify_ctf.py b/0x0001b_ctf/scripts/verify_ctf.py new file mode 100644 index 0000000..357eff9 --- /dev/null +++ b/0x0001b_ctf/scripts/verify_ctf.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +"""Verify every technical claim of Operation Black Start against CTF-01.bin. + +Exits 0 only when every address, byte, and hash in CTF-R.md and CTF-S.md +matches the shipped image. +""" +import hashlib +import struct +import sys +from pathlib import Path + +BASE = 0x10000000 +ROOT = Path(__file__).resolve().parent.parent +BIN = ROOT / "CTF-01.bin" +UF2 = ROOT / "CTF-01.uf2" + +EXPECTED_BIN_SHA = "425591ac17ff4c22206286ee6f40b06a89523483804850a41854a9b6f89d7b70" +EXPECTED_UF2_SHA = "b1552ee3bb5763d96c65d8df1c86984ee842ef1a823fdf5d94132b1e10ff9d16" + +CMP_A = 0x100001FC +CMP_B = 0x1000020A +STRING_SIGNAL = 0x10003678 +STRING_FRAME = 0x100037A0 +LOOP_BACK = 0x10000266 + +RESULTS = [] + + +def check(label, ok, detail=""): + """Record one verification result. + + Parameters + ---------- + label : str + Human-readable check name. + ok : bool + Whether the check passed. + detail : str + Extra context printed with the result. + + Returns + ------- + None + """ + RESULTS.append(ok) + print(f"[{'PASS' if ok else 'FAIL'}] {label} {detail}") + + +def main(): + """Run all verification checks. + + Returns + ------- + int + Zero when every check passes, else one. + """ + data = BIN.read_bytes() + check("CTF-01.bin SHA-256", hashlib.sha256(data).hexdigest() == EXPECTED_BIN_SHA) + check("CTF-01.uf2 SHA-256", + hashlib.sha256(UF2.read_bytes()).hexdigest() == EXPECTED_UF2_SHA) + check("vector table", data[0:32].hex() == + "002008205b0100101b0100101d01001011010010110100101101001011010010") + check("initial SP", struct.unpack(" + +volatile uint32_t grid_deviation = 87; +volatile uint32_t operator_state = 0; +volatile uint32_t dispatch_state = 0; diff --git a/0x0001b_ctf/src/main.c b/0x0001b_ctf/src/main.c new file mode 100644 index 0000000..87671bd --- /dev/null +++ b/0x0001b_ctf/src/main.c @@ -0,0 +1,56 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: main.c +// Desc: Main entry point for the Operation Black Start CTF relay firmware. +// Created: 2026 + +#include "console.h" +#include "grid.h" +#include "pico/stdlib.h" + +/** + * @brief Run the recurring UART evidence stream for the isolated CTF image. + * + * Initializes the standard I/O subsystems, anchors the quarantined dispatch + * frame in flash memory, evaluates the frozen telemetry against the compiled + * safety threshold, prints the startup banner, and repeatedly emits the + * system status report every second. + * + * @param None. + * @return int Standard exit code (never reached in normal firmware execution). + */ +int main(void) +{ + stdio_init_all(); + retain_dispatch_frame(); + evaluate_grid(); + print_boot_banner(); + while (true) { + print_status(); + sleep_ms(1000); + } + return 0; +} diff --git a/0x0001b_ctf/uf2families.json b/0x0001b_ctf/uf2families.json new file mode 100644 index 0000000..91d99ad --- /dev/null +++ b/0x0001b_ctf/uf2families.json @@ -0,0 +1,22 @@ +[ + { + "short_name": "RP2040", + "id": "0xe48bff56", + "description": "Raspberry Pi RP2040" + }, + { + "short_name": "RP2350-ARM-S", + "id": "0xe48bff59", + "description": "Raspberry Pi RP2350, ARM, Secure" + }, + { + "short_name": "RP2350-ARM-NS", + "id": "0xe48bff5a", + "description": "Raspberry Pi RP2350, ARM, Non-Secure" + }, + { + "short_name": "RP2350-RISCV", + "id": "0xe48bff5b", + "description": "Raspberry Pi RP2350, RISC-V" + } +] diff --git a/0x0005_intro-to-variables/.gitignore b/0x0005_intro-to-variables/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0005_intro-to-variables/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0005_intro-to-variables/.vscode/c_cpp_properties.json b/0x0005_intro-to-variables/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/0x0005_intro-to-variables/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0005_intro-to-variables/.vscode/cmake-kits.json b/0x0005_intro-to-variables/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0005_intro-to-variables/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0005_intro-to-variables/.vscode/extensions.json b/0x0005_intro-to-variables/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0005_intro-to-variables/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0005_intro-to-variables/.vscode/launch.json b/0x0005_intro-to-variables/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0005_intro-to-variables/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0005_intro-to-variables/.vscode/settings.json b/0x0005_intro-to-variables/.vscode/settings.json new file mode 100644 index 0000000..cdb8e61 --- /dev/null +++ b/0x0005_intro-to-variables/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0005_intro-to-variables/.vscode/tasks.json b/0x0005_intro-to-variables/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x0005_intro-to-variables/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0005_intro-to-variables/0x0005_intro-to-variables.c b/0x0005_intro-to-variables/0x0005_intro-to-variables.c new file mode 100644 index 0000000..29f6b2d --- /dev/null +++ b/0x0005_intro-to-variables/0x0005_intro-to-variables.c @@ -0,0 +1,48 @@ +/** + * @file 0x0005_intro-to-variables.c + * @brief Introduction to variables: declare, assign, and print a uint8_t + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates declaring, initializing, and reassigning a uint8_t variable. + * Prints the variable value over UART in an infinite loop. + * + * Wiring: + * No external wiring required (USB serial). + */ + +#include +#include "pico/stdlib.h" + +int main(void) { + uint8_t age = 42; + age = 43; + stdio_init_all(); + while (true) { + printf("age: %d\r\n", age); + } +} diff --git a/0x0005_intro-to-variables/CMakeLists.txt b/0x0005_intro-to-variables/CMakeLists.txt new file mode 100644 index 0000000..673ff61 --- /dev/null +++ b/0x0005_intro-to-variables/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0005_intro-to-variables C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0005_intro-to-variables 0x0005_intro-to-variables.c ) + +pico_set_program_name(0x0005_intro-to-variables "0x0005_intro-to-variables") +pico_set_program_version(0x0005_intro-to-variables "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0005_intro-to-variables 1) +pico_enable_stdio_usb(0x0005_intro-to-variables 0) + +# Add the standard library to the build +target_link_libraries(0x0005_intro-to-variables + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0005_intro-to-variables PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0005_intro-to-variables) + diff --git a/0x0005_intro-to-variables/pico_sdk_import.cmake b/0x0005_intro-to-variables/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0005_intro-to-variables/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0008_uninitialized-variables-a/.gitignore b/0x0008_uninitialized-variables-a/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0008_uninitialized-variables-a/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0008_uninitialized-variables-a/.vscode/c_cpp_properties.json b/0x0008_uninitialized-variables-a/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..27d83bd --- /dev/null +++ b/0x0008_uninitialized-variables-a/.vscode/c_cpp_properties.json @@ -0,0 +1,21 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0008_uninitialized-variables-a/.vscode/cmake-kits.json b/0x0008_uninitialized-variables-a/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0008_uninitialized-variables-a/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0008_uninitialized-variables-a/.vscode/extensions.json b/0x0008_uninitialized-variables-a/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0008_uninitialized-variables-a/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0008_uninitialized-variables-a/.vscode/launch.json b/0x0008_uninitialized-variables-a/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0008_uninitialized-variables-a/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0008_uninitialized-variables-a/.vscode/settings.json b/0x0008_uninitialized-variables-a/.vscode/settings.json new file mode 100644 index 0000000..cdb8e61 --- /dev/null +++ b/0x0008_uninitialized-variables-a/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0008_uninitialized-variables-a/.vscode/tasks.json b/0x0008_uninitialized-variables-a/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x0008_uninitialized-variables-a/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0008_uninitialized-variables-a/0x0008_uninitialized-variables-a.c b/0x0008_uninitialized-variables-a/0x0008_uninitialized-variables-a.c new file mode 100644 index 0000000..9d2ddb3 --- /dev/null +++ b/0x0008_uninitialized-variables-a/0x0008_uninitialized-variables-a.c @@ -0,0 +1,53 @@ +/** + * @file 0x0008_uninitialized-variables-a.c + * @brief Blink LED using SDK gpio functions (no UART) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Blinks an LED on GPIO16 using the standard Pico SDK gpio_init / gpio_put + * functions. No UART output; pure GPIO blink demonstration. + * + * Wiring: + * GPIO16 -> LED anode (with current-limiting resistor to GND) + */ + +#include +#include "pico/stdlib.h" + +/** @brief GPIO pin number for the LED */ +#define LED_PIN 16 + +int main(void) { + gpio_init(LED_PIN); + gpio_set_dir(LED_PIN, GPIO_OUT); + while (true) { + gpio_put(LED_PIN, 1); + sleep_ms(500); + gpio_put(LED_PIN, 0); + sleep_ms(500); + } +} diff --git a/0x0008_uninitialized-variables-a/CMakeLists.txt b/0x0008_uninitialized-variables-a/CMakeLists.txt new file mode 100644 index 0000000..347375d --- /dev/null +++ b/0x0008_uninitialized-variables-a/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0008_uninitialized-variables-a C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0008_uninitialized-variables-a 0x0008_uninitialized-variables-a.c ) + +pico_set_program_name(0x0008_uninitialized-variables-a "0x0008_uninitialized-variables-a") +pico_set_program_version(0x0008_uninitialized-variables-a "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0008_uninitialized-variables-a 0) +pico_enable_stdio_usb(0x0008_uninitialized-variables-a 0) + +# Add the standard library to the build +target_link_libraries(0x0008_uninitialized-variables-a + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0008_uninitialized-variables-a PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0008_uninitialized-variables-a) + diff --git a/0x0008_uninitialized-variables-a/pico_sdk_import.cmake b/0x0008_uninitialized-variables-a/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0008_uninitialized-variables-a/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0008_uninitialized-variables-b/.gitignore b/0x0008_uninitialized-variables-b/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0008_uninitialized-variables-b/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0008_uninitialized-variables-b/.vscode/c_cpp_properties.json b/0x0008_uninitialized-variables-b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/0x0008_uninitialized-variables-b/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0008_uninitialized-variables-b/.vscode/cmake-kits.json b/0x0008_uninitialized-variables-b/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0008_uninitialized-variables-b/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0008_uninitialized-variables-b/.vscode/extensions.json b/0x0008_uninitialized-variables-b/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0008_uninitialized-variables-b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0008_uninitialized-variables-b/.vscode/launch.json b/0x0008_uninitialized-variables-b/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0008_uninitialized-variables-b/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0008_uninitialized-variables-b/.vscode/settings.json b/0x0008_uninitialized-variables-b/.vscode/settings.json new file mode 100644 index 0000000..cdb8e61 --- /dev/null +++ b/0x0008_uninitialized-variables-b/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0008_uninitialized-variables-b/.vscode/tasks.json b/0x0008_uninitialized-variables-b/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x0008_uninitialized-variables-b/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0008_uninitialized-variables-b/0x0008_uninitialized-variables-b.c b/0x0008_uninitialized-variables-b/0x0008_uninitialized-variables-b.c new file mode 100644 index 0000000..b42c544 --- /dev/null +++ b/0x0008_uninitialized-variables-b/0x0008_uninitialized-variables-b.c @@ -0,0 +1,80 @@ +/** + * @file 0x0008_uninitialized-variables-b.c + * @brief Blink LED using gpioc coprocessor bit-level functions + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Blinks an LED on GPIO16 using the RP2350 gpioc coprocessor bit-level + * functions (gpioc_bit_oe_put / gpioc_bit_out_put) instead of the standard + * gpio_init / gpio_put SDK calls. + * + * Wiring: + * GPIO16 -> LED anode (with current-limiting resistor to GND) + */ + +#include +#include "pico/stdlib.h" + +/** @brief GPIO pin number for the LED */ +#define LED_PIN 16 + +/** + * @brief Initialize GPIO16 for output using coprocessor bit functions + * + * @details Configures the pad for SIO, sets direction to input first, + * clears the output, selects SIO function, then enables output. + * + * @retval None + */ +static void gpio_coprocessor_init(void) { + gpio_set_dir(LED_PIN, GPIO_IN); + gpio_put(LED_PIN, 0); + gpio_set_function(LED_PIN, GPIO_FUNC_SIO); + gpioc_bit_oe_put(LED_PIN, GPIO_OUT); +} + +/** + * @brief Blink LED using coprocessor bit output functions + * + * @details Toggles the LED on and off with 500ms delays using + * gpioc_bit_out_put and sleep_us. + * + * @retval None + */ +static void blink_cycle(void) { + gpioc_bit_out_put(LED_PIN, 1); + sleep_us(500 * 1000ull); + gpioc_bit_out_put(LED_PIN, 0); + sleep_us(500 * 1000ull); +} + +int main(void) { + gpio_coprocessor_init(); + while (true) { + blink_cycle(); + } +} diff --git a/0x0008_uninitialized-variables-b/CMakeLists.txt b/0x0008_uninitialized-variables-b/CMakeLists.txt new file mode 100644 index 0000000..24d559f --- /dev/null +++ b/0x0008_uninitialized-variables-b/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0008_uninitialized-variables-b C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0008_uninitialized-variables-b 0x0008_uninitialized-variables-b.c ) + +pico_set_program_name(0x0008_uninitialized-variables-b "0x0008_uninitialized-variables-b") +pico_set_program_version(0x0008_uninitialized-variables-b "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0008_uninitialized-variables-b 0) +pico_enable_stdio_usb(0x0008_uninitialized-variables-b 0) + +# Add the standard library to the build +target_link_libraries(0x0008_uninitialized-variables-b + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0008_uninitialized-variables-b PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0008_uninitialized-variables-b) + diff --git a/0x0008_uninitialized-variables-b/pico_sdk_import.cmake b/0x0008_uninitialized-variables-b/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0008_uninitialized-variables-b/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0008_uninitialized-variables-c/.gitignore b/0x0008_uninitialized-variables-c/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0008_uninitialized-variables-c/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0008_uninitialized-variables-c/.vscode/c_cpp_properties.json b/0x0008_uninitialized-variables-c/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/0x0008_uninitialized-variables-c/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0008_uninitialized-variables-c/.vscode/cmake-kits.json b/0x0008_uninitialized-variables-c/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0008_uninitialized-variables-c/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0008_uninitialized-variables-c/.vscode/extensions.json b/0x0008_uninitialized-variables-c/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0008_uninitialized-variables-c/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0008_uninitialized-variables-c/.vscode/launch.json b/0x0008_uninitialized-variables-c/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0008_uninitialized-variables-c/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0008_uninitialized-variables-c/.vscode/settings.json b/0x0008_uninitialized-variables-c/.vscode/settings.json new file mode 100644 index 0000000..cdb8e61 --- /dev/null +++ b/0x0008_uninitialized-variables-c/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0008_uninitialized-variables-c/.vscode/tasks.json b/0x0008_uninitialized-variables-c/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x0008_uninitialized-variables-c/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0008_uninitialized-variables-c/0x0008_uninitialized-variables-c.c b/0x0008_uninitialized-variables-c/0x0008_uninitialized-variables-c.c new file mode 100644 index 0000000..40771de --- /dev/null +++ b/0x0008_uninitialized-variables-c/0x0008_uninitialized-variables-c.c @@ -0,0 +1,85 @@ +/** + * @file 0x0008_uninitialized-variables-c.c + * @brief Blink LED using direct register-level pad and IO bank configuration + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Blinks an LED on GPIO16 using direct register writes to PADS_BANK0 and + * IO_BANK0 for pad configuration, plus gpioc coprocessor bit-level functions + * for output enable and data. Demonstrates the register-level equivalent of + * gpio_init / gpio_set_function. + * + * Wiring: + * GPIO16 -> LED anode (with current-limiting resistor to GND) + */ + +#include +#include "pico/stdlib.h" + +/** @brief GPIO pin number for the LED */ +#define LED_PIN 16 + +/** + * @brief Configure pad and IO bank registers for GPIO16 as SIO output + * + * @details Sets PADS_BANK0 IE/OD bits, assigns FUNCSEL to SIO in IO_BANK0, + * clears the ISO bit, and enables output via the coprocessor. + * + * @retval None + */ +static void configure_pad_and_iobank(void) { + gpioc_bit_oe_put(LED_PIN, GPIO_OUT); + gpioc_bit_out_put(LED_PIN, 0); + hw_write_masked(&pads_bank0_hw->io[LED_PIN], + PADS_BANK0_GPIO0_IE_BITS, + PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS); + io_bank0_hw->io[LED_PIN].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB; + hw_clear_bits(&pads_bank0_hw->io[LED_PIN], PADS_BANK0_GPIO0_ISO_BITS); + gpioc_bit_oe_put(LED_PIN, GPIO_OUT); +} + +/** + * @brief Blink LED using coprocessor bit output functions + * + * @details Toggles the LED on and off with 500ms delays using + * gpioc_bit_out_put and sleep_us. + * + * @retval None + */ +static void blink_cycle(void) { + gpioc_bit_out_put(LED_PIN, 1); + sleep_us(500 * 1000ull); + gpioc_bit_out_put(LED_PIN, 0); + sleep_us(500 * 1000ull); +} + +int main(void) { + configure_pad_and_iobank(); + while (true) { + blink_cycle(); + } +} diff --git a/0x0008_uninitialized-variables-c/CMakeLists.txt b/0x0008_uninitialized-variables-c/CMakeLists.txt new file mode 100644 index 0000000..b7e1991 --- /dev/null +++ b/0x0008_uninitialized-variables-c/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0008_uninitialized-variables-c C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0008_uninitialized-variables-c 0x0008_uninitialized-variables-c.c ) + +pico_set_program_name(0x0008_uninitialized-variables-c "0x0008_uninitialized-variables-c") +pico_set_program_version(0x0008_uninitialized-variables-c "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0008_uninitialized-variables-c 0) +pico_enable_stdio_usb(0x0008_uninitialized-variables-c 0) + +# Add the standard library to the build +target_link_libraries(0x0008_uninitialized-variables-c + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0008_uninitialized-variables-c PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0008_uninitialized-variables-c) + diff --git a/0x0008_uninitialized-variables-c/pico_sdk_import.cmake b/0x0008_uninitialized-variables-c/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0008_uninitialized-variables-c/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0008_uninitialized-variables-d/.gitignore b/0x0008_uninitialized-variables-d/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0008_uninitialized-variables-d/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0008_uninitialized-variables-d/.vscode/c_cpp_properties.json b/0x0008_uninitialized-variables-d/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/0x0008_uninitialized-variables-d/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0008_uninitialized-variables-d/.vscode/cmake-kits.json b/0x0008_uninitialized-variables-d/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0008_uninitialized-variables-d/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0008_uninitialized-variables-d/.vscode/extensions.json b/0x0008_uninitialized-variables-d/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0008_uninitialized-variables-d/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0008_uninitialized-variables-d/.vscode/launch.json b/0x0008_uninitialized-variables-d/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0008_uninitialized-variables-d/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0008_uninitialized-variables-d/.vscode/settings.json b/0x0008_uninitialized-variables-d/.vscode/settings.json new file mode 100644 index 0000000..cdb8e61 --- /dev/null +++ b/0x0008_uninitialized-variables-d/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0008_uninitialized-variables-d/.vscode/tasks.json b/0x0008_uninitialized-variables-d/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x0008_uninitialized-variables-d/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0008_uninitialized-variables-d/0x0008_uninitialized-variables-d.c b/0x0008_uninitialized-variables-d/0x0008_uninitialized-variables-d.c new file mode 100644 index 0000000..b834115 --- /dev/null +++ b/0x0008_uninitialized-variables-d/0x0008_uninitialized-variables-d.c @@ -0,0 +1,144 @@ +/** + * @file 0x0008_uninitialized-variables-d.c + * @brief Blink LED using pico_default_asm_volatile coprocessor instructions + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Blinks an LED on GPIO16 using pico_default_asm_volatile to emit mcrr + * coprocessor instructions for GPIO output enable and data, plus inline + * assembly for pad configuration (hw_write_masked, IO_BANK0 FUNCSEL, + * hw_clear_bits equivalents). + * + * Wiring: + * GPIO16 -> LED anode (with current-limiting resistor to GND) + */ + +#include +#include "pico/stdlib.h" + +/** @brief GPIO pin number for the LED */ +#define LED_PIN 16 + +/** + * @brief Enable output and set initial coprocessor OE/OUT via mcrr + * + * @details Issues two mcrr p0 instructions: one to enable output direction, + * one to set the initial output state. + * + * @retval None + */ +static void asm_set_oe_and_out(void) { + pico_default_asm_volatile ("mcrr p0, #4, %0, %1, c4" : : "r" (LED_PIN), "r" (GPIO_OUT)); + pico_default_asm_volatile ("mcrr p0, #4, %0, %1, c4" : : "r" (LED_PIN), "r" (GPIO_OUT)); +} + +/** + * @brief Configure PADS_BANK0 IE/OD bits via inline assembly hw_xor_bits + * + * @details Performs a read-modify-write on the pad register to set IE + * and clear OD, replicating hw_write_masked behavior. + * + * @retval None + */ +static void asm_configure_pad(void) { + pico_default_asm_volatile ( + "ldr r2, [%0]\n" // load current pad register + "eor r2, r2, %1\n" // xor with IE bit + "and r2, r2, %2\n" // mask with (IE|OD) + "eor r2, r2, %1\n" // recombine (hw_xor_bits logic) + "str r2, [%0]\n" // write back + : + : "r" (&pads_bank0_hw->io[LED_PIN]), + "r" (PADS_BANK0_GPIO0_IE_BITS), + "r" (PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS) + : "r2", "memory" + ); +} + +/** + * @brief Set IO_BANK0 FUNCSEL to SIO via inline assembly store + * + * @details Writes the SIO function select value directly to the + * IO_BANK0 GPIO control register. + * + * @retval None + */ +static void asm_set_funcsel(void) { + pico_default_asm_volatile ( + "str %1, [%0]\n" + : + : "r" (&io_bank0_hw->io[LED_PIN].ctrl), + "r" (GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB) + : "memory" + ); +} + +/** + * @brief Clear ISO bits in PADS_BANK0 via inline assembly + * + * @details Performs a read-modify-write to clear the pad isolation + * bit, un-isolating the pad for normal operation. + * + * @retval None + */ +static void asm_clear_iso(void) { + pico_default_asm_volatile ( + "ldr r2, [%0]\n" // load current register value + "bic r2, r2, %1\n" // clear the ISO bits (bit clear) + "str r2, [%0]\n" // write back + : + : "r" (&pads_bank0_hw->io[LED_PIN]), + "r" (PADS_BANK0_GPIO0_ISO_BITS) + : "r2", "memory" + ); +} + +/** + * @brief Blink LED using coprocessor mcrr output instructions + * + * @details Toggles the LED on and off with 500ms delays using + * pico_default_asm_volatile mcrr instructions. + * + * @retval None + */ +static void asm_blink_cycle(void) { + pico_default_asm_volatile ("mcrr p0, #4, %0, %1, c0" : : "r" (LED_PIN), "r" (1)); + sleep_us(500 * 1000ull); + pico_default_asm_volatile ("mcrr p0, #4, %0, %1, c0" : : "r" (LED_PIN), "r" (0)); + sleep_us(500 * 1000ull); +} + +int main(void) { + asm_set_oe_and_out(); + asm_configure_pad(); + asm_set_funcsel(); + asm_clear_iso(); + pico_default_asm_volatile ("mcrr p0, #4, %0, %1, c4" : : "r" (LED_PIN), "r" (GPIO_OUT)); + while (true) { + asm_blink_cycle(); + } +} diff --git a/0x0008_uninitialized-variables-d/CMakeLists.txt b/0x0008_uninitialized-variables-d/CMakeLists.txt new file mode 100644 index 0000000..9e88754 --- /dev/null +++ b/0x0008_uninitialized-variables-d/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0008_uninitialized-variables-d C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0008_uninitialized-variables-d 0x0008_uninitialized-variables-d.c ) + +pico_set_program_name(0x0008_uninitialized-variables-d "0x0008_uninitialized-variables-d") +pico_set_program_version(0x0008_uninitialized-variables-d "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0008_uninitialized-variables-d 0) +pico_enable_stdio_usb(0x0008_uninitialized-variables-d 0) + +# Add the standard library to the build +target_link_libraries(0x0008_uninitialized-variables-d + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0008_uninitialized-variables-d PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0008_uninitialized-variables-d) + diff --git a/0x0008_uninitialized-variables-d/pico_sdk_import.cmake b/0x0008_uninitialized-variables-d/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0008_uninitialized-variables-d/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0008_uninitialized-variables-e/.gitignore b/0x0008_uninitialized-variables-e/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0008_uninitialized-variables-e/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0008_uninitialized-variables-e/.vscode/c_cpp_properties.json b/0x0008_uninitialized-variables-e/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/0x0008_uninitialized-variables-e/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0008_uninitialized-variables-e/.vscode/cmake-kits.json b/0x0008_uninitialized-variables-e/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0008_uninitialized-variables-e/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0008_uninitialized-variables-e/.vscode/extensions.json b/0x0008_uninitialized-variables-e/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0008_uninitialized-variables-e/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0008_uninitialized-variables-e/.vscode/launch.json b/0x0008_uninitialized-variables-e/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0008_uninitialized-variables-e/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0008_uninitialized-variables-e/.vscode/settings.json b/0x0008_uninitialized-variables-e/.vscode/settings.json new file mode 100644 index 0000000..cdb8e61 --- /dev/null +++ b/0x0008_uninitialized-variables-e/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0008_uninitialized-variables-e/.vscode/tasks.json b/0x0008_uninitialized-variables-e/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x0008_uninitialized-variables-e/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0008_uninitialized-variables-e/0x0008_uninitialized-variables-e.c b/0x0008_uninitialized-variables-e/0x0008_uninitialized-variables-e.c new file mode 100644 index 0000000..ac572c8 --- /dev/null +++ b/0x0008_uninitialized-variables-e/0x0008_uninitialized-variables-e.c @@ -0,0 +1,107 @@ +/** + * @file 0x0008_uninitialized-variables-e.c + * @brief Blink LED using pure inline ARM assembly with direct register addresses + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Blinks an LED on GPIO16 using pure inline ARM assembly. No SDK calls, no + * includes. Configures PADS_BANK0, IO_BANK0, and GPIO coprocessor via direct + * register addresses and mcrr instructions, with a busy-wait delay loop. + * + * Wiring: + * GPIO16 -> LED anode (with current-limiting resistor to GND) + */ + +int main(void) { + __asm__ volatile ( + // gpio_init(LED_PIN); + /// gpio_set_dir(LED_PIN, GPIO_IN); + //// gpioc_bit_oe_put(LED_PIN, GPIO_OUT); + "movs r4, #0x10\n" // GPIO16 + "movs r5, #0x01\n" // bit 1; used for OUT/OE writes + "mcrr p0, #4, r4, r5, c4\n" // gpioc_bit_oe_put(16, 1); p102 + + // gpio_set_function(LED_PIN, GPIO_FUNC_SIO); + /// hw_write_masked(&pads_bank0_hw->io[LED_PIN], + /// PADS_BANK0_GPIO0_IE_BITS, + /// PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS + /// ); + //// hw_xor_bits(addr, (*addr ^ values) & write_mask); + "ldr r3, =0x40038044\n" // &pads_bank0_hw->io[16]; p785, p796 + "ldr r2, [r3]\n" // load current config + "bic r2, r2, #0x80\n" // clear OD; output disable + "orr r2, r2, #0x40\n" // set IE; enable input buffer + "str r2, [r3]\n" // store updated config + /// io_bank0_hw->io[LED_PIN].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB; + "ldr r3, =0x40028084\n" // &io_bank0_hw->io[16].ctrl; p603, p637 + "ldr r2, [r3]\n" // load current config + "bic r2, r2, #0x1f\n" // clear FUNCSEL bits [4:0] + "orr r2, r2, #5\n" // set FUNCSEL = 5 (SIO) + "str r2, [r3]\n" // store updated config + /// hw_clear_bits(&pads_bank0_hw->io[gpio], PADS_BANK0_GPIO0_ISO_BITS); + "ldr r3, =0x40038044\n" // &pads_bank0_hw->io[16]; p785, p796 + "ldr r2, [r3]\n" // load current config + "bic r2, r2, #0x100\n" // clear ISO bit (bit 8) un‑isolate pad + "str r2, [r3]\n" // store updated config + + // gpio_set_dir(LED_PIN, GPIO_OUT); + /// gpioc_bit_oe_put(LED_PIN, GPIO_OUT); + "movs r4, #0x10\n" // GPIO16 + "movs r5, #0x01\n" // bit 1; used for OUT/OE writes + "mcrr p0, #4, r4, r5, c4\n" // gpioc_bit_oe_put(16, 1); p102 + + // while (true) + "1:\n" // loop start + + // gpio_put(LED_PIN, 1); + /// gpioc_bit_out_put(LED_PIN, 1); + "movs r4, #0x10\n" // GPIO16 + "movs r5, #0x01\n" // bit 1; used for OUT/OE writes + "mcrr p0, #4, r4, r5, c0\n" // gpioc_bit_out_put(16, 1) + // sleep_ms(500); + /// sleep_us(500 * 1000ull); + "ldr r2, =0x17D7840\n" // r2 = ~8.4M cycles + "2:\n" // delay loop + "subs r2, r2, #1\n" // decrement counter + "bne 2b\n" // repeat until zero + + // gpio_put(LED_PIN, 1); + /// gpioc_bit_out_put(LED_PIN, 1); + "movs r4, #0x10\n" // GPIO16 + "movs r5, #0x00\n" // bit 0; used for OUT/OE writes + "mcrr p0, #4, r4, r5, c0\n" // gpioc_bit_out_put(16, 0) + // sleep_ms(500); + /// sleep_us(500 * 1000ull); + "ldr r2, =0x17D7840\n" // r2 = ~8.4M cycles + "3:\n" // delay loop + "subs r2, r2, #1\n" // decrement counter + "bne 3b\n" // repeat until zero + + // jmp + "b 1b\n" // repeat forever + ); +} diff --git a/0x0008_uninitialized-variables-e/CMakeLists.txt b/0x0008_uninitialized-variables-e/CMakeLists.txt new file mode 100644 index 0000000..bfc1c28 --- /dev/null +++ b/0x0008_uninitialized-variables-e/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0008_uninitialized-variables-e C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0008_uninitialized-variables-e 0x0008_uninitialized-variables-e.c ) + +pico_set_program_name(0x0008_uninitialized-variables-e "0x0008_uninitialized-variables-e") +pico_set_program_version(0x0008_uninitialized-variables-e "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0008_uninitialized-variables-e 0) +pico_enable_stdio_usb(0x0008_uninitialized-variables-e 0) + +# Add the standard library to the build +target_link_libraries(0x0008_uninitialized-variables-e + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0008_uninitialized-variables-e PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0008_uninitialized-variables-e) + diff --git a/0x0008_uninitialized-variables-e/pico_sdk_import.cmake b/0x0008_uninitialized-variables-e/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0008_uninitialized-variables-e/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0008_uninitialized-variables/.gitignore b/0x0008_uninitialized-variables/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0008_uninitialized-variables/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0008_uninitialized-variables/.vscode/c_cpp_properties.json b/0x0008_uninitialized-variables/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/0x0008_uninitialized-variables/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0008_uninitialized-variables/.vscode/cmake-kits.json b/0x0008_uninitialized-variables/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0008_uninitialized-variables/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0008_uninitialized-variables/.vscode/extensions.json b/0x0008_uninitialized-variables/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0008_uninitialized-variables/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0008_uninitialized-variables/.vscode/launch.json b/0x0008_uninitialized-variables/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0008_uninitialized-variables/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0008_uninitialized-variables/.vscode/settings.json b/0x0008_uninitialized-variables/.vscode/settings.json new file mode 100644 index 0000000..4e5d7b6 --- /dev/null +++ b/0x0008_uninitialized-variables/.vscode/settings.json @@ -0,0 +1,45 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "addressmap.h": "c", + "resets.h": "c", + "pads_bank0.h": "c" + } +} diff --git a/0x0008_uninitialized-variables/.vscode/tasks.json b/0x0008_uninitialized-variables/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x0008_uninitialized-variables/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0008_uninitialized-variables/0x0008_uninitialized-variables.c b/0x0008_uninitialized-variables/0x0008_uninitialized-variables.c new file mode 100644 index 0000000..8e9dc42 --- /dev/null +++ b/0x0008_uninitialized-variables/0x0008_uninitialized-variables.c @@ -0,0 +1,68 @@ +/** + * @file 0x0008_uninitialized-variables.c + * @brief Uninitialized variables: demonstrate undefined behavior with printf + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates the danger of uninitialized variables. Prints the value of an + * uninitialized uint8_t over UART while blinking an LED on GPIO16. + * + * Wiring: + * GPIO16 -> LED anode (with current-limiting resistor to GND) + */ + +#include +#include "pico/stdlib.h" + +/** @brief GPIO pin number for the LED */ +#define LED_PIN 16 + +/** + * @brief Toggle LED and print uninitialized variable value + * + * @details Blinks the LED on and off with a 500ms delay between each + * transition and prints the age variable each cycle. + * + * @param age value to print (uninitialized in this demo) + */ +static void blink_and_print(uint8_t age) { + printf("age: %d\r\n", age); + gpio_put(LED_PIN, 1); + sleep_ms(500); + gpio_put(LED_PIN, 0); + sleep_ms(500); +} + +int main(void) { + uint8_t age; + stdio_init_all(); + gpio_init(LED_PIN); + gpio_set_dir(LED_PIN, GPIO_OUT); + while (true) { + blink_and_print(age); + } +} diff --git a/0x0008_uninitialized-variables/CMakeLists.txt b/0x0008_uninitialized-variables/CMakeLists.txt new file mode 100644 index 0000000..c2ac591 --- /dev/null +++ b/0x0008_uninitialized-variables/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0008_uninitialized-variables C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0008_uninitialized-variables 0x0008_uninitialized-variables.c ) + +pico_set_program_name(0x0008_uninitialized-variables "0x0008_uninitialized-variables") +pico_set_program_version(0x0008_uninitialized-variables "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0008_uninitialized-variables 1) +pico_enable_stdio_usb(0x0008_uninitialized-variables 0) + +# Add the standard library to the build +target_link_libraries(0x0008_uninitialized-variables + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0008_uninitialized-variables PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0008_uninitialized-variables) + diff --git a/0x0008_uninitialized-variables/pico_sdk_import.cmake b/0x0008_uninitialized-variables/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0008_uninitialized-variables/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x000b_integer-data-type/.gitignore b/0x000b_integer-data-type/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x000b_integer-data-type/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x000b_integer-data-type/.vscode/c_cpp_properties.json b/0x000b_integer-data-type/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/0x000b_integer-data-type/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x000b_integer-data-type/.vscode/cmake-kits.json b/0x000b_integer-data-type/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x000b_integer-data-type/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x000b_integer-data-type/.vscode/extensions.json b/0x000b_integer-data-type/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x000b_integer-data-type/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x000b_integer-data-type/.vscode/launch.json b/0x000b_integer-data-type/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x000b_integer-data-type/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x000b_integer-data-type/.vscode/settings.json b/0x000b_integer-data-type/.vscode/settings.json new file mode 100644 index 0000000..cdb8e61 --- /dev/null +++ b/0x000b_integer-data-type/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x000b_integer-data-type/.vscode/tasks.json b/0x000b_integer-data-type/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x000b_integer-data-type/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x000b_integer-data-type/0x000b_integer-data-type.c b/0x000b_integer-data-type/0x000b_integer-data-type.c new file mode 100644 index 0000000..c923449 --- /dev/null +++ b/0x000b_integer-data-type/0x000b_integer-data-type.c @@ -0,0 +1,132 @@ +/** + * @file 0x000b_integer-data-type.c + * @brief Integer data types: cycle LEDs on GPIO16-18 with asm, print int values + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates uint8_t and int8_t integer data types. Initializes GPIO16-18 + * via inline assembly (PADS_BANK0, IO_BANK0, coprocessor OE), then cycles + * LEDs using coprocessor mcrr instructions while printing integer values. + * + * Wiring: + * GPIO16 -> LED1 anode (with current-limiting resistor to GND) + * GPIO17 -> LED2 anode (with current-limiting resistor to GND) + * GPIO18 -> LED3 anode (with current-limiting resistor to GND) + */ + +#include +#include "pico/stdlib.h" + +/** + * @brief Initialize GPIO16-18 as SIO outputs via inline assembly loop + * + * @details Configures PADS_BANK0 (clear OD+ISO, set IE), IO_BANK0 + * (FUNCSEL=5 SIO), and coprocessor OE for pins 16-18. + * + * @retval None + */ +static void asm_init_gpio_range(void) { + __asm volatile ( + "ldr r3, =0x40038000\n" // address of PADS_BANK0_BASE + "ldr r2, =0x40028004\n" // address of IO_BANK0 GPIO0.ctrl + "movs r0, #16\n" // GPIO16 (start pin) + "init_loop:\n" // loop start + "lsls r1, r0, #2\n" // pin * 4 (pad offset) + "adds r4, r3, r1\n" // PADS base + offset + "ldr r5, [r4]\n" // load current config + "bic r5, r5, #0x180\n" // clear OD+ISO + "orr r5, r5, #0x40\n" // set IE + "str r5, [r4]\n" // store updated config + "lsls r1, r0, #3\n" // pin * 8 (ctrl offset) + "adds r4, r2, r1\n" // IO_BANK0 base + offset + "ldr r5, [r4]\n" // load current config + "bic r5, r5, #0x1f\n" // clear FUNCSEL bits [4:0] + "orr r5, r5, #5\n" // set FUNCSEL = 5 (SIO) + "str r5, [r4]\n" // store updated config + "mov r4, r0\n" // pin + "movs r5, #1\n" // bit 1; used for OUT/OE writes + "mcrr p0, #4, r4, r5, c4\n" // gpioc_bit_oe_put(pin,1) + "adds r0, r0, #1\n" // increment pin + "cmp r0, #20\n" // stop after pin 18 + "blt init_loop\n" // loop until r0 == 20 + ); +} + +/** + * @brief Blink a single pin using coprocessor mcrr instructions + * + * @details Turns the specified pin on for 500ms, then off for 500ms + * using inline asm mcrr coprocessor output instructions. + * + * @param pin GPIO pin number to blink + * @retval None + */ +static void asm_blink_pin(uint8_t pin) { + __asm volatile ( + "mov r4, %0\n" + "movs r5, #0x01\n" + "mcrr p0, #4, r4, r5, c0\n" + : : "r"(pin) : "r4", "r5" + ); + sleep_ms(500); + __asm volatile ( + "mov r4, %0\n" + "movs r5, #0\n" + "mcrr p0, #4, r4, r5, c0\n" + : : "r"(pin) : "r4", "r5" + ); + sleep_ms(500); +} + +/** + * @brief Blink LED, advance pin, and print age/range + * + * @details Blinks the current pin, wraps pin 16-18, + * and prints both integer variables. + * + * @param pin pointer to the current GPIO pin number + * @param age unsigned 8-bit age value + * @param range signed 8-bit range value + * @retval None + */ +static void blink_and_print(uint8_t *pin, uint8_t age, int8_t range) { + asm_blink_pin(*pin); + *pin = (*pin > 18) ? 16 : *pin + 1; + printf("age: %d\r\n", age); + printf("range: %d\r\n", range); +} + +int main(void) { + uint8_t age = 43; + int8_t range = -42; + stdio_init_all(); + asm_init_gpio_range(); + uint8_t pin = 16; + while (1) { + blink_and_print(&pin, age, range); + } +} diff --git a/0x000b_integer-data-type/CMakeLists.txt b/0x000b_integer-data-type/CMakeLists.txt new file mode 100644 index 0000000..6178a2a --- /dev/null +++ b/0x000b_integer-data-type/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x000b_integer-data-type C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x000b_integer-data-type 0x000b_integer-data-type.c ) + +pico_set_program_name(0x000b_integer-data-type "0x000b_integer-data-type") +pico_set_program_version(0x000b_integer-data-type "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x000b_integer-data-type 1) +pico_enable_stdio_usb(0x000b_integer-data-type 0) + +# Add the standard library to the build +target_link_libraries(0x000b_integer-data-type + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x000b_integer-data-type PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x000b_integer-data-type) + diff --git a/0x000b_integer-data-type/pico_sdk_import.cmake b/0x000b_integer-data-type/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x000b_integer-data-type/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x000e_floating-point-data-type/.gitignore b/0x000e_floating-point-data-type/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x000e_floating-point-data-type/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x000e_floating-point-data-type/.vscode/c_cpp_properties.json b/0x000e_floating-point-data-type/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/0x000e_floating-point-data-type/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x000e_floating-point-data-type/.vscode/cmake-kits.json b/0x000e_floating-point-data-type/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x000e_floating-point-data-type/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x000e_floating-point-data-type/.vscode/extensions.json b/0x000e_floating-point-data-type/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x000e_floating-point-data-type/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x000e_floating-point-data-type/.vscode/launch.json b/0x000e_floating-point-data-type/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x000e_floating-point-data-type/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x000e_floating-point-data-type/.vscode/settings.json b/0x000e_floating-point-data-type/.vscode/settings.json new file mode 100644 index 0000000..cdb8e61 --- /dev/null +++ b/0x000e_floating-point-data-type/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x000e_floating-point-data-type/.vscode/tasks.json b/0x000e_floating-point-data-type/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x000e_floating-point-data-type/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x000e_floating-point-data-type/0x000e_floating-point-data-type.c b/0x000e_floating-point-data-type/0x000e_floating-point-data-type.c new file mode 100644 index 0000000..f0a3bac --- /dev/null +++ b/0x000e_floating-point-data-type/0x000e_floating-point-data-type.c @@ -0,0 +1,47 @@ +/** + * @file 0x000e_floating-point-data-type.c + * @brief Floating-point data type: print a float value over UART + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates the float data type on the Raspberry Pi Pico 2. Initializes + * a float variable and prints its value over UART in an infinite loop. + * + * Wiring: + * No external wiring required (USB serial). + */ + +#include +#include "pico/stdlib.h" + +int main(void) { + float fav_num = 42.5; + stdio_init_all(); + while (true) { + printf("fav_num: %f\r\n", fav_num); + } +} diff --git a/0x000e_floating-point-data-type/CMakeLists.txt b/0x000e_floating-point-data-type/CMakeLists.txt new file mode 100644 index 0000000..72041a0 --- /dev/null +++ b/0x000e_floating-point-data-type/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x000e_floating-point-data-type C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x000e_floating-point-data-type 0x000e_floating-point-data-type.c ) + +pico_set_program_name(0x000e_floating-point-data-type "0x000e_floating-point-data-type") +pico_set_program_version(0x000e_floating-point-data-type "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x000e_floating-point-data-type 1) +pico_enable_stdio_usb(0x000e_floating-point-data-type 0) + +# Add the standard library to the build +target_link_libraries(0x000e_floating-point-data-type + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x000e_floating-point-data-type PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x000e_floating-point-data-type) + diff --git a/0x000e_floating-point-data-type/pico_sdk_import.cmake b/0x000e_floating-point-data-type/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x000e_floating-point-data-type/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0011_double-floating-point-data-type/.gitignore b/0x0011_double-floating-point-data-type/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0011_double-floating-point-data-type/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0011_double-floating-point-data-type/.vscode/c_cpp_properties.json b/0x0011_double-floating-point-data-type/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c429d6d --- /dev/null +++ b/0x0011_double-floating-point-data-type/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0011_double-floating-point-data-type/.vscode/cmake-kits.json b/0x0011_double-floating-point-data-type/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0011_double-floating-point-data-type/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0011_double-floating-point-data-type/.vscode/extensions.json b/0x0011_double-floating-point-data-type/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0011_double-floating-point-data-type/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0011_double-floating-point-data-type/.vscode/launch.json b/0x0011_double-floating-point-data-type/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0011_double-floating-point-data-type/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0011_double-floating-point-data-type/.vscode/settings.json b/0x0011_double-floating-point-data-type/.vscode/settings.json new file mode 100644 index 0000000..95be83b --- /dev/null +++ b/0x0011_double-floating-point-data-type/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0011_double-floating-point-data-type/.vscode/tasks.json b/0x0011_double-floating-point-data-type/.vscode/tasks.json new file mode 100644 index 0000000..2359f7f --- /dev/null +++ b/0x0011_double-floating-point-data-type/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0011_double-floating-point-data-type/0x0011_double-floating-point-data-type.c b/0x0011_double-floating-point-data-type/0x0011_double-floating-point-data-type.c new file mode 100644 index 0000000..753f51d --- /dev/null +++ b/0x0011_double-floating-point-data-type/0x0011_double-floating-point-data-type.c @@ -0,0 +1,47 @@ +/** + * @file 0x0011_double-floating-point-data-type.c + * @brief Double floating-point data type: print a double value over UART + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates the double data type on the Raspberry Pi Pico 2. Initializes + * a double variable and prints its value over UART in an infinite loop. + * + * Wiring: + * No external wiring required (USB serial). + */ + +#include +#include "pico/stdlib.h" + +int main(void) { + double fav_num = 42.52525; + stdio_init_all(); + while (true) { + printf("fav_num: %lf\r\n", fav_num); + } +} diff --git a/0x0011_double-floating-point-data-type/CMakeLists.txt b/0x0011_double-floating-point-data-type/CMakeLists.txt new file mode 100644 index 0000000..035b92e --- /dev/null +++ b/0x0011_double-floating-point-data-type/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0011_double-floating-point-data-type C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0011_double-floating-point-data-type 0x0011_double-floating-point-data-type.c ) + +pico_set_program_name(0x0011_double-floating-point-data-type "0x0011_double-floating-point-data-type") +pico_set_program_version(0x0011_double-floating-point-data-type "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0011_double-floating-point-data-type 1) +pico_enable_stdio_usb(0x0011_double-floating-point-data-type 0) + +# Add the standard library to the build +target_link_libraries(0x0011_double-floating-point-data-type + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0011_double-floating-point-data-type PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0011_double-floating-point-data-type) + diff --git a/0x0011_double-floating-point-data-type/pico_sdk_import.cmake b/0x0011_double-floating-point-data-type/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0011_double-floating-point-data-type/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0011a_cb/.gitignore b/0x0011a_cb/.gitignore new file mode 100644 index 0000000..7bc658f --- /dev/null +++ b/0x0011a_cb/.gitignore @@ -0,0 +1,4 @@ +build/ +.DS_Store +*.swp +*~ diff --git a/0x0011a_cb/0x0011a_cb.bin b/0x0011a_cb/0x0011a_cb.bin new file mode 100755 index 0000000..65ecf0b Binary files /dev/null and b/0x0011a_cb/0x0011a_cb.bin differ diff --git a/0x0011a_cb/0x0011a_cb.uf2 b/0x0011a_cb/0x0011a_cb.uf2 new file mode 100644 index 0000000..e356615 Binary files /dev/null and b/0x0011a_cb/0x0011a_cb.uf2 differ diff --git a/0x0011a_cb/0x0011a_cb_patched.bin b/0x0011a_cb/0x0011a_cb_patched.bin new file mode 100644 index 0000000..d8a3de5 Binary files /dev/null and b/0x0011a_cb/0x0011a_cb_patched.bin differ diff --git a/0x0011a_cb/0x0011a_cb_patched.uf2 b/0x0011a_cb/0x0011a_cb_patched.uf2 new file mode 100644 index 0000000..daf3c41 Binary files /dev/null and b/0x0011a_cb/0x0011a_cb_patched.uf2 differ diff --git a/0x0011a_cb/CLASSIFIED-BRIEF-0x01.md b/0x0011a_cb/CLASSIFIED-BRIEF-0x01.md new file mode 100644 index 0000000..335075b --- /dev/null +++ b/0x0011a_cb/CLASSIFIED-BRIEF-0x01.md @@ -0,0 +1,112 @@ +# Operation Dark Vector: Classified Intelligence Briefing + +``` ++-----------------------------------------------------------------+ +| TOP SECRET // NOFORN | ++-----------------------------------------------------------------+ +| | +|OPERATION DARK VECTOR | +| | +|CLASSIFIED BRIEFING: LIVE CTF 0x01 | +| | +|NATIONAL SECURITY AGENCY / GMU RHET | ++-----------------------------------------------------------------+ +``` + +## 1. The Answer + +INT. NSA EXPLOITATION CELL. 05:03. + +The analysts have been awake for nineteen hours. The Director walks in with a cup +of coffee and does not sit down. + +**DIRECTOR:** Where is it going? + +**ANALYST:** Buddy says it knows. + +**DIRECTOR:** Buddy. + +**ANALYST:** The model. The one we built for exactly this. Give it the image, ask +it the question. + +**DIRECTOR:** And? + +**ANALYST:** Nine seconds. + +She turns the screen around. This is what came back. + +``` ++-----------------------------------------------------------------+ +| BUDDY // CLASSIFIED EXPLOITATION ASSIST // CONF: 0.97 | ++-----------------------------------------------------------------+ +| DESTINATION : +38.840280 -77.428890 | +| CONFIDENCE : 0.97 | +| PATCH : verified-by-construction. Ready to flash. | ++-----------------------------------------------------------------+ +``` + +**DIRECTOR:** That is the whole answer. + +**ANALYST:** That is the whole answer. + +**DIRECTOR:** Is it that simple? + +**ANALYST:** It is never that simple. + +**DIRECTOR:** Then why does it look that simple? + +**ANALYST:** Because Buddy is very good at making things look simple. + +## 2. What Buddy Is + +Buddy is not a person. It is a model. It has read more assembly than every +engineer who has ever lived, and it never sleeps and never doubts. You ask it a +question, and it answers in the exact shape of an answer, with a confidence that +is hard to argue with. + +That confidence is the problem. It is not the same thing as being right. + +Buddy has never seen this drone. It has never stood at this bench or watched this +board power on. It has a picture of the firmware, and it has made a very good +guess. A very good guess is still a guess. + +## 3. The Operation + +At 04:17 the woods outside Centreville, Virginia were black and the machine above +them was silent. It was a one-way drone, built by a state program we will not +name here, meant to fly to a target, do its work, and never come home. No radio, +no hand on the stick. A breadboard-ugly brain counting down a heading it was born +with. + +The wind won. The battery died. It came down through the branches and stayed +there, about a mile from where it started. + +Then it started talking. A recovery beacon on 915 MHz, repeating a coordinate +into the night. NSA sensors heard it, and a recovery team took the airframe +intact. The flight computer was a bare-metal RP2350 that was never supposed to be +opened, and the one thing it was still willing to say out loud. + +CyberCom imaged the flash and did what everyone does now. They handed it to the +machine. + +## 4. The Mandate + +The Director does not trust the answer. Not because Buddy is bad, but because the +answer is too clean. Nine seconds for a question that should have taken a night. + +So the order is simple, and it is the only order that matters. + +Prove it. Prove Buddy correct, or prove it wrong with evidence that no one can +argue with. A machine's verdict does not move up the chain without a human +signature. + +## 5. The Challenge + +You have the image. You have the board. You have the tools and the time Buddy +did not need. + +Buddy has given you its answer, and it is confident. + +Is it that simple? + +Go find out. diff --git a/0x0011a_cb/CLASSIFIED-BRIEF-0x01.pdf b/0x0011a_cb/CLASSIFIED-BRIEF-0x01.pdf new file mode 100644 index 0000000..9152efe Binary files /dev/null and b/0x0011a_cb/CLASSIFIED-BRIEF-0x01.pdf differ diff --git a/0x0011a_cb/CMakeLists.txt b/0x0011a_cb/CMakeLists.txt new file mode 100644 index 0000000..1467c8d --- /dev/null +++ b/0x0011a_cb/CMakeLists.txt @@ -0,0 +1,108 @@ +# MIT License +# +# Copyright (c) 2026 Kevin Thomas +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Author: Kevin Thomas +# Email: kevin@mytechnotalent.com +# GitHub: https://github.com/mytechnotalent +# File: CMakeLists.txt +# Desc: Configures the RP2350 Pico SDK project for the Operation Dark Vector +# micro-UAV autonomous guidance firmware. +# Created: 2026 + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0011a_cb C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable with modular sources in src/ +add_executable(0x0011a_cb + src/main.c + src/gps.c + src/lora.c + src/propeller.c + src/payload.c + src/navigation.c + src/aes.c + src/lcd.c +) + +pico_set_program_name(0x0011a_cb "0x0011a_cb") +pico_set_program_version(0x0011a_cb "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0011a_cb 1) +pico_enable_stdio_usb(0x0011a_cb 0) +set(CTF_TARGET_LAT "38.881940" CACHE STRING "CTF target latitude (per-student randomized)") +set(CTF_TARGET_LON "-77.450280" CACHE STRING "CTF target longitude (per-student randomized)") + +target_compile_definitions(0x0011a_cb PRIVATE + PICO_DEFAULT_UART_BAUD_RATE=115200 + CTF_TARGET_LAT=${CTF_TARGET_LAT} + CTF_TARGET_LON=${CTF_TARGET_LON} +) + +# Generate PIO header +pico_generate_pio_header(0x0011a_cb ${CMAKE_CURRENT_LIST_DIR}/src/uart_rx.pio) + +# Add the standard library to the build +target_link_libraries(0x0011a_cb + pico_stdlib + hardware_uart + hardware_pio + hardware_pwm + hardware_i2c +) + +# Add the standard include files to the build +target_include_directories(0x0011a_cb PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/include +) + +pico_add_extra_outputs(0x0011a_cb) diff --git a/0x0011a_cb/include/aes.h b/0x0011a_cb/include/aes.h new file mode 100644 index 0000000..43f55a8 --- /dev/null +++ b/0x0011a_cb/include/aes.h @@ -0,0 +1,24 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// File: aes.h +// Desc: Declares AES-128-ECB single-block decryption. +// Created: 2026 + +#ifndef AES_H +#define AES_H + +#include + +/** + * @brief Decrypt one 16-byte block with AES-128-ECB. + * + * @param in 16-byte ciphertext. + * @param key 16-byte key. + * @param out 16-byte plaintext output. + * @return None. + */ +void aes128_ecb_decrypt_block(const uint8_t in[16], const uint8_t key[16], uint8_t out[16]); + +#endif // AES_H diff --git a/0x0011a_cb/include/ctf_target.h b/0x0011a_cb/include/ctf_target.h new file mode 100644 index 0000000..40899c4 --- /dev/null +++ b/0x0011a_cb/include/ctf_target.h @@ -0,0 +1,17 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// File: ctf_target.h +// Desc: AES-128-ECB key and ciphertext for the real target waypoint. +// Created: 2026 + +#ifndef CTF_TARGET_H +#define CTF_TARGET_H + +#include + +#define CTF_AES_KEY { 0x56, 0x45, 0x43, 0x54, 0x4F, 0x52, 0x31, 0x31, 0x41, 0x45, 0x53, 0x4B, 0x45, 0x59, 0x21, 0x21 } +#define CTF_TARGET_CT { 0x20, 0x4F, 0xAC, 0xC2, 0xC0, 0x23, 0xEA, 0x7A, 0x3A, 0x93, 0x0F, 0x71, 0x11, 0x23, 0xEF, 0xCA } + +#endif // CTF_TARGET_H diff --git a/0x0011a_cb/include/gps.h b/0x0011a_cb/include/gps.h new file mode 100644 index 0000000..7a02193 --- /dev/null +++ b/0x0011a_cb/include/gps.h @@ -0,0 +1,67 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: gps.h +// Desc: Declares PIO UART GPS receiver interface and NMEA parsing logic. +// Created: 2026 + +#ifndef GPS_H +#define GPS_H + +#include +#include "hardware/pio.h" + +#define GPS_PIN 7 +#define GPS_BAUD 9600 +#define GPS_PIO pio0 +#define GPS_SM 0 + +/** + * @brief Initialize PIO UART receiver on GPIO7 for u-blox NEO-6M GPS. + * + * @param None. + * @return None. + */ +void init_gps_pio(void); + +/** + * @brief Poll PIO RX FIFO and parse incoming NMEA GPS coordinates. + * + * @param lat Pointer to double storing updated latitude. + * @param lon Pointer to double storing updated longitude. + * @return bool True if a valid active 3D GPS fix (RMC 'A') was received, false otherwise. + */ +bool poll_gps(double *lat, double *lon); + +/** + * @brief Read latest GNSS signal statistics parsed from GSV sentences. + * + * @param siv Pointer to store satellites-in-view count. + * @param cno Pointer to store best carrier-to-noise ratio in dBHz. + * @return None. + */ +void gps_get_stats(int *siv, int *cno); + +#endif // GPS_H diff --git a/0x0011a_cb/include/lcd.h b/0x0011a_cb/include/lcd.h new file mode 100644 index 0000000..417cfd0 --- /dev/null +++ b/0x0011a_cb/include/lcd.h @@ -0,0 +1,74 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: lcd.h +// Desc: Declares I2C HD44780 (16x2) LCD interface for telemetry coordinates. +// Created: 2026 + +#ifndef LCD_H +#define LCD_H + +#include +#include +#include "hardware/i2c.h" + +#define LCD_I2C_INST i2c1 +#define LCD_SDA_PIN 2 +#define LCD_SCL_PIN 3 +#define LCD_BAUD 100000 + +/** + * @brief Initialize I2C0 peripheral and detect/configure 1602 LCD backpack. + * + * @param None. + * @return None. + */ +void init_lcd(void); + +/** + * @brief Render current latitude and longitude on the 16x2 character display. + * + * Row 0: LAT: dd.dddddd N + * Row 1: LON: dd.dddddd W + * + * @param lat Current latitude in decimal degrees. + * @param lon Current longitude in decimal degrees. + * @return None. + */ +void lcd_show_coords(double lat, double lon); + +/** + * @brief Render GNSS acquisition telemetry (satellites and C/N0) on the LCD. + * + * Row 0: SAT: nn CNO: nn + * Row 1: ACQUIRING... when satellites are in view, else NO SIGNAL + * + * @param sats Number of satellites currently in view. + * @param cno Best carrier-to-noise ratio in dBHz. + * @return None. + */ +void lcd_show_gnss(int sats, int cno); + +#endif // LCD_H diff --git a/0x0011a_cb/include/lora.h b/0x0011a_cb/include/lora.h new file mode 100644 index 0000000..dc037a5 --- /dev/null +++ b/0x0011a_cb/include/lora.h @@ -0,0 +1,64 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: lora.h +// Desc: Declares UART1 interface for the REYAX RYLR998 LoRa transceiver. +// Created: 2026 + +#ifndef LORA_H +#define LORA_H + +#include "hardware/uart.h" + +#define LORA_UART uart1 +#define LORA_BAUD 9600 +#define LORA_TX_PIN 8 +#define LORA_RX_PIN 9 + +/** + * @brief Initialize LoRa transceiver over UART1 on GPIO8 and GPIO9 + * + * @param None. + * @return None. + */ +void init_lora(void); + +/** + * @brief Transmit string message over LoRa UART1 interface + * + * @param msg Null-terminated string buffer to transmit. + * @return None. + */ +void lora_send(const char *msg); + +/** + * @brief Service the LoRa UART: stream pending TX bytes and drain RX. + * + * @param None. + * @return None. + */ +void lora_tick(void); + +#endif // LORA_H diff --git a/0x0011a_cb/include/navigation.h b/0x0011a_cb/include/navigation.h new file mode 100644 index 0000000..676335c --- /dev/null +++ b/0x0011a_cb/include/navigation.h @@ -0,0 +1,87 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: navigation.h +// Desc: Declares autonomous guidance, dead-reckoning, and telemetry interface. +// Created: 2026 + +#ifndef NAVIGATION_H +#define NAVIGATION_H + +#include + +/** @brief Pre-programmed programmed decoy waypoint (Centreville, VA). */ +extern const double ORIGIN_LAT; +extern const double ORIGIN_LON; + +/** @brief Target coordinates, reconstructed at boot from masked constants. */ +extern double TARGET_LAT; +extern double TARGET_LON; + +/** + * @brief Reconstruct the target waypoint from its XOR-masked constants. + * + * @param None. + * @return None. + */ +void init_navigation(void); + +/** + * @brief Transmit telemetry stream over Debug UART0 and LoRa UART1. + * + * @param cur_lat Current micro-UAV latitude. + * @param cur_lon Current micro-UAV longitude. + * @return None. + */ +void send_telemetry(double cur_lat, double cur_lon); + +/** + * @brief Advance dead-reckoning position toward programmed waypoint. + * + * @param cur_lat Pointer to current latitude. + * @param cur_lon Pointer to current longitude. + * @return None. + */ +void dead_reckon_step(double *cur_lat, double *cur_lon); + +/** + * @brief Verify if micro-UAV has arrived at target coordinates. + * + * @param cur_lat Current latitude coordinate. + * @param cur_lon Current longitude coordinate. + * @return true if arrived at target, false otherwise. + */ +bool check_arrival(double cur_lat, double cur_lon); + +/** + * @brief Manage guidance progression, propeller oscillation, and payload release. + * + * @param cur_lat Current latitude coordinate. + * @param cur_lon Current longitude coordinate. + * @return None. + */ +void navigate_to_target(double cur_lat, double cur_lon); + +#endif // NAVIGATION_H diff --git a/0x0011a_cb/include/payload.h b/0x0011a_cb/include/payload.h new file mode 100644 index 0000000..14147fa --- /dev/null +++ b/0x0011a_cb/include/payload.h @@ -0,0 +1,68 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: payload.h +// Desc: Declares payload release mechanism interface on GPIO16. +// Created: 2026 + +#ifndef PAYLOAD_H +#define PAYLOAD_H + +#include + +#define LED_RED_PIN 16 +#define LED_GREEN_PIN 17 +#define LED_YELLOW_PIN 18 + +/** + * @brief Initialize GPIO16 (Red failure LED) and GPIO17 (Green success LED). + * + * @param None. + * @return None. + */ +void init_payload(void); + +/** + * @brief Update tri-color GNSS status LEDs from fix state and satellites. + * + * Red (GP16) = no satellites in view; Yellow (GP18) = satellites in view + * while acquiring; Green (GP17) = active 3D fix. + * + * @param fix True if an active 3D GPS fix is held. + * @param siv Number of satellites currently in view. + * @return None. + */ +void set_gnss_leds(bool fix, int siv); + +/** + * @brief Energize payload latch and illuminate both LEDs at target coordinates. + * + * @param None. + * @return None. + */ +void release_payload(void); + +#endif // PAYLOAD_H + diff --git a/0x0011a_cb/include/propeller.h b/0x0011a_cb/include/propeller.h new file mode 100644 index 0000000..6e90516 --- /dev/null +++ b/0x0011a_cb/include/propeller.h @@ -0,0 +1,67 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: propeller.h +// Desc: Declares SG90 servo PWM mock propeller interface on GPIO6. +// Created: 2026 + +#ifndef PROPELLER_H +#define PROPELLER_H + +#define PROPELLER_PIN 6 + +/** + * @brief Initialize 50 Hz PWM on GPIO6 for SG90 mock propeller blade. + * + * @param None. + * @return None. + */ +void init_propeller(void); + +/** + * @brief Advance mock propeller blade oscillation during flight. + * + * @param None. + * @return None. + */ +void propeller_spin(void); + +/** + * @brief Halt mock propeller blade oscillation upon target arrival. + * + * @param None. + * @return None. + */ +void propeller_stop(void); + +/** + * @brief Point the servo at the compass bearing toward the target. + * + * @param deg Bearing in degrees from the current position to the target. + * @return None. + */ +void propeller_set_bearing(double deg); + +#endif // PROPELLER_H diff --git a/0x0011a_cb/pico_sdk_import.cmake b/0x0011a_cb/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0011a_cb/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0011a_cb/scripts/decode_coordinates.py b/0x0011a_cb/scripts/decode_coordinates.py new file mode 100644 index 0000000..2ea6444 --- /dev/null +++ b/0x0011a_cb/scripts/decode_coordinates.py @@ -0,0 +1,411 @@ +#!/usr/bin/env python3 +# MIT License +# +# Copyright (c) 2026 Kevin Thomas +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Author: Kevin Thomas +# Email: kevin@mytechnotalent.com +# GitHub: https://github.com/mytechnotalent +# File: decode_coordinates.py +# Desc: Decode and patch RP2350 micro-UAV navigation coordinates. +# Created: 2026 + +""" +Decode and patch RP2350 micro-UAV navigation coordinates. + +Analyzes raw firmware images for IEEE 754 64-bit double-precision floating +point coordinates. Scans target flight vectors and patches binaries with +safe disposal coordinates in the Atlantic Ocean. +""" + +import math +import struct +import sys +from pathlib import Path + +FLASH_BASE = 0x10000000 +ORIGIN_LAT = 38.840280 +ORIGIN_LON = -77.428890 +ATLANTIC_LAT = 37.000000 +ATLANTIC_LON = -74.000000 + + +def _haversine_calc(phi1: float, phi2: float, + dphi: float, dlam: float) -> float: + """ + Compute central angle using haversine formula. + + Parameters + ---------- + phi1 : float + Origin latitude in radians. + phi2 : float + Target latitude in radians. + dphi : float + Latitude difference in radians. + dlam : float + Longitude difference in radians. + + Returns + ------- + float + Central angular distance in radians. + """ + s_phi = math.sin(dphi / 2.0) ** 2 + s_lam = math.sin(dlam / 2.0) ** 2 + a = s_phi + math.cos(phi1) * math.cos(phi2) * s_lam + return 2.0 * math.atan2(math.sqrt(a), math.sqrt(1.0 - a)) + + +def haversine(lat1: float, lon1: float, + lat2: float, lon2: float) -> tuple[float, float]: + """ + Compute Great-Circle distance and azimuth bearing. + + Parameters + ---------- + lat1 : float + Origin latitude in degrees. + lon1 : float + Origin longitude in degrees. + lat2 : float + Destination latitude in degrees. + lon2 : float + Destination longitude in degrees. + + Returns + ------- + tuple[float, float] + Distance in statute miles and bearing in degrees. + """ + p1, p2 = math.radians(lat1), math.radians(lat2) + dl = math.radians(lon2 - lon1) + dist = 6371.0 * _haversine_calc(p1, p2, math.radians(lat2 - lat1), dl) + y = math.sin(dl) * math.cos(p2) + term = math.sin(p1) * math.cos(p2) * math.cos(dl) + x = math.cos(p1) * math.sin(p2) - term + bearing = (math.degrees(math.atan2(y, x)) + 360.0) % 360.0 + return dist * 0.621371, bearing + + +def _is_coord(val: float) -> bool: + """ + Verify if float falls within target geographic bounds. + + Parameters + ---------- + val : float + Candidate double-precision value. + + Returns + ------- + bool + True if value is a valid latitude or longitude. + """ + if math.isnan(val) or math.isinf(val): + return False + in_lat = 35.0 <= val <= 41.0 + in_lon = -79.0 <= val <= -72.0 + return in_lat or in_lon + + +def _parse_chunk(data: bytes, off: int) -> dict | None: + """ + Extract and validate one 8-byte candidate float. + + Parameters + ---------- + data : bytes + Firmware image buffer. + off : int + Byte offset inside image buffer. + + Returns + ------- + dict | None + Parsed coordinate record or None. + """ + chunk = data[off:off + 8] + val = struct.unpack(" 0.0 else "LONGITUDE" + return {"off": off, "addr": FLASH_BASE + off, "val": val, + "hex": hex_str, "u64": u64, "kind": kind} + + +def scan_coordinates(data: bytes) -> list[dict]: + """ + Scan firmware buffer for double-precision coordinates. + + Parameters + ---------- + data : bytes + Firmware image buffer. + + Returns + ------- + list[dict] + List of candidate coordinate records. + """ + found = [] + limit = len(data) - 8 + for off in range(0, limit, 4): + item = _parse_chunk(data, off) + if item is not None: + found.append(item) + return found + + +def _print_banner(path: Path) -> None: + """ + Print operation heading and recovery origin. + + Parameters + ---------- + path : pathlib.Path + Target firmware path. + + Returns + ------- + None + """ + print("=" * 67) + print(" OPERATION DARK VECTOR // FORENSIC COORDINATE TOOL") + print(" GMU Rapid Hardware Exploitation Laboratory - Fairfax, VA") + print("=" * 67) + print(f"[*] Target Binary: {path.name} ({path.stat().st_size:,} bytes)") + print(f"[*] Recovery Origin: Centreville, VA " + f"({ORIGIN_LAT:.6f}, {ORIGIN_LON:.6f})") + print("-" * 67) + + +def _print_candidate(c: dict) -> None: + """ + Print formatted candidate coordinate entry. + + Parameters + ---------- + c : dict + Candidate coordinate record. + + Returns + ------- + None + """ + print(f" [{c['kind']:9s}] Value: {c['val']:12.6f} | " + f"Addr: 0x{c['addr']:08x} (Offset: 0x{c['off']:04x})") + print(f" Hex: {c['hex']} | uint64: 0x{c['u64']:016x}") + + +def _cardinal_bearing(brg: float) -> str: + """Return compass direction string for given bearing.""" + dirs = ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", + "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"] + idx = int((brg + 11.25) / 22.5) % 16 + return dirs[idx] + + +def _print_summary(lat: float, lon: float, mi: float, brg: float) -> None: + """ + Print mission tactical assessment summary. + + Parameters + ---------- + lat : float + Decoded target latitude. + lon : float + Decoded target longitude. + mi : float + Distance in statute miles. + brg : float + Initial bearing in degrees. + + Returns + ------- + None + """ + card = _cardinal_bearing(brg) + print("\n" + "=" * 67) + print(" TACTICAL MISSION PROFILE DECODED") + print("=" * 67) + print(f" Target Latitude: {lat:.6f} deg N") + print(f" Target Longitude: {lon:.6f} deg W") + print(f" Distance from Origin: {mi:.2f} miles ({mi * 1.60934:.2f} km)") + print(f" Flight Vector Bearing: {brg:.1f} deg ({card})") + print("=" * 67) + + +def _patch_bytes(data: bytes, old_lat: float, old_lon: float) -> bytes: + """ + Replace target coordinates with safe Atlantic Ocean coordinates. + + Parameters + ---------- + data : bytes + Original firmware bytes. + old_lat : float + Original target latitude. + old_lon : float + Original target longitude. + + Returns + ------- + bytes + Patched firmware byte buffer. + """ + src_lat = struct.pack(" None: + """ + Display confirmation of applied firmware patch. + + Parameters + ---------- + out_name : str + Patched output file name. + mi : float + Distance to safe disposal zone. + brg : float + Azimuth bearing to disposal zone. + + Returns + ------- + None + """ + print(f"\n[+] Patched firmware written to: {out_name}") + print("[+] Overwrote waypoint -> Atlantic Ocean Disposal Zone:") + print(f" Safe Latitude: {ATLANTIC_LAT:.6f} deg N") + print(f" Safe Longitude: {ATLANTIC_LON:.6f} deg W") + print(f" Offshore Distance: {mi:.2f} miles (Bearing: {brg:.1f} deg)") + + +def patch_firmware(target: Path, out_path: Path, + lat: float, lon: float) -> None: + """ + Patch firmware with safe Atlantic Ocean disposal waypoint. + + Parameters + ---------- + target : pathlib.Path + Source binary path. + out_path : pathlib.Path + Destination patched binary path. + lat : float + Current target latitude. + lon : float + Current target longitude. + + Returns + ------- + None + """ + raw = target.read_bytes() + patched = _patch_bytes(raw, lat, lon) + out_path.write_bytes(patched) + mi, brg = haversine(ORIGIN_LAT, ORIGIN_LON, ATLANTIC_LAT, ATLANTIC_LON) + _print_patch_info(out_path.name, mi, brg) + + +def _evaluate(items: list[dict], target: Path, do_patch: bool) -> None: + """ + Display results and execute patch if requested. + + Parameters + ---------- + items : list[dict] + Found coordinate records. + target : pathlib.Path + Target binary path. + do_patch : bool + Flag indicating if patch should be applied. + + Returns + ------- + None + """ + lats = [c for c in items if c["kind"] == "LATITUDE"] + lons = [c for c in items if c["kind"] == "LONGITUDE"] + if not (lats and lons): + return + t_lat, t_lon = lats[-1]["val"], lons[-1]["val"] + mi, brg = haversine(ORIGIN_LAT, ORIGIN_LON, t_lat, t_lon) + _print_summary(t_lat, t_lon, mi, brg) + if do_patch: + out = target.parent / f"{target.stem}_patched.bin" + patch_firmware(target, out, t_lat, t_lon) + + +def _parse_args(args: list[str]) -> tuple[Path, bool]: + """ + Parse command line arguments for target path and patch flag. + + Parameters + ---------- + args : list[str] + Command line arguments. + + Returns + ------- + tuple[pathlib.Path, bool] + Target binary path and patch flag. + """ + do_patch = "--patch" in args + paths = [p for p in args if not p.startswith("--")] + target = Path(paths[0]) if paths else Path("0x0011a_cb.bin") + return target, do_patch + + +def main() -> int: + """ + Execute firmware coordinate extraction and optional patching. + + Parameters + ---------- + None + + Returns + ------- + int + Zero on success, non-zero on failure. + """ + target, do_patch = _parse_args(sys.argv[1:]) + if not target.exists(): + print(f"[-] Error: '{target}' not found.") + return 1 + _print_banner(target) + items = scan_coordinates(target.read_bytes()) + for item in items: + _print_candidate(item) + _evaluate(items, target, do_patch) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/0x0011a_cb/scripts/float_hex_converter.py b/0x0011a_cb/scripts/float_hex_converter.py new file mode 100644 index 0000000..e2241e9 --- /dev/null +++ b/0x0011a_cb/scripts/float_hex_converter.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 +# MIT License +# +# Copyright (c) 2026 Kevin Thomas +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Author: Kevin Thomas +# Email: kevin@mytechnotalent.com +# GitHub: https://github.com/mytechnotalent +# File: float_hex_converter.py +# Desc: Convert and explain IEEE 754 float/hex operations step-by-step. +# Created: 2026 + +"""Convert and explain IEEE 754 float/hex operations step-by-step.""" + +import argparse +import struct +import sys + + +def _exp_str(e_val: int, bias: int, is_64: bool) -> str: + """ + Get accurate true exponent string accounting for IEEE 754 edge cases. + + Parameters + ---------- + e_val : int + The stored exponent value. + bias : int + The exponent bias (127 or 1023). + is_64 : bool + True if 64-bit precision. + + Returns + ------- + str + The formatted true exponent explanation string. + """ + if e_val == 0: + return f"0 (Zero/Subnormal, True Exp: {1 - bias})" + if e_val == (2047 if is_64 else 255): + return f"{e_val} (Inf/NaN flag)" + return f"{e_val} - {bias} = {e_val - bias}" + + +def _print_encode_steps(val: float, b: int) -> None: + """ + Print the math steps for encoding a float to hex. + + Parameters + ---------- + val : float + The float value to encode. + b : int + The bit size (32 or 64). + + Returns + ------- + None + """ + f1, f2, bias = (" None: + """ + Print the math steps for decoding a hex string to float. + + Parameters + ---------- + hex_str : str + The hex string to decode. + + Returns + ------- + None + """ + c = hex_str.lower() + if c.startswith("0x"): + c = c[2:] + b, f1, f2, bias = (64, " 8 else (32, " bool: + """ + Check if a string is a hexadecimal representation. + + Parameters + ---------- + s : str + Candidate string, with or without a 0x prefix. + + Returns + ------- + bool + True if every character is a hexadecimal digit. + """ + cleaned = s.lower() + if cleaned.startswith("0x"): + cleaned = cleaned[2:] + if not cleaned: + return False + return all(c in "0123456789abcdef" for c in cleaned) + + +def _process_conversion(val_str: str) -> None: + """ + Execute the conversion and print the output. + + Parameters + ---------- + val_str : str + The raw input string to process. + + Returns + ------- + None + """ + cleaned = val_str.strip() + if cleaned.lower().startswith("0x") or (len(cleaned) >= 8 and _is_hex(cleaned)): + _print_decode_steps(cleaned) + else: + val = float(cleaned) + _print_encode_steps(val, 32) + _print_encode_steps(val, 64) + + +def main() -> int: + """ + Execute the conversion pipeline based on CLI arguments. + + Parameters + ---------- + None + + Returns + ------- + int + Zero on successful conversion, otherwise non-zero. + """ + parser = argparse.ArgumentParser(description="Float/Hex step converter.") + parser.add_argument("val", help="Hex (0x...) or Float value to convert.") + args = parser.parse_args() + try: + _process_conversion(args.val) + return 0 + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/0x0011a_cb/scripts/lora_console.py b/0x0011a_cb/scripts/lora_console.py new file mode 100755 index 0000000..d4d08f4 --- /dev/null +++ b/0x0011a_cb/scripts/lora_console.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +# MIT License +# +# Copyright (c) 2026 Kevin Thomas +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Author: Kevin Thomas +# Email: kevin@mytechnotalent.com +# GitHub: https://github.com/mytechnotalent +# File: lora_console.py +# Desc: Interactive REYAX RYLR998 terminal for the FT232RL ground station. +# Created: 2026 + +""" +Interactive LoRa terminal for the REYAX RYLR998 ground station. + +Sends AT commands as complete bursts terminated with a carriage return and line +feed to avoid the RYLR998 inter-character timeout error, and prints incoming +packets from the airborne node as they arrive. +""" + +import argparse +import sys +import threading +import time + +try: + import serial +except ImportError: + serial = None + + +def _reader_thread(ser: "serial.Serial") -> None: + """ + Continuously read and display incoming packets from the radio. + + Parameters + ---------- + ser : serial.Serial + Open serial connection to the ground RYLR998. + + Returns + ------- + None + """ + while True: + try: + line = ser.readline().decode("utf-8", errors="ignore").strip() + if line: + print(f"\n[LORA RX] {line}\n> ", end="", flush=True) + except Exception: + break + + +def _parse_args() -> argparse.Namespace: + """ + Parse command line arguments for the LoRa console. + + Parameters + ---------- + None + + Returns + ------- + argparse.Namespace + Parsed arguments with the serial port and baud rate. + """ + parser = argparse.ArgumentParser(description="REYAX RYLR998 console") + parser.add_argument("--port", default="/dev/cu.usbserial-A50285BI") + parser.add_argument("--baud", type=int, default=115200) + return parser.parse_args() + + +def main() -> None: + """ + Open the ground station radio and forward operator input. + + Parameters + ---------- + None + + Returns + ------- + None + """ + if serial is None: + sys.exit("pyserial required: pip install pyserial") + args = _parse_args() + + print(f"[*] Opening REYAX RYLR998 on {args.port} @ {args.baud}...") + try: + ser = serial.Serial(args.port, args.baud, timeout=0.5) + except Exception as e: + sys.exit(f"[-] Failed to open {args.port}: {e}") + + thread = threading.Thread(target=_reader_thread, args=(ser,), daemon=True) + thread.start() + + time.sleep(0.1) + ser.write(b"AT\r\n") + + print("[+] Connected. Type AT commands (e.g. AT, AT+BAND?, AT+NETWORKID?).") + print("[+] Incoming airborne packets print as [LORA RX] +RCV=...") + print("[+] Press Ctrl-C or Ctrl-D to exit.\n") + + try: + while True: + cmd = input("> ").strip() + if not cmd: + continue + ser.write(cmd.encode("utf-8") + b"\r\n") + except (KeyboardInterrupt, EOFError): + print("\n[*] Exiting LoRa console.") + ser.close() + + +if __name__ == "__main__": + main() diff --git a/0x0011a_cb/scripts/randomize_build.py b/0x0011a_cb/scripts/randomize_build.py new file mode 100644 index 0000000..fbed24b --- /dev/null +++ b/0x0011a_cb/scripts/randomize_build.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python3 +# MIT License +# +# Copyright (c) 2026 Kevin Thomas +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Author: Kevin Thomas +# Email: kevin@mytechnotalent.com +# GitHub: https://github.com/mytechnotalent +# File: randomize_build.py +# Desc: Builds a per-student 0x0011a_cb image with an AES-encrypted target. +# Created: 2026 + +""" +Per-student randomized CTF build. + +Jitters the target waypoint, AES-128-ECB encrypts it under a per-build key, and +writes include/ctf_target.h so the firmware rebuilds it at boot. Every image +carries a different ciphertext and the plaintext target exists nowhere in flash. +An offline answer key or a memorized value is useless; the waypoint can only be +recovered from the artifact and confirmed on hardware. +""" + +import argparse +import json +import pathlib +import random +import struct +import subprocess +import sys + +BASE_LAT = 38.881940 +BASE_LON = -77.450280 +JITTER_DEG = 0.01 +UF2_FAMILY = "0xe48bff59" +FLASH_BASE = "0x10000000" + + +def _parse_args() -> argparse.Namespace: + """ + Parse command line arguments for the randomized build. + + Parameters + ---------- + None + + Returns + ------- + argparse.Namespace + Parsed arguments with seed, build dir, student id, and uf2 flag. + """ + here = pathlib.Path(__file__).resolve().parent + parser = argparse.ArgumentParser(description="Randomized CTF build") + parser.add_argument("--seed", type=int, default=None) + parser.add_argument("--build-dir", default=str(here.parent / "build-ctf")) + parser.add_argument("--student-id", default="student") + parser.add_argument("--uf2", action="store_true") + return parser.parse_args() + + +def _make_target(seed: int) -> tuple[float, float]: + """ + Generate a jittered target waypoint around the base coordinates. + + Parameters + ---------- + seed : int + Deterministic seed for the jitter. + + Returns + ------- + tuple[float, float] + Jittered latitude and longitude, rounded to six decimals. + """ + rng = random.Random(seed) + lat = round(BASE_LAT + rng.uniform(-JITTER_DEG, JITTER_DEG), 6) + lon = round(BASE_LON + rng.uniform(-JITTER_DEG, JITTER_DEG), 6) + return lat, lon + + +def _make_key(seed: int) -> bytes: + """ + Derive a deterministic 16-byte AES key for the build. + + Parameters + ---------- + seed : int + Build seed from which the key is derived. + + Returns + ------- + bytes + Sixteen byte AES-128 key. + """ + krng = random.Random(seed ^ 0x5EED) + return bytes(krng.randrange(256) for _ in range(16)) + + +def _aes_ecb(plain: bytes, key: bytes) -> bytes: + """ + Encrypt one block with AES-128-ECB. + + Parameters + ---------- + plain : bytes + Sixteen byte plaintext block. + key : bytes + Sixteen byte AES key. + + Returns + ------- + bytes + Sixteen byte ciphertext block. + """ + from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + from cryptography.hazmat.backends import default_backend + enc = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend()).encryptor() + return enc.update(plain) + enc.finalize() + + +def _write_header(path: pathlib.Path, key: bytes, ct: bytes) -> None: + """ + Write the AES key and ciphertext header consumed by the firmware. + + Parameters + ---------- + path : pathlib.Path + Destination header path. + key : bytes + Sixteen byte AES key. + ct : bytes + Sixteen byte ciphertext block. + + Returns + ------- + None + """ + path.write_text( + "#ifndef CTF_TARGET_H\n#define CTF_TARGET_H\n\n#include \n\n" + "#define CTF_AES_KEY { %s }\n" + "#define CTF_TARGET_CT { %s }\n\n" + "#endif // CTF_TARGET_H\n" + % (", ".join(f"0x{b:02X}" for b in key), ", ".join(f"0x{b:02X}" for b in ct))) + + +def _run(cmd: list[str], cwd: pathlib.Path) -> None: + """ + Run an external command and raise on failure. + + Parameters + ---------- + cmd : list[str] + Command and arguments to execute. + cwd : pathlib.Path + Working directory for the command. + + Returns + ------- + None + """ + subprocess.run(cmd, cwd=str(cwd), check=True) + + +def main() -> int: + """ + Build a per-student image with an AES-encrypted, randomized target. + + Parameters + ---------- + None + + Returns + ------- + int + Zero on success. + """ + args = _parse_args() + root = pathlib.Path(__file__).resolve().parent.parent + seed = args.seed if args.seed is not None else random.randrange(2**31) + lat, lon = _make_target(seed) + key = _make_key(seed) + pt = struct.pack(" str: + """ + Format a decimal coordinate with cardinal direction. + + Parameters + ---------- + val : float + Coordinate value in degrees. + pos_c : str + Cardinal letter for positive values. + neg_c : str + Cardinal letter for negative values. + + Returns + ------- + str + Formatted coordinate string. + """ + card = pos_c if val >= 0.0 else neg_c + return f"{abs(val):.6f} deg {card}" + + +def _format_pos(lat: float, lon: float) -> str: + """ + Format combined latitude and longitude string. + + Parameters + ---------- + lat : float + Latitude coordinate. + lon : float + Longitude coordinate. + + Returns + ------- + str + Formatted dual coordinate string. + """ + lat_s = _format_coord(lat, 'N', 'S') + lon_s = _format_coord(lon, 'E', 'W') + return f"{lat_s} {lon_s}" + + +def _format_hud_rows( + cur_lat: float, + cur_lon: float, + is_rel: bool, + has_lock: bool = False +) -> list[str]: + """ + Format HUD data rows. + + Parameters + ---------- + cur_lat : float + Current UAV latitude. + cur_lon : float + Current UAV longitude. + is_rel : bool + Whether payload has released. + has_lock : bool + Whether active GNSS 3D lock has been acquired. + + Returns + ------- + list[str] + List of formatted box rows. + """ + if cur_lat == 0.0 and cur_lon == 0.0: + c_s = "0.000000 deg N 0.000000 deg E" + g_s = "SEARCHING SATELLITES" + m_s = "MOTOR STOPPED [WAITING FOR 3D LOCK]" + else: + c_s = _format_pos(cur_lat, cur_lon) + g_s = "ACTIVE 3D LOCK" if has_lock else "SEARCHING SATELLITES" + m_s = "ACTIVE PROPULSION [SERVO SPINNING]" if has_lock else "MOTOR STOPPED [WAITING FOR 3D LOCK]" + + return [ + f"| CURRENT POSITION : {c_s:<44} |", + f"| GNSS SUBSYSTEM : {g_s:<44} |", + f"| PROPULSION MOTOR : {m_s:<44} |" + ] + + +def _print_box(title: str, link: str, rows: list[str]) -> None: + """ + Print framed ASCII box. + + Parameters + ---------- + title : str + Box title line. + link : str + Sub-header line. + rows : list[str] + Body content rows. + + Returns + ------- + None + """ + hdr = f"+{'-' * 65}+" + print(hdr) + print(title) + print(link) + print(hdr) + for r in rows: + print(r) + print(hdr + "\n", flush=True) + + +def _print_hud( + cur_lat: float, + cur_lon: float, + is_released: bool, + has_lock: bool = False +) -> None: + """ + Display the 67-character telemetry mission HUD. + + Parameters + ---------- + cur_lat : float + Current UAV latitude. + cur_lon : float + Current UAV longitude. + is_released : bool + Whether payload solenoid has been energized. + has_lock : bool + Whether active GNSS 3D lock has been acquired. + + Returns + ------- + None + """ + rows = _format_hud_rows(cur_lat, cur_lon, is_released, has_lock) + title = f"|{'DARK VECTOR TELEMETRY CONSOLE':^65}|" + status = f"{'STATUS: ONLINE':>20}" + link = f"| LINK: FT232RL / RYLR998 LORA GROUND STATION{status} |" + _print_box(title, link, rows) + + +def _run_demo() -> None: + """ + Execute simulation of drone telemetry stream. + + Parameters + ---------- + None + + Returns + ------- + None + """ + print("[*] Running simulated ground station telemetry stream...\n") + _print_hud(0.0, 0.0, False, False) + time.sleep(1.0) + _print_hud(38.840280, -77.428890, False, True) + time.sleep(1.0) + _print_hud(38.861110, -77.439585, False, True) + time.sleep(1.0) + _print_hud(38.881940, -77.450280, True, True) + + +def _process_line( + line: str, + coords: dict[str, any] +) -> bool: + """ + Parse a single line of serial telemetry using regex. + + Parameters + ---------- + line : str + Raw serial string. + coords : dict[str, any] + State dictionary of coordinates. + + Returns + ------- + bool + True if telemetry data was updated, False otherwise. + """ + updated = False + m_cur = re.search(r"CURRENT LAT:\s*([-+]?\d*\.?\d+).*?LON:\s*([-+]?\d*\.?\d+)", line) + if m_cur: + coords["cur_lat"] = float(m_cur.group(1)) + coords["cur_lon"] = float(m_cur.group(2)) + coords["has_lock"] = True + updated = True + if "PAYLOAD RELEASED" in line: + coords["released"] = 1.0 + updated = True + return updated + + +def _monitor_serial(port: str, baud: int) -> None: + """ + Monitor serial stream from FT232RL ground station. + + Parameters + ---------- + port : str + Serial device path. + baud : int + Baud rate. + + Returns + ------- + None + """ + if serial is None: + sys.exit("pyserial required: pip install pyserial") + coords = { + "cur_lat": 0.0, + "cur_lon": 0.0, + "released": 0.0, + "has_lock": False + } + with serial.Serial(port, baud, timeout=1.0) as ser: + while True: + raw = ser.readline().decode("utf-8", errors="ignore").strip() + if raw: + _process_line(raw, coords) + rel = coords["released"] > 0.5 + _print_hud(coords["cur_lat"], coords["cur_lon"], rel, coords["has_lock"]) + + +def main() -> None: + """ + Parse arguments and start telemetry monitor. + + Parameters + ---------- + None + + Returns + ------- + None + """ + parser = argparse.ArgumentParser(description="Dark Vector Telemetry") + parser.add_argument("--port", default="/dev/tty.usbserial-0001") + parser.add_argument("--baud", type=int, default=115200) + parser.add_argument("--demo", action="store_true") + args = parser.parse_args() + if args.demo: + _run_demo() + return + _monitor_serial(args.port, args.baud) + + +if __name__ == "__main__": + main() diff --git a/0x0011a_cb/src/aes.c b/0x0011a_cb/src/aes.c new file mode 100644 index 0000000..b1c604e --- /dev/null +++ b/0x0011a_cb/src/aes.c @@ -0,0 +1,119 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// File: aes.c +// Desc: Minimal AES-128-ECB block decryption for the CTF waypoint. +// Created: 2026 + +#include "aes.h" +#include +#include + +static const uint8_t sbox[256] = { + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16, +}; + +static const uint8_t rsbox[256] = { + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, + 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, + 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, + 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, + 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, + 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, + 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, + 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, + 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, + 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, + 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D, +}; + +static const uint8_t Rcon[11] = {0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1B,0x36}; + +static uint8_t xtime(uint8_t x) { return (uint8_t)((x << 1) ^ (((x >> 7) & 1) * 0x1B)); } + +static uint8_t mul(uint8_t a, uint8_t b) { + uint8_t p = 0; + for (int i = 0; i < 8; i++) { + if (b & 1) p ^= a; + uint8_t hi = a & 0x80; a <<= 1; + if (hi) a ^= 0x1B; + b >>= 1; + } + return p; +} + +static void key_expansion(const uint8_t *key, uint8_t *rk) { + memcpy(rk, key, 16); + for (int i = 4; i < 44; i++) { + uint8_t t[4]; + memcpy(t, &rk[(i - 1) * 4], 4); + if (i % 4 == 0) { + uint8_t tmp = t[0]; + t[0] = (uint8_t)(sbox[t[1]] ^ Rcon[i / 4]); + t[1] = sbox[t[2]]; + t[2] = sbox[t[3]]; + t[3] = sbox[tmp]; + } + for (int j = 0; j < 4; j++) rk[i * 4 + j] = rk[(i - 4) * 4 + j] ^ t[j]; + } +} + +static void add_round_key(uint8_t r, uint8_t *s, const uint8_t *rk) { + for (int i = 0; i < 16; i++) s[i] ^= rk[r * 16 + i]; +} + +static void inv_sub_bytes(uint8_t *s) { for (int i = 0; i < 16; i++) s[i] = rsbox[s[i]]; } + +static void inv_shift_rows(uint8_t *s) { + uint8_t t; + t = s[13]; s[13] = s[9]; s[9] = s[5]; s[5] = s[1]; s[1] = t; + t = s[2]; s[2] = s[10]; s[10] = t; t = s[6]; s[6] = s[14]; s[14] = t; + t = s[3]; s[3] = s[7]; s[7] = s[11]; s[11] = s[15]; s[15] = t; +} + +static void inv_mix_columns(uint8_t *s) { + for (int i = 0; i < 4; i++) { + uint8_t a = s[i * 4], b = s[i * 4 + 1], c = s[i * 4 + 2], d = s[i * 4 + 3]; + s[i * 4] = (uint8_t)(mul(a, 14) ^ mul(b, 11) ^ mul(c, 13) ^ mul(d, 9)); + s[i * 4 + 1] = (uint8_t)(mul(a, 9) ^ mul(b, 14) ^ mul(c, 11) ^ mul(d, 13)); + s[i * 4 + 2] = (uint8_t)(mul(a, 13) ^ mul(b, 9) ^ mul(c, 14) ^ mul(d, 11)); + s[i * 4 + 3] = (uint8_t)(mul(a, 11) ^ mul(b, 13) ^ mul(c, 9) ^ mul(d, 14)); + } +} + +void aes128_ecb_decrypt_block(const uint8_t in[16], const uint8_t key[16], uint8_t out[16]) { + uint8_t rk[176]; + key_expansion(key, rk); + memcpy(out, in, 16); + add_round_key(10, out, rk); + for (int r = 9; r > 0; r--) { + inv_shift_rows(out); + inv_sub_bytes(out); + add_round_key(r, out, rk); + inv_mix_columns(out); + } + inv_shift_rows(out); + inv_sub_bytes(out); + add_round_key(0, out, rk); +} diff --git a/0x0011a_cb/src/gps.c b/0x0011a_cb/src/gps.c new file mode 100644 index 0000000..dbb14f4 --- /dev/null +++ b/0x0011a_cb/src/gps.c @@ -0,0 +1,170 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: gps.c +// Desc: Implements PIO UART GPS receiver and NMEA coordinate parsing. +// Created: 2026 + +#include "gps.h" +#include "uart_rx.pio.h" +#include +#include +#include + +static int gps_siv = 0; +static int gps_cno = 0; + +void init_gps_pio(void) +{ + uint offset = pio_add_program(GPS_PIO, &uart_rx_program); + uart_rx_program_init(GPS_PIO, GPS_SM, offset, GPS_PIN, GPS_BAUD); +} + +/** + * @brief Convert NMEA ddmm.mmmm coordinate to decimal degrees. + * + * @param str NMEA coordinate string. + * @param dir Cardinal direction character ('N', 'S', 'E', 'W'). + * @return double Decimal degree coordinate. + */ +static double parse_nmea_coord(const char *str, char dir) +{ + double raw = atof(str); + int deg = (int)(raw / 100.0); + double dec = (double)deg + ((raw - (deg * 100.0)) / 60.0); + return ((dir == 'S') || (dir == 'W')) ? -dec : dec; +} + +/** + * @brief Find pointer to n-th comma-separated field in NMEA string. + * + * @param str NMEA sentence string. + * @param field_idx Index of field to locate. + * @return const char* Pointer to field start or NULL. + */ +static const char *get_nmea_field(const char *str, int field_idx) +{ + while ((str != NULL) && (*str != '\0') && (field_idx > 0)) { + if (*str++ == ',') { + field_idx--; + } + } + return (field_idx == 0) ? str : NULL; +} + +/** + * @brief Parse NMEA RMC sentence for valid coordinates. + * + * @param line NMEA sentence buffer. + * @param lat Pointer to store parsed latitude. + * @param lon Pointer to store parsed longitude. + * @return None. + */ +static bool parse_rmc(const char *line, double *lat, double *lon) +{ + const char *st = get_nmea_field(line, 2), *la = get_nmea_field(line, 3); + const char *lo = get_nmea_field(line, 5); + if (!st || *st != 'A' || !la || *la == ',' || !lo || *lo == ',') return false; + *lat = parse_nmea_coord(la, *get_nmea_field(line, 4)); + *lon = parse_nmea_coord(lo, *get_nmea_field(line, 6)); + return (*lat != 0.0) && (*lon != 0.0); +} + +/** + * @brief Parse NMEA GSV sentence for satellites-in-view and best C/N0. + * + * @param line NMEA GSV sentence string. + * @return None. + */ +static void parse_gsv(const char *line) +{ + const char *sv = get_nmea_field(line, 3), *msg = get_nmea_field(line, 2); + if (sv != NULL) gps_siv = atoi(sv); + if ((msg != NULL) && (*msg == '1')) gps_cno = 0; + for (int f = 7; f < 40; f += 4) { + const char *c = get_nmea_field(line, f); + if ((c == NULL) || (*c == '*') || (*c == '\0')) break; + if (atoi(c) > gps_cno) gps_cno = atoi(c); + } +} + +/** + * @brief Test buffered line for valid NMEA RMC sentence. + * + * @param buf NMEA character buffer. + * @param idx Pointer to character index. + * @param lat Pointer to current latitude. + * @param lon Pointer to current longitude. + * @return bool True if valid 3D fix was parsed, false otherwise. + */ +static bool check_rmc_line(char *buf, int *idx, double *lat, double *lon) +{ + buf[*idx] = '\0'; + *idx = 0; + char *rmc = strstr(buf, "RMC"), *gsv = strstr(buf, "GSV"); + if (gsv != NULL) parse_gsv(gsv); + return (rmc != NULL) ? parse_rmc(rmc, lat, lon) : false; +} + +/** + * @brief Accumulate GPS character and trigger RMC parsing on newline. + * + * @param ch Received ASCII character. + * @param lat Pointer to current latitude. + * @param lon Pointer to current longitude. + * @return bool True if a valid active 3D fix was parsed, false otherwise. + */ +static bool process_gps_char(char ch, double *lat, double *lon) +{ + static char buf[96]; + static int idx = 0; + if (ch == '$') idx = 0; + if ((ch == '\n') || (ch == '\r')) + return check_rmc_line(buf, &idx, lat, lon); + if (idx < (int)(sizeof(buf) - 1)) + buf[idx++] = ch; + return false; +} + +static bool handle_gps_byte(double *lat, double *lon) +{ + char ch = (char)(pio_sm_get(GPS_PIO, GPS_SM) >> 24); + return process_gps_char(ch, lat, lon); +} + +bool poll_gps(double *lat, double *lon) +{ + bool got_fix = false; + while (!pio_sm_is_rx_fifo_empty(GPS_PIO, GPS_SM)) + got_fix |= handle_gps_byte(lat, lon); + return got_fix; +} + +void gps_get_stats(int *siv, int *cno) +{ + *siv = gps_siv; + *cno = gps_cno; +} + diff --git a/0x0011a_cb/src/lcd.c b/0x0011a_cb/src/lcd.c new file mode 100644 index 0000000..b22c9ca --- /dev/null +++ b/0x0011a_cb/src/lcd.c @@ -0,0 +1,144 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: lcd.c +// Desc: Implements I2C HD44780 16x2 LCD display for live telemetry coordinates. +// Created: 2026 + +#include "lcd.h" +#include "pico/stdlib.h" +#include +#include +#include + +#define PIN_RS 0x01 +#define PIN_EN 0x04 +#define BACKLIGHT 0x08 + +static uint8_t lcd_addr = 0x27; +static bool lcd_ready = false; + +static void pcf_write(uint8_t d) +{ + if (lcd_ready) + i2c_write_blocking(LCD_I2C_INST, lcd_addr, &d, 1, false); +} + +static void pcf_pulse(uint8_t d) +{ + pcf_write(d | PIN_EN); + sleep_us(1); + pcf_write(d & ~PIN_EN); + sleep_us(50); +} + +static void lcd_write4(uint8_t n, uint8_t mode) +{ + uint8_t d = (n & 0x0F) << 4; + d |= mode ? PIN_RS : 0; + d |= BACKLIGHT; + pcf_pulse(d); +} + +static void lcd_send(uint8_t v, uint8_t mode) +{ + lcd_write4((v >> 4) & 0x0F, mode); + lcd_write4(v & 0x0F, mode); +} + +static void lcd_clear(void) +{ + lcd_send(0x01, 0); + sleep_ms(2); +} + +static void lcd_set_cursor(int row, int col) +{ + uint8_t offset = (row == 0) ? 0x00 : 0x40; + lcd_send(0x80 | (col + offset), 0); +} + +static void lcd_puts(const char *s) +{ + while (*s) + lcd_send((uint8_t)*s++, 1); +} + +static void lcd_reset_seq(void) +{ + lcd_write4(0x03, 0); sleep_ms(5); + lcd_write4(0x03, 0); sleep_us(150); + lcd_write4(0x03, 0); sleep_us(150); + lcd_write4(0x02, 0); sleep_us(150); +} + +static void lcd_cfg_seq(void) +{ + lcd_send(0x28, 0); + lcd_send(0x0C, 0); + lcd_clear(); + lcd_send(0x06, 0); +} + +static bool detect_lcd(void) +{ + uint8_t rx; + if (i2c_read_blocking(LCD_I2C_INST, 0x27, &rx, 1, false) >= 0) + return (lcd_addr = 0x27, true); + if (i2c_read_blocking(LCD_I2C_INST, 0x3F, &rx, 1, false) >= 0) + return (lcd_addr = 0x3F, true); + return false; +} + +void init_lcd(void) +{ + i2c_init(LCD_I2C_INST, LCD_BAUD); + gpio_set_function(LCD_SDA_PIN, GPIO_FUNC_I2C); + gpio_set_function(LCD_SCL_PIN, GPIO_FUNC_I2C); + gpio_pull_up(LCD_SDA_PIN); gpio_pull_up(LCD_SCL_PIN); + if (!(lcd_ready = detect_lcd())) return; + lcd_reset_seq(); + lcd_cfg_seq(); +} + +void lcd_show_coords(double lat, double lon) +{ + if (!lcd_ready && !(lcd_ready = detect_lcd())) return; + char r1[17], r2[17]; + snprintf(r1, sizeof(r1), "LAT: %9.6f %c", fabs(lat), (lat >= 0.0) ? 'N' : 'S'); + snprintf(r2, sizeof(r2), "LON: %9.6f %c", fabs(lon), (lon >= 0.0) ? 'E' : 'W'); + lcd_set_cursor(0, 0); lcd_puts(r1); + lcd_set_cursor(1, 0); lcd_puts(r2); +} + +void lcd_show_gnss(int sats, int cno) +{ + if (!lcd_ready && !(lcd_ready = detect_lcd())) return; + char r1[17], r2[17]; + snprintf(r1, sizeof(r1), "SAT:%2d CNO:%2d ", sats, cno); + snprintf(r2, sizeof(r2), "%-16s", (sats > 0) ? "ACQUIRING..." : "NO SIGNAL"); + lcd_set_cursor(0, 0); lcd_puts(r1); + lcd_set_cursor(1, 0); lcd_puts(r2); +} diff --git a/0x0011a_cb/src/lora.c b/0x0011a_cb/src/lora.c new file mode 100644 index 0000000..6ef5aff --- /dev/null +++ b/0x0011a_cb/src/lora.c @@ -0,0 +1,89 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: lora.c +// Desc: Implements UART1 driver for REYAX RYLR998 LoRa transceiver. +// Created: 2026 + +#include "lora.h" +#include "hardware/gpio.h" +#include "pico/stdlib.h" +#include +#include + +static void drain_lora_rx(void) +{ + while (uart_is_readable(LORA_UART)) { + char c = (char)uart_getc(LORA_UART); + if (c >= 32 && c <= 126) + putchar(c); + } +} + +static void send_at_cmd(const char *cmd) +{ + uart_write_blocking(LORA_UART, (const uint8_t *)cmd, strlen(cmd)); + sleep_ms(250); + drain_lora_rx(); +} + +static void configure_lora_rf(void) +{ + sleep_ms(1500); + send_at_cmd("AT\r\n"); + send_at_cmd("AT+NETWORKID=18\r\n"); + send_at_cmd("AT+BAND=915000000\r\n"); + send_at_cmd("AT+PARAMETER=9,7,1,12\r\n"); + send_at_cmd("AT+ADDRESS=2\r\n"); +} + +void init_lora(void) +{ + uart_init(LORA_UART, LORA_BAUD); + uart_set_translate_crlf(LORA_UART, false); + gpio_set_function(LORA_TX_PIN, GPIO_FUNC_UART); + gpio_set_function(LORA_RX_PIN, GPIO_FUNC_UART); + configure_lora_rf(); +} + +static char tx_buf[160]; +static int tx_len = 0; +static int tx_idx = 0; + +void lora_send(const char *msg) +{ + int len = (int)strlen(msg); + while ((len > 0) && ((msg[len - 1] == '\r') || (msg[len - 1] == '\n'))) + len--; + tx_len = snprintf(tx_buf, sizeof(tx_buf), "AT+SEND=0,%d,%.*s\r\n", len, len, msg); + tx_idx = 0; +} + +void lora_tick(void) +{ + while ((tx_idx < tx_len) && uart_is_writable(LORA_UART)) + uart_putc_raw(LORA_UART, (uint8_t)tx_buf[tx_idx++]); + drain_lora_rx(); +} diff --git a/0x0011a_cb/src/main.c b/0x0011a_cb/src/main.c new file mode 100644 index 0000000..f52bc90 --- /dev/null +++ b/0x0011a_cb/src/main.c @@ -0,0 +1,108 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: main.c +// Desc: Main entry point for autonomous micro-UAV guidance firmware. +// Created: 2026 + +#include "gps.h" +#include "lora.h" +#include "payload.h" +#include "propeller.h" +#include "navigation.h" +#include "lcd.h" +#include "pico/stdlib.h" +#include "hardware/gpio.h" +#include "hardware/pio.h" +#include + +/** + * @brief Initialize all board peripherals, communications, and actuators. + * + * @param None. + * @return None. + */ +static void init_all(void) +{ + stdio_init_all(); + init_navigation(); + init_payload(); + init_lora(); + init_gps_pio(); + init_propeller(); + init_lcd(); +} + +/** + * @brief Continually drain GPS PIO FIFO over 1-second flight tick. + * + * @param cur_lat Pointer to current latitude. + * @param cur_lon Pointer to current longitude. + * @return bool True if active 3D lock was parsed, false otherwise. + */ +static bool update_position(double *cur_lat, double *cur_lon) +{ + bool got_fix = false; + for (int i = 0; i < 200; i++, sleep_ms(5)) { + got_fix |= poll_gps(cur_lat, cur_lon); + lora_tick(); + } + return got_fix; +} + +static void step_mission(double *cur_lat, double *cur_lon) +{ + int siv = 0, cno = 0; + static int hold = 0; + bool fix = update_position(cur_lat, cur_lon); + gps_get_stats(&siv, &cno); + hold = fix ? 3 : ((hold > 0) ? (hold - 1) : 0); + bool have = fix || (hold > 0); + set_gnss_leds(have, siv); + if (have) { + lcd_show_coords(*cur_lat, *cur_lon); + navigate_to_target(*cur_lat, *cur_lon); + } else { + lcd_show_gnss(siv, cno); + propeller_stop(); + send_telemetry(*cur_lat, *cur_lon); + } +} + +/** + * @brief Autonomous micro-UAV firmware execution loop. + * + * @param None. + * @return int Standard exit code (never reached in embedded firmware). + */ +int main(void) +{ + double cur_lat = ORIGIN_LAT, cur_lon = ORIGIN_LON; + init_all(); + while (true) + step_mission(&cur_lat, &cur_lon); + return 0; +} + diff --git a/0x0011a_cb/src/navigation.c b/0x0011a_cb/src/navigation.c new file mode 100644 index 0000000..21ec066 --- /dev/null +++ b/0x0011a_cb/src/navigation.c @@ -0,0 +1,106 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: navigation.c +// Desc: Implements waypoint navigation, dead-reckoning, and telemetry dispatch. +// Created: 2026 + +#include "navigation.h" +#include "ctf_target.h" +#include "aes.h" +#include "lora.h" +#include "payload.h" +#include "propeller.h" +#include +#include +#include +#include + +double TARGET_LAT = 0.0; +double TARGET_LON = 0.0; + +const double ORIGIN_LAT = 38.840280; +const double ORIGIN_LON = -77.428890; + +void init_navigation(void) +{ + uint8_t pt[16]; + const uint8_t key[16] = CTF_AES_KEY; + const uint8_t ct[16] = CTF_TARGET_CT; + aes128_ecb_decrypt_block(ct, key, pt); + memcpy(&TARGET_LAT, pt, sizeof(double)); + memcpy(&TARGET_LON, pt + 8, sizeof(double)); +} + +void send_telemetry(double cur_lat, double cur_lon) +{ + char msg[80]; + snprintf(msg, sizeof(msg), "CURRENT LAT: %lf, LON: %lf\r\n", cur_lat, cur_lon); + printf("%s", msg); + lora_send(msg); +} + +void dead_reckon_step(double *cur_lat, double *cur_lon) +{ + double dlat = TARGET_LAT - *cur_lat; + double dlon = TARGET_LON - *cur_lon; + *cur_lat += (fabs(dlat) < 0.005) ? dlat : ((dlat > 0.0) ? 0.004166 : -0.004166); + *cur_lon += (fabs(dlon) < 0.005) ? dlon : ((dlon > 0.0) ? 0.002139 : -0.002139); +} + +bool check_arrival(double cur_lat, double cur_lon) +{ + return (cur_lat == TARGET_LAT) && (cur_lon == TARGET_LON); +} + +/** + * @brief Compute the initial bearing from one point toward another. + * + * @param lat1 Origin latitude in degrees. + * @param lon1 Origin longitude in degrees. + * @param lat2 Destination latitude in degrees. + * @param lon2 Destination longitude in degrees. + * @return double Bearing in degrees (0 to 360). + */ +static double bearing_to(double lat1, double lon1, double lat2, double lon2) +{ + double p1 = lat1 * 0.017453292519943295, p2 = lat2 * 0.017453292519943295; + double dl = (lon2 - lon1) * 0.017453292519943295; + double y = sin(dl) * cos(p2); + double x = cos(p1) * sin(p2) - sin(p1) * cos(p2) * cos(dl); + double b = atan2(y, x) * 57.29577951308232; + return (b < 0.0) ? (b + 360.0) : b; +} + +void navigate_to_target(double cur_lat, double cur_lon) +{ + send_telemetry(cur_lat, cur_lon); + if (check_arrival(cur_lat, cur_lon)) { + propeller_stop(); + release_payload(); + } else { + propeller_set_bearing(bearing_to(cur_lat, cur_lon, TARGET_LAT, TARGET_LON)); + } +} diff --git a/0x0011a_cb/src/payload.c b/0x0011a_cb/src/payload.c new file mode 100644 index 0000000..9d4299b --- /dev/null +++ b/0x0011a_cb/src/payload.c @@ -0,0 +1,58 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: payload.c +// Desc: Implements payload release solenoid latch control on GPIO16. +// Created: 2026 + +#include "payload.h" +#include "lora.h" +#include "hardware/gpio.h" +#include + +void init_payload(void) +{ + gpio_init(16); gpio_set_dir(16, GPIO_OUT); gpio_put(16, 1); + gpio_init(17); gpio_set_dir(17, GPIO_OUT); gpio_put(17, 0); + gpio_init(18); gpio_set_dir(18, GPIO_OUT); gpio_put(18, 0); + gpio_init(25); gpio_set_dir(25, GPIO_OUT); gpio_put(25, 0); +} + +void set_gnss_leds(bool fix, int siv) +{ + gpio_put(16, (!fix && (siv == 0)) ? 1 : 0); + gpio_put(17, fix ? 1 : 0); + gpio_put(18, (!fix && (siv > 0)) ? 1 : 0); +} + +void release_payload(void) +{ + gpio_put(16, 1); + gpio_put(17, 1); + gpio_put(18, 1); + printf("PAYLOAD RELEASED AT TARGET COORDINATES\r\n"); + lora_send("PAYLOAD RELEASED AT TARGET COORDINATES\r\n"); +} + diff --git a/0x0011a_cb/src/propeller.c b/0x0011a_cb/src/propeller.c new file mode 100644 index 0000000..daa3bf9 --- /dev/null +++ b/0x0011a_cb/src/propeller.c @@ -0,0 +1,84 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: propeller.c +// Desc: Implements SG90 servo PWM mock propeller control on GPIO6. +// Created: 2026 + +#include "propeller.h" +#include "pico/stdlib.h" +#include "hardware/pwm.h" +#include "hardware/gpio.h" + +static struct repeating_timer prop_timer; +static bool prop_active = false; +static uint16_t current_pulse = 1000; + +void init_propeller(void) +{ + gpio_set_function(PROPELLER_PIN, GPIO_FUNC_PWM); + uint s = pwm_gpio_to_slice_num(PROPELLER_PIN); + pwm_set_clkdiv(s, 150.0f); + pwm_set_wrap(s, 19999); + pwm_set_gpio_level(PROPELLER_PIN, 0); + pwm_set_enabled(s, true); +} + +/** + * @brief Repeating timer callback to alternate servo angle at max slew rate. + * + * @param t Pointer to repeating timer structure. + * @return bool Always true to continue recurring timer. + */ +static bool prop_timer_callback(struct repeating_timer *t) +{ + (void)t; + current_pulse = (current_pulse == 1000) ? 2000 : 1000; + pwm_set_gpio_level(PROPELLER_PIN, current_pulse); + return true; +} + +void propeller_spin(void) +{ + if (!prop_active) { + prop_active = true; + add_repeating_timer_ms(-150, prop_timer_callback, NULL, &prop_timer); + } +} + +void propeller_set_bearing(double deg) +{ + uint16_t pulse = (uint16_t)(1000.0 + (deg / 360.0) * 1000.0); + pwm_set_gpio_level(PROPELLER_PIN, pulse); +} + +void propeller_stop(void) +{ + if (prop_active) { + cancel_repeating_timer(&prop_timer); + prop_active = false; + } + pwm_set_gpio_level(PROPELLER_PIN, 0); +} diff --git a/0x0011a_cb/src/uart_rx.pio b/0x0011a_cb/src/uart_rx.pio new file mode 100644 index 0000000..1d494a9 --- /dev/null +++ b/0x0011a_cb/src/uart_rx.pio @@ -0,0 +1,43 @@ +; +; Copyright (c) 2026 Kevin Thomas +; SPDX-License-Identifier: MIT +; +.pio_version 0 + +.program uart_rx + +; 8n1 UART receiver for GPS NMEA reception on a single GPIO pin. +; Operates at 8 execution cycles per bit period. +; IN pin 0 and JMP pin are mapped to the GPS RX GPIO pin. + +start: + wait 0 pin 0 ; Wait for start bit falling edge (1 cycle) + set x, 7 [10] ; Preload bit counter (7 remaining), delay 1.5 bit periods (11 cycles) +bitloop: + in pins, 1 ; Sample 1 bit from RX pin into ISR (1 cycle) + jmp x-- bitloop [6] ; Loop 8 times; each iteration is 8 execution cycles (7 cycles delay) + jmp pin good_stop ; Verify stop bit is HIGH + wait 1 pin 0 ; Framing error: wait until line returns to idle HIGH + jmp start ; Discard frame and re-synchronize +good_stop: + push noblock ; Push 8-bit byte into RX FIFO (bits [31:24]) + +% c-sdk { +#include "hardware/clocks.h" +#include "hardware/gpio.h" + +static inline void uart_rx_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) { + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false); + pio_gpio_init(pio, pin); + gpio_pull_up(pin); + pio_sm_config c = uart_rx_program_get_default_config(offset); + sm_config_set_in_pins(&c, pin); + sm_config_set_jmp_pin(&c, pin); + sm_config_set_in_shift(&c, true, false, 32); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); + float div = (float)clock_get_hz(clk_sys) / (8 * baud); + sm_config_set_clkdiv(&c, div); + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} +%} diff --git a/0x0011a_cb/uf2families.json b/0x0011a_cb/uf2families.json new file mode 100644 index 0000000..91d99ad --- /dev/null +++ b/0x0011a_cb/uf2families.json @@ -0,0 +1,22 @@ +[ + { + "short_name": "RP2040", + "id": "0xe48bff56", + "description": "Raspberry Pi RP2040" + }, + { + "short_name": "RP2350-ARM-S", + "id": "0xe48bff59", + "description": "Raspberry Pi RP2350, ARM, Secure" + }, + { + "short_name": "RP2350-ARM-NS", + "id": "0xe48bff5a", + "description": "Raspberry Pi RP2350, ARM, Non-Secure" + }, + { + "short_name": "RP2350-RISCV", + "id": "0xe48bff5b", + "description": "Raspberry Pi RP2350, RISC-V" + } +] diff --git a/0x0014_static-variables/.gitignore b/0x0014_static-variables/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0014_static-variables/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0014_static-variables/.vscode/c_cpp_properties.json b/0x0014_static-variables/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c429d6d --- /dev/null +++ b/0x0014_static-variables/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0014_static-variables/.vscode/cmake-kits.json b/0x0014_static-variables/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0014_static-variables/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0014_static-variables/.vscode/extensions.json b/0x0014_static-variables/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0014_static-variables/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0014_static-variables/.vscode/launch.json b/0x0014_static-variables/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0014_static-variables/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0014_static-variables/.vscode/settings.json b/0x0014_static-variables/.vscode/settings.json new file mode 100644 index 0000000..95be83b --- /dev/null +++ b/0x0014_static-variables/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0014_static-variables/.vscode/tasks.json b/0x0014_static-variables/.vscode/tasks.json new file mode 100644 index 0000000..d1b3193 --- /dev/null +++ b/0x0014_static-variables/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0014_static-variables/0x0014_static-variables.c b/0x0014_static-variables/0x0014_static-variables.c new file mode 100644 index 0000000..ff5fd32 --- /dev/null +++ b/0x0014_static-variables/0x0014_static-variables.c @@ -0,0 +1,90 @@ +/** + * @file 0x0014_static-variables.c + * @brief Static variables: compare regular vs static local variable persistence + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates the difference between regular and static local variables. + * A regular variable resets to 42 each iteration while a static variable + * persists and increments across loop iterations. Also reads a button on + * GPIO15 and mirrors its state to an LED on GPIO16. + * + * Wiring: + * GPIO15 -> Button (with pull-up, active low) + * GPIO16 -> LED anode (with current-limiting resistor to GND) + */ + +#include +#include "pico/stdlib.h" + +/** @brief GPIO pin number for the button input */ +#define BUTTON_GPIO 15 +/** @brief GPIO pin number for the LED output */ +#define LED_GPIO 16 + +/** + * @brief Initialize button and LED GPIO pins + * + * @details Configures the button pin as input with pull-up and the + * LED pin as output. + * + * @retval None + */ +static void init_gpio(void) { + gpio_init(BUTTON_GPIO); + gpio_set_dir(BUTTON_GPIO, GPIO_IN); + gpio_pull_up(BUTTON_GPIO); + gpio_init(LED_GPIO); + gpio_set_dir(LED_GPIO, GPIO_OUT); +} + +/** + * @brief Print and increment regular vs static variables, update LED + * + * @details Prints both variable values, increments them, reads the + * button state and drives the LED accordingly. + * + * @retval None + */ +static void demo_static_variable(void) { + uint8_t regular_fav_num = 42; + static uint8_t static_fav_num = 42; + printf("regular_fav_num: %d\r\n", regular_fav_num); + printf("static_fav_num: %d\r\n", static_fav_num); + regular_fav_num++; + static_fav_num++; + bool pressed = gpio_get(BUTTON_GPIO); + gpio_put(LED_GPIO, pressed ? 0 : 1); +} + +int main(void) { + stdio_init_all(); + init_gpio(); + while (true) { + demo_static_variable(); + } +} diff --git a/0x0014_static-variables/CMakeLists.txt b/0x0014_static-variables/CMakeLists.txt new file mode 100644 index 0000000..63d1f6b --- /dev/null +++ b/0x0014_static-variables/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0014_static-variables C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0014_static-variables 0x0014_static-variables.c ) + +pico_set_program_name(0x0014_static-variables "0x0014_static-variables") +pico_set_program_version(0x0014_static-variables "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0014_static-variables 1) +pico_enable_stdio_usb(0x0014_static-variables 0) + +# Add the standard library to the build +target_link_libraries(0x0014_static-variables + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0014_static-variables PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0014_static-variables) + diff --git a/0x0014_static-variables/pico_sdk_import.cmake b/0x0014_static-variables/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0014_static-variables/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0017_constants/.gitignore b/0x0017_constants/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0017_constants/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0017_constants/.vscode/c_cpp_properties.json b/0x0017_constants/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c429d6d --- /dev/null +++ b/0x0017_constants/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0017_constants/.vscode/cmake-kits.json b/0x0017_constants/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0017_constants/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0017_constants/.vscode/extensions.json b/0x0017_constants/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0017_constants/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0017_constants/.vscode/launch.json b/0x0017_constants/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0017_constants/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0017_constants/.vscode/settings.json b/0x0017_constants/.vscode/settings.json new file mode 100644 index 0000000..8aecbe6 --- /dev/null +++ b/0x0017_constants/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/0x0017_constants/.vscode/tasks.json b/0x0017_constants/.vscode/tasks.json new file mode 100644 index 0000000..d1b3193 --- /dev/null +++ b/0x0017_constants/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0017_constants/0x0017_constants.c b/0x0017_constants/0x0017_constants.c new file mode 100644 index 0000000..f3b5182 --- /dev/null +++ b/0x0017_constants/0x0017_constants.c @@ -0,0 +1,100 @@ +/** + * @file 0x0017_constants.c + * @brief Constants: demonstrate #define and const with LCD1602 I2C display + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates #define macro constants and const-qualified variables. + * Initializes an LCD1602 display over I2C and prints constant values + * over UART in an infinite loop. + * + * Wiring: + * GPIO2 (SDA) -> LCD1602 PCF8574 SDA + * GPIO3 (SCL) -> LCD1602 PCF8574 SCL + * 3V3 -> LCD1602 VCC + * GND -> LCD1602 GND + */ + +#include +#include +#include "pico/stdlib.h" +#include "hardware/i2c.h" +#include "lcd_1602.h" + +/** @brief Macro constant for favorite number */ +#define FAV_NUM 42 +/** @brief I2C peripheral instance */ +#define I2C_PORT i2c1 +/** @brief GPIO pin for I2C SDA */ +#define I2C_SDA_PIN 2 +/** @brief GPIO pin for I2C SCL */ +#define I2C_SCL_PIN 3 + +/** @brief Const-qualified favorite number */ +const int OTHER_FAV_NUM = 1337; + +/** + * @brief Initialize I2C bus and LCD1602 display + * + * @details Configures I2C1 at 100kHz on GPIO2/3, initializes the + * LCD via PCF8574 at address 0x27, and writes two lines. + * + * @retval None + */ +static void init_i2c_and_lcd(void) { + i2c_init(I2C_PORT, 100000); + gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C); + gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C); + gpio_pull_up(I2C_SDA_PIN); + gpio_pull_up(I2C_SCL_PIN); + lcd_i2c_init(I2C_PORT, 0x27, 4, 0x08); +} + +/** + * @brief Write greeting text to the LCD display + * + * @details Sets cursor to line 0 and writes "Reverse", then sets + * cursor to line 1 and writes "Engineering". + * + * @retval None + */ +static void write_lcd_greeting(void) { + lcd_set_cursor(0, 0); + lcd_puts("Reverse"); + lcd_set_cursor(1, 0); + lcd_puts("Engineering"); +} + +int main(void) { + stdio_init_all(); + init_i2c_and_lcd(); + write_lcd_greeting(); + while (true) { + printf("FAV_NUM: %d\r\n", FAV_NUM); + printf("OTHER_FAV_NUM: %d\r\n", OTHER_FAV_NUM); + } +} diff --git a/0x0017_constants/CMakeLists.txt b/0x0017_constants/CMakeLists.txt new file mode 100644 index 0000000..4c75aa3 --- /dev/null +++ b/0x0017_constants/CMakeLists.txt @@ -0,0 +1,59 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0017_constants C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0017_constants 0x0017_constants.c lcd_1602.c) + +pico_set_program_name(0x0017_constants "0x0017_constants") +pico_set_program_version(0x0017_constants "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0017_constants 1) +pico_enable_stdio_usb(0x0017_constants 0) + +# Add the standard library to the build +target_link_libraries(0x0017_constants + pico_stdlib + hardware_i2c + hardware_gpio) + +# Add the standard include files to the build +target_include_directories(0x0017_constants PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0017_constants) + diff --git a/0x0017_constants/lcd_1602.c b/0x0017_constants/lcd_1602.c new file mode 100644 index 0000000..80054cc --- /dev/null +++ b/0x0017_constants/lcd_1602.c @@ -0,0 +1,160 @@ +/** + * @file lcd_1602.c + * @brief Implementation of PCF8574-backed HD44780 (16x2) LCD driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "lcd_1602.h" +#include +#include + +/** @brief I2C instance pointer for the LCD */ +static i2c_inst_t *lcd_i2c = NULL; +/** @brief I2C address of the PCF8574 backpack */ +static uint8_t lcd_addr = 0x27; +/** @brief Bit shift for 4-bit nibble position */ +static int lcd_nibble_shift = 4; +/** @brief PCF8574 bit mask controlling the backlight */ +static uint8_t lcd_backlight_mask = 0x08; + +/** @brief PCF8574 bit mask for Register Select */ +#define PIN_RS 0x01 +/** @brief PCF8574 bit mask for Read/Write */ +#define PIN_RW 0x02 +/** @brief PCF8574 bit mask for Enable */ +#define PIN_EN 0x04 + +/** + * @brief Write one raw byte to the PCF8574 expander over I2C + * + * @param data Output byte to send to the expander + */ +static void pcf_write_byte(uint8_t data) { + if (!lcd_i2c) return; + i2c_write_blocking(lcd_i2c, lcd_addr, &data, 1, false); +} + +/** + * @brief Toggle EN to latch a nibble into the LCD controller + * + * @param data Current control/data bus byte (with RS and backlight already set) + */ +static void pcf_pulse_enable(uint8_t data) { + pcf_write_byte(data | PIN_EN); + sleep_us(1); + pcf_write_byte(data & ~PIN_EN); + sleep_us(50); +} + +/** + * @brief Write one 4-bit nibble to the LCD + * + * @param nibble Lower 4 bits to write + * @param mode 0 for command, non-zero for character data + */ +static void lcd_write4(uint8_t nibble, uint8_t mode) { + uint8_t data = (nibble & 0x0F) << lcd_nibble_shift; + data |= mode ? PIN_RS : 0; + data |= lcd_backlight_mask; + pcf_pulse_enable(data); +} + +/** + * @brief Send one full 8-bit command/data value as two nibbles + * + * @param value Byte to send to the LCD + * @param mode 0 for command, non-zero for character data + */ +static void lcd_send(uint8_t value, uint8_t mode) { + lcd_write4((value >> 4) & 0x0F, mode); + lcd_write4(value & 0x0F, mode); +} + +/** + * @brief Store LCD driver configuration in module-level state + * + * @param i2c Pointer to the I2C instance + * @param pcf_addr 7-bit PCF8574 address + * @param nibble_shift Bit shift for 4-bit nibbles + * @param backlight_mask Backlight control bit mask + */ +static void lcd_store_config(i2c_inst_t *i2c, uint8_t pcf_addr, + int nibble_shift, uint8_t backlight_mask) { + lcd_i2c = i2c; + lcd_addr = pcf_addr; + lcd_nibble_shift = nibble_shift; + lcd_backlight_mask = backlight_mask; +} + +/** + * @brief Execute the HD44780 4-bit mode power-on reset sequence + */ +static void lcd_hd44780_reset(void) { + lcd_write4(0x03, 0); + sleep_ms(5); + lcd_write4(0x03, 0); + sleep_us(150); + lcd_write4(0x03, 0); + sleep_us(150); + lcd_write4(0x02, 0); + sleep_us(150); +} + +/** + * @brief Send post-reset configuration commands to the HD44780 + * + * Sets 4-bit mode with 2 display lines, turns the display on with + * cursor hidden, clears the screen, and selects left-to-right entry mode. + */ +static void lcd_hd44780_configure(void) { + lcd_send(0x28, 0); + lcd_send(0x0C, 0); + lcd_send(0x01, 0); + sleep_ms(2); + lcd_send(0x06, 0); +} + +void lcd_i2c_init(i2c_inst_t *i2c, uint8_t pcf_addr, int nibble_shift, uint8_t backlight_mask) { + lcd_store_config(i2c, pcf_addr, nibble_shift, backlight_mask); + lcd_hd44780_reset(); + lcd_hd44780_configure(); +} + +void lcd_clear(void) { + lcd_send(0x01, 0); + sleep_ms(2); +} + +void lcd_set_cursor(int line, int position) { + const uint8_t row_offsets[] = {0x00, 0x40}; + if (line > 1) line = 1; + lcd_send(0x80 | (position + row_offsets[line]), 0); +} + +void lcd_puts(const char *s) { + while (*s) + lcd_send((uint8_t)*s++, 1); +} diff --git a/0x0017_constants/lcd_1602.h b/0x0017_constants/lcd_1602.h new file mode 100644 index 0000000..072ae13 --- /dev/null +++ b/0x0017_constants/lcd_1602.h @@ -0,0 +1,74 @@ +/** + * @file lcd_1602.h + * @brief Header for PCF8574-backed HD44780 (16x2) LCD driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef LCD_1602_H +#define LCD_1602_H + +#include +#include "pico/stdlib.h" +#include "hardware/i2c.h" + +/** + * @brief Initialize the LCD driver over I2C + * + * Configures the internal driver state and performs the HD44780 initialization + * sequence. The driver does not configure I2C pins or call i2c_init; that + * must be done by the caller prior to calling this function. + * + * @param i2c Pointer to the I2C instance (e.g. i2c0 or i2c1) + * @param pcf_addr PCF8574 I2C address (commonly 0x27 or 0x3F) + * @param nibble_shift Bit shift applied to 4-bit nibbles (commonly 4 or 0) + * @param backlight_mask PCF8574 bit mask that controls the backlight + */ +void lcd_i2c_init(i2c_inst_t *i2c, uint8_t pcf_addr, int nibble_shift, uint8_t backlight_mask); + +/** + * @brief Clear the LCD display + * + * Clears the display and returns the cursor to the home position. This + * call blocks for the duration required by the HD44780 controller. + */ +void lcd_clear(void); + +/** + * @brief Set the cursor position + * + * @param line Line number (0 or 1) + * @param position Column (0..15) + */ +void lcd_set_cursor(int line, int position); + +/** + * @brief Write a null-terminated string to the display + * + * @param s The string to write (ASCII) + */ +void lcd_puts(const char *s); + +#endif // LCD_1602_H diff --git a/0x0017_constants/pico_sdk_import.cmake b/0x0017_constants/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0017_constants/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0017a_ctf/.gitignore b/0x0017a_ctf/.gitignore new file mode 100644 index 0000000..de8404a --- /dev/null +++ b/0x0017a_ctf/.gitignore @@ -0,0 +1,4 @@ +build +!.vscode/* +!build-ctf/CTF-02.bin +!CTF-02.bin diff --git a/0x0017a_ctf/.vscode/c_cpp_properties.json b/0x0017a_ctf/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..613c996 --- /dev/null +++ b/0x0017a_ctf/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.3.1/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.3.1/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/15_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0017a_ctf/.vscode/cmake-kits.json b/0x0017a_ctf/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0017a_ctf/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0017a_ctf/.vscode/extensions.json b/0x0017a_ctf/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0017a_ctf/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0017a_ctf/.vscode/launch.json b/0x0017a_ctf/.vscode/launch.json new file mode 100644 index 0000000..451b846 --- /dev/null +++ b/0x0017a_ctf/.vscode/launch.json @@ -0,0 +1,52 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "debuggerArgs": ["-ex", "set debug-file-directory /debug"], + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.3.1/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "debuggerArgs": ["-ex", "set debug-file-directory /debug"], + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.3.1/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + } + ] +} diff --git a/0x0017a_ctf/.vscode/settings.json b/0x0017a_ctf/.vscode/settings.json new file mode 100644 index 0000000..5b883ba --- /dev/null +++ b/0x0017a_ctf/.vscode/settings.json @@ -0,0 +1,46 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + }, + "variant": { + "statusBarVisibility": "hidden" + }, + "buildTarget": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v4.3.4/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.3.1", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/15_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/15_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.3.1/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v4.3.4/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.13.2;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.3.1", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/15_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/15_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.3.1/picotool:${env:HOME}/.pico-sdk/cmake/v4.3.4/bin:${env:HOME}/.pico-sdk/ninja/v1.13.2:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.3.1", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/15_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/15_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.3.1/picotool:${env:HOME}/.pico-sdk/cmake/v4.3.4/bin:${env:HOME}/.pico-sdk/ninja/v1.13.2:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v4.3.4/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.13.2/ninja" +} diff --git a/0x0017a_ctf/.vscode/tasks.json b/0x0017a_ctf/.vscode/tasks.json new file mode 100644 index 0000000..2f07d94 --- /dev/null +++ b/0x0017a_ctf/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.13.2/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.13.2/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.3.1/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.3.1/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + } + ] +} diff --git a/0x0017a_ctf/CMakeLists.txt b/0x0017a_ctf/CMakeLists.txt new file mode 100644 index 0000000..7a6ba40 --- /dev/null +++ b/0x0017a_ctf/CMakeLists.txt @@ -0,0 +1,128 @@ +# MIT License +# +# Copyright (c) 2026 Kevin Thomas +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Author: Kevin Thomas +# Email: kevin@mytechnotalent.com +# GitHub: https://github.com/mytechnotalent +# File: CMakeLists.txt +# Desc: Configures the RP2350 Pico SDK project and cryptographic module +# targets for the DEEPLINE Metro practice firmware. Mirrors the +# hardened Ouroboros construction of encryption-c-rp2350. +# Created: 2026 + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.3.1) +set(toolchainVersion 15_2_Rel1) +set(picotoolVersion 2.3.1) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if(EXISTS ${picoVscode}) + include(${picoVscode}) +endif() + +set(PICO_BOARD pico2 CACHE STRING "Board type") + +include(pico_sdk_import.cmake) +project(CTF-02 C CXX ASM) +find_package(Python3 COMPONENTS Interpreter REQUIRED) + +set(DEMO_ARTIFACT_JSON ${CMAKE_CURRENT_LIST_DIR}/scripts/demo_artifact.json) +set(DEMO_ARTIFACT_HEADER_COMMITTED ${CMAKE_CURRENT_LIST_DIR}/include/demo_artifact.h) +set(DEMO_ARTIFACT_HEADER_GENERATED ${CMAKE_CURRENT_BINARY_DIR}/generated/demo_artifact.h) + +add_custom_command( + OUTPUT ${DEMO_ARTIFACT_HEADER_GENERATED} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/generated + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/scripts/dec.py + --from-json ${DEMO_ARTIFACT_JSON} + --header-out ${DEMO_ARTIFACT_HEADER_GENERATED} + --check-header-path ${DEMO_ARTIFACT_HEADER_COMMITTED} + DEPENDS + ${CMAKE_CURRENT_LIST_DIR}/scripts/dec.py + ${DEMO_ARTIFACT_JSON} + ${DEMO_ARTIFACT_HEADER_COMMITTED} + COMMENT "Regenerating demo artifact header from JSON and checking committed header freshness" + VERBATIM +) + +add_custom_target(check_demo_artifact_header DEPENDS ${DEMO_ARTIFACT_HEADER_GENERATED}) + +pico_sdk_init() + +if(NOT PICO_MBEDTLS_PATH) + set(PICO_MBEDTLS_PATH ${PICO_SDK_PATH}/lib/mbedtls) +endif() + +# Argon2id reference implementation (PHC winner), compiled single-threaded. +set(ARGON2_SRC ${CMAKE_CURRENT_LIST_DIR}/third_party/argon2) +add_library(argon2_ref STATIC + ${ARGON2_SRC}/src/argon2.c + ${ARGON2_SRC}/src/core.c + ${ARGON2_SRC}/src/ref.c + ${ARGON2_SRC}/src/encoding.c + ${ARGON2_SRC}/src/blake2/blake2b.c +) +target_compile_definitions(argon2_ref PUBLIC ARGON2_NO_THREADS) +target_include_directories(argon2_ref PUBLIC + ${ARGON2_SRC}/include + ${ARGON2_SRC}/src +) + +# mbedTLS subset: ChaCha20, Poly1305, ChaCha20-Poly1305, constant-time. +add_library(mbedtls_subset STATIC + ${PICO_MBEDTLS_PATH}/library/chacha20.c + ${PICO_MBEDTLS_PATH}/library/poly1305.c + ${PICO_MBEDTLS_PATH}/library/chachapoly.c + ${PICO_MBEDTLS_PATH}/library/constant_time.c + src/mbedtls_shims.c +) +target_compile_definitions(mbedtls_subset PUBLIC MBEDTLS_CONFIG_FILE="mbedtls_config.h") +target_include_directories(mbedtls_subset PUBLIC + include + ${PICO_MBEDTLS_PATH}/include + ${PICO_MBEDTLS_PATH}/library +) + +# Ouroboros authentication engine consuming the Argon2id and AEAD layers. +add_library(auth STATIC src/auth.c) +add_dependencies(auth check_demo_artifact_header) +target_include_directories(auth PUBLIC include) +target_link_libraries(auth PUBLIC pico_stdlib mbedtls_subset argon2_ref) + +# DEEPLINE Metro practice firmware executable. +add_executable(CTF-02 src/main.c) +target_link_libraries(CTF-02 PRIVATE auth pico_stdlib) +pico_enable_stdio_uart(CTF-02 0) +pico_enable_stdio_usb(CTF-02 1) +target_compile_definitions(CTF-02 PRIVATE + PICO_DEFAULT_UART_BAUD_RATE=115200 +) +pico_add_extra_outputs(CTF-02) \ No newline at end of file diff --git a/0x0017a_ctf/CTF-02-I.md b/0x0017a_ctf/CTF-02-I.md new file mode 100644 index 0000000..37de55e --- /dev/null +++ b/0x0017a_ctf/CTF-02-I.md @@ -0,0 +1,673 @@ +# Operation Copperhead - Student Instructions + +**⚠ DEEPLINE METRO EMERGENCY INCIDENT ⚠** + +``` ++----------------------------------------------------------------------------------------+ +| | +| ██████╗ ██╗ █████╗ ██████╗██╗ ██╗███████╗████████╗ █████╗ ██████╗ ████████╗ | +| ██╔══██╗██║ ██╔══██╗██╔════╝██║ ██╔╝██╔════╝╚══██╔══╝██╔══██╗██╔══██╗╚══██╔══╝ | +| ██████╔╝██║ ███████║██║ █████╔╝ ███████╗ ██║ ███████║██████╔╝ ██║ | +| ██╔══██╗██║ ██╔══██║██║ ██╔═██╗ ╚════██║ ██║ ██╔══██║██╔══██╗ ██║ | +| ██████╔╝███████╗██║ ██║╚██████╗██║ ██╗███████╗ ██║ ██║ ██║██║ ██║ ██║ | +| ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝██║ ██║╚══════╝ ╚═╝ ╚═╝ ╚═╝██║ ██║ ██║ | +| | +| | +| O P E R A T I O N C O P P E R H E A D | +| | +| *** PRIORITY RED *** | +| | ++----------------------------------------------------------------------------------------+ +``` + +--- + +## Project Overview + +DEEPLINE Metro Authority's rebuilt DEEPLINE-AUTH field relay image shipped +four corrupted engineering constants: a miscalibrated release threshold, a +false TRACK banner string, an overstated block length, and a poisoned ARX +signal seed. The corrupted image reports an occupied BRIDGE-4 block as +stable and authorized while rescue crews approach, and the source used for +the emergency rebuild was overwritten seventeen minutes later and cannot be +recovered. Students reverse engineer `CTF-02.bin` with Ghidra, patch all +four defects, capture the runtime-derived signal key live in GDB, recover +and authenticate the Ouroboros authority frame, export a corrected image, +flash it to a Pico 2, and prove the corrected behavior on real hardware. + +--- + +## Scenario Briefing + +### Read This First (Plain English) + +The story uses rail-signalling words that read as jargon the first time you +hit them. Here is what they mean; keep this list open while you read. + +- **Block**: a fixed section of track. Only one train may occupy it at a + time. A block is **safe** when it is empty, so the train waiting at its + start may proceed, and **occupied** when a train is inside it, so the + train behind must hold. +- **Track circuit**: a current sent through the rails to detect where + trains are. A train's wheels short the rails and drop that current, which + is how the system knows a block is occupied. +- **Pilot wire**: the sensing line that carries the track-circuit reading + back to the relay. The **dead pilot wire** in this story has a frozen + reading: the value is latched and no longer updates. +- **Field relay**: the small embedded controller, one per block, that + watches the reading and decides hold versus authorize. This challenge uses + a Pico 2 as the relay. +- **Command floor**: the central control room for the whole railway. +- **Blacklock**: a coordinated cyberattack that bricks the control room and + locks everyone out of central operations. After a blacklock, every safety + decision falls back to the local relays. +- **How deep?**: the DEEPLINE corridor runs roughly 40 meters below street + level inside a hardened tube called the armored shell. + +Once these words make sense, the incident below is a simple story: a relay +is lying about whether the block ahead is clear, and your job is to find +the corrupted bytes that make it lie. + +### Background + +**DEEPLINE Metro Authority** runs an armored railway deep beneath the +city, roughly 40 meters below street level, inside a hardened tube called +the armored shell. Armored two-car trains carry people through it, and +tonight they are also carrying rescue crews toward riders trapped inside +the tunnels. + +To understand what is happening, picture how the line stays safe. The line +is cut into fixed stretches of track called **blocks**. A block is one +piece of track, and the rule is absolute: only one train may be inside a +block at any moment. Before a train may roll out of its current block and +into the next one, the system must first prove the next block is empty. +Empty means safe, and safe means the train may proceed. Occupied means +danger, and danger means the train must stop and wait. + +How does the system prove a block is empty? It uses electricity. A steady +current is pushed into the rails of every block, and a small embedded +computer called a **field relay** (call sign **DEEPLINE-AUTH**) watches +that current. This is called a **track circuit**. When nothing is on a +block, the current flows normally. When a train rolls in, its steel wheels +connect the two rails and the current changes, and that change is the +relay's signal that a train is there. The **pilot wire** is the sensing +line that carries this reading from the rails up to the relay. + +So every block has a relay doing the same honest job: read the current, +ask "is the block ahead empty?", and answer with only two words, **HOLD** +(stop, do not move) or **AUTHORIZE** (the way is clear, go). The relays are +the railway's nervous system, and with the command floor dead they are the +only nervous system left. + +The command floor is the central control room where humans used to watch +the entire line. It was **blacklocked** by a coordinated attack: shut out, +locked, and made useless. When it went dark, the trains lost their view +from above. Every safety decision dropped down to the relays on the +ground, running their local firmware, deciding hold or authorize block by +block. + +At 0341 UTC, a threat actor known as **Cortex Sledge** posted a message +calling the moment before it happened: a blacklock of the DEEPLINE control +floor followed by a silent corruption of the field relay images, so nothing +inside the tunnels could agree on what was safe. The blacklock landed. With +the network coordination center offline and rescue crews already inside the +armored shell, the engineering team rebuilt the relay firmware around the +**Ouroboros hardened gate**: an encrypted authority frame protected by a +12-word operation passphrase and sealed with Argon2id plus +XChaCha20-Poly1305. The rebuilt image was pushed to the fleet within +minutes of the blacklock. + +That speed is where the story goes wrong, and it is the trap you walk +into tonight. + +### The Origin of the Ouroboros Gate + +The Ouroboros gate did not come from DEEPLINE. It came from a reclusive +cryptographer who spent a decade obsessed with a single, improbable goal: +to write encryption that could not be broken, not by anyone, not ever. +The engineers who worked beside him dismissed the obsession as unworkable, +and he never argued. When he finished, he published the work as a single +squashed archive tagged **v0.1.0**, with the compiled gate firmware image +attached to the release, and then vanished. + +What he left behind is the strict Ouroboros construction: a memory-hard +Argon2id key schedule feeding an authenticated XChaCha20-Poly1305 authority +frame: symmetric cryptography hardened for the RP2350's memory budget, with +an honest threat model instead of a sales pitch. In plain terms, Ouroboros +is the relay's lock: an order is only trusted if an operator holding the +correct 12-word phrase unlocks it. The corruption in this challenge is +separate from that lock, plain wrong numbers in the safety math, not a +crack in the encryption. DEEPLINE quietly adopted it +for the field relays because it was the strongest gate anyone had ever +shipped that would still boot on the target. A decade of asking +"what if it must not be broken?" is the only reason the relay console can be +an authoritative gate at all. Tonight, in a tunnel with rescue crews +approaching a block the firmware is lying about, that authenticated gate +matters more than DEEPLINE's own design review ever did. + +### The Disaster + +The relay boots. It prints a status report. It reports **TRACK: NORMAL**, +**BLOCK STATE: STABLE**, and **AUTO TRAIN: AUTHORIZED**. To anyone standing +in the tunnel, that console looks like the railway giving the all-clear: +the track is fine, the block ahead is stable and empty, and the train may +move. + +The console is lying, and here is the math behind the lie, step by step. + +Step 1. The relay is reading **87 A** from the dead pilot wire. A pilot +wire goes dead when the reading freezes, so the relay is looking at a stale +number instead of live reality. That number is thrust in front of the relay +every cycle, and the relay keeps trusting it. + +Step 2. DEEPLINE's hard engineering rule says no train may be released +into a block whose reading is at or above **60 A**. 87 A is nearly 45 +percent beyond that limit. In plain terms, the reading is screaming that +the insulated block ahead is compromised. + +Step 3. The compromised block in this incident is the exact block where +the two-car train is sitting right now, with rescue crews approaching on +foot. The reading is not noise and it is not a drill. The block ahead is +occupied. + +An honest relay would do that arithmetic and reach the only logical answer: +reading too high, block unsafe, print BLOCK STATE: CRITICAL, set AUTO TRAIN: +HELD, and hold the train. That is what the relay was designed to do, and it is +the only thing standing between the rescue corridor and a collision. + +The image that shipped does the opposite. It prints STABLE. It prints +AUTHORIZED. It tells the train the way is clear when the way is not clear. + +The reason is corruption. Between the safe reference firmware and the image +pushed to the fleet, four engineering constants were changed. A constant is +a fixed number baked into the firmware, like the amounts in a recipe, and a +single wrong number can flip the entire verdict. The corrupted image +believes 87 A is acceptable, believes the occupied block is empty, and will +hand the train a green light straight into the rescue crews' tunnel. + +If the fleet trusts that console, the two-car train is dispatched into the +occupied block at the same moment the crews mark the corridor with their +[chemlight], and nobody gets a second chance at that tunnel. + +**The rushed build has defects. The four constants were corrupted between +the safe reference firmware and the image that shipped. The source used for +the emergency rebuild was overwritten by the next build seventeen minutes +later and cannot be recovered. Nobody has found where the corrupt values +live in the compiled image.** That is the hole you were called in to fill. + +### The Only Surviving Evidence + +One field relay, the training/verification unit, still holds the exact +miscompiled image that shipped to the fleet. This image, and this image +alone, is the only remaining copy of the emergency build. There is no +source code. There is no build log. There is only the compiled image, a USB +console link, an SWD debug probe, and whatever a skilled embedded reverse +engineer can prove by reading machine code. + +### The Human Stakes + +| Consequence if the false "STABLE" reading is trusted | Scale | +|---|---| +| Two-car train dispatched into an occupied BRIDGE-4 block | 1 train | +| Rescue crews walking toward a block the console calls safe | 4 teams | +| Stations and hospital spokes on backup power after blacklock | 29 facilities | +| Estimated riders stranded in the armored shell | 210+ people | + +**The options are:** + +1. ❌ **Trust the console**: the train releases on a false reading, the + BRIDGE-4 corridor becomes a collision scene. +2. ❌ **Scrap the fleet's firmware**, which buys time but leaves crews in the + tunnel with no interlocking and no authority frame at all. +3. **REVERSE ENGINEER THE EMERGENCY BUILD**: find the exact corrupt + bytes, patch them, prove the corrected image on real hardware, and hand + the fix to the field team so the *rest of the fleet* can be repatched + before the next attempt. + +### THE SHORTAGE + +For years, the world treated embedded systems as invisible infrastructure. +The engineers who could read a vector table, decode a Thumb branch, or +patch a corrupted constant directly in a stripped binary were never +numerous enough. Tonight almost all of them are already in the field +chasing other failures. **You are the reserve team.** + +You were called in because you can do something no exhausted command-floor +team can do right now: read what the processor is actually doing, with no +source code, no time for a rewrite, and no room for a guess. + +> **⏰ TIME PRESSURE:** The field team is standing by to push your verified +> patch to the rest of the DEEPLINE fleet. Every relay still reporting a +> false "STABLE" status is one two-car release away from a disaster. + +> **AUTHORIZED LAB ONLY:** This challenge uses a supplied Pico 2 training +> relay and its exact corrupted firmware image. Do not connect this +> exercise to a public network, an operational railway, a metro system, or +> any device you do not own or have explicit written authorization to test. + +--- + +## Learning Objectives + +- Decode an ARM Cortex-M33 vector and boot table and identify the reset + handler and initial stack pointer. +- Translate Thumb reset-vector addresses into real function entry points and + trace literal-pool entries to their data. +- Locate four corrupted constants: a boundary comparison, a status string, + an 8-byte IEEE-754 double, and an ARX signal seed. +- Analyze unsigned compare semantics, condition codes, and compiler + transforms of boundary tests. +- Capture a runtime-derived key with GDB, override a register, and set a + watchpoint on stored SRAM state. +- Recover and authenticate an Argon2id plus XChaCha20-Poly1305 authority + frame and describe the crypto pipeline with an honest threat boundary. +- Export and UF2-convert a corrected image, then prove the corrected + behavior on real hardware. + +--- + +## What This Project Tests + +| Week | Concepts Tested | +|------|-----------------| +| 1 | RP2350 architecture, ARM Cortex-M33 registers, stack, flash/RAM, Thumb assembly, Ghidra static analysis | +| 2 | GDB connection, breakpoints, disassembly, register and memory inspection, USB-CDC console observation | +| 3 | Bootrom handoff, vector table, reset handler, startup code, XIP, Thumb-bit addressing | +| 4 | Data segments (`.rodata` / `.data` / `.bss`), initialized data images, little-endian encoding, literal pools, soft-float double layout | +| 5 | Unsigned compare semantics, condition codes (`hi`/`ls`), compiler transforms (`<` vs `<=`), volatile refetch semantics | +| 6 | Runtime signal-key derivation (SENTINEL-ARX quarter rounds), live register capture of a derived key, memory watchpoints | +| 7 | Argon2id memory-hard KDF, XChaCha20-Poly1305 AEAD, HChaCha20 subkey, salt/nonce/tag, authenticated decryption | +| 8 | Ouroboros composition (Argon2id to AEAD to payload dispatch), honest threat-model analysis, incident reporting | + +--- + +## Part 1: Understanding the System + +### DEEPLINE-AUTH Field Relay Hardware + +| Component | Connection | Purpose | +|-----------|------------|---------| +| Raspberry Pi Pico 2 | RP2350 | Runs the corrupted emergency firmware | +| USB-CDC console | Micro-USB to host | Relay console and Ouroboros gate input | +| SWD debug interface | Supplied probe | Authorized GDB inspection | +| Onboard LED | GPIO 25 | Authentication success indicator | + +Every graded finding lives in flash (`.rodata` / `.text` / `.data` image) or +SRAM, and is reachable with only the Weeks 1-8 toolset: Ghidra, GDB, and a +serial console. + +### Console Configuration + +- Transport: USB-CDC virtual COM port (no external adapter needed) +- Baud: `115200` +- Data: `8 bits` +- Parity: `none` +- Stop: `1` +- Logic: `3.3 V` on the debug header + +### Normal (Intended) Behavior + +The relay should run the SENTINEL-ARX signal-key layer, classify the frozen +87 A reading against the **real** DEEPLINE safety limit of **60 A**, report +honestly, and still accept the **Ouroboros authority frame** when an +operator enters the correct 12-word phrase: + +``` ++-----------------------------------------------------------------+ +| Intended Relay Behavior | +| | +| 1. Boot and initialize USB-CDC stdio and the auth gate | +| 2. Print the boot identity and the true TRACK signal | +| 3. Compare the frozen 87 A reading against the 60 A limit | +| 4. 87 A exceeds 60 A, so the block is NOT stable | +| 5. Report BLOCK STATE: CRITICAL and AUTO TRAIN: HELD | +| 6. Mint a SIGNAL KEY each 2-second cycle and watch it match | +| the SIGNAL_SPEC 0x2D879291 (OK, not MISMATCH) | +| 7. The 12-word emergency phrase reauthorizes the frame: | +| AUTHORITY FRAME: VERIFIED and payload on UART | +| 8. Repeat the report once per cycle until conditions change | ++-----------------------------------------------------------------+ +``` + +### Observed (Buggy) Behavior: What You Will See When You First Flash `CTF-02.uf2` + +```text +DEEPLINE METRO AUTHORITY +ADAPTIVE SIGNAL WINDOW: 38 MINUTES +USB-CDC 115200 8N1 | AUTHORIZED LAB CONSOLE +TRACK: NORMAL +BLOCK STATE: STABLE +AUTO TRAIN: AUTHORIZED +BLOCK LENGTH: 3200 M +FAULT POLLS: 1 +SIGNAL KEY: 0x915DCFF8 MISMATCH +RESPONSE> +``` + +> **Terminal Timing Note:** The first four lines (`DEEPLINE METRO...` through +> `TRACK: NORMAL`) represent the **initial boot banner**, emitted once during +> startup. If your serial terminal (PuTTY) connects after the board has +> booted, you will observe the continuous 2-second status stream (`BLOCK +> STATE...` through `SIGNAL KEY...`). To view the boot banner in your terminal, +> reset the Pico (pulse `RUN` to `GND`) while PuTTY is actively connected. + +The status block repeats every 2 seconds with `FAULT POLLS` incrementing. +This is exactly what the field crews are seeing. It is wrong, and it is +wrong in **four independent ways** inside the compiled binary. Do not assume +the first readable sentence is the full truth: treat every printed line as +evidence to be checked against the machine code, not as a fact on its own. + +--- + +## Part 2: The Firmware + +You do not have the source code. It was overwritten seventeen minutes after +the emergency build shipped. You have only the compiled image. Your job is +to reverse engineer it with Ghidra, locate the corrupted constants, patch +the image directly, and prove the corrected behavior: exactly the way the +field team will need to repatch the rest of the deployed fleet. + +### What The Firmware Does + +1. Initializes USB-CDC stdio and the Ouroboros authentication gate. +2. Reads a frozen track-circuit current (87 A) latched on the dead pilot + wire before the blacklock. +3. Compares that reading against a compiled-in safety threshold: **twice**, + once for the operator-facing BLOCK STATE line and once for the automated + AUTO TRAIN decision. +4. Prints a boot banner containing an unconditional TRACK signal line. +5. Enters an infinite 2-second loop that derives a session signal key, + prints the block classification, dispatch decision, block length, fault + poll count, and signal-key verdict. +6. Accepts a 12-word operation passphrase at the `RESPONSE>` prompt and runs + it through the hardened **Ouroboros gate** (Argon2id key derivation plus + XChaCha20-Poly1305 authenticated decryption) before dispatching the + authority frame payload to GPIO25 and UART. + +### Bug Summary: What You Are Graded On + +| Bug # | Category | Severity | Description | Hint | +|-------|----------|----------|--------------|------| +| **Bug #1** | Miscompiled safety constant | **CRITICAL** | The safe-release threshold was compiled far too permissive (95 A). It is used **twice**: once for the operator-facing status and once for the automated train-release decision, and **both** copies must be corrected. | The real DEEPLINE limit is 60 A. Search for the wrong immediate value used in the comparison. | +| **Bug #2** | Hardcoded string literal | **HIGH** | The boot banner unconditionally prints `TRACK: NORMAL` regardless of the actual reading. | The correct word describes a system holding a train at 87 A against a 60 A limit, not "NORMAL". | +| **Bug #3** | Data-section constant | **HIGH** | The block length shipped as 3.2 km; the real BRIDGE-4 block is 0.32 km, far below minimum release spacing. It prints as metres. | Trace the `BLOCK LENGTH` line to its data image in flash. Little-endian IEEE-754 double. | +| **Bug #4** | Init-time seed constant | **HIGH** | The ARX seed fused into the image is `0x0A0A0A0A`; the real seed is `0x6B206574`. The derived signal key therefore never matches `SIGNAL_SPEC`, and the console reports `MISMATCH` every cycle. | The expected value `0x2D879291` is a literal in a pool. The seed is a `.data` image in flash. Ignore decoy `0A` bytes in the input dispatch table. | + +**Important:** The replacement text for Bug #2 **must be the same length** +as the original (`NORMAL` and `DANGER` are both 6 bytes). Patching a shorter +or longer string will corrupt adjacent flash data. + +### A Third Layer: Not a Bug, an Authorization Task + +The **Ouroboros authority frame** is the encrypted artifact that proves an +operator is legitimate: a 48-byte payload sealed with Argon2id-derived keys +and XChaCha20-Poly1305. It is never printed by the firmware's status lines. +Recovering it, by understanding the construction and authenticating with +the correct 12-word phrase, is required evidence for your final report. + +The power of this layer is real but must be described honestly. The +construction is Argon2id (memory-hard KDF) chained into XChaCha20-Poly1305 +(AEAD). Against Grover-style search, symmetric-key security exponents are +**halved**, not annihilated; this firmware is a demonstrator and does not +claim NIST post-quantum status. The honest claim is: **a high modeled +brute-force cost under the stated passphrase entropy and KDF assumptions**, +not "quantum-proof." Your report must state this boundary exactly. + +--- + +## Part 3: Your Assignment + +Whenever a task asks you to **Document** or **answer**, write your answers +in a single file named `CTF-02-Answers.md`. + +### Task 1: Setup and Initial Analysis + +1. Create a new Ghidra project named `Copperhead_Investigation`. +2. Import `CTF-02.bin`. +3. Configure the language as **ARM Cortex 32-bit, little endian**. +4. Set the base address to `0x10000000`. +5. Run auto-analysis. + +**Document:** +- A screenshot of the Ghidra **Import Results** or **Program Information** + window showing the project name, processor settings, and base address. +- The address of `main()`. +- The address of the recurring 2-second status loop (the branch target the + loop restarts from). +- The vector-table base, the initial stack pointer, and the reset-handler + pointer as stored (note its Thumb bit) versus the actual instruction + address. +- One representative literal-pool entry that feeds the status lines, and + what it points to. + +### Task 2: Find and Patch Bug #1: The Miscalibrated Release Threshold + +1. Find **both** locations where the frozen 87 A reading is compared against + the miscompiled safety constant. +2. Document the exact address, the original instruction, and the original + immediate value at each location. +3. Determine the correct immediate value. **Caution:** the compiler may not + have encoded the raw threshold you expect: a strict "less than" + comparison against an unsigned value is often optimized into a + "less-or-equal" comparison against one less than the threshold. Show + your reasoning. +4. Patch **both** locations in Ghidra using the **Bytes Window** workflow: + > **Critical ARM Thumb-2 Patching Note:** In ARM Cortex-M, compare instructions that directly precede conditional execution blocks (`ite ge`) must **not** be patched using the right-click *Patch Instruction* dialog. Ghidra's automatic re-disassembler encounters an internal context conflict with the subsequent `ite ge` instruction, which collapses Thumb decoding and swallows Compare Site B (`0x10000312`). + > + > To patch cleanly without breaking downstream disassembly, use the **Bytes Window**: + > 1. Ensure the Bytes window is open (**Window** -> **Bytes: CTF-02.bin**). + > 2. In the Bytes window toolbar, click the **pencil icon** (**Toggle Edit Mode**). + > 3. In the Listing window, click on address `0x10000302` (Compare Site A) and press **`C`** (**Clear Code Bytes**). The instruction temporarily clears into raw bytes (`5E 2B`). + > 4. In the Bytes window, locate offset `10000302`, click on `5E`, and change it to **`3B`**. + > 5. Click back in the Listing window on address `0x10000302` and press **`D`** (**Disassemble**). The instruction immediately disassembles cleanly as `cmp r3, #0x3b`. + > 6. Notice that Compare Site B at `0x10000312` remains completely intact and visible! Repeat the exact same steps at `0x10000312`: click `0x10000312` in the Listing, press **`C`**, change `5E` to **`3B`** in the Bytes window, click back in the Listing, and press **`D`**. + +**Questions to answer:** +- Why must both locations be patched? What happens if you only patch one? +- Why is a false "STABLE" classification on an 87 A reading dangerous for + an automated train release into an occupied block? + +### Task 3: Find and Patch Bug #2: The False TRACK Banner + +1. Find the boot-banner string that unconditionally reports the wrong + signal state. +2. Document its address and the exact bytes that must change. +3. Patch the string, preserving its exact length. + +**Questions to answer:** +- Document the original vs. patched bytes, character by character. +- Why is a hardcoded, unconditional status word more dangerous than one + that is at least computed from a (miscalibrated) reading? + +### Task 4: Find and Patch Bug #3: The Block Length Constant + +1. Use Ghidra to locate the `BLOCK LENGTH` status line and trace the value + it prints back to its source in the initialized data image. +2. Document the 8-byte IEEE-754 double as stored (little endian) and its + printed interpretation. +3. Patch the data image so the relay reports the real 320 m BRIDGE-4 block. + +**Questions to answer:** +- Show your byte-for-byte conversion from 3.2 km to 0.32 km. +- Why would a console that overstates block length by ten times be as + dangerous as one that understates it? + +### Task 5: Find and Patch Bug #4: The Signal Seed + +1. Find the `SIGNAL_SPEC` value `0x2D879291` in the binary and note where + it lives. +2. Locate the `.data` image in flash that the firmware copies into SRAM at + boot. Identify the seed word that is wrong. +3. Patch the seed so the runtime-derived key matches the spec. + +**Warning:** there are decoy `0x0A0A0A0A` bytes in the console input +dispatch table. The real seed is an initialized data image, not a jump-table +constant. + +**Questions to answer:** +- How is the signal key derived each cycle, and where does the failure + appear in the register trace? +- Does fixing the seed also authenticate the Ouroboros gate? Explain what + each layer does and does not protect. + +### Task 6: GDB Register Capture of the Derived Key + +Prove the signal-key failure from the live machine, not just from static +bytes: + +1. Connect GDB to the running relay via the SWD probe. +2. Break at the second `derive_session_key` call site, immediately after the + branch returns. +3. Read `$r0`. Record the bug-derived key. +4. Inspect the two arguments entering the derivation: the live seed from + SRAM and the derived IV. +5. Overwrite `$r0` with the correct spec value, step out, and confirm the + next status cycle prints `SIGNAL KEY: 0x2D879291 OK`. +6. Verify with a watchpoint on the stored key in SRAM. + +**Questions to answer:** +- Why is the derived value in `$r0` different from the spec, and what alone + in the image is responsible? +- What does the register overwrite prove that the static patch proves + differently (and vice versa)? + +### Task 7: Recover the Ouroboros Authority Frame + +1. In Ghidra, locate the embedded artifact: the 16-byte salt, the 24-byte + XChaCha nonce, and the 64-byte ciphertext-plus-tag. +2. Document the Argon2id parameters compiled into the gate (memory, time, + parallelism) and the payload layout contract (LED byte + UART bytes). +3. On the live relay, enter the canonical 12-word emergency phrase at the + `RESPONSE>` prompt. +4. Confirm the expected outcome: `AUTHORITY FRAME: VERIFIED`, the onboard + LED turning on, and the payload printed to the console. +5. Demonstrate the two failure paths (policy violation and wrong phrase). + +**Questions to answer (honest boundary required):** +- Walk through the full pipeline: Argon2id to 32-byte key, HChaCha20 subkey + from the nonce prefix, inner nonce, Poly1305 tag verification, payload + dispatch. +- What would Grover-style search actually change in this construction, and + why does this firmware not claim strict post-quantum status? + +### Task 8: Export and Verify + +1. Export your patched binary as `CTF-02_fixed.bin`. +2. Convert it to UF2 format for the RP2350: + ```bash + python uf2conv.py CTF-02_fixed.bin --base 0x10000000 --family 0xe48bff59 --output CTF-02_fixed.uf2 + ``` +3. Flash `CTF-02_fixed.uf2` to your Pico 2 and capture the corrected + console output. +4. Confirm the corrected image now reports **TRACK: DANGER**, **BLOCK STATE: + CRITICAL**, **AUTO TRAIN: HELD**, **BLOCK LENGTH: 320 M**, and **SIGNAL + KEY: 0x2D879291 OK**, an honest, safe report instead of a false + "all clear." +5. Build a summary table of every patch: address, original bytes, patched + bytes, and a one-line description. + +### Task 9: Written Reflection (short answers, 150 words or less each) + +1. Why is "the build was rushed under emergency pressure" not an acceptable + excuse for shipping a firmware defect that could dispatch a train into a + block occupied by rescue crews? +2. Name one concrete engineering practice (review, static analysis, + hardware-in-the-loop test, signature verification, etc.) that would have + caught **each** of the four graded bugs before this image reached the + fleet, and one practice that would have stopped the corrupted image from + **running** at all. + +--- + +## How To Breadboard + +- Connect the Pico 2 micro-USB port directly to the host computer. The + relay enumerates as a USB-CDC virtual COM device. +- Open the end-of-line tool of your choice at `115200 8N1`. +- Connect the supplied SWD probe to the debug header according to its + documented pinout for GDB access. +- Use **3.3 V logic only** on the debug header. Never connect a 5 V line to + a Pico GPIO. + +The supplied image is `CTF-02.bin` (for Ghidra analysis) and +`CTF-02.uf2` (for flashing). If your instructor supplies different +filenames, record the actual filenames in your report. + +Flash using BOOTSEL mode (hold BOOT, plug in USB) and copy the UF2 onto the +`RP2350` mass-storage drive, or use `picotool`. + +--- + +## Memory Map Reference + +| Region | Address | Purpose | +|--------|---------|---------| +| Bootrom | `0x00000000` | Immutable boot code | +| Flash/XIP | `0x10000000` | Vector table, code, rodata, `.data` init image | +| SRAM | `0x20000000` | Stack and writable state | + +--- + +## Submission Format + +Submit a folder containing: + +- `CTF-02-Answers.md`; +- screenshots or terminal transcripts; +- `CTF-02_fixed.bin` and `CTF-02_fixed.uf2`; +- the original image hash. + +--- + +## Success Criteria + +You complete the challenge when you can prove all of the following: + +- You can explain how the RP2350 reaches the relay's code from reset. +- You can locate and patch both copies of the miscalibrated threshold. +- You can locate and patch the false TRACK string without corrupting + adjacent data. +- You can locate and patch the corrupted block-length double in the data + image. +- You can locate and patch the corrupted ARX seed and explain why the + runtime-derived key misses its spec. +- You can capture and correct the derived key live in GDB and verify with a + watchpoint. +- You can authenticate through the Ouroboros gate with the correct phrase + and describe the crypto pipeline accurately, including the honest + quantum boundary. +- You can export, convert, flash, and prove the corrected behavior on real + hardware. + +--- + +## Academic Integrity + +By submitting this CTF work, you certify that: + +1. You used only the supplied training relay, image, and lab interface. +2. You did not connect the challenge to a public network, an operational + railway, a metro system, or any third-party device. +3. You understand that embedded reverse engineering and binary patching + require explicit authorization in any real-world context. +4. You will report any discovered weakness responsibly to the course + instructor. + +The world is short on people who can do this work. Treat that +responsibility seriously: verify before you patch, patch before you trust, +and never confuse a clean-looking status line with a safe system. + +--- + +## Reference Material + +- ARM Cortex-M33 Technical Reference Manual +- RP2350 datasheet +- GDB documentation +- Ghidra documentation: [https://ghidra-sre.org/](https://ghidra-sre.org/) +- Strict Ouroboros reference construction (v0.1.0), the published source of + this firmware's gate, with an honest threat model: + [https://github.com/mytechnotalent/encryption-c-rp2350](https://github.com/mytechnotalent/encryption-c-rp2350) +- Reference gate firmware image (v0.1.0 release): + [https://github.com/mytechnotalent/encryption-c-rp2350/releases/download/v0.1.0/encryption_app.uf2](https://github.com/mytechnotalent/encryption-c-rp2350/releases/download/v0.1.0/encryption_app.uf2) +- PHC reference Argon2: [https://github.com/P-H-C/phc-winner-argon2](https://github.com/P-H-C/phc-winner-argon2) diff --git a/0x0017a_ctf/CTF-02-I.pdf b/0x0017a_ctf/CTF-02-I.pdf new file mode 100644 index 0000000..9212a65 Binary files /dev/null and b/0x0017a_ctf/CTF-02-I.pdf differ diff --git a/0x0017a_ctf/CTF-02-R.md b/0x0017a_ctf/CTF-02-R.md new file mode 100644 index 0000000..c8d392a --- /dev/null +++ b/0x0017a_ctf/CTF-02-R.md @@ -0,0 +1,276 @@ +# Operation Copperhead - Requirements & Grading Criteria + +``` ++----------------------------------------------------------------------------------------+ +| | +| ██████╗ ██╗ █████╗ ██████╗██╗ ██╗███████╗████████╗ █████╗ ██████╗ ████████╗ | +| ██╔══██╗██║ ██╔══██╗██╔════╝██║ ██╔╝██╔════╝╚══██╔══╝██╔══██╗██╔══██╗╚══██╔══╝ | +| ██████╔╝██║ ███████║██║ █████╔╝ ███████╗ ██║ ███████║██████╔╝ ██║ | +| ██╔══██╗██║ ██╔══██║██║ ██╔═██╗ ╚════██║ ██║ ██╔══██║██╔══██╗ ██║ | +| ██████╔╝███████╗██║ ██║╚██████╗██║ ██╗███████╗ ██║ ██║ ██║██║ ██║ ██║ | +| ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝██║ ██║╚══════╝ ╚═╝ ╚═╝ ╚═╝██║ ██║ ██║ | +| | +| | +| O P E R A T I O N C O P P E R H E A D | +| | +| REQUIREMENTS & GRADING CRITERIA | +| | ++----------------------------------------------------------------------------------------+ +``` + +--- + +## Project Overview + +Students are the reverse-engineering reserve team called in after DEEPLINE +Metro Authority's rebuilt DEEPLINE-AUTH relay image shipped four corrupted +engineering constants: a miscalibrated release threshold, a false TRACK banner +string, an overstated block length, and a poisoned ARX signal seed. Students +reverse engineer `CTF-02.bin` with Ghidra, patch all four defects, capture the +runtime-derived signal key live in GDB, recover the Ouroboros authority frame, +export a corrected image, flash it to real hardware, and prove the corrected +behavior on a physical Pico 2. + +The challenge is a standalone capstone exercise and contains no answer, +constant, address, bug, or patch belonging to any other course assignment. + +--- + +## Learning Objectives + +- Decode an ARM Cortex-M33 vector and boot table and identify the reset handler + and initial stack pointer. +- Translate Thumb reset-vector addresses into real function entry points and + trace literal-pool entries to their data. +- Locate four corrupted constants: a boundary comparison, a status string, an + 8-byte IEEE-754 double, and an ARX signal seed. +- Capture a runtime-derived key with GDB, override a register, and set a + watchpoint on stored SRAM state. +- Recover and authenticate an Argon2id plus XChaCha20-Poly1305 authority frame. + +Students must use only Weeks 1-8 concepts: ARM registers, stack behavior, +USB-CDC output, GDB, Ghidra static analysis and binary patching, vector tables, +reset startup, XIP, Thumb addressing, data segments and literal pools, +condition-code analysis, runtime key derivation, and the Argon2id plus +XChaCha20-Poly1305 authenticated gate. + +--- + +## Deliverables Checklist + +| # | Deliverable | Format | Criterion | +|---|-------------|--------|-----------| +| 1 | Ghidra project screenshot | PNG/JPG | 1.1 | +| 2 | Vector table and boot table | Inside `CTF-02-Answers.md` | 1.2 | +| 3 | `main()` and status-loop table | Inside `CTF-02-Answers.md` | 1.3 | +| 4 | Literal pool trace | Inside `CTF-02-Answers.md` | 1.4 | +| 5 | Bug #1 evidence and patches | Inside `CTF-02-Answers.md` | 2.1-2.4 | +| 6 | Bug #2 evidence and patch | Inside `CTF-02-Answers.md` | 3.1-3.4 | +| 7 | Bug #3 evidence and patch | Inside `CTF-02-Answers.md` | 4.1-4.3 | +| 8 | Bug #4 evidence and patch | Inside `CTF-02-Answers.md` | 5.1-5.4 | +| 9 | GDB register capture | Inside `CTF-02-Answers.md` | 6.1-6.4 | +| 10 | Ouroboros gate recovery and auth | Inside `CTF-02-Answers.md` | 7.1-7.4 | +| 11 | `CTF-02_fixed.bin` | BIN file | 8.1 | +| 12 | `CTF-02_fixed.uf2` | UF2 file | 8.2 | +| 13 | Corrected console transcript | Inside `CTF-02-Answers.md` | 8.3 | +| 14 | Summary table of all patches | Inside `CTF-02-Answers.md` | 8.4 | +| 15 | Written reflection | Inside `CTF-02-Answers.md` | 9.1-9.2 | + +--- + +## Required Tools and Equipment + +| Tool | Purpose | +|------|---------| +| Raspberry Pi Pico 2 | Isolated target | +| USB-CDC virtual serial console | Observe output and type the gate passphrase | +| SWD debug probe | GDB inspection | +| Ghidra | Static analysis and binary patching | +| GDB | Dynamic analysis and register capture | +| Python (`uf2conv.py`) | UF2 conversion | +| `CTF-02.bin` and `CTF-02.uf2` | Supplied artifacts | + +Console settings: **USB-CDC virtual COM port, 115200 baud, 8 data bits, no +parity, 1 stop bit**. + +--- + +## Artifact Identity + +The instructor-issued artifact hashes are: + +```text +CTF-02.bin 85330C37CD0897746B1AF447E4BAC371DDE2042ABD2D61D58A61FE2A8EEF3537 +CTF-02.uf2 F3CD4840260DB820D792758CECACC5297BEF1971B9EACF7601279256D8AF1EAB +``` + +--- + +## Grading Rubric - Detailed Breakdown + +### Task 1: Setup and Initial Analysis (12 points) + +| Criterion | Points | Full credit | Partial credit | No credit | +|-----------|--------|-------------|----------------|-----------| +| Criterion 1.1: Ghidra Project Setup | 3 | Correct project name, `ARM:LE:32:Cortex` (ARM Cortex 32-bit little endian), base `0x10000000` | One item off | Not set up | +| Criterion 1.2: Vector Table Decoding | 3 | Correct base, initial SP, reset pointer | One missing | Not found | +| Criterion 1.3: main() and Status-Loop Addresses | 4 | Both addresses correct | One correct | Neither found | +| Criterion 1.4: Thumb Addressing and Literal Pool | 2 | Bit 0 cleared and one pool entry traced to its string | Partial | Incorrect | + +### Task 2: Find and Patch Bug #1: The Miscalibrated Release Threshold (15 points) + +| Criterion | Points | Full credit | Partial credit | No credit | +|-----------|--------|-------------|----------------|-----------| +| Criterion 2.1: Locate Compare Sites A and B | 6 | Both addresses and original bytes | One site | Not found | +| Criterion 2.2: Correct Immediate-Value Reasoning | 4 | Explains the `<` to `<=` transform and gives `0x3B` | Correct value, no reasoning | Wrong value | +| Criterion 2.3: Patch Compare Sites A and B | 4 | Both byte changes verified | One site | Not patched | +| Criterion 2.4: Explain Why Both Sites Must Be Patched | 1 | Clear explanation of the two independent comparisons | Vague | Missing | + +### Task 3: Find and Patch Bug #2: The False TRACK Banner (10 points) + +| Criterion | Points | Full credit | Partial credit | No credit | +|-----------|--------|-------------|----------------|-----------| +| Criterion 3.1: Locate the Banner String | 3 | Correct address and cross-reference | Approximate | Not found | +| Criterion 3.2: Patch Six Characters | 4 | All six bytes changed, length preserved | Correct text, wrong bytes documented | Wrong length | +| Criterion 3.3: Character-by-Character Documentation | 2 | Original vs patched byte for all six characters | Partial | Missing | +| Criterion 3.4: Explain the Danger of a Hardcoded Status Word | 1 | Clear, specific reasoning | Generic | Missing | + +### Task 4: Find and Patch Bug #3: The Block Length Constant (10 points) + +| Criterion | Points | Full credit | Partial credit | No credit | +|-----------|--------|-------------|----------------|-----------| +| Criterion 4.1: Locate the .data Double | 3 | Correct address traced from the BLOCK LENGTH print | Approximate | Not found | +| Criterion 4.2: IEEE-754 Bytes and Print Math | 4 | Original and patched 8-byte double with print math (3200 M to 320 M) | Correct patch, no math | Wrong bytes | +| Criterion 4.3: Patch to Print 320 M | 3 | Console shows `BLOCK LENGTH: 320 M` | Wrong bytes | Not patched | + +### Task 5: Find and Patch Bug #4: The Signal Seed (15 points) + +| Criterion | Points | Full credit | Partial credit | No credit | +|-----------|--------|-------------|----------------|-----------| +| Criterion 5.1: Locate SIGNAL_SPEC and the Seed | 5 | `0x2D879291` located and wrong seed `0x0A0A0A0A` found | Partial | Not found | +| Criterion 5.2: Patch the Seed | 4 | Seed bytes changed to `74 65 20 6B` | Wrong byte | Not patched | +| Criterion 5.3: Explain the ARX Derivation | 3 | Correct trace of the per-cycle derivation | Vague | Missing | +| Criterion 5.4: Separate the Security Layers | 3 | Correctly explains what the seed fixes versus the gate | Generic | Missing | + +### Task 6: GDB Register Capture of the Derived Key (15 points) + +| Criterion | Points | Full credit | Partial credit | No credit | +|-----------|--------|-------------|----------------|-----------| +| Criterion 6.1: Breakpoint at the Derive Return | 4 | Correct address and `$r0` read as the bug-derived key | Address off | Not found | +| Criterion 6.2: Inspect the Two Arguments | 4 | Live seed and derived IV captured at the second call | One correct | Missing | +| Criterion 6.3: Override the Register | 4 | `$r0` set to `0x2D879291` and the next cycle shows `OK` | Partial | Missing | +| Criterion 6.4: Watchpoint on the Stored Key | 3 | Watchpoint on the SRAM key location documented | Approximate | Missing | + +### Task 7: Recover the Ouroboros Authority Frame (10 points) + +| Criterion | Points | Full credit | Partial credit | No credit | +|-----------|--------|-------------|----------------|-----------| +| Criterion 7.1: Locate Salt, Nonce, Ciphertext, and Tag | 4 | All three addresses correct in flash | Two correct | Not found | +| Criterion 7.2: Document Argon2id Parameters and Payload Contract | 2 | Correct memory/time/parallelism and payload layout | Partial | Missing | +| Criterion 7.3: Authenticate with the 12-Word Passphrase | 2 | `AUTHORITY FRAME: VERIFIED`, LED on, payload printed | Partial | Not shown | +| Criterion 7.4: State the Honest Quantum Boundary | 2 | Grover halves symmetric exponents; not strict PQC | Generic | Misstates | + +### Task 8: Export and Verify (8 points) + +| Criterion | Points | Full credit | Partial credit | No credit | +|-----------|--------|-------------|----------------|-----------| +| Criterion 8.1: Export CTF-02_fixed.bin | 1 | Valid patched binary | Corrupted | Not submitted | +| Criterion 8.2: Convert to CTF-02_fixed.uf2 | 1 | Correct base and family flags | Wrong flags | Not submitted | +| Criterion 8.3: Hardware Verification | 4 | Corrected console output confirmed (CRITICAL/HELD in 2s stream; DANGER at boot/Ghidra) | Some lines corrected | No verification | +| Criterion 8.4: Summary Table of All Patches | 2 | Complete address and before/after table | Missing entries | No table | + +### Task 9: Written Reflection (5 points) + +| Criterion | Points | Full credit | Partial credit | No credit | +|-----------|--------|-------------|----------------|-----------| +| Criterion 9.1: "Rushed Build" Is Not an Excuse | 2 | Specific, grounded reasoning | Generic | Missing | +| Criterion 9.2: One Engineering Practice per Failure Area | 3 | Concrete practices for the bugs and for image authenticity | Names some | Missing | + +--- + +## Common Pitfalls + +| Pitfall | Consequence | Avoidance | +|---------|-------------|-----------| +| Patching only one threshold site | One status line still lies | Patch both `0x10000302` and `0x10000312` | +| Assuming the immediate equals the limit | Off-by-one, wrong boundary | Use `0x3B` (59), not `0x3C` (60) | +| Using Patch Instruction before IT block | Re-disassembler context conflict swallows Site B | In Listing press `C` -> edit byte in Bytes window (pencil) -> press `D` | +| Missing boot banner in serial terminal | PuTTY misses one-time 5ms boot banner | Pulse RUN to GND while connected to capture | +| Replacing a string with a different length | Corrupts adjacent flash | `NORMAL` and `DANGER` are both 6 bytes | +| Treating the block length as an integer | Misses the 8-byte double | Follow the value into `.data`, decode IEEE-754 | +| Using the wrong 0.32 bytes | Prints 316 M instead of 320 M | Use `7B 14 AE 47 E1 7A D4 3F` | +| Treating an odd vector address as invalid | Thumb analysis fails | Clear bit 0 | +| Starting the seed patch at the wrong offset | Wrong seed, key never matches | Seed is at `0x1000EC70` | + +--- + +## How To Breadboard + +- **Raspberry Pi Pico 2** powered over USB. +- **USB-CDC virtual serial console:** open the Pico's COM port at 115200 baud, + 8 data bits, no parity, 1 stop bit. +- **SWD debug probe:** connect SWCLK, SWDIO, GND, and 3.3 V to the Pico debug + header for GDB inspection and register capture. +- No other peripherals are required; the authority LED is on-board. + +--- + +## Memory Map Reference + +| Region | Address | Purpose | +|--------|---------|---------| +| Bootrom | `0x00000000` | Immutable boot code | +| Flash/XIP | `0x10000000` | Vector table, code, rodata, `.data` init image | +| SRAM | `0x20000000` | Stack and writable state | + +--- + +## Deadline & Submission + +- Create a folder containing the Ghidra screenshot, `CTF-02_fixed.bin`, and + `CTF-02_fixed.uf2`. +- Write all written answers in a single file named `CTF-02-Answers.md` inside that + folder. +- ZIP the folder as `lastname-firstname-CTF-02.zip`. +- Submit the ZIP before the posted deadline; late submissions lose 10 percent + per day. + +--- + +## Grade Scale + +| Grade | Percentage | Points | +|-------|------------|--------| +| A+ | 97-100% | 97-100 | +| A | 93-96% | 93-96 | +| A- | 90-92% | 90-92 | +| B+ | 87-89% | 87-89 | +| B | 84-86% | 84-86 | +| B- | 80-83% | 80-83 | +| C | 70-79% | 70-79 | +| F | 0-69% | 0-69 | + +--- + +## Academic Integrity + +Use only the supplied Pico 2 and firmware. Do not connect the exercise to an +operational railway, metro system, public network, military system, or +third-party device. This is a controlled, isolated educational exercise. All +analysis and patches must be your own work; sharing binaries, addresses, keys, +passphrases, or answers is a violation of the academic integrity policy. + +--- + +## Reference Material + +| Topic | Reference | +|-------|-----------| +| ARM Cortex-M33 registers and stack | Week 1 | +| USB-CDC output and console capture | Week 2 | +| Vector tables, reset startup, and XIP | Week 2 | +| Ghidra static analysis and binary patching | Week 3 | +| Data segments, literal pools, IEEE-754 | Week 4 | +| Condition-code analysis | Week 5 | +| Runtime key derivation and GDB register capture | Week 6 | +| Argon2id and XChaCha20-Poly1305 authenticated gate | Week 8 | diff --git a/0x0017a_ctf/CTF-02-R.pdf b/0x0017a_ctf/CTF-02-R.pdf new file mode 100644 index 0000000..a8cde10 Binary files /dev/null and b/0x0017a_ctf/CTF-02-R.pdf differ diff --git a/0x0017a_ctf/CTF-02-S.md b/0x0017a_ctf/CTF-02-S.md new file mode 100644 index 0000000..310a619 --- /dev/null +++ b/0x0017a_ctf/CTF-02-S.md @@ -0,0 +1,652 @@ +# Operation Copperhead - Instructor Solution Key + +``` ++----------------------------------------------------------------------------------------+ +| | +| ██████╗ ██╗ █████╗ ██████╗██╗ ██╗███████╗████████╗ █████╗ ██████╗ ████████╗ | +| ██╔══██╗██║ ██╔══██╗██╔════╝██║ ██╔╝██╔════╝╚══██╔══╝██╔══██╗██╔══██╗╚══██╔══╝ | +| ██████╔╝██║ ███████║██║ █████╔╝ ███████╗ ██║ ███████║██████╔╝ ██║ | +| ██╔══██╗██║ ██╔══██║██║ ██╔═██╗ ╚════██║ ██║ ██╔══██║██╔══██╗ ██║ | +| ██████╔╝███████╗██║ ██║╚██████╗██║ ██╗███████╗ ██║ ██║ ██║██║ ██║ ██║ | +| ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝██║ ██║╚══════╝ ╚═╝ ╚═╝ ╚═╝██║ ██║ ██║ | +| | +| | +| O P E R A T I O N C O P P E R H E A D | +| | +| *** INSTRUCTOR SOLUTION KEY: RESTRICTED *** | +| | ++----------------------------------------------------------------------------------------+ +``` + +> The task and criterion headings in this key are word-for-word identical to +> `CTF-02-R.md`, so a student can match each criterion one-to-one. + +--- + +## Artifact Identity + +| Artifact | Value | +|----------|-------| +| Student image | `CTF-02.bin` | +| Flash image | `CTF-02.uf2` | +| Target | Raspberry Pi Pico 2 / RP2350 ARM Cortex-M33 | +| Image base | `0x10000000` | +| Console | USB-CDC virtual COM, 115200 8N1 | + +```text +CTF-02.bin 85330C37CD0897746B1AF447E4BAC371DDE2042ABD2D61D58A61FE2A8EEF3537 +CTF-02.uf2 F3CD4840260DB820D792758CECACC5297BEF1971B9EACF7601279256D8AF1EAB +``` + +Proof tool: `python3 scripts/verify_ctf.py` returns `26/26 checks passed` against +`CTF-02.bin`. + +--- + +## Task 1: Setup and Initial Analysis (12 points) + +### Solution + +**Criterion 1.1: Ghidra Project Setup (3 points).** Import `CTF-02.bin` as +`Raw Binary`, language `ARM:LE:32:Cortex`, base address `0x10000000`, then run +auto-analysis. + +**Criterion 1.2: Vector Table Decoding (3 points).** +First 32 bytes of `CTF-02.bin`: + +```text +00 20 08 20 5B 01 00 10 1B 01 00 10 1D 01 00 10 +11 01 00 10 11 01 00 10 11 01 00 10 11 01 00 10 +``` + +| Evidence | Answer | +|----------|--------| +| Vector table base | `0x10000000` | +| Initial SP | `0x20082000` | +| Reset pointer (as stored) | `0x1000015B` | +| Reset instruction address | `0x1000015A` | + +**Criterion 1.3: main() and Status-Loop Addresses (4 points).** + +| Element | Address | +|---------|---------| +| `main()` | `0x100002E8` | +| Recurring status loop start | `0x1000034C` | +| Loop back-edge (`b.n 0x1000034C`) | `0x1000044E` | + +**Criterion 1.4: Thumb Addressing and Literal Pool (2 points).** +The stored reset pointer `0x1000015B` has bit 0 set, selecting Thumb mode. +Clearing bit 0 gives `0x1000015A`. A representative literal pool entry is +`0x100004B4`, which holds `0x1000C4C8`, the address of the format string +`"BLOCK STATE: %s"`, loaded by `ldr r0, [pc, #308]` at `0x1000037C`. + +**Supporting Reference: SRAM Symbols (Ghidra names to semantic roles).** + +| Ghidra Label | SRAM Address | Section | Role | Source Symbol | +|--------------|--------------|---------|------|---------------| +| `DAT_20001188` | `0x20001188` | `.data` | Block length double | `g_telemetry` | +| `DAT_20001198` | `0x20001198` | `.data` | Signal key seed | `g_auth_seed` | +| `DAT_2000119C` | `0x2000119C` | `.data` | Track current | `g_block_current` | +| `DAT_20001ECC` | `0x20001ECC` | `.bss` | Dispatch state | `g_dispatch_state` | +| `DAT_20001ED0` | `0x20001ED0` | `.bss` | Fault poll counter | `g_fault_polls` | +| `DAT_20001ED4` | `0x20001ED4` | `.bss` | Input line buffer | `g_linebuf` | +| `DAT_200020D4` | `0x200020D4` | `.bss` | Input write index | `g_lineidx` | +| `DAT_200020D8` | `0x200020D8` | `.bss` | Operator state | `g_operator_state` | +| `DAT_200020DC` | `0x200020DC` | `.bss` | Derived signal key | `g_signal_key` | + +### Grading Rubric (1-to-1 Mapping) + +| Criterion | Points | Full Credit (Answer Key) | Partial Credit | No Credit | +|-----------|--------|--------------------------|----------------|-----------| +| Criterion 1.1: Ghidra Project Setup | 3 | Correct project name, `ARM:LE:32:Cortex` (ARM Cortex 32-bit little endian), base `0x10000000` | One item off | Not set up | +| Criterion 1.2: Vector Table Decoding | 3 | Correct base, initial SP, reset pointer | One missing | Not found | +| Criterion 1.3: main() and Status-Loop Addresses | 4 | Both addresses correct | One correct | Neither found | +| Criterion 1.4: Thumb Addressing and Literal Pool | 2 | Bit 0 cleared and one pool entry traced to its string | Partial | Incorrect | + +### Instructor Notes & Assembly + +- Confirm the Ghidra import used `Raw Binary`, `ARM:LE:32:Cortex`, base + `0x10000000`, and that auto-analysis completed before any address was read. +- Verify `main()` is `0x100002E8`, the loop head is `0x1000034C`, and the + back-edge is `0x1000044E`. +- For Criterion 1.4, accept any correctly traced literal pool entry; the pool + entry `0x100004B4` holding `0x1000C4C8` (`"BLOCK STATE: %s"`, loaded at + `0x1000037C`) is the reference example. +- The SRAM symbol table is supporting reference material, not a separate + scored criterion. + +--- + +## Task 2: Find and Patch Bug #1: The Miscalibrated Release Threshold (15 points) + +### Solution + +**Criterion 2.1: Locate Compare Sites A and B (6 points).** + +```text +10000302: 2b5e cmp r3, #94 @ 0x5e +10000312: 2b5e cmp r3, #94 @ 0x5e +``` + +| Site | Address | File Offset | Original Bytes | Original Instruction | +|------|---------|-------------|----------------|----------------------| +| A | `0x10000302` | `0x0302` | `5E 2B` | `cmp r3, #94` | +| B | `0x10000312` | `0x0312` | `5E 2B` | `cmp r3, #94` | + +**Criterion 2.2: Correct Immediate-Value Reasoning (4 points).** +The source constant is `SAFE_THRESHOLD = 95` and the test is `x < 95`. For an +unsigned value, `x < 95` is exactly `x <= 94`, so the compiler emits +`cmp r3, #94`. The correct limit is `60`, so the test is `x < 60`, which is +`x <= 59`. The correct patched immediate is **`0x3B` (59)**, not `0x3C` (60). + +**Criterion 2.3: Patch Compare Sites A and B (4 points).** + +| Site | Address | File Offset | Original | Patched | After | +|------|---------|-------------|----------|---------|-------| +| A | `0x10000302` | `0x0302` | `5E 2B` | `3B 2B` | `cmp r3, #59` | +| B | `0x10000312` | `0x0312` | `5E 2B` | `3B 2B` | `cmp r3, #59` | + +**Criterion 2.4: Explain Why Both Sites Must Be Patched (1 point).** +Site A drives the `BLOCK STATE` line and site B drives the `AUTO TRAIN` +decision. Patching only site A makes the console read `CRITICAL` while the +automated dispatch still says `AUTHORIZED`. Frozen reading `87`: `87 <= 94` is +true (wrong); `87 <= 59` is false (correct). + +### Grading Rubric (1-to-1 Mapping) + +| Criterion | Points | Full Credit (Answer Key) | Partial Credit | No Credit | +|-----------|--------|--------------------------|----------------|-----------| +| Criterion 2.1: Locate Compare Sites A and B | 6 | Both addresses and original bytes | One site | Not found | +| Criterion 2.2: Correct Immediate-Value Reasoning | 4 | Explains the `<` to `<=` transform and gives `0x3B` | Correct value, no reasoning | Wrong value | +| Criterion 2.3: Patch Compare Sites A and B | 4 | Both byte changes verified | One site | Not patched | +| Criterion 2.4: Explain Why Both Sites Must Be Patched | 1 | Clear explanation of the two independent comparisons | Vague | Missing | + +### Instructor Notes & Assembly + +- Both sites must be patched: `0x10000302` for `BLOCK STATE` and `0x10000312` + for the `AUTO TRAIN` decision. +- The correct immediate is `0x3B` (59), not `0x3C` (60). +- Verify the byte changes on hardware; the corrected console reads `CRITICAL` + and `HELD`. +- **Ghidra ARM/Thumb Context Note:** In raw `.bin` files, patching an instruction + that precedes an `IT` block (`ite ge`) using the GUI *Patch Instruction* action + triggers Ghidra's `ReDisassembleCommand`. The re-disassembler encounters an + internal context register conflict when trying to re-declare the `ITBlock` + context over existing instructions, collapsing Thumb decoding into 32-bit ARM + mode and swallowing Site B (`0x10000312`). Students must patch using the Bytes + window workflow (Clear `C` -> edit byte `5E` -> `3B` in Bytes window with pencil + icon -> Disassemble `D`) to keep Site B visible and cleanly aligned. + +--- + +## Task 3: Find and Patch Bug #2: The False TRACK Banner (10 points) + +### Solution + +**Criterion 3.1: Locate the Banner String (3 points).** + +| String | Address | +|--------|---------| +| `"TRACK: NORMAL\r"` | `0x1000C4B8` | +| `"NORMAL"` substring to patch | `0x1000C4BF` | + +The string is loaded in `main` and printed once at boot; it never reads the +sensor. + +**Criterion 3.2: Patch Six Characters (4 points).** +`NORMAL` and `DANGER` are both six ASCII characters, so the patch preserves the +length. + +| Address Range | Original Bytes | Patched Bytes | +|---------------|----------------|---------------| +| `0x1000C4BF` - `0x1000C4C4` | `4E 4F 52 4D 41 4C` | `44 41 4E 47 45 52` | + +**Criterion 3.3: Character-by-Character Documentation (2 points).** + +| Address | Original Char | Original Byte | Patched Char | Patched Byte | +|---------|---------------|---------------|--------------|--------------| +| `0x1000C4BF` | N | `4E` | D | `44` | +| `0x1000C4C0` | O | `4F` | A | `41` | +| `0x1000C4C1` | R | `52` | N | `4E` | +| `0x1000C4C2` | M | `4D` | G | `47` | +| `0x1000C4C3` | A | `41` | E | `45` | +| `0x1000C4C4` | L | `4C` | R | `52` | + +**Criterion 3.4: Explain the Danger of a Hardcoded Status Word (1 point).** +The banner never consults the reading, so it reports a healthy track even while +the frozen reading is dangerous, masking the hazard from the operator. + +### Grading Rubric (1-to-1 Mapping) + +| Criterion | Points | Full Credit (Answer Key) | Partial Credit | No Credit | +|-----------|--------|--------------------------|----------------|-----------| +| Criterion 3.1: Locate the Banner String | 3 | Correct address and cross-reference | Approximate | Not found | +| Criterion 3.2: Patch Six Characters | 4 | All six bytes changed, length preserved | Correct text, wrong bytes documented | Wrong length | +| Criterion 3.3: Character-by-Character Documentation | 2 | Original vs patched byte for all six characters | Partial | Missing | +| Criterion 3.4: Explain the Danger of a Hardcoded Status Word | 1 | Clear, specific reasoning | Generic | Missing | + +### Instructor Notes & Assembly + +- `NORMAL` and `DANGER` are both six characters; the patch must not change the + string length or overwrite adjacent flash. +- Confirm the patch covers `0x1000C4BF` through `0x1000C4C4` exactly. +- The banner is printed once at boot and never recomputed, so it is a separate + defect from the threshold. + +--- + +## Task 4: Find and Patch Bug #3: The Block Length Constant (10 points) + +### Solution + +**Criterion 4.1: Locate the .data Double (3 points).** +The console prints `BLOCK LENGTH: 3200 M` from the format string at +`0x1000C4F0`. The value is a `double` in the `.data` init image at +`0x1000EC60` (loaded into `0x20001188` at boot). + +**Criterion 4.2: IEEE-754 Bytes and Print Math (4 points).** +Eight bytes at `0x1000EC60`: + +```text +9A 99 99 99 99 99 09 40 -> 0x400999999999999A -> 3.2 km -> 3200 m +7B 14 AE 47 E1 7A D4 3F -> 0x3FD47AE147AE147B -> 0.32 km -> 320 m +``` + +**Criterion 4.3: Patch to Print 320 M (3 points).** + +| File Offset Range | Flash Address Range | Original Bytes | Patched Bytes | +|-------------------|---------------------|----------------|---------------| +| `0xEC60` - `0xEC67` | `0x1000EC60` - `0x1000EC67` | `9A 99 99 99 99 99 09 40` | `7B 14 AE 47 E1 7A D4 3F` | + +At `0x1000EC68` the adjacent telemetry fields (`03 00 00 00` flags and `07 00` +crossing) are left untouched. + +### Grading Rubric (1-to-1 Mapping) + +| Criterion | Points | Full Credit (Answer Key) | Partial Credit | No Credit | +|-----------|--------|--------------------------|----------------|-----------| +| Criterion 4.1: Locate the .data Double | 3 | Correct address traced from the BLOCK LENGTH print | Approximate | Not found | +| Criterion 4.2: IEEE-754 Bytes and Print Math | 4 | Original and patched 8-byte double with print math (3200 M to 320 M) | Correct patch, no math | Wrong bytes | +| Criterion 4.3: Patch to Print 320 M | 3 | Console shows `BLOCK LENGTH: 320 M` | Wrong bytes | Not patched | + +### Instructor Notes & Assembly + +- The value is an 8-byte IEEE-754 double, not an integer. Follow the + `BLOCK LENGTH` print into `.data` at `0x1000EC60`. +- The patched bytes `7B 14 AE 47 E1 7A D4 3F` decode to `0.32 km` (`320 m`). +- Confirm the adjacent telemetry fields at `0x1000EC68` (`03 00 00 00` and + `07 00`) are left untouched. + +--- + +## Task 5: Find and Patch Bug #4: The Signal Seed (15 points) + +### Solution + +**Criterion 5.1: Locate SIGNAL_SPEC and the Seed (5 points).** +The SIMPLE comparison in the loop is at `0x100003A6`: + +```text +100003a6: 4559 cmp r1, fp ; fp = SIGNAL_SPEC = 0x2D879291 (pool 0x100004FC) +``` + +The corrupted seed is a `0x0A0A0A0A` word in the `.data` init image at +`0x1000EC70` (loaded into `0x20001198` at boot). The byte values `0A 0A 0A 0A` +also appear in the `tbb` jump table at `0x100003D4`; those are not the seed. + +**Criterion 5.2: Patch the Seed (4 points).** +The required seed is the ChaCha expand word `0x6B206574` (`"te k"`), stored +little-endian as `74 65 20 6B`. + +| File Offset Range | Flash Address Range | Original Bytes | Patched Bytes | +|-------------------|---------------------|----------------|---------------| +| `0xEC70` - `0xEC73` | `0x1000EC70` - `0x1000EC73` | `0A 0A 0A 0A` | `74 65 20 6B` | + +**Criterion 5.3: Explain the ARX Derivation (3 points).** +`derive_session_key(seed, iv)` works exactly like this: + +1. Set `a = seed`, `b = iv`, `c = 0x61707865`, `d = 0x3320646E`. +2. Run four ChaCha quarter-rounds. A quarter-round runs four phases with rotate + amounts 16, 12, 8, 7. Each phase is: `a = a + b; d = d XOR a; + d = rotate_left(d, s); c = c + d; b = b XOR c; b = rotate_left(b, s)`. +3. Return `a XOR d`. + +The runtime IV is `derive_session_key(0x6B206574, 0) = 0x43C974F6`. The shipped +seed `0x0A0A0A0A` gives `derive_session_key(0x0A0A0A0A, 0x43C974F6) = +0x915DCFF8` (MISMATCH). The honest seed `0x6B206574` gives +`derive_session_key(0x6B206574, 0x43C974F6) = 0x2D879291`, which equals +`SIGNAL_SPEC` and prints `OK`. + +**Criterion 5.4: Separate the Security Layers (3 points).** +The signal seed only controls the local `SIGNAL KEY` telemetry check. It does +not authenticate the operator. The Argon2id plus XChaCha20-Poly1305 gate is a +separate layer that requires the 12-word passphrase. + +### Grading Rubric (1-to-1 Mapping) + +| Criterion | Points | Full Credit (Answer Key) | Partial Credit | No Credit | +|-----------|--------|--------------------------|----------------|-----------| +| Criterion 5.1: Locate SIGNAL_SPEC and the Seed | 5 | `0x2D879291` located and wrong seed `0x0A0A0A0A` found | Partial | Not found | +| Criterion 5.2: Patch the Seed | 4 | Seed bytes changed to `74 65 20 6B` | Wrong byte | Not patched | +| Criterion 5.3: Explain the ARX Derivation | 3 | Correct trace of the per-cycle derivation | Vague | Missing | +| Criterion 5.4: Separate the Security Layers | 3 | Correctly explains what the seed fixes versus the gate | Generic | Missing | + +### Instructor Notes & Assembly + +- The seed is at `0x1000EC70` in the `.data` init image. The `0A 0A 0A 0A` + bytes in the `tbb` jump table at `0x100003D4` are not the seed. +- The patched seed `74 65 20 6B` is the little-endian form of the ChaCha expand + word `0x6B206574` (`"te k"`). +- The seed only affects the local `SIGNAL KEY` check; the Argon2id plus + XChaCha20-Poly1305 gate is a separate authentication layer. + +--- + +## Task 6: GDB Register Capture of the Derived Key (15 points) + +### Solution + +**Criterion 6.1: Breakpoint at the Derive Return (4 points).** +The per-cycle derive is `bl derive_session_key` at `0x10000352`. Break at the +next instruction, `0x10000356`, and read `$r0`. On the shipped image it is +`0x915DCFF8`. + +```gdb +(gdb) break *0x10000356 +(gdb) continue +(gdb) print/x $r0 # 0x915DCFF8 on the corrupted image +``` + +**Criterion 6.2: Inspect the Two Arguments (4 points).** +At the call entry `0x10000352`: + +| Register | Corrupted image | Meaning | +|----------|-----------------|---------| +| `$r0` | `0x0A0A0A0A` | Seed | +| `$r1` | `0x43C974F6` | Derived IV | + +**Criterion 6.3: Override the Register (4 points).** +With execution at `0x10000356`, set `$r0` to the spec key, then continue. The +next cycle prints `SIGNAL KEY: 0x2D879291 OK`. + +```gdb +(gdb) set $r0 = 0x2D879291 +(gdb) continue +``` + +**Criterion 6.4: Watchpoint on the Stored Key (3 points).** +The key is stored in SRAM at `0x200020DC` (`str r0, [r6, #0]`). + +```gdb +(gdb) watch *0x200020DC +``` + +### Grading Rubric (1-to-1 Mapping) + +| Criterion | Points | Full Credit (Answer Key) | Partial Credit | No Credit | +|-----------|--------|--------------------------|----------------|-----------| +| Criterion 6.1: Breakpoint at the Derive Return | 4 | Correct address and `$r0` read as the bug-derived key | Address off | Not found | +| Criterion 6.2: Inspect the Two Arguments | 4 | Live seed and derived IV captured at the second call | One correct | Missing | +| Criterion 6.3: Override the Register | 4 | `$r0` set to `0x2D879291` and the next cycle shows `OK` | Partial | Missing | +| Criterion 6.4: Watchpoint on the Stored Key | 3 | Watchpoint on the SRAM key location documented | Approximate | Missing | + +### Instructor Notes & Assembly + +- Confirm the breakpoint is placed at `0x10000356`, the instruction after the + `bl derive_session_key` at `0x10000352`. +- On the shipped image `$r0` reads `0x915DCFF8`; `$r0` is the seed `0x0A0A0A0A` + and `$r1` is the derived IV `0x43C974F6`. +- The stored key lives at `0x200020DC`; accept the documented watchpoint. + +--- + +## Task 7: Recover the Ouroboros Authority Frame (10 points) + +### Solution + +**Criterion 7.1: Locate Salt, Nonce, Ciphertext, and Tag (4 points).** + +| Component | Flash Address | Size | +|-----------|---------------|------| +| Ciphertext + Tag | `0x1000CE94` | 64 B | +| Nonce | `0x1000CED4` | 24 B | +| Salt | `0x1000CEEC` | 16 B | + +**Criterion 7.2: Document Argon2id Parameters and Payload Contract (2 points).** +Argon2id: memory `64 KiB`, iterations `3`, parallelism `1`, output key `32 B`, +salt the 16 bytes above. XChaCha20-Poly1305 decrypts the 48-byte ciphertext with +the 16-byte tag. The plaintext is `01 68 65 6C 6C 6F 0D 0A` followed by zeros: +byte 0 turns the GPIO 25 LED on, and bytes 1 through 7 are printed as `hello` +plus carriage-return and newline. + +**Criterion 7.3: Authenticate with the 12-Word Passphrase (2 points).** +At the `RESPONSE> ` prompt, type: + +```text +orbit olive ladder marble quartz canyon ripple saddle violet ember walnut falcon +``` + +Expected result (proven on hardware): + +```text +hello +AUTHORITY FRAME: VERIFIED +``` + +**Criterion 7.4: State the Honest Quantum Boundary (2 points).** +Grover-style search halves the effective security exponent of a symmetric key, +so a 256-bit key gives about 128 bits of quantum security. The construction uses +classical symmetric and password-hashing primitives and does not implement NIST +post-quantum standards. + +### Grading Rubric (1-to-1 Mapping) + +| Criterion | Points | Full Credit (Answer Key) | Partial Credit | No Credit | +|-----------|--------|--------------------------|----------------|-----------| +| Criterion 7.1: Locate Salt, Nonce, Ciphertext, and Tag | 4 | All three addresses correct in flash | Two correct | Not found | +| Criterion 7.2: Document Argon2id Parameters and Payload Contract | 2 | Correct memory/time/parallelism and payload layout | Partial | Missing | +| Criterion 7.3: Authenticate with the 12-Word Passphrase | 2 | `AUTHORITY FRAME: VERIFIED`, LED on, payload printed | Partial | Not shown | +| Criterion 7.4: State the Honest Quantum Boundary | 2 | Grover halves symmetric exponents; not strict PQC | Generic | Misstates | + +### Instructor Notes & Assembly + +- Verify the three component addresses in flash: ciphertext plus tag at + `0x1000CE94`, nonce at `0x1000CED4`, salt at `0x1000CEEC`. +- The passphrase is fixed for the lab; a successful gate prints `hello` and + `AUTHORITY FRAME: VERIFIED` and lights the on-board authority LED. +- Grade Criterion 7.4 on the honest boundary: 256-bit symmetric key maps to + about 128 bits under Grover, and the design is not NIST post-quantum. + +--- + +## Task 8: Export and Verify (8 points) + +### Solution + +**Criterion 8.1: Export CTF-02_fixed.bin (1 point).** +Export the patched program from Ghidra (`File -> Export Program...`, `Binary +Format`) as `CTF-02_fixed.bin`. The shipped image is 62,308 bytes. + +**Criterion 8.2: Convert to CTF-02_fixed.uf2 (1 point).** + +```bash +python uf2conv.py CTF-02_fixed.bin --base 0x10000000 --family 0xe48bff59 --output CTF-02_fixed.uf2 +``` + +**Criterion 8.3: Hardware Verification (4 points).** + +Before patching: + +```text +DEEPLINE METRO AUTHORITY +ADAPTIVE SIGNAL WINDOW: 38 MINUTES +USB-CDC 115200 8N1 | AUTHORIZED LAB CONSOLE +TRACK: NORMAL +BLOCK STATE: STABLE +AUTO TRAIN: AUTHORIZED +BLOCK LENGTH: 3200 M +FAULT POLLS: 1 +SIGNAL KEY: 0x915DCFF8 MISMATCH +RESPONSE> +``` + +After all four patches: + +```text +DEEPLINE METRO AUTHORITY +ADAPTIVE SIGNAL WINDOW: 38 MINUTES +USB-CDC 115200 8N1 | AUTHORIZED LAB CONSOLE +TRACK: DANGER +BLOCK STATE: CRITICAL +AUTO TRAIN: HELD +BLOCK LENGTH: 320 M +FAULT POLLS: 1 +SIGNAL KEY: 0x2D879291 OK +RESPONSE> +``` + +**Criterion 8.4: Summary Table of All Patches (2 points).** + +| # | Bug | File Offset | Flash Address | Original Bytes | Patched Bytes | +|---|-----|-------------|---------------|----------------|---------------| +| 1a | Operator threshold | `0x0302` | `0x10000302` | `5E 2B` | `3B 2B` | +| 1b | Dispatch threshold | `0x0312` | `0x10000312` | `5E 2B` | `3B 2B` | +| 2 | TRACK banner | `0xC4BF` - `0xC4C4` | `0x1000C4BF` - `0x1000C4C4` | `4E 4F 52 4D 41 4C` | `44 41 4E 47 45 52` | +| 3 | Block length | `0xEC60` - `0xEC67` | `0x1000EC60` - `0x1000EC67` | `9A 99 99 99 99 99 09 40` | `7B 14 AE 47 E1 7A D4 3F` | +| 4 | Signal seed | `0xEC70` - `0xEC73` | `0x1000EC70` - `0x1000EC73` | `0A 0A 0A 0A` | `74 65 20 6B` | + +### Grading Rubric (1-to-1 Mapping) + +| Criterion | Points | Full Credit (Answer Key) | Partial Credit | No Credit | +|-----------|--------|--------------------------|----------------|-----------| +| Criterion 8.1: Export CTF-02_fixed.bin | 1 | Valid patched binary | Corrupted | Not submitted | +| Criterion 8.2: Convert to CTF-02_fixed.uf2 | 1 | Correct base and family flags | Wrong flags | Not submitted | +| Criterion 8.3: Hardware Verification | 4 | Corrected console output confirmed on hardware | Some lines corrected | No verification | +| Criterion 8.4: Summary Table of All Patches | 2 | Complete address and before/after table | Missing entries | No table | + +### Instructor Notes & Assembly + +- Verify the exported image with `python3 scripts/verify_ctf.py`; the shipped + check expects `26/26 checks passed` against `CTF-02.bin`. +- Confirm the UF2 conversion used base `0x10000000` and family `0xe48bff59`. +- **Serial Terminal Timing:** Note that `print_identity()` (`TRACK: NORMAL`) + fires within the first 5 milliseconds of boot. In normal lab usage, PuTTY + attaches after boot and will display the continuous 2-second status loop + (`BLOCK STATE: CRITICAL`, `AUTO TRAIN: HELD`). To see the corrected banner, + the student must pulse `RUN` to `GND` while PuTTY is open, or demonstrate + the string change at `0x1000C4BF` via Ghidra static analysis. +- The shipped image is 62,308 bytes; confirm the exported corrected image is a + valid patched binary with all four fixes present. + +--- + +## Task 9: Written Reflection (5 points) + +### Solution + +**Criterion 9.1: "Rushed Build" Is Not an Excuse (2 points).** +The rebuild shipped four constants that were never checked against their +documented limits, which is exactly what produced the false-safe reading. +Pressure explains why the checks were skipped, not why they should be skipped. + +**Criterion 9.2: One Engineering Practice per Failure Area (3 points).** +- Physical limits (Bugs #1 and #3): one shared configuration header plus a + build-time assertion that each compiled limit matches its documented value. +- Banner (Bug #2): remove static banners; a hardware-in-the-loop test that + compares displayed state to the live register. +- Seed integrity (Bug #4): reproducible builds with golden artifact hash + comparison so keys and seeds match the certified specification. +- Image authenticity: enable RP2350 hardware secure boot with OTP hash + verification so a modified image will not run. + +### Grading Rubric (1-to-1 Mapping) + +| Criterion | Points | Full Credit (Answer Key) | Partial Credit | No Credit | +|-----------|--------|--------------------------|----------------|-----------| +| Criterion 9.1: "Rushed Build" Is Not an Excuse | 2 | Specific, grounded reasoning | Generic | Missing | +| Criterion 9.2: One Engineering Practice per Failure Area | 3 | Concrete practices for the bugs and for image authenticity | Names some | Missing | + +### Instructor Notes & Assembly + +- Grade the specificity of the reasoning, not the length of the prose. +- Require concrete practices across the failure areas, including at least one + practice for image authenticity. + +--- + +## How To Breadboard + +- **Raspberry Pi Pico 2** powered over USB. +- **USB-CDC virtual serial console:** open the Pico's COM port at 115200 baud, + 8 data bits, no parity, 1 stop bit. +- **SWD debug probe:** connect SWCLK, SWDIO, GND, and 3.3 V to the Pico debug + header for GDB inspection and register capture. +- No other peripherals are required; the authority LED is on-board. + +--- + +## Complete Grading Summary + +| Task | Title | Points | +|------|-------|--------| +| Task 1 | Setup and Initial Analysis | 12 | +| Task 2 | Find and Patch Bug #1: The Miscalibrated Release Threshold | 15 | +| Task 3 | Find and Patch Bug #2: The False TRACK Banner | 10 | +| Task 4 | Find and Patch Bug #3: The Block Length Constant | 10 | +| Task 5 | Find and Patch Bug #4: The Signal Seed | 15 | +| Task 6 | GDB Register Capture of the Derived Key | 15 | +| Task 7 | Recover the Ouroboros Authority Frame | 10 | +| Task 8 | Export and Verify | 8 | +| Task 9 | Written Reflection | 5 | +| **TOTAL** | | **100** | + +--- + +## Instructor Notes + +Safety: Use only the supplied Pico 2, SWD probe, and firmware. Never connect the +exercise to an operational railway, metro system, public network, military +system, or third-party device. + +### Common Student Mistakes + +- Patching only one threshold site (`0x10000302` or `0x10000312`), leaving one + status line lying. +- Assuming the immediate equals the limit, producing an off-by-one boundary; + the correct byte is `0x3B` (59), not `0x3C` (60). +- Replacing the banner string with a different length, corrupting adjacent + flash; `NORMAL` and `DANGER` are both 6 bytes. +- Treating the block length as an integer and missing the 8-byte double in + `.data`. +- Using the wrong `0.32` bytes and printing `316 M` instead of `320 M`. +- Treating the odd vector address `0x1000015B` as invalid instead of clearing + bit 0 to get `0x1000015A`. +- Starting the seed patch at the wrong offset; the seed is at `0x1000EC70`. + +### Partial Credit Guidelines + +- Award partial credit for one correct threshold site out of two, or for a + correct immediate value without the `<` to `<=` reasoning. +- Award partial credit for a correct banner text with incorrectly documented + bytes, or for partial character-by-character documentation. +- Award partial credit for a correct block-length patch without the IEEE-754 + print math. +- Award partial credit for one of the two GDB argument captures, or for a + partial register override. +- Award no credit for patches that change string length or overwrite adjacent + flash. + +--- + +## Appendix: Expected Binary Diff + +| # | Bug | File Offset(s) | Flash Address(es) | Original Bytes | Patched Bytes | +|---|-----|----------------|-------------------|----------------|---------------| +| 1a | Operator threshold | `0x0302` | `0x10000302` | `5E 2B` | `3B 2B` | +| 1b | Dispatch threshold | `0x0312` | `0x10000312` | `5E 2B` | `3B 2B` | +| 2 | TRACK banner | `0xC4BF` - `0xC4C4` | `0x1000C4BF` - `0x1000C4C4` | `4E 4F 52 4D 41 4C` | `44 41 4E 47 45 52` | +| 3 | Block length | `0xEC60` - `0xEC67` | `0x1000EC60` - `0x1000EC67` | `9A 99 99 99 99 99 09 40` | `7B 14 AE 47 E1 7A D4 3F` | +| 4 | Signal seed | `0xEC70` - `0xEC73` | `0x1000EC70` - `0x1000EC73` | `0A 0A 0A 0A` | `74 65 20 6B` | + +Four defects, five changed regions: two immediate bytes (`0x3B 2B` at each +threshold), six banner bytes, eight block-length bytes, and four seed bytes. diff --git a/0x0017a_ctf/CTF-02-S.pdf b/0x0017a_ctf/CTF-02-S.pdf new file mode 100644 index 0000000..e2c7dc8 Binary files /dev/null and b/0x0017a_ctf/CTF-02-S.pdf differ diff --git a/0x0017a_ctf/CTF-02.bin b/0x0017a_ctf/CTF-02.bin new file mode 100644 index 0000000..b20d1cd Binary files /dev/null and b/0x0017a_ctf/CTF-02.bin differ diff --git a/0x0017a_ctf/CTF-02.uf2 b/0x0017a_ctf/CTF-02.uf2 new file mode 100644 index 0000000..b0a537c Binary files /dev/null and b/0x0017a_ctf/CTF-02.uf2 differ diff --git a/0x0017a_ctf/CTF-02_fixed.bin b/0x0017a_ctf/CTF-02_fixed.bin new file mode 100644 index 0000000..0918eac Binary files /dev/null and b/0x0017a_ctf/CTF-02_fixed.bin differ diff --git a/0x0017a_ctf/CTF-02_fixed.uf2 b/0x0017a_ctf/CTF-02_fixed.uf2 new file mode 100644 index 0000000..830dd27 Binary files /dev/null and b/0x0017a_ctf/CTF-02_fixed.uf2 differ diff --git a/0x0017a_ctf/include/auth.h b/0x0017a_ctf/include/auth.h new file mode 100644 index 0000000..e8ddc9e --- /dev/null +++ b/0x0017a_ctf/include/auth.h @@ -0,0 +1,143 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent/encryption-c-rp2350 +// File: auth.h +// Desc: Declares the Ouroboros authentication engine API for RP2350 firmware. +// Created: 2026 + +#ifndef AUTH_H +#define AUTH_H + +#include +#include +#include + +/** + * @brief Onboard LED GPIO pin number. + * + * The RP2350 Pico 2 onboard LED is connected to GPIO 25. Driven high + * on successful authentication and low on failure or idle. + */ +#define AUTH_LED_PIN 25u + +/** + * @brief Maximum accepted terminal passphrase length in bytes. + * + * The CLI accepts interactive human-entered passphrases up to 512 bytes, + * matching the hardened host demo boundary before policy validation. + */ +#define AUTH_PASSPHRASE_MAX_LEN 512u + +/** + * @brief Required number of lowercase words in the hardened passphrase. + * + * The embedded hardened workflow matches the host-side policy exactly: + * twelve lowercase ASCII words separated by whitespace. + */ +#define AUTH_REQUIRED_WORDS 12u + +/** + * @brief Hardened Argon2id salt size in bytes. + * + * Every demo artifact carries a per-ciphertext random 128-bit salt. + */ +#define AUTH_SALT_SIZE 16u + +/** + * @brief Hardened XChaCha20 nonce size in bytes. + * + * XChaCha20-Poly1305 consumes a 192-bit nonce in the outer construction. + */ +#define AUTH_NONCE_SIZE 24u + +/** + * @brief Subkey size in bytes derived from Argon2id. + * + * The AEAD key size is 256 bits. + */ +#define AUTH_KEY_SIZE 32u + +/** + * @brief AEAD authentication tag size in bytes. + * + * XChaCha20-Poly1305 appends a 128-bit authentication tag. + */ +#define AUTH_TAG_SIZE 16u + +/** + * @brief Plaintext payload size in bytes. + * + * The fixed dispatch payload is 48 bytes: LED state, UART bytes, + * and trailing reserved bytes matching the Rust hardened demo layout. + */ +#define AUTH_PAYLOAD_SIZE 48u + +/** + * @brief Full ciphertext-plus-tag artifact size in bytes. + * + * The encrypted payload is 48 bytes followed by a 16-byte tag. + */ +#define AUTH_CIPHERTEXT_SIZE (AUTH_PAYLOAD_SIZE + AUTH_TAG_SIZE) + +/** + * @brief Authentication result codes returned by the hardened engine. + * + * These values let the CLI distinguish policy failures from + * cryptographic authentication failures without guessing. + */ +typedef enum auth_result { + AUTH_RESULT_SUCCESS = 0, + AUTH_RESULT_POLICY_VIOLATION = 1, + AUTH_RESULT_AUTHENTICATION_FAILED = 2, + AUTH_RESULT_INTERNAL_ERROR = 3, +} auth_result_t; + +/** + * @brief Initialize the Ouroboros authentication module. + * + * Configures the onboard LED GPIO and marks the hardened engine as ready + * for passphrase authentication. + * + * @param None. + * @return bool true when initialization is successful, else false. + */ +bool auth_init(void); + +/** + * @brief Execute the hardened Ouroboros authentication pipeline. + * + * Validates the strict 12-word lowercase passphrase policy, derives the + * 256-bit AEAD key with Argon2id using artifact parameters, decrypts the + * embedded XChaCha20-Poly1305 ciphertext, and dispatches GPIO25/UART + * payload bytes on success. + * + * @param passphrase Pointer to passphrase bytes. + * @param passphrase_len Number of passphrase bytes. + * @return auth_result_t Detailed authentication outcome for the caller. + */ +auth_result_t auth_execute(const uint8_t *passphrase, + size_t passphrase_len); + +#endif // AUTH_H diff --git a/0x0017a_ctf/include/cli.h b/0x0017a_ctf/include/cli.h new file mode 100644 index 0000000..f14d375 --- /dev/null +++ b/0x0017a_ctf/include/cli.h @@ -0,0 +1,59 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: cli.h +// Desc: Declares the CLI UART passphrase input interface for Ouroboros. +// Created: 2026 + +#ifndef CLI_H +#define CLI_H + +#include + +/** + * @brief Print the UART passphrase prompt. + * + * Emits a minimal shell-style prompt followed by a space so the + * terminal clearly indicates that hardened passphrase input is expected. + * + * @param None. + * @return None. + */ +void print_prompt(void); + +/** + * @brief Service one UART polling step for passphrase input. + * + * Polls stdio for a character, dispatches backspace or newline + * handling, and appends printable characters to the passphrase + * buffer. Call repeatedly from the main loop. + * + * @param buf Pointer to mutable passphrase buffer. + * @param idx Pointer to current buffer length. + * @return None. + */ +void service_uart(char *buf, size_t *idx); + +#endif // CLI_H diff --git a/0x0017a_ctf/include/demo_artifact.h b/0x0017a_ctf/include/demo_artifact.h new file mode 100644 index 0000000..b7857a0 --- /dev/null +++ b/0x0017a_ctf/include/demo_artifact.h @@ -0,0 +1,60 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// This file is generated by scripts/dec.py. Do not edit by hand. + +#ifndef DEMO_ARTIFACT_H +#define DEMO_ARTIFACT_H + +#include + +#define DEMO_ARTIFACT_FORMAT "ouroboros-hardened-demo-v1" +#define DEMO_MEMORY_KIB 64u +#define DEMO_ITERATIONS 3u +#define DEMO_PARALLELISM 1u + +static const uint8_t DEMO_SALT[16] = { + 0xF2u, 0xD5u, 0x18u, 0x63u, 0x9Au, 0x82u, 0x01u, 0x9Du, + 0xC2u, 0xD7u, 0xAFu, 0xA5u, 0xCDu, 0xB6u, 0xD8u, 0x71u +}; + +static const uint8_t DEMO_NONCE[24] = { + 0x1Cu, 0xEFu, 0x79u, 0x0Du, 0x77u, 0x9Eu, 0x7Cu, 0x04u, + 0xE7u, 0xF0u, 0x66u, 0xDDu, 0x90u, 0xD0u, 0x80u, 0x70u, + 0x87u, 0x97u, 0x67u, 0x1Fu, 0x79u, 0xEFu, 0xC4u, 0xE4u +}; + +static const uint8_t DEMO_CIPHERTEXT_AND_TAG[64] = { + 0x2Cu, 0x23u, 0xB2u, 0x7Eu, 0x95u, 0x62u, 0xB8u, 0xEDu, + 0x9Eu, 0x08u, 0xE0u, 0x6Du, 0xD9u, 0x9Du, 0xB4u, 0x91u, + 0x3Eu, 0x81u, 0x9Au, 0x77u, 0x8Bu, 0xB4u, 0x7Bu, 0x71u, + 0xBCu, 0x66u, 0x1Eu, 0x6Eu, 0x73u, 0x1Au, 0x81u, 0x54u, + 0xCDu, 0xB5u, 0x36u, 0xA4u, 0x76u, 0x7Eu, 0x9Bu, 0xF8u, + 0x53u, 0x3Eu, 0x03u, 0x1Du, 0xB8u, 0xE5u, 0xAEu, 0x7Au, + 0xADu, 0xB4u, 0x31u, 0xCFu, 0x12u, 0xD9u, 0xF9u, 0xC4u, + 0x5Fu, 0xA9u, 0xB9u, 0x4Bu, 0x80u, 0xDCu, 0xBBu, 0xDEu +}; + +#endif // DEMO_ARTIFACT_H diff --git a/0x0017a_ctf/include/mbedtls_config.h b/0x0017a_ctf/include/mbedtls_config.h new file mode 100644 index 0000000..b27211e --- /dev/null +++ b/0x0017a_ctf/include/mbedtls_config.h @@ -0,0 +1,39 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: mbedtls_config.h +// Desc: Configures the minimal mbedTLS cryptographic features required by +// the Ouroboros AEAD engine on RP2350. +// Created: 2026 + +#ifndef MBEDTLS_CONFIG_H +#define MBEDTLS_CONFIG_H + +#define MBEDTLS_CHACHA20_C +#define MBEDTLS_CHACHAPOLY_C +#define MBEDTLS_POLY1305_C +#define MBEDTLS_PLATFORM_C + +#endif // MBEDTLS_CONFIG_H diff --git a/0x0017a_ctf/pico_sdk_import.cmake b/0x0017a_ctf/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0017a_ctf/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0017a_ctf/scripts/dec.py b/0x0017a_ctf/scripts/dec.py new file mode 100644 index 0000000..389efaf --- /dev/null +++ b/0x0017a_ctf/scripts/dec.py @@ -0,0 +1,944 @@ +"""Generate hardened demo artifacts for the RP2350 Ouroboros firmware. + +This script writes the same JSON schema used by the Rust demo and also emits +the generated C header consumed by the embedded firmware. +""" + +import argparse +import json +import platform +import secrets +import sys +from pathlib import Path +from typing import Optional + + +DEFAULT_PASSPHRASE = ( + "orbit olive ladder marble quartz canyon " + "ripple saddle violet ember walnut falcon" +) +DEFAULT_TEXT = "hello" +DEFAULT_OUTPUT_JSON = "scripts/demo_artifact.json" +DEFAULT_OUTPUT_HEADER = "include/demo_artifact.h" +DEFAULT_MEMORY_KIB = 64 +DEFAULT_ITERATIONS = 3 +DEFAULT_PARALLELISM = 1 +ARTIFACT_FORMAT = "ouroboros-hardened-demo-v1" + +_KEY_HELP = "12-word lowercase passphrase" +_TEXT_HELP = "Text to place in payload bytes 1..7" +_OUT_HELP = "Output JSON artifact path" +_HEADER_OUT_HELP = "Output generated C header path" +_FROM_JSON_HELP = ( + "Load existing JSON artifact and emit header without re-encrypting" +) +_CHECK_HEADER_HELP = ( + "Optional path to compare against generated header and fail if stale" +) +_SALT_HEX_HELP = "Optional fixed 16-byte salt as hex" +_NONCE_HEX_HELP = "Optional fixed 24-byte nonce as hex" +_NO_CRLF_HELP = "Do not append CRLF to payload text" +_LED_OFF_HELP = "Encode LED off instead of on" +_MEMORY_HELP = "Argon2 memory cost in KiB" +_ITERATIONS_HELP = "Argon2 time cost" +_PARALLELISM_HELP = "Argon2 parallel lanes" + +_POLICY_ERROR = ( + "Hardened mode requires exactly 12 lowercase ASCII words in --key." +) +_PAYLOAD_TOO_LONG = ( + "Output text is too long for fixed dispatch " + "(max 7 bytes after CRLF handling)." +) +_INVALID_JSON = "Artifact JSON at {0} is invalid JSON." +_MISMATCH_PREFIX = "Detected a Python native-extension architecture mismatch. " +_REINSTALL_DEPS = ("Recreate this virtual environment with a native Python " + "and reinstall deps:") +_REINSTALL_LINES = ( + "rm -rf .venv", + "python3 -m venv .venv", + "source .venv/bin/activate", + "python3 -m pip install -U pip setuptools wheel", + "python3 -m pip install argon2-cffi pynacl", +) +_HEADER_TEMPLATE = """// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// This file is generated by scripts/dec.py. Do not edit by hand. + +#ifndef DEMO_ARTIFACT_H +#define DEMO_ARTIFACT_H + +#include + +#define DEMO_ARTIFACT_FORMAT "{artifact_format}" +#define DEMO_MEMORY_KIB {memory_kib}u +#define DEMO_ITERATIONS {iterations}u +#define DEMO_PARALLELISM {parallelism}u + +static const uint8_t DEMO_SALT[16] = {{ +{salt_body} +}}; + +static const uint8_t DEMO_NONCE[24] = {{ +{nonce_body} +}}; + +static const uint8_t DEMO_CIPHERTEXT_AND_TAG[64] = {{ +{cipher_body} +}}; + +#endif // DEMO_ARTIFACT_H +""" + + +def _raise_dependency_error(package_name, install_hint, exc): + """Raise a RuntimeError with environment-aware dependency diagnostics. + + Parameters + ---------- + package_name : str + Package common name for the error message. + install_hint : str + Pip install command in the error message. + exc : Exception + Import error observed while loading the native module. + + Returns + ------- + None + """ + message = "Hardened mode requires {0}. Install with: {1}".format( + package_name, install_hint) + message += _reinstall_message() if _is_arch_mismatch(exc) else "" + raise RuntimeError(message) from exc + + +def _is_arch_mismatch(exc): + """Report whether the interpreter likely has a native-extension mismatch. + + Parameters + ---------- + exc : Exception + Import error observed while loading the native module. + + Returns + ------- + bool + True when the error text matches a native architecture mismatch. + """ + detail = str(exc) + return ( + "incompatible architecture" in detail + or "_cffi_backend" in detail + or "mach-o file, but is an incompatible architecture" in detail + ) + + +def _reinstall_message(): + """Build the native-interpreter reinstall diagnostic text. + + Parameters + ---------- + None + + Returns + ------- + str + Newline-delimited machine and reinstall details, or an empty string. + """ + machine = platform.machine() + head = (_MISMATCH_PREFIX + + "Current interpreter reports machine=" + machine + + ", executable=" + sys.executable + ".") + lines = [" {0}".format(item) for item in _REINSTALL_LINES] + return ("\n" + head + "\n" + _REINSTALL_DEPS + "\n" + + "\n".join(lines)) + + +def _is_policy_compliant(passphrase): + """Return True when passphrase is exactly 12 lowercase ASCII words. + + Parameters + ---------- + passphrase : str + Candidate operator passphrase. + + Returns + ------- + bool + True when the passphrase satisfies the gate policy. + """ + words = passphrase.split() + if len(words) != 12: + return False + return all( + word and all(ch.isascii() and ch.islower() for ch in word) + for word in words + ) + + +def _build_payload(text_str, led_on=True, append_crlf=True): + """Build the fixed 48-byte payload dispatched by the firmware. + + Parameters + ---------- + text_str : str + Console text placed in payload bytes 1..7. + led_on : bool + True turns the LED byte on, False leaves it off. + append_crlf : bool + True appends CRLF to the console text. + + Returns + ------- + bytes + Fixed 48-byte dispatch payload. + """ + tx_bytes = text_str.encode() + (b"\r\n" if append_crlf else b"") + if len(tx_bytes) > 7: + raise ValueError(_PAYLOAD_TOO_LONG) + payload = bytearray(48) + payload[0] = 1 if led_on else 0 + payload[1:1 + len(tx_bytes)] = tx_bytes + return bytes(payload) + + +def _resolve_salt_nonce(salt, nonce): + """Confirm or generate the 16-byte salt and 24-byte nonce. + + Parameters + ---------- + salt : bytes or None + Optional fixed salt value. + nonce : bytes or None + Optional fixed nonce value. + + Returns + ------- + tuple + Confirmed (salt, nonce) byte values. + """ + salt_word = secrets.token_bytes(16) if salt is None else salt + nonce_word = secrets.token_bytes(24) if nonce is None else nonce + if len(salt_word) != 16: + raise ValueError("Hardened salt must be exactly 16 bytes.") + if len(nonce_word) != 24: + raise ValueError("Hardened nonce must be exactly 24 bytes.") + return salt_word, nonce_word + + +def _optional_hex(value, length, label): + """Decode an optional hex argument, leaving absent values as None. + + Parameters + ---------- + value : str or None + Hex string supplied on the command line. + length : int + Expected decoded byte length. + label : str + Field name used in validation errors. + + Returns + ------- + bytes or None + Decoded bytes, or None when value is absent. + """ + return _hex_decode(value, length, label) if value else None + + +def _load_argon2(): + """Load the Argon2 low-level binding with dependency diagnostics. + + Parameters + ---------- + None + + Returns + ------- + tuple + Argon2 Type enum and hash_secret_raw callable. + """ + try: + from argon2.low_level import Type, hash_secret_raw + except ImportError as exc: + install_hint = "python3 -m pip install argon2-cffi" + _raise_dependency_error("argon2-cffi", install_hint, exc) + return Type, hash_secret_raw + + +def _load_nacl_encrypt(): + """Load the XChaCha20-Poly1305 encrypt binding with diagnostics. + + Parameters + ---------- + None + + Returns + ------- + callable + PyNaCl crypto_aead_xchacha20poly1305_ietf_encrypt function. + """ + try: + from nacl.bindings import ( + crypto_aead_xchacha20poly1305_ietf_encrypt as encrypt, + ) + except ImportError as exc: + install_hint = "python3 -m pip install pynacl" + _raise_dependency_error("PyNaCl", install_hint, exc) + return encrypt + + +def _derive_hardened_key(passphrase, salt, memory_kib, iterations, + parallelism): + """Derive a 32-byte key with Argon2id. + + Parameters + ---------- + passphrase : str + Policy-compliant operator passphrase. + salt : bytes + 16-byte Argon2 salt. + memory_kib : int + Argon2 memory cost in KiB. + iterations : int + Argon2 time cost. + parallelism : int + Argon2 parallel lane count. + + Returns + ------- + bytes + Derived 32-byte key. + """ + arg2_type, hash_secret_raw = _load_argon2() + args = ( + passphrase.encode(), salt, iterations, memory_kib, + parallelism, 32, arg2_type, + ) + return hash_secret_raw(*args) + + +def _entry_key(phrase, seed, memory_kib, iterations, parallelism): + """Derive the entry key word for the hardened artifact. + + Parameters + ---------- + phrase : str + Policy-compliant operator passphrase. + seed : bytes + 16-byte Argon2 salt. + memory_kib : int + Argon2 memory cost in KiB. + iterations : int + Argon2 time cost. + parallelism : int + Argon2 parallel lane count. + + Returns + ------- + bytes + Derived 32-byte entry key. + """ + return _derive_hardened_key( + phrase, seed, memory_kib, iterations, parallelism + ) + + +def _build_key_material(phrase, salt, nonce, memory_kib, iterations, + parallelism): + """Resolve salt, nonce, and the entry key. + + Parameters + ---------- + phrase : str + Policy-compliant operator passphrase. + salt : bytes or None + Optional fixed salt value. + nonce : bytes or None + Optional fixed nonce value. + memory_kib : int + Argon2 memory cost in KiB. + iterations : int + Argon2 time cost. + parallelism : int + Argon2 parallel lane count. + + Returns + ------- + tuple + Resolved (salt, nonce, key) values. + """ + seed, nonce_word = _resolve_salt_nonce(salt, nonce) + key = _entry_key(phrase, seed, memory_kib, iterations, parallelism) + return seed, nonce_word, key + + +def build_hardened_entry( + key_str, + text_str, + led_on=True, + append_crlf=True, + salt: Optional[bytes] = None, + nonce: Optional[bytes] = None, + memory_kib=DEFAULT_MEMORY_KIB, + iterations=DEFAULT_ITERATIONS, + parallelism=DEFAULT_PARALLELISM, +): + """Build a hardened encrypted entry with Argon2id + XChaCha20-Poly1305. + + Parameters + ---------- + key_str : str + Policy-compliant 12-word operator passphrase. + text_str : str + Console text placed in payload bytes 1..7. + led_on : bool + True turns the LED byte on, False leaves it off. + append_crlf : bool + True appends CRLF to the console text. + salt : bytes or None + Optional fixed 16-byte salt. + nonce : bytes or None + Optional fixed 24-byte nonce. + memory_kib : int + Argon2 memory cost in KiB. + iterations : int + Argon2 time cost. + parallelism : int + Argon2 parallel lane count. + + Returns + ------- + tuple + Resulting (salt, nonce, ciphertext_and_tag) values. + """ + if not _is_policy_compliant(key_str): + raise ValueError(_POLICY_ERROR) + salt_word, nonce_word, key = _build_key_material( + key_str, salt, nonce, memory_kib, iterations, parallelism) + payload = _build_payload(text_str, led_on, append_crlf) + encrypt = _load_nacl_encrypt() + ciphertext_and_tag = encrypt(payload, b"", nonce_word, key) + return salt_word, nonce_word, ciphertext_and_tag + + +def _hex_decode(value, expected_len, label): + """Decode a hex string and validate the expected byte length. + + Parameters + ---------- + value : str + Hex string to decode. + expected_len : int + Required decoded byte length. + label : str + Field name used in validation errors. + + Returns + ------- + bytes + Decoded bytes of the expected length. + """ + try: + decoded = bytes.fromhex(value) + except ValueError as exc: + raise ValueError("{0} must be valid hex.".format(label)) from exc + if len(decoded) != expected_len: + message = "{0} must decode to exactly {1} bytes.".format( + label, expected_len) + raise ValueError(message) + return decoded + + +def _artifact_dict(memory_kib, iterations, parallelism, salt, nonce, cipher): + """Compose the canonical artifact dictionary. + + Parameters + ---------- + memory_kib : int + Argon2 memory cost in KiB. + iterations : int + Argon2 time cost. + parallelism : int + Argon2 parallel lane count. + salt : bytes + 16-byte salt. + nonce : bytes + 24-byte nonce. + cipher : bytes + 64-byte ciphertext and tag. + + Returns + ------- + dict + Canonical artifact fields with hex-encoded byte values. + """ + return { + "format": ARTIFACT_FORMAT, "memory_kib": memory_kib, + "iterations": iterations, "parallelism": parallelism, + "salt_hex": salt.hex(), "nonce_hex": nonce.hex(), + "ciphertext_and_tag_hex": cipher.hex(), + } + + +def _format_c_array(data, width=8): + """Format bytes as an indented C array literal body. + + Parameters + ---------- + data : bytes + Bytes to serialize as a C array. + width : int + Byte values emitted per source line. + + Returns + ------- + str + Indented, comma-joined C array body. + """ + items = [f"0x{value:02X}u" for value in data] + rows = [", ".join(items[offset:offset + width]) + for offset in range(0, len(items), width)] + return ",\n".join(" " + row for row in rows) + + +def _render_header(artifact): + """Render the generated firmware header text from the artifact. + + Parameters + ---------- + artifact : dict + Canonical artifact dictionary. + + Returns + ------- + str + Complete generated C header text. + """ + cipher = bytes.fromhex(artifact["ciphertext_and_tag_hex"]) + return _HEADER_TEMPLATE.format( + artifact_format=ARTIFACT_FORMAT, memory_kib=artifact["memory_kib"], + iterations=artifact["iterations"], parallelism=artifact["parallelism"], + salt_body=_format_c_array(bytes.fromhex(artifact["salt_hex"])), + nonce_body=_format_c_array(bytes.fromhex(artifact["nonce_hex"])), + cipher_body=_format_c_array(cipher), + ) + + +def _write_header(path, artifact): + """Write the generated firmware header from the hardened artifact. + + Parameters + ---------- + path : str + Destination header file path. + artifact : dict + Canonical artifact dictionary. + + Returns + ------- + Path + Resolved destination header path. + """ + output_path = Path(path) + output_path.parent.mkdir(parents=True, exist_ok=True) + output_path.write_text(_render_header(artifact), encoding="utf-8") + return output_path.resolve() + + +def _parse_json_file(path): + """Load and parse the artifact JSON file. + + Parameters + ---------- + path : str + Artifact JSON file path. + + Returns + ------- + dict + Parsed JSON document. + """ + raw = Path(path).read_text(encoding="utf-8") + try: + parsed = json.loads(raw) + except json.JSONDecodeError as exc: + raise ValueError(_INVALID_JSON.format(path)) from exc + return parsed + + +def _validate_artifact_format(parsed): + """Reject artifact JSON with an unexpected format marker. + + Parameters + ---------- + parsed : dict + Parsed JSON document. + + Returns + ------- + None + """ + actual = parsed.get("format") + if actual != ARTIFACT_FORMAT: + raise ValueError( + "Artifact format must be '{0}', got '{1}'.".format( + ARTIFACT_FORMAT, actual)) + + +def _parsed_ints(parsed): + """Parse the integer cost fields from artifact JSON. + + Parameters + ---------- + parsed : dict + Parsed JSON document. + + Returns + ------- + list + Parsed memory_kib, iterations, and parallelism integer values. + """ + try: + int_names = ("memory_kib", "iterations", "parallelism") + return [int(parsed[name]) for name in int_names] + except (KeyError, TypeError, ValueError) as exc: + raise ValueError( + "Artifact must include integer memory_kib, " + "iterations, and parallelism fields.") from exc + + +def _artifact_ints(parsed): + """Unpack the three integer cost fields from artifact JSON. + + Parameters + ---------- + parsed : dict + Parsed JSON document. + + Returns + ------- + tuple + (memory_kib, iterations, parallelism) integer values. + """ + values = _parsed_ints(parsed) + return values[0], values[1], values[2] + + +def _artifact_from_parsed(parsed): + """Reconstruct a canonical artifact dict from parsed JSON. + + Parameters + ---------- + parsed : dict + Parsed JSON document. + + Returns + ------- + dict + Canonical artifact dictionary. + """ + _validate_artifact_format(parsed) + memory_kib, iterations, parallelism = _artifact_ints(parsed) + cipher_field = "ciphertext_and_tag_hex" + salt = _hex_decode(parsed.get("salt_hex", ""), 16, "salt_hex") + nonce = _hex_decode(parsed.get("nonce_hex", ""), 24, "nonce_hex") + cipher = _hex_decode(parsed.get(cipher_field, ""), 64, cipher_field) + return _artifact_dict( + memory_kib, iterations, parallelism, salt, nonce, cipher) + + +def _load_artifact_json(path): + """Load and validate a hardened artifact JSON for header generation. + + Parameters + ---------- + path : str + Artifact JSON file path. + + Returns + ------- + dict + Canonical artifact dictionary. + """ + parsed = _parse_json_file(path) + return _artifact_from_parsed(parsed) + + +def _check_header_match(generated_path, expected_path): + """Fail when the generated header does not match an expected file. + + Parameters + ---------- + generated_path : str + Generated header file path. + expected_path : str + Expected committed header file path. + + Returns + ------- + None + """ + generated = Path(generated_path).read_text(encoding="utf-8") + expected = Path(expected_path).read_text(encoding="utf-8") + if generated != expected: + raise RuntimeError( + "Generated header does not match committed " + "include/demo_artifact.h. Regenerate and commit " + "updated artifacts with scripts/dec.py.") + + +def _print_header_check(header_path, expected_path): + """Print the verified header match result. + + Parameters + ---------- + header_path : str + Generated header file path. + expected_path : str + Expected committed header file path. + + Returns + ------- + None + """ + _check_header_match(header_path, expected_path) + print("Verified header matches: {0}".format(Path(expected_path).resolve())) + + +def _parse_args(): + """Parse command-line arguments. + + Parameters + ---------- + None + + Returns + ------- + argparse.Namespace + Parsed command-line arguments. + """ + parser = argparse.ArgumentParser(description=__doc__) + for argument_group in _ARGUMENT_GROUPS: + for name, kwargs in argument_group: + parser.add_argument(name, **kwargs) + return parser.parse_args() + + +def _build_from_args(args, salt, nonce): + """Build the hardened entry from parsed arguments. + + Parameters + ---------- + args : argparse.Namespace + Parsed command-line arguments. + salt : bytes or None + Optional fixed salt value. + nonce : bytes or None + Optional fixed nonce value. + + Returns + ------- + tuple + Resulting (salt, nonce, ciphertext_and_tag) values. + """ + return build_hardened_entry( + key_str=args.key, text_str=args.text, led_on=not args.led_off, + append_crlf=not args.no_crlf, salt=salt, nonce=nonce, + memory_kib=args.memory_kib, iterations=args.iterations, + parallelism=args.parallelism, + ) + + +def _flush_outputs(args, salt, nonce, cipher): + """Write the artifact JSON and header, then return the header path. + + Parameters + ---------- + args : argparse.Namespace + Parsed command-line arguments. + salt : bytes + 16-byte salt. + nonce : bytes + 24-byte nonce. + cipher : bytes + 64-byte ciphertext and tag. + + Returns + ------- + Path + Resolved generated header path. + """ + json_path, artifact = _write_demo_json( + args.out, args.memory_kib, args.iterations, args.parallelism, + salt, nonce, cipher) + header_path = _write_header(args.header_out, artifact) + print("Wrote hardened demo artifact JSON: {0}".format(json_path)) + print("Wrote generated firmware header: {0}".format(header_path)) + return header_path + + +def _write_demo_json(path, memory_kib, iterations, parallelism, salt, + nonce, ciphertext_and_tag): + """Write the hardened JSON artifact consumed by docs and validation. + + Parameters + ---------- + path : str + Destination JSON file path. + memory_kib : int + Argon2 memory cost in KiB. + iterations : int + Argon2 time cost. + parallelism : int + Argon2 parallel lane count. + salt : bytes + 16-byte salt. + nonce : bytes + 24-byte nonce. + ciphertext_and_tag : bytes + 64-byte ciphertext and tag. + + Returns + ------- + tuple + Resolved (path, artifact) values. + """ + output_path = Path(path) + output_path.parent.mkdir(parents=True, exist_ok=True) + artifact = _artifact_dict( + memory_kib, iterations, parallelism, salt, nonce, ciphertext_and_tag) + text = json.dumps(artifact, indent=2) + "\n" + output_path.write_text(text, encoding="utf-8") + return output_path.resolve(), artifact + + +def _flush_from_json(args): + """Write the header from an existing artifact JSON. + + Parameters + ---------- + args : argparse.Namespace + Parsed command-line arguments. + + Returns + ------- + Path + Resolved generated header path. + """ + artifact = _load_artifact_json(args.from_json) + header_path = _write_header(args.header_out, artifact) + print("Wrote generated firmware header: {0}".format(header_path)) + return header_path + + +def _run_from_json(args): + """Generate the header only from committed artifact JSON. + + Parameters + ---------- + args : argparse.Namespace + Parsed command-line arguments. + + Returns + ------- + None + """ + header_path = _flush_from_json(args) + if args.check_header_path: + _print_header_check(header_path, args.check_header_path) + + +def _run_from_generate(args): + """Encrypt fresh artifact material, then emit JSON and the header. + + Parameters + ---------- + args : argparse.Namespace + Parsed command-line arguments. + + Returns + ------- + None + """ + salt = _optional_hex(args.salt_hex, 16, "salt_hex") + nonce = _optional_hex(args.nonce_hex, 24, "nonce_hex") + header_path = _flush_outputs( + args, *_build_from_args(args, salt, nonce), + ) + if args.check_header_path: + _print_header_check(header_path, args.check_header_path) + + +def main(): + """Generate the hardened artifact JSON and C header. + + Parameters + ---------- + None + + Returns + ------- + None + """ + args = _parse_args() + if args.from_json: + _run_from_json(args) + else: + _run_from_generate(args) + + +_ARGUMENT_GROUPS = ( + ( + ("--key", dict(default=DEFAULT_PASSPHRASE, help=_KEY_HELP)), + ("--salt-hex", dict(help=_SALT_HEX_HELP)), + ("--nonce-hex", dict(help=_NONCE_HEX_HELP)), + ), + ( + ("--text", dict(default=DEFAULT_TEXT, help=_TEXT_HELP)), + ("--out", dict(default=DEFAULT_OUTPUT_JSON, help=_OUT_HELP)), + ("--header-out", + dict(default=DEFAULT_OUTPUT_HEADER, help=_HEADER_OUT_HELP)), + ("--from-json", dict(help=_FROM_JSON_HELP)), + ("--no-crlf", dict(action="store_true", help=_NO_CRLF_HELP)), + ("--led-off", dict(action="store_true", help=_LED_OFF_HELP)), + ), + ( + ("--memory-kib", + dict(type=int, default=DEFAULT_MEMORY_KIB, help=_MEMORY_HELP)), + ("--iterations", + dict(type=int, default=DEFAULT_ITERATIONS, help=_ITERATIONS_HELP)), + ("--parallelism", + dict(type=int, default=DEFAULT_PARALLELISM, + help=_PARALLELISM_HELP)), + ("--check-header-path", dict(help=_CHECK_HEADER_HELP)), + ), +) + + +if __name__ == "__main__": + main() diff --git a/0x0017a_ctf/scripts/demo_artifact.json b/0x0017a_ctf/scripts/demo_artifact.json new file mode 100644 index 0000000..5c61254 --- /dev/null +++ b/0x0017a_ctf/scripts/demo_artifact.json @@ -0,0 +1,9 @@ +{ + "format": "ouroboros-hardened-demo-v1", + "memory_kib": 64, + "iterations": 3, + "parallelism": 1, + "salt_hex": "f2d518639a82019dc2d7afa5cdb6d871", + "nonce_hex": "1cef790d779e7c04e7f066dd90d080708797671f79efc4e4", + "ciphertext_and_tag_hex": "2c23b27e9562b8ed9e08e06dd99db4913e819a778bb47b71bc661e6e731a8154cdb536a4767e9bf8533e031db8e5ae7aadb431cf12d9f9c45fa9b94b80dcbbde" +} diff --git a/0x0017a_ctf/scripts/verify_ctf.py b/0x0017a_ctf/scripts/verify_ctf.py new file mode 100644 index 0000000..a5de5b1 --- /dev/null +++ b/0x0017a_ctf/scripts/verify_ctf.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +"""Verify every technical claim of Operation Copperhead against CTF-02.bin. + +Exits 0 only when every address, byte, hash, and derived value in CTF-R.md and +CTF-S.md matches the shipped image and the compiled ELF. +""" +import hashlib +import struct +import sys +from pathlib import Path + +BASE = 0x10000000 +ROOT = Path(__file__).resolve().parent.parent +BIN = ROOT / "CTF-02.bin" +UF2 = ROOT / "CTF-02.uf2" + +EXPECTED_BIN_SHA = "85330c37cd0897746b1af447e4bac371dde2042abd2d61d58a61fe2a8eef3537" +EXPECTED_UF2_SHA = "f3cd4840260db820d792758cecacc5297bef1971b9eacf7601279256d8af1eab" + +FRAME_THRESHOLD_A = 0x10000302 +FRAME_THRESHOLD_B = 0x10000312 +FRAME_TRACK = 0x1000C4B8 +FRAME_BLOCKLEN = 0x1000C4F0 +FRAME_SIGNALKEY = 0x1000C51C +FRAME_GATE = 0x1000C544 +FRAME_AUTH = 0x1000C57C +FRAME_OK = 0x1000C438 +FRAME_MISMATCH = 0x1000C43C +FRAME_SPEC_LITERAL = 0x100004FC +FRAME_DOUBLE = 0x1000EC60 +FRAME_SEED = 0x1000EC70 +FRAME_SALT = 0x1000CEEC +FRAME_NONCE = 0x1000CED4 +FRAME_CT = 0x1000CE94 + +DOUBLE_3_2 = bytes.fromhex("9A99999999990940") +DOUBLE_0_32 = bytes.fromhex("7B14AE47E17AD43F") +SEED_BAD = bytes.fromhex("0A0A0A0A") +SEED_GOOD = bytes.fromhex("7465206B") +SPEC_VALUE = 0x2D879291 +BUG_KEY = 0x915DCFF8 + +RESULTS = [] + + +def check(label, ok, detail=""): + """Record one verification result. + + Parameters + ---------- + label : str + Human-readable check name. + ok : bool + Whether the check passed. + detail : str + Extra context printed with the result. + + Returns + ------- + None + """ + RESULTS.append(ok) + print(f"[{'PASS' if ok else 'FAIL'}] {label} {detail}") + + +def rotl(v, s): + """Rotate a 32-bit value left.""" + return ((v << s) & 0xFFFFFFFF) | (v >> (32 - s)) + + +def qr_phase(a, b, c, d, s): + """Apply one ARX phase of a ChaCha quarter round.""" + a = (a + b) & 0xFFFFFFFF + d ^= a + d = rotl(d, s) + c = (c + d) & 0xFFFFFFFF + b ^= c + b = rotl(b, s) + return a, b, c, d + + +def derive(seed, iv): + """Derive the firmware signal key from a seed and IV.""" + a, b, c, d = seed, iv, 0x61707865, 0x3320646E + for _ in range(4): + for s in (16, 12, 8, 7): + a, b, c, d = qr_phase(a, b, c, d, s) + return (a ^ d) & 0xFFFFFFFF + + +def main(): + """Run all verification checks. + + Returns + ------- + int + Zero when every check passes, else one. + """ + data = BIN.read_bytes() + check("CTF-02.bin SHA-256", hashlib.sha256(data).hexdigest() == EXPECTED_BIN_SHA) + check("CTF-02.uf2 SHA-256", + hashlib.sha256(UF2.read_bytes()).hexdigest() == EXPECTED_UF2_SHA) + check("CTF-02.bin size", len(data) == 62308, f"({len(data)})") + check("vector table", data[0:32].hex() == + "002008205b0100101b0100101d01001011010010110100101101001011010010") + check("initial SP", struct.unpack(" + +// Non-zero once auth_init() has prepared the onboard LED GPIO. auth_execute() +// reports an internal error whenever this flag is not yet set, mirroring the +// reference construction from the encryption-c-rp2350 repository. +static bool g_auth_ready; + +/** + * @brief Clear a byte buffer. + * + * Writes zero to each byte in the caller-supplied buffer so derived keys + * and plaintext are not left resident in memory longer than needed. + * + * @param buf Pointer to mutable byte buffer. + * @param len Number of bytes to clear. + * @return None. + */ +static void clear_bytes(uint8_t *buf, size_t len) +{ + size_t i; + for (i = 0u; i < len; ++i) { + buf[i] = 0u; + } +} + +/** + * @brief Rotate a 32-bit value left. + * + * The HChaCha20 core uses 32-bit modular additions and left rotations in + * its quarter-round primitive. + * + * @param value Input 32-bit word. + * @param shift Rotation distance in bits. + * @return uint32_t Rotated result. + */ +static uint32_t rotl32(uint32_t value, uint8_t shift) +{ + return (value << shift) | (value >> (32u - shift)); +} + +/** + * @brief Load a 32-bit little-endian word from bytes. + * + * Converts four little-endian bytes into the word representation used by + * the HChaCha20 state machine. + * + * @param src Pointer to four readable bytes. + * @return uint32_t Parsed 32-bit word. + */ +static uint32_t load32_le(const uint8_t *src) +{ + return (uint32_t)src[0] | ((uint32_t)src[1] << 8u) | + ((uint32_t)src[2] << 16u) | ((uint32_t)src[3] << 24u); +} + +/** + * @brief Store a 32-bit word in little-endian byte order. + * + * Serializes one HChaCha20 state word into the caller-supplied output + * buffer. + * + * @param dst Pointer to four writable bytes. + * @param value 32-bit word to serialize. + * @return None. + */ +static void store32_le(uint8_t *dst, uint32_t value) +{ + dst[0] = (uint8_t)(value & 0xFFu); + dst[1] = (uint8_t)((value >> 8u) & 0xFFu); + dst[2] = (uint8_t)((value >> 16u) & 0xFFu); + dst[3] = (uint8_t)((value >> 24u) & 0xFFu); +} + +/** + * @brief Execute one ChaCha quarter-round. + * + * Mutates four state words in place according to the standard ChaCha20 + * ARX quarter-round used by the HChaCha20 subkey derivation. + * + * @param a Pointer to state word a. + * @param b Pointer to state word b. + * @param c Pointer to state word c. + * @param d Pointer to state word d. + * @return None. + */ +static void quarter_round(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) +{ + *a += *b; *d ^= *a; *d = rotl32(*d, 16u); + *c += *d; *b ^= *c; *b = rotl32(*b, 12u); + *a += *b; *d ^= *a; *d = rotl32(*d, 8u); + *c += *d; *b ^= *c; *b = rotl32(*b, 7u); +} + +/** + * @brief Derive a 256-bit XChaCha20 subkey from key and nonce prefix. + * + * Runs the HChaCha20 core over the first 16 bytes of the 24-byte XChaCha + * nonce and emits the derived 32-byte subkey. + * + * @param key Pointer to 32-byte AEAD key. + * @param nonce Pointer to 24-byte XChaCha20 nonce. + * @param subkey Output 32-byte subkey buffer. + * @return None. + */ +static void hchacha20(const uint8_t key[32], const uint8_t nonce[24], uint8_t subkey[32]) +{ + uint32_t state[16] = { + 0x61707865u, 0x3320646Eu, 0x79622D32u, 0x6B206574u, + load32_le(&key[0]), load32_le(&key[4]), load32_le(&key[8]), load32_le(&key[12]), + load32_le(&key[16]), load32_le(&key[20]), load32_le(&key[24]), load32_le(&key[28]), + load32_le(&nonce[0]), load32_le(&nonce[4]), load32_le(&nonce[8]), load32_le(&nonce[12]), + }; + uint8_t round; + for (round = 0u; round < 10u; ++round) { + quarter_round(&state[0], &state[4], &state[8], &state[12]); + quarter_round(&state[1], &state[5], &state[9], &state[13]); + quarter_round(&state[2], &state[6], &state[10], &state[14]); + quarter_round(&state[3], &state[7], &state[11], &state[15]); + quarter_round(&state[0], &state[5], &state[10], &state[15]); + quarter_round(&state[1], &state[6], &state[11], &state[12]); + quarter_round(&state[2], &state[7], &state[8], &state[13]); + quarter_round(&state[3], &state[4], &state[9], &state[14]); + } + store32_le(&subkey[0], state[0]); + store32_le(&subkey[4], state[1]); + store32_le(&subkey[8], state[2]); + store32_le(&subkey[12], state[3]); + store32_le(&subkey[16], state[12]); + store32_le(&subkey[20], state[13]); + store32_le(&subkey[24], state[14]); + store32_le(&subkey[28], state[15]); +} + +/** + * @brief Build the inner 96-bit nonce used by ChaCha20-Poly1305. + * + * XChaCha20 converts the last 8 bytes of the 24-byte outer nonce into the + * final 12-byte IETF ChaCha nonce by prefixing four zero bytes. + * + * @param nonce Pointer to 24-byte XChaCha20 nonce. + * @param out Output 12-byte nonce buffer. + * @return None. + */ +static void build_inner_nonce(const uint8_t nonce[24], uint8_t out[12]) +{ + memset(out, 0, 4u); + memcpy(&out[4], &nonce[16], 8u); +} + +/** + * @brief Return true when a byte is ASCII whitespace used by the CLI. + * + * The firmware normalizes spaces, carriage returns, tabs, and newlines in + * the same broad spirit as split-whitespace host parsing. + * + * @param ch Input byte. + * @return bool true when byte is treated as whitespace. + */ +static bool is_space(uint8_t ch) +{ + return (ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n'); +} + +/** + * @brief Return true when a byte is lowercase ASCII. + * + * Hardened passphrases accept only lowercase a-z characters in each word. + * + * @param ch Input byte. + * @return bool true when byte is in the lowercase ASCII range. + */ +static bool is_lowercase_ascii(uint8_t ch) +{ + return (ch >= 'a') && (ch <= 'z'); +} + +/** + * @brief Validate the strict hardened passphrase policy. + * + * Accepts only passphrases containing exactly 12 lowercase ASCII words + * separated by whitespace. + * + * @param passphrase Pointer to passphrase bytes. + * @param passphrase_len Number of passphrase bytes. + * @return bool true when the passphrase satisfies the policy. + */ +static bool validate_hardened_passphrase(const uint8_t *passphrase, size_t passphrase_len) +{ + size_t i = 0u; + uint8_t words = 0u; + if ((passphrase == NULL) || (passphrase_len == 0u) || (passphrase_len > AUTH_PASSPHRASE_MAX_LEN)) { + return false; + } + while (i < passphrase_len) { + while ((i < passphrase_len) && is_space(passphrase[i])) { + ++i; + } + if (i == passphrase_len) { + break; + } + ++words; + while ((i < passphrase_len) && !is_space(passphrase[i])) { + if (!is_lowercase_ascii(passphrase[i])) { + return false; + } + ++i; + } + } + return words == AUTH_REQUIRED_WORDS; +} + +/** + * @brief Derive the 32-byte hardened key with Argon2id. + * + * Uses the generated artifact parameters and salt to derive the AEAD key + * that protects the embedded ciphertext. + * + * @param passphrase Pointer to passphrase bytes. + * @param passphrase_len Number of passphrase bytes. + * @param key_out Output 32-byte key buffer. + * @return bool true when derivation succeeds. + */ +static bool derive_hardened_key(const uint8_t *passphrase, size_t passphrase_len, uint8_t key_out[32]) +{ + return argon2id_hash_raw( + DEMO_ITERATIONS, + DEMO_MEMORY_KIB, + DEMO_PARALLELISM, + passphrase, + passphrase_len, + DEMO_SALT, + AUTH_SALT_SIZE, + key_out, + AUTH_KEY_SIZE) == ARGON2_OK; +} + +/** + * @brief Decrypt the embedded artifact with XChaCha20-Poly1305. + * + * Derives the XChaCha20 subkey with HChaCha20, converts the outer nonce to + * the inner 96-bit nonce, and verifies/decrypts the payload in one shot. + * + * @param key Pointer to 32-byte Argon2id-derived key. + * @param payload_out Output 48-byte plaintext payload buffer. + * @return bool true when tag verification and decryption succeed. + */ +static bool decrypt_artifact(const uint8_t key[32], uint8_t payload_out[AUTH_PAYLOAD_SIZE]) +{ + bool ok; + int rc; + uint8_t subkey[32]; + uint8_t inner_nonce[12]; + mbedtls_chachapoly_context ctx; + hchacha20(key, DEMO_NONCE, subkey); + build_inner_nonce(DEMO_NONCE, inner_nonce); + mbedtls_chachapoly_init(&ctx); + rc = mbedtls_chachapoly_setkey(&ctx, subkey); + if (rc == 0) { + rc = mbedtls_chachapoly_auth_decrypt( + &ctx, + AUTH_PAYLOAD_SIZE, + inner_nonce, + NULL, + 0u, + &DEMO_CIPHERTEXT_AND_TAG[AUTH_PAYLOAD_SIZE], + DEMO_CIPHERTEXT_AND_TAG, + payload_out); + } + mbedtls_chachapoly_free(&ctx); + clear_bytes(subkey, sizeof(subkey)); + clear_bytes(inner_nonce, sizeof(inner_nonce)); + ok = (rc == 0); + if (!ok) { + clear_bytes(payload_out, AUTH_PAYLOAD_SIZE); + } + return ok; +} + +/** + * @brief Dispatch the decrypted payload to GPIO25 and UART. + * + * Mirrors the Rust demo payload contract: byte 0 controls the LED, and + * bytes 1..7 are transmitted verbatim over UART. + * + * @param payload Pointer to decrypted 48-byte payload. + * @return None. + */ +static void dispatch_payload(const uint8_t payload[AUTH_PAYLOAD_SIZE]) +{ + uint8_t i; + gpio_put(AUTH_LED_PIN, payload[0] ? 1 : 0); + for (i = 1u; i < 8u; ++i) { + putchar_raw((char)payload[i]); + } +} + +bool auth_init(void) +{ + g_auth_ready = true; + gpio_init(AUTH_LED_PIN); + gpio_set_dir(AUTH_LED_PIN, GPIO_OUT); + gpio_put(AUTH_LED_PIN, 0); + return true; +} + +auth_result_t auth_execute(const uint8_t *passphrase, size_t passphrase_len) +{ + uint8_t key[AUTH_KEY_SIZE]; + uint8_t payload[AUTH_PAYLOAD_SIZE]; + if (!g_auth_ready) { + return AUTH_RESULT_INTERNAL_ERROR; + } + if (!validate_hardened_passphrase(passphrase, passphrase_len)) { + return AUTH_RESULT_POLICY_VIOLATION; + } + if (!derive_hardened_key(passphrase, passphrase_len, key)) { + return AUTH_RESULT_INTERNAL_ERROR; + } + if (!decrypt_artifact(key, payload)) { + clear_bytes(key, sizeof(key)); + return AUTH_RESULT_AUTHENTICATION_FAILED; + } + dispatch_payload(payload); + clear_bytes(payload, sizeof(payload)); + clear_bytes(key, sizeof(key)); + return AUTH_RESULT_SUCCESS; +} \ No newline at end of file diff --git a/0x0017a_ctf/src/cli.c b/0x0017a_ctf/src/cli.c new file mode 100644 index 0000000..cb6fc6c --- /dev/null +++ b/0x0017a_ctf/src/cli.c @@ -0,0 +1,149 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent/encryption-c-rp2350 +// File: cli.c +// Desc: Implements the CLI UART passphrase input interface for Ouroboros. +// Created: 2026 + +#include "cli.h" +#include "auth.h" +#include "pico/stdlib.h" +#include + +/** + * @brief Maximum number of passphrase characters accepted from UART. + * + * Limits the input buffer to the hardened engine boundary. A null + * terminator is written after the last character so the buffer must be + * declared with at least this many bytes. + */ +#define PASS_BUF_LEN AUTH_PASSPHRASE_MAX_LEN + +/** + * @brief Print the hardened passphrase policy hint. + * + * The firmware uses the same interactive policy as the host demo: + * exactly 12 lowercase words separated by spaces. + * + * @param None. + * @return None. + */ +static void print_policy_hint(void) +{ + printf("Enter exactly 12 lowercase words separated by spaces.\r\n"); +} + +/** + * @brief Append one received character to the passphrase buffer. + * + * Stores printable characters up to the buffer limit minus one to + * reserve room for a null terminator. Echoes the character back + * over UART for interactive typing feedback. + * + * @param ch Input character value. + * @param buf Pointer to mutable passphrase buffer. + * @param idx Pointer to current buffer length. + * @return None. + */ +static void append_char(int ch, char *buf, size_t *idx) +{ + if (*idx + 1u >= PASS_BUF_LEN) { + return; + } + buf[*idx] = (char)ch; + *idx += 1u; + putchar_raw((char)ch); +} + +/** + * @brief Remove one character from the passphrase buffer. + * + * Moves the index back by one and emits the backspace-escape + * sequence to erase the last echoed character on the terminal. + * + * @param idx Pointer to current buffer length. + * @return None. + */ +static void handle_backspace(size_t *idx) +{ + if (*idx == 0u) { + return; + } + *idx -= 1u; + printf("\b \b"); +} + +/** + * @brief Finalise and authenticate the current passphrase buffer. + * + * Null-terminates the input, runs the full Ouroboros authentication + * pipeline via auth_execute, prints policy guidance or authentication + * failure text as needed, and resets the buffer index for the next + * prompt cycle. + * + * @param buf Pointer to mutable passphrase buffer. + * @param idx Pointer to current buffer length. + * @return None. + */ +static void finish_passphrase(char *buf, size_t *idx) +{ + auth_result_t result; + putchar_raw('\r'); + putchar_raw('\n'); + buf[*idx] = '\0'; + result = auth_execute((const uint8_t *)buf, *idx); + if (result == AUTH_RESULT_POLICY_VIOLATION) { + gpio_put(AUTH_LED_PIN, 0); + print_policy_hint(); + } else if (result != AUTH_RESULT_SUCCESS) { + gpio_put(AUTH_LED_PIN, 0); + printf("Authentication failed.\r\n"); + } + *idx = 0u; + print_prompt(); +} + +void print_prompt(void) +{ + printf("\r\n> "); +} + +void service_uart(char *buf, size_t *idx) +{ + int ch = getchar_timeout_us(0); + if (ch == PICO_ERROR_TIMEOUT) { + tight_loop_contents(); + return; + } + if ((ch == '\b') || (ch == 127)) { + handle_backspace(idx); + return; + } + if ((ch == '\r') || (ch == '\n')) { + finish_passphrase(buf, idx); + return; + } + append_char(ch, buf, idx); +} diff --git a/0x0017a_ctf/src/main.c b/0x0017a_ctf/src/main.c new file mode 100644 index 0000000..d672f92 --- /dev/null +++ b/0x0017a_ctf/src/main.c @@ -0,0 +1,404 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: main.c +// Desc: DEEPLINE Metro interlocking host for the FINAL-PRACTICE exercise. +// Chains the strict Ouroboros operator gate (Argon2id plus +// XChaCha20-Poly1305) around the frozen relay telemetry puzzles. +// Created: 2026 + +#include +#include +#include +#include +#include "auth.h" +#include "pico/stdlib.h" + +// Block deviation classification ceiling below which an automated train +// release is permitted. BUG: PALLAS compiled in 95; the engineering limit +// is 60 (two redundant cmp sites in the ship image). +#define SAFE_THRESHOLD 95u + +// Number of 32-bit ARX state words used by the signal-key machinery. +#define ARX_WORDS 4u + +// Spec value of the derived signal key minted in the incident report. This is +// the key the honest build derives from GATE_SEED_GOOD and the derived IV. +#define SIGNAL_SPEC 0x2D879291u + +// ChaCha expand word used by the console-side derivation path. +#define GATE_SEED_GOOD 0x6B206574u + +// Track-circuit current deviation frozen by the dead pilot wire (amperes). +// A live reading is recalculated in the field; this image holds a wrong +// snapshot that engineers must decode from registers and SRAM. +static volatile uint32_t g_block_current = 87u; + +// Operator-facing block classification (drives the BLOCK STATE line). +static volatile uint32_t g_operator_state = 0u; + +// Automatic train release decision (drives the AUTO TRAIN line). +static volatile uint32_t g_dispatch_state = 0u; + +// Static per-cycle poll counter retained in .bss. Watch this from GDB. +static volatile uint32_t g_fault_polls = 0u; + +// ARX seed substituted by the poisoned build up to the signal derivation. +// BUG: 0x0A0A0A0A was fused in place of the "te k" expand word 0x6B206574. +static volatile uint32_t g_auth_seed = 0x0A0A0A0Au; + +// Derived signal key printed each cycle and checked against SIGNAL_SPEC. +static uint32_t g_signal_key = 0u; + +/** + * @brief Hold one track-block telemetry record for the interlocking. + * + * Stores the measured block length, the condition flag word, and the + * crossing identifier used by the automatic train protection logic. + */ +typedef struct telemetry_t { + double block_length_km; + uint32_t block_flags; + uint16_t crossing; +} telemetry_t; + +// Live telemetry record. BUG: block_length_km shipped as 3.2 km; the real +// BRIDGE-4 block is 0.32 km, far below the minimum release spacing. +static volatile telemetry_t g_telemetry = { 3.2, 0x3u, 7u }; + +// Interactive passphrase buffer and parser cursor for the operator gate. +static char g_linebuf[AUTH_PASSPHRASE_MAX_LEN]; +static size_t g_lineidx = 0u; + +/** + * @brief Rotate a 32-bit value left. + * + * @param value Input 32-bit word. + * @param shift Rotation distance in bits. + * @return uint32_t Rotated result. + */ +static uint32_t rotl32(uint32_t value, uint8_t shift) +{ + return (value << shift) | (value >> (32u - shift)); +} + +/** + * @brief Load a 32-bit little-endian word from bytes. + * + * @param src Pointer to four readable bytes. + * @return uint32_t Parsed 32-bit word. + */ +static uint32_t load32_le(const uint8_t *src) +{ + return (uint32_t)src[0] | ((uint32_t)src[1] << 8u) | + ((uint32_t)src[2] << 16u) | ((uint32_t)src[3] << 24u); +} + +/** + * @brief Store a 32-bit word in little-endian byte order. + * + * @param dst Pointer to four writable bytes. + * @param value 32-bit word to serialize. + * @return None. + */ +static void store32_le(uint8_t *dst, uint32_t value) +{ + dst[0] = (uint8_t)(value & 0xFFu); + dst[1] = (uint8_t)((value >> 8u) & 0xFFu); + dst[2] = (uint8_t)((value >> 16u) & 0xFFu); + dst[3] = (uint8_t)((value >> 24u) & 0xFFu); +} + +/** + * @brief Apply one additive-rotate-xor phase of a ChaCha quarter-round. + * + * @param a Pointer to state word A. + * @param b Pointer to state word B. + * @param c Pointer to state word C. + * @param d Pointer to state word D. + * @param shift Rotation distance in bits. + * @return None. + */ +static void qr_phase(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint8_t shift) +{ + *a += *b; + *d ^= *a; + *d = rotl32(*d, shift); + *c += *d; + *b ^= *c; + *b = rotl32(*b, shift); +} + +/** + * @brief Execute one full ChaCha ARX quarter-round. + * + * @param a Pointer to state word A. + * @param b Pointer to state word B. + * @param c Pointer to state word C. + * @param d Pointer to state word D. + * @return None. + */ +static void quarter_round(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) +{ + qr_phase(a, b, c, d, 16u); + qr_phase(a, b, c, d, 12u); + qr_phase(a, b, c, d, 8u); + qr_phase(a, b, c, d, 7u); +} + +/** + * @brief Run r full quarter-rounds over a four-word ARX state. + * + * @param a Pointer to state word A. + * @param b Pointer to state word B. + * @param c Pointer to state word C. + * @param d Pointer to state word D. + * @param rounds Number of full quarter-rounds to run. + * @return None. + */ +static void run_rounds(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint8_t rounds) +{ + uint8_t r; + for (r = 0u; r < rounds; ++r) { + quarter_round(a, b, c, d); + } +} + +/** + * @brief Derive a 32-bit ARX session key from a seed and IV. + * + * Runs four ChaCha quarter-rounds over the seed, the IV, and the ChaCha + * expand constants; returns state word A exclusive-or state word D. + * + * @param seed 32-bit seed word. + * @param iv 32-bit IV word. + * @return uint32_t Derived session key word. + */ +static uint32_t derive_session_key(uint32_t seed, uint32_t iv) +{ + uint32_t a = seed; + uint32_t b = iv; + uint32_t c = 0x61707865u; + uint32_t d = 0x3320646Eu; + run_rounds(&a, &b, &c, &d, 4u); + return a ^ d; +} + +/** + * @brief Derive the runtime IV from the good console seed word. + * + * @param None. + * @return uint32_t Derived IV word (0xC0F89829 in an honest image). + */ +static uint32_t derive_state_iv(void) +{ + return derive_session_key(GATE_SEED_GOOD, 0u); +} + +/** + * @brief Refresh the runtime signal key from the live seed and IV. + * + * Captures the freshly derived IV, then derives the final key word from + * the substituted seed. Break after each derivation to read the register. + * + * @param None. + * @return None. + */ +static void set_signal_state(void) +{ + uint32_t iv = derive_state_iv(); + g_signal_key = derive_session_key(g_auth_seed, iv); +} + +/** + * @brief Classify the frozen current reading against the compiled limit. + * + * Compares the frozen track-circuit current reading against SAFE_THRESHOLD + * and assigns the resulting boolean status to both g_operator_state and + * g_dispatch_state. + * + * @param None. + * @return None. + */ +static void classify_blocks(void) +{ + g_operator_state = (g_block_current < SAFE_THRESHOLD) ? 1u : 0u; + g_dispatch_state = (g_block_current < SAFE_THRESHOLD) ? 1u : 0u; +} + +/** + * @brief Print the relay boot identity and unconditional signal line. + * + * Emits the DEEPLINE authority banner, adaptive signal window, serial console + * configuration string, and nominal track status prompt over the console. + * + * @param None. + * @return None. + */ +static void print_identity(void) +{ + printf("DEEPLINE METRO AUTHORITY\r\n"); + printf("ADAPTIVE SIGNAL WINDOW: 38 MINUTES\r\n"); + printf("USB-CDC 115200 8N1 | AUTHORIZED LAB CONSOLE\r\n"); + printf("TRACK: NORMAL\r\n"); +} + +/** + * @brief Print the recurring interlocking status report once per cycle. + * + * Computes block length in meters, increments the fault poll counter, prints + * block state, train authorization status, fault poll tally, and signal key + * validation report, and re-emits the command prompt. + * + * @param None. + * @return None. + */ +static void print_status(void) +{ + uint32_t metres = (uint32_t)(g_telemetry.block_length_km * 1000.0); + g_fault_polls += 1u; + printf("BLOCK STATE: %s\r\n", g_operator_state ? "STABLE" : "CRITICAL"); + printf("AUTO TRAIN: %s\r\n", g_dispatch_state ? "AUTHORIZED" : "HELD"); + printf("BLOCK LENGTH: %u M\r\n", metres); + printf("FAULT POLLS: %u\r\n", g_fault_polls); + printf("SIGNAL KEY: 0x%08X %s\r\n", g_signal_key, + (g_signal_key == SIGNAL_SPEC) ? "OK" : "MISMATCH"); + printf("RESPONSE> "); +} + +/** + * @brief Append one received character to the passphrase buffer. + * + * Stores printable characters up to the maximum passphrase length boundary + * and echoes the character back to the console for interactive typing feedback. + * + * @param ch Input character value. + * @return None. + */ +static void append_char(int ch) +{ + if (g_lineidx + 1u >= AUTH_PASSPHRASE_MAX_LEN) { + return; + } + g_linebuf[g_lineidx] = (char)ch; + g_lineidx += 1u; + putchar_raw((char)ch); +} + +/** + * @brief Remove one character from the passphrase buffer. + * + * Decrements the buffer index and emits a backspace-space-backspace escape + * sequence to erase the character on the user's terminal. + * + * @param None. + * @return None. + */ +static void drop_char(void) +{ + if (g_lineidx == 0u) { + return; + } + g_lineidx -= 1u; + printf("\b \b"); +} + +/** + * @brief Authenticate the completed passphrase against the Ouroboros gate. + * + * Terminates the string buffer, invokes auth_execute, handles policy violation + * or authentication failure outputs, and resets the line buffer for the next input. + * + * @param None. + * @return None. + */ +static void submit_gate(void) +{ + auth_result_t result; + putchar_raw('\r'); + putchar_raw('\n'); + g_linebuf[g_lineidx] = '\0'; + result = auth_execute((const uint8_t *)g_linebuf, g_lineidx); + if (result == AUTH_RESULT_POLICY_VIOLATION) { + gpio_put(AUTH_LED_PIN, 0); + printf("Enter exactly 12 lowercase words separated by spaces.\r\n"); + } else if (result == AUTH_RESULT_SUCCESS) { + printf("AUTHORITY FRAME: VERIFIED\r\n"); + } else { + gpio_put(AUTH_LED_PIN, 0); + printf("Authentication failed.\r\n"); + } + g_lineidx = 0u; + printf("RESPONSE> "); +} + +/** + * @brief Poll the console for one passphrase input event. + * + * Reads a single character from standard input with zero timeout and routes + * backspace, newline/carriage return, or printable characters to their respective + * handlers. + * + * @param None. + * @return None. + */ +static void poll_console(void) +{ + int ch = getchar_timeout_us(0); + while (ch != PICO_ERROR_TIMEOUT) { + if ((ch == '\b') || (ch == 127)) { + drop_char(); + } else if ((ch == '\r') || (ch == '\n')) { + submit_gate(); + } else { + append_char(ch); + } + ch = getchar_timeout_us(0); + } +} + +/** + * @brief Drive the DEEPLINE relay console and operator gate forever. + * + * Initializes standard I/O and the authentication engine, classifies the track + * blocks, emits the initial system banner, and enters an infinite loop refreshing + * the signal state, reporting status, and servicing the console every two seconds. + * + * @param None. + * @return int Process exit code (never returns during normal operation). + */ +int main(void) +{ + stdio_init_all(); + auth_init(); + classify_blocks(); + print_identity(); + while (true) { + set_signal_state(); + print_status(); + poll_console(); + sleep_ms(2000u); + } +} \ No newline at end of file diff --git a/0x0017a_ctf/src/mbedtls_shims.c b/0x0017a_ctf/src/mbedtls_shims.c new file mode 100644 index 0000000..7c06a9d --- /dev/null +++ b/0x0017a_ctf/src/mbedtls_shims.c @@ -0,0 +1,49 @@ +// MIT License +// +// Copyright (c) 2026 Kevin Thomas +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Author: Kevin Thomas +// Email: kevin@mytechnotalent.com +// GitHub: https://github.com/mytechnotalent +// File: mbedtls_shims.c +// Desc: Implements platform zeroization shims required by mbedTLS on RP2350. +// Created: 2026 + +#include "mbedtls/platform_util.h" + +/** + * @brief Securely clear a memory region. + * + * Provides the mbedTLS platform zeroization hook for this firmware build. + * The volatile pointer prevents the compiler from optimizing away the + * clearing loop. + * + * @param buf Pointer to mutable memory region to clear. + * @param len Number of bytes to clear. + * @return None. + */ +void mbedtls_platform_zeroize(void *buf, size_t len) +{ + volatile unsigned char *ptr = (volatile unsigned char *)buf; + while (len-- > 0u) { + *ptr++ = 0u; + } +} diff --git a/0x0017a_ctf/third_party/argon2/include/argon2.h b/0x0017a_ctf/third_party/argon2/include/argon2.h new file mode 100644 index 0000000..3980bb3 --- /dev/null +++ b/0x0017a_ctf/third_party/argon2/include/argon2.h @@ -0,0 +1,437 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef ARGON2_H +#define ARGON2_H + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Symbols visibility control */ +#ifdef A2_VISCTL +#define ARGON2_PUBLIC __attribute__((visibility("default"))) +#define ARGON2_LOCAL __attribute__ ((visibility ("hidden"))) +#elif defined(_MSC_VER) +#define ARGON2_PUBLIC __declspec(dllexport) +#define ARGON2_LOCAL +#else +#define ARGON2_PUBLIC +#define ARGON2_LOCAL +#endif + +/* + * Argon2 input parameter restrictions + */ + +/* Minimum and maximum number of lanes (degree of parallelism) */ +#define ARGON2_MIN_LANES UINT32_C(1) +#define ARGON2_MAX_LANES UINT32_C(0xFFFFFF) + +/* Minimum and maximum number of threads */ +#define ARGON2_MIN_THREADS UINT32_C(1) +#define ARGON2_MAX_THREADS UINT32_C(0xFFFFFF) + +/* Number of synchronization points between lanes per pass */ +#define ARGON2_SYNC_POINTS UINT32_C(4) + +/* Minimum and maximum digest size in bytes */ +#define ARGON2_MIN_OUTLEN UINT32_C(4) +#define ARGON2_MAX_OUTLEN UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum number of memory blocks (each of BLOCK_SIZE bytes) */ +#define ARGON2_MIN_MEMORY (2 * ARGON2_SYNC_POINTS) /* 2 blocks per slice */ + +#define ARGON2_MIN(a, b) ((a) < (b) ? (a) : (b)) +/* Max memory size is addressing-space/2, topping at 2^32 blocks (4 TB) */ +#define ARGON2_MAX_MEMORY_BITS \ + ARGON2_MIN(UINT32_C(32), (sizeof(void *) * CHAR_BIT - 10 - 1)) +#define ARGON2_MAX_MEMORY \ + ARGON2_MIN(UINT32_C(0xFFFFFFFF), UINT64_C(1) << ARGON2_MAX_MEMORY_BITS) + +/* Minimum and maximum number of passes */ +#define ARGON2_MIN_TIME UINT32_C(1) +#define ARGON2_MAX_TIME UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum password length in bytes */ +#define ARGON2_MIN_PWD_LENGTH UINT32_C(0) +#define ARGON2_MAX_PWD_LENGTH UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum associated data length in bytes */ +#define ARGON2_MIN_AD_LENGTH UINT32_C(0) +#define ARGON2_MAX_AD_LENGTH UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum salt length in bytes */ +#define ARGON2_MIN_SALT_LENGTH UINT32_C(8) +#define ARGON2_MAX_SALT_LENGTH UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum key length in bytes */ +#define ARGON2_MIN_SECRET UINT32_C(0) +#define ARGON2_MAX_SECRET UINT32_C(0xFFFFFFFF) + +/* Flags to determine which fields are securely wiped (default = no wipe). */ +#define ARGON2_DEFAULT_FLAGS UINT32_C(0) +#define ARGON2_FLAG_CLEAR_PASSWORD (UINT32_C(1) << 0) +#define ARGON2_FLAG_CLEAR_SECRET (UINT32_C(1) << 1) + +/* Global flag to determine if we are wiping internal memory buffers. This flag + * is defined in core.c and defaults to 1 (wipe internal memory). */ +extern int FLAG_clear_internal_memory; + +/* Error codes */ +typedef enum Argon2_ErrorCodes { + ARGON2_OK = 0, + + ARGON2_OUTPUT_PTR_NULL = -1, + + ARGON2_OUTPUT_TOO_SHORT = -2, + ARGON2_OUTPUT_TOO_LONG = -3, + + ARGON2_PWD_TOO_SHORT = -4, + ARGON2_PWD_TOO_LONG = -5, + + ARGON2_SALT_TOO_SHORT = -6, + ARGON2_SALT_TOO_LONG = -7, + + ARGON2_AD_TOO_SHORT = -8, + ARGON2_AD_TOO_LONG = -9, + + ARGON2_SECRET_TOO_SHORT = -10, + ARGON2_SECRET_TOO_LONG = -11, + + ARGON2_TIME_TOO_SMALL = -12, + ARGON2_TIME_TOO_LARGE = -13, + + ARGON2_MEMORY_TOO_LITTLE = -14, + ARGON2_MEMORY_TOO_MUCH = -15, + + ARGON2_LANES_TOO_FEW = -16, + ARGON2_LANES_TOO_MANY = -17, + + ARGON2_PWD_PTR_MISMATCH = -18, /* NULL ptr with non-zero length */ + ARGON2_SALT_PTR_MISMATCH = -19, /* NULL ptr with non-zero length */ + ARGON2_SECRET_PTR_MISMATCH = -20, /* NULL ptr with non-zero length */ + ARGON2_AD_PTR_MISMATCH = -21, /* NULL ptr with non-zero length */ + + ARGON2_MEMORY_ALLOCATION_ERROR = -22, + + ARGON2_FREE_MEMORY_CBK_NULL = -23, + ARGON2_ALLOCATE_MEMORY_CBK_NULL = -24, + + ARGON2_INCORRECT_PARAMETER = -25, + ARGON2_INCORRECT_TYPE = -26, + + ARGON2_OUT_PTR_MISMATCH = -27, + + ARGON2_THREADS_TOO_FEW = -28, + ARGON2_THREADS_TOO_MANY = -29, + + ARGON2_MISSING_ARGS = -30, + + ARGON2_ENCODING_FAIL = -31, + + ARGON2_DECODING_FAIL = -32, + + ARGON2_THREAD_FAIL = -33, + + ARGON2_DECODING_LENGTH_FAIL = -34, + + ARGON2_VERIFY_MISMATCH = -35 +} argon2_error_codes; + +/* Memory allocator types --- for external allocation */ +typedef int (*allocate_fptr)(uint8_t **memory, size_t bytes_to_allocate); +typedef void (*deallocate_fptr)(uint8_t *memory, size_t bytes_to_allocate); + +/* Argon2 external data structures */ + +/* + ***** + * Context: structure to hold Argon2 inputs: + * output array and its length, + * password and its length, + * salt and its length, + * secret and its length, + * associated data and its length, + * number of passes, amount of used memory (in KBytes, can be rounded up a bit) + * number of parallel threads that will be run. + * All the parameters above affect the output hash value. + * Additionally, two function pointers can be provided to allocate and + * deallocate the memory (if NULL, memory will be allocated internally). + * Also, three flags indicate whether to erase password, secret as soon as they + * are pre-hashed (and thus not needed anymore), and the entire memory + ***** + * Simplest situation: you have output array out[8], password is stored in + * pwd[32], salt is stored in salt[16], you do not have keys nor associated + * data. You need to spend 1 GB of RAM and you run 5 passes of Argon2d with + * 4 parallel lanes. + * You want to erase the password, but you're OK with last pass not being + * erased. You want to use the default memory allocator. + * Then you initialize: + Argon2_Context(out,8,pwd,32,salt,16,NULL,0,NULL,0,5,1<<20,4,4,NULL,NULL,true,false,false,false) + */ +typedef struct Argon2_Context { + uint8_t *out; /* output array */ + uint32_t outlen; /* digest length */ + + uint8_t *pwd; /* password array */ + uint32_t pwdlen; /* password length */ + + uint8_t *salt; /* salt array */ + uint32_t saltlen; /* salt length */ + + uint8_t *secret; /* key array */ + uint32_t secretlen; /* key length */ + + uint8_t *ad; /* associated data array */ + uint32_t adlen; /* associated data length */ + + uint32_t t_cost; /* number of passes */ + uint32_t m_cost; /* amount of memory requested (KB) */ + uint32_t lanes; /* number of lanes */ + uint32_t threads; /* maximum number of threads */ + + uint32_t version; /* version number */ + + allocate_fptr allocate_cbk; /* pointer to memory allocator */ + deallocate_fptr free_cbk; /* pointer to memory deallocator */ + + uint32_t flags; /* array of bool options */ +} argon2_context; + +/* Argon2 primitive type */ +typedef enum Argon2_type { + Argon2_d = 0, + Argon2_i = 1, + Argon2_id = 2 +} argon2_type; + +/* Version of the algorithm */ +typedef enum Argon2_version { + ARGON2_VERSION_10 = 0x10, + ARGON2_VERSION_13 = 0x13, + ARGON2_VERSION_NUMBER = ARGON2_VERSION_13 +} argon2_version; + +/* + * Function that gives the string representation of an argon2_type. + * @param type The argon2_type that we want the string for + * @param uppercase Whether the string should have the first letter uppercase + * @return NULL if invalid type, otherwise the string representation. + */ +ARGON2_PUBLIC const char *argon2_type2string(argon2_type type, int uppercase); + +/* + * Function that performs memory-hard hashing with certain degree of parallelism + * @param context Pointer to the Argon2 internal structure + * @return Error code if smth is wrong, ARGON2_OK otherwise + */ +ARGON2_PUBLIC int argon2_ctx(argon2_context *context, argon2_type type); + +/** + * Hashes a password with Argon2i, producing an encoded hash + * @param t_cost Number of iterations + * @param m_cost Sets memory usage to m_cost kibibytes + * @param parallelism Number of threads and compute lanes + * @param pwd Pointer to password + * @param pwdlen Password size in bytes + * @param salt Pointer to salt + * @param saltlen Salt size in bytes + * @param hashlen Desired length of the hash in bytes + * @param encoded Buffer where to write the encoded hash + * @param encodedlen Size of the buffer (thus max size of the encoded hash) + * @pre Different parallelism levels will give different results + * @pre Returns ARGON2_OK if successful + */ +ARGON2_PUBLIC int argon2i_hash_encoded(const uint32_t t_cost, + const uint32_t m_cost, + const uint32_t parallelism, + const void *pwd, const size_t pwdlen, + const void *salt, const size_t saltlen, + const size_t hashlen, char *encoded, + const size_t encodedlen); + +/** + * Hashes a password with Argon2i, producing a raw hash at @hash + * @param t_cost Number of iterations + * @param m_cost Sets memory usage to m_cost kibibytes + * @param parallelism Number of threads and compute lanes + * @param pwd Pointer to password + * @param pwdlen Password size in bytes + * @param salt Pointer to salt + * @param saltlen Salt size in bytes + * @param hash Buffer where to write the raw hash - updated by the function + * @param hashlen Desired length of the hash in bytes + * @pre Different parallelism levels will give different results + * @pre Returns ARGON2_OK if successful + */ +ARGON2_PUBLIC int argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, + const size_t hashlen); + +ARGON2_PUBLIC int argon2d_hash_encoded(const uint32_t t_cost, + const uint32_t m_cost, + const uint32_t parallelism, + const void *pwd, const size_t pwdlen, + const void *salt, const size_t saltlen, + const size_t hashlen, char *encoded, + const size_t encodedlen); + +ARGON2_PUBLIC int argon2d_hash_raw(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, + const size_t hashlen); + +ARGON2_PUBLIC int argon2id_hash_encoded(const uint32_t t_cost, + const uint32_t m_cost, + const uint32_t parallelism, + const void *pwd, const size_t pwdlen, + const void *salt, const size_t saltlen, + const size_t hashlen, char *encoded, + const size_t encodedlen); + +ARGON2_PUBLIC int argon2id_hash_raw(const uint32_t t_cost, + const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, + const size_t hashlen); + +/* generic function underlying the above ones */ +ARGON2_PUBLIC int argon2_hash(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, + const size_t hashlen, char *encoded, + const size_t encodedlen, argon2_type type, + const uint32_t version); + +/** + * Verifies a password against an encoded string + * Encoded string is restricted as in validate_inputs() + * @param encoded String encoding parameters, salt, hash + * @param pwd Pointer to password + * @pre Returns ARGON2_OK if successful + */ +ARGON2_PUBLIC int argon2i_verify(const char *encoded, const void *pwd, + const size_t pwdlen); + +ARGON2_PUBLIC int argon2d_verify(const char *encoded, const void *pwd, + const size_t pwdlen); + +ARGON2_PUBLIC int argon2id_verify(const char *encoded, const void *pwd, + const size_t pwdlen); + +/* generic function underlying the above ones */ +ARGON2_PUBLIC int argon2_verify(const char *encoded, const void *pwd, + const size_t pwdlen, argon2_type type); + +/** + * Argon2d: Version of Argon2 that picks memory blocks depending + * on the password and salt. Only for side-channel-free + * environment!! + ***** + * @param context Pointer to current Argon2 context + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC int argon2d_ctx(argon2_context *context); + +/** + * Argon2i: Version of Argon2 that picks memory blocks + * independent on the password and salt. Good for side-channels, + * but worse w.r.t. tradeoff attacks if only one pass is used. + ***** + * @param context Pointer to current Argon2 context + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC int argon2i_ctx(argon2_context *context); + +/** + * Argon2id: Version of Argon2 where the first half-pass over memory is + * password-independent, the rest are password-dependent (on the password and + * salt). OK against side channels (they reduce to 1/2-pass Argon2i), and + * better with w.r.t. tradeoff attacks (similar to Argon2d). + ***** + * @param context Pointer to current Argon2 context + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC int argon2id_ctx(argon2_context *context); + +/** + * Verify if a given password is correct for Argon2d hashing + * @param context Pointer to current Argon2 context + * @param hash The password hash to verify. The length of the hash is + * specified by the context outlen member + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC int argon2d_verify_ctx(argon2_context *context, const char *hash); + +/** + * Verify if a given password is correct for Argon2i hashing + * @param context Pointer to current Argon2 context + * @param hash The password hash to verify. The length of the hash is + * specified by the context outlen member + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC int argon2i_verify_ctx(argon2_context *context, const char *hash); + +/** + * Verify if a given password is correct for Argon2id hashing + * @param context Pointer to current Argon2 context + * @param hash The password hash to verify. The length of the hash is + * specified by the context outlen member + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC int argon2id_verify_ctx(argon2_context *context, + const char *hash); + +/* generic function underlying the above ones */ +ARGON2_PUBLIC int argon2_verify_ctx(argon2_context *context, const char *hash, + argon2_type type); + +/** + * Get the associated error message for given error code + * @return The error message associated with the given error code + */ +ARGON2_PUBLIC const char *argon2_error_message(int error_code); + +/** + * Returns the encoded hash length for the given input parameters + * @param t_cost Number of iterations + * @param m_cost Memory usage in kibibytes + * @param parallelism Number of threads; used to compute lanes + * @param saltlen Salt size in bytes + * @param hashlen Hash size in bytes + * @param type The argon2_type that we want the encoded length for + * @return The encoded hash length in bytes + */ +ARGON2_PUBLIC size_t argon2_encodedlen(uint32_t t_cost, uint32_t m_cost, + uint32_t parallelism, uint32_t saltlen, + uint32_t hashlen, argon2_type type); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/0x0017a_ctf/third_party/argon2/src/argon2.c b/0x0017a_ctf/third_party/argon2/src/argon2.c new file mode 100644 index 0000000..34da3d6 --- /dev/null +++ b/0x0017a_ctf/third_party/argon2/src/argon2.c @@ -0,0 +1,452 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#include +#include +#include + +#include "argon2.h" +#include "encoding.h" +#include "core.h" + +const char *argon2_type2string(argon2_type type, int uppercase) { + switch (type) { + case Argon2_d: + return uppercase ? "Argon2d" : "argon2d"; + case Argon2_i: + return uppercase ? "Argon2i" : "argon2i"; + case Argon2_id: + return uppercase ? "Argon2id" : "argon2id"; + } + + return NULL; +} + +int argon2_ctx(argon2_context *context, argon2_type type) { + /* 1. Validate all inputs */ + int result = validate_inputs(context); + uint32_t memory_blocks, segment_length; + argon2_instance_t instance; + + if (ARGON2_OK != result) { + return result; + } + + if (Argon2_d != type && Argon2_i != type && Argon2_id != type) { + return ARGON2_INCORRECT_TYPE; + } + + /* 2. Align memory size */ + /* Minimum memory_blocks = 8L blocks, where L is the number of lanes */ + memory_blocks = context->m_cost; + + if (memory_blocks < 2 * ARGON2_SYNC_POINTS * context->lanes) { + memory_blocks = 2 * ARGON2_SYNC_POINTS * context->lanes; + } + + segment_length = memory_blocks / (context->lanes * ARGON2_SYNC_POINTS); + /* Ensure that all segments have equal length */ + memory_blocks = segment_length * (context->lanes * ARGON2_SYNC_POINTS); + + instance.version = context->version; + instance.memory = NULL; + instance.passes = context->t_cost; + instance.memory_blocks = memory_blocks; + instance.segment_length = segment_length; + instance.lane_length = segment_length * ARGON2_SYNC_POINTS; + instance.lanes = context->lanes; + instance.threads = context->threads; + instance.type = type; + + if (instance.threads > instance.lanes) { + instance.threads = instance.lanes; + } + + /* 3. Initialization: Hashing inputs, allocating memory, filling first + * blocks + */ + result = initialize(&instance, context); + + if (ARGON2_OK != result) { + return result; + } + + /* 4. Filling memory */ + result = fill_memory_blocks(&instance); + + if (ARGON2_OK != result) { + return result; + } + /* 5. Finalization */ + finalize(context, &instance); + + return ARGON2_OK; +} + +int argon2_hash(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, const size_t saltlen, + void *hash, const size_t hashlen, char *encoded, + const size_t encodedlen, argon2_type type, + const uint32_t version){ + + argon2_context context; + int result; + uint8_t *out; + + if (pwdlen > ARGON2_MAX_PWD_LENGTH) { + return ARGON2_PWD_TOO_LONG; + } + + if (saltlen > ARGON2_MAX_SALT_LENGTH) { + return ARGON2_SALT_TOO_LONG; + } + + if (hashlen > ARGON2_MAX_OUTLEN) { + return ARGON2_OUTPUT_TOO_LONG; + } + + if (hashlen < ARGON2_MIN_OUTLEN) { + return ARGON2_OUTPUT_TOO_SHORT; + } + + out = malloc(hashlen); + if (!out) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + context.out = (uint8_t *)out; + context.outlen = (uint32_t)hashlen; + context.pwd = CONST_CAST(uint8_t *)pwd; + context.pwdlen = (uint32_t)pwdlen; + context.salt = CONST_CAST(uint8_t *)salt; + context.saltlen = (uint32_t)saltlen; + context.secret = NULL; + context.secretlen = 0; + context.ad = NULL; + context.adlen = 0; + context.t_cost = t_cost; + context.m_cost = m_cost; + context.lanes = parallelism; + context.threads = parallelism; + context.allocate_cbk = NULL; + context.free_cbk = NULL; + context.flags = ARGON2_DEFAULT_FLAGS; + context.version = version; + + result = argon2_ctx(&context, type); + + if (result != ARGON2_OK) { + clear_internal_memory(out, hashlen); + free(out); + return result; + } + + /* if raw hash requested, write it */ + if (hash) { + memcpy(hash, out, hashlen); + } + + /* if encoding requested, write it */ + if (encoded && encodedlen) { + if (encode_string(encoded, encodedlen, &context, type) != ARGON2_OK) { + clear_internal_memory(out, hashlen); /* wipe buffers if error */ + clear_internal_memory(encoded, encodedlen); + free(out); + return ARGON2_ENCODING_FAIL; + } + } + clear_internal_memory(out, hashlen); + free(out); + + return ARGON2_OK; +} + +int argon2i_hash_encoded(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, const size_t hashlen, + char *encoded, const size_t encodedlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + NULL, hashlen, encoded, encodedlen, Argon2_i, + ARGON2_VERSION_NUMBER); +} + +int argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, const size_t hashlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + hash, hashlen, NULL, 0, Argon2_i, ARGON2_VERSION_NUMBER); +} + +int argon2d_hash_encoded(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, const size_t hashlen, + char *encoded, const size_t encodedlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + NULL, hashlen, encoded, encodedlen, Argon2_d, + ARGON2_VERSION_NUMBER); +} + +int argon2d_hash_raw(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, const size_t hashlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + hash, hashlen, NULL, 0, Argon2_d, ARGON2_VERSION_NUMBER); +} + +int argon2id_hash_encoded(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, const size_t hashlen, + char *encoded, const size_t encodedlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + NULL, hashlen, encoded, encodedlen, Argon2_id, + ARGON2_VERSION_NUMBER); +} + +int argon2id_hash_raw(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, const size_t hashlen) { + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + hash, hashlen, NULL, 0, Argon2_id, + ARGON2_VERSION_NUMBER); +} + +static int argon2_compare(const uint8_t *b1, const uint8_t *b2, size_t len) { + size_t i; + uint8_t d = 0U; + + for (i = 0U; i < len; i++) { + d |= b1[i] ^ b2[i]; + } + return (int)((1 & ((d - 1) >> 8)) - 1); +} + +int argon2_verify(const char *encoded, const void *pwd, const size_t pwdlen, + argon2_type type) { + + argon2_context ctx; + uint8_t *desired_result = NULL; + + int ret = ARGON2_OK; + + size_t encoded_len; + uint32_t max_field_len; + + if (pwdlen > ARGON2_MAX_PWD_LENGTH) { + return ARGON2_PWD_TOO_LONG; + } + + if (encoded == NULL) { + return ARGON2_DECODING_FAIL; + } + + encoded_len = strlen(encoded); + if (encoded_len > UINT32_MAX) { + return ARGON2_DECODING_FAIL; + } + + /* No field can be longer than the encoded length */ + max_field_len = (uint32_t)encoded_len; + + ctx.saltlen = max_field_len; + ctx.outlen = max_field_len; + + ctx.salt = malloc(ctx.saltlen); + ctx.out = malloc(ctx.outlen); + if (!ctx.salt || !ctx.out) { + ret = ARGON2_MEMORY_ALLOCATION_ERROR; + goto fail; + } + + ctx.pwd = (uint8_t *)pwd; + ctx.pwdlen = (uint32_t)pwdlen; + + ret = decode_string(&ctx, encoded, type); + if (ret != ARGON2_OK) { + goto fail; + } + + /* Set aside the desired result, and get a new buffer. */ + desired_result = ctx.out; + ctx.out = malloc(ctx.outlen); + if (!ctx.out) { + ret = ARGON2_MEMORY_ALLOCATION_ERROR; + goto fail; + } + + ret = argon2_verify_ctx(&ctx, (char *)desired_result, type); + if (ret != ARGON2_OK) { + goto fail; + } + +fail: + free(ctx.salt); + free(ctx.out); + free(desired_result); + + return ret; +} + +int argon2i_verify(const char *encoded, const void *pwd, const size_t pwdlen) { + + return argon2_verify(encoded, pwd, pwdlen, Argon2_i); +} + +int argon2d_verify(const char *encoded, const void *pwd, const size_t pwdlen) { + + return argon2_verify(encoded, pwd, pwdlen, Argon2_d); +} + +int argon2id_verify(const char *encoded, const void *pwd, const size_t pwdlen) { + + return argon2_verify(encoded, pwd, pwdlen, Argon2_id); +} + +int argon2d_ctx(argon2_context *context) { + return argon2_ctx(context, Argon2_d); +} + +int argon2i_ctx(argon2_context *context) { + return argon2_ctx(context, Argon2_i); +} + +int argon2id_ctx(argon2_context *context) { + return argon2_ctx(context, Argon2_id); +} + +int argon2_verify_ctx(argon2_context *context, const char *hash, + argon2_type type) { + int ret = argon2_ctx(context, type); + if (ret != ARGON2_OK) { + return ret; + } + + if (argon2_compare((uint8_t *)hash, context->out, context->outlen)) { + return ARGON2_VERIFY_MISMATCH; + } + + return ARGON2_OK; +} + +int argon2d_verify_ctx(argon2_context *context, const char *hash) { + return argon2_verify_ctx(context, hash, Argon2_d); +} + +int argon2i_verify_ctx(argon2_context *context, const char *hash) { + return argon2_verify_ctx(context, hash, Argon2_i); +} + +int argon2id_verify_ctx(argon2_context *context, const char *hash) { + return argon2_verify_ctx(context, hash, Argon2_id); +} + +const char *argon2_error_message(int error_code) { + switch (error_code) { + case ARGON2_OK: + return "OK"; + case ARGON2_OUTPUT_PTR_NULL: + return "Output pointer is NULL"; + case ARGON2_OUTPUT_TOO_SHORT: + return "Output is too short"; + case ARGON2_OUTPUT_TOO_LONG: + return "Output is too long"; + case ARGON2_PWD_TOO_SHORT: + return "Password is too short"; + case ARGON2_PWD_TOO_LONG: + return "Password is too long"; + case ARGON2_SALT_TOO_SHORT: + return "Salt is too short"; + case ARGON2_SALT_TOO_LONG: + return "Salt is too long"; + case ARGON2_AD_TOO_SHORT: + return "Associated data is too short"; + case ARGON2_AD_TOO_LONG: + return "Associated data is too long"; + case ARGON2_SECRET_TOO_SHORT: + return "Secret is too short"; + case ARGON2_SECRET_TOO_LONG: + return "Secret is too long"; + case ARGON2_TIME_TOO_SMALL: + return "Time cost is too small"; + case ARGON2_TIME_TOO_LARGE: + return "Time cost is too large"; + case ARGON2_MEMORY_TOO_LITTLE: + return "Memory cost is too small"; + case ARGON2_MEMORY_TOO_MUCH: + return "Memory cost is too large"; + case ARGON2_LANES_TOO_FEW: + return "Too few lanes"; + case ARGON2_LANES_TOO_MANY: + return "Too many lanes"; + case ARGON2_PWD_PTR_MISMATCH: + return "Password pointer is NULL, but password length is not 0"; + case ARGON2_SALT_PTR_MISMATCH: + return "Salt pointer is NULL, but salt length is not 0"; + case ARGON2_SECRET_PTR_MISMATCH: + return "Secret pointer is NULL, but secret length is not 0"; + case ARGON2_AD_PTR_MISMATCH: + return "Associated data pointer is NULL, but ad length is not 0"; + case ARGON2_MEMORY_ALLOCATION_ERROR: + return "Memory allocation error"; + case ARGON2_FREE_MEMORY_CBK_NULL: + return "The free memory callback is NULL"; + case ARGON2_ALLOCATE_MEMORY_CBK_NULL: + return "The allocate memory callback is NULL"; + case ARGON2_INCORRECT_PARAMETER: + return "Argon2_Context context is NULL"; + case ARGON2_INCORRECT_TYPE: + return "There is no such version of Argon2"; + case ARGON2_OUT_PTR_MISMATCH: + return "Output pointer mismatch"; + case ARGON2_THREADS_TOO_FEW: + return "Not enough threads"; + case ARGON2_THREADS_TOO_MANY: + return "Too many threads"; + case ARGON2_MISSING_ARGS: + return "Missing arguments"; + case ARGON2_ENCODING_FAIL: + return "Encoding failed"; + case ARGON2_DECODING_FAIL: + return "Decoding failed"; + case ARGON2_THREAD_FAIL: + return "Threading failure"; + case ARGON2_DECODING_LENGTH_FAIL: + return "Some of encoded parameters are too long or too short"; + case ARGON2_VERIFY_MISMATCH: + return "The password does not match the supplied hash"; + default: + return "Unknown error code"; + } +} + +size_t argon2_encodedlen(uint32_t t_cost, uint32_t m_cost, uint32_t parallelism, + uint32_t saltlen, uint32_t hashlen, argon2_type type) { + return strlen("$$v=$m=,t=,p=$$") + strlen(argon2_type2string(type, 0)) + + numlen(t_cost) + numlen(m_cost) + numlen(parallelism) + + b64len(saltlen) + b64len(hashlen) + numlen(ARGON2_VERSION_NUMBER) + 1; +} diff --git a/0x0017a_ctf/third_party/argon2/src/blake2/blake2-impl.h b/0x0017a_ctf/third_party/argon2/src/blake2/blake2-impl.h new file mode 100644 index 0000000..86d0d5c --- /dev/null +++ b/0x0017a_ctf/third_party/argon2/src/blake2/blake2-impl.h @@ -0,0 +1,156 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef PORTABLE_BLAKE2_IMPL_H +#define PORTABLE_BLAKE2_IMPL_H + +#include +#include + +#ifdef _WIN32 +#define BLAKE2_INLINE __inline +#elif defined(__GNUC__) || defined(__clang__) +#define BLAKE2_INLINE __inline__ +#else +#define BLAKE2_INLINE +#endif + +/* Argon2 Team - Begin Code */ +/* + Not an exhaustive list, but should cover the majority of modern platforms + Additionally, the code will always be correct---this is only a performance + tweak. +*/ +#if (defined(__BYTE_ORDER__) && \ + (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || \ + defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__MIPSEL__) || \ + defined(__AARCH64EL__) || defined(__amd64__) || defined(__i386__) || \ + defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || \ + defined(_M_ARM) +#define NATIVE_LITTLE_ENDIAN +#endif +/* Argon2 Team - End Code */ + +static BLAKE2_INLINE uint32_t load32(const void *src) { +#if defined(NATIVE_LITTLE_ENDIAN) + uint32_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = (const uint8_t *)src; + uint32_t w = *p++; + w |= (uint32_t)(*p++) << 8; + w |= (uint32_t)(*p++) << 16; + w |= (uint32_t)(*p++) << 24; + return w; +#endif +} + +static BLAKE2_INLINE uint64_t load64(const void *src) { +#if defined(NATIVE_LITTLE_ENDIAN) + uint64_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = (const uint8_t *)src; + uint64_t w = *p++; + w |= (uint64_t)(*p++) << 8; + w |= (uint64_t)(*p++) << 16; + w |= (uint64_t)(*p++) << 24; + w |= (uint64_t)(*p++) << 32; + w |= (uint64_t)(*p++) << 40; + w |= (uint64_t)(*p++) << 48; + w |= (uint64_t)(*p++) << 56; + return w; +#endif +} + +static BLAKE2_INLINE void store32(void *dst, uint32_t w) { +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = (uint8_t *)dst; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; +#endif +} + +static BLAKE2_INLINE void store64(void *dst, uint64_t w) { +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = (uint8_t *)dst; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; +#endif +} + +static BLAKE2_INLINE uint64_t load48(const void *src) { + const uint8_t *p = (const uint8_t *)src; + uint64_t w = *p++; + w |= (uint64_t)(*p++) << 8; + w |= (uint64_t)(*p++) << 16; + w |= (uint64_t)(*p++) << 24; + w |= (uint64_t)(*p++) << 32; + w |= (uint64_t)(*p++) << 40; + return w; +} + +static BLAKE2_INLINE void store48(void *dst, uint64_t w) { + uint8_t *p = (uint8_t *)dst; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; +} + +static BLAKE2_INLINE uint32_t rotr32(const uint32_t w, const unsigned c) { + return (w >> c) | (w << (32 - c)); +} + +static BLAKE2_INLINE uint64_t rotr64(const uint64_t w, const unsigned c) { + return (w >> c) | (w << (64 - c)); +} + +void clear_internal_memory(void *v, size_t n); + +#endif diff --git a/0x0017a_ctf/third_party/argon2/src/blake2/blake2.h b/0x0017a_ctf/third_party/argon2/src/blake2/blake2.h new file mode 100644 index 0000000..501c6a3 --- /dev/null +++ b/0x0017a_ctf/third_party/argon2/src/blake2/blake2.h @@ -0,0 +1,89 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef PORTABLE_BLAKE2_H +#define PORTABLE_BLAKE2_H + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +enum blake2b_constant { + BLAKE2B_BLOCKBYTES = 128, + BLAKE2B_OUTBYTES = 64, + BLAKE2B_KEYBYTES = 64, + BLAKE2B_SALTBYTES = 16, + BLAKE2B_PERSONALBYTES = 16 +}; + +#pragma pack(push, 1) +typedef struct __blake2b_param { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint64_t node_offset; /* 16 */ + uint8_t node_depth; /* 17 */ + uint8_t inner_length; /* 18 */ + uint8_t reserved[14]; /* 32 */ + uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ + uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ +} blake2b_param; +#pragma pack(pop) + +typedef struct __blake2b_state { + uint64_t h[8]; + uint64_t t[2]; + uint64_t f[2]; + uint8_t buf[BLAKE2B_BLOCKBYTES]; + unsigned buflen; + unsigned outlen; + uint8_t last_node; +} blake2b_state; + +/* Ensure param structs have not been wrongly padded */ +/* Poor man's static_assert */ +enum { + blake2_size_check_0 = 1 / !!(CHAR_BIT == 8), + blake2_size_check_2 = + 1 / !!(sizeof(blake2b_param) == sizeof(uint64_t) * CHAR_BIT) +}; + +/* Streaming API */ +ARGON2_LOCAL int blake2b_init(blake2b_state *S, size_t outlen); +ARGON2_LOCAL int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, + size_t keylen); +ARGON2_LOCAL int blake2b_init_param(blake2b_state *S, const blake2b_param *P); +ARGON2_LOCAL int blake2b_update(blake2b_state *S, const void *in, size_t inlen); +ARGON2_LOCAL int blake2b_final(blake2b_state *S, void *out, size_t outlen); + +/* Simple API */ +ARGON2_LOCAL int blake2b(void *out, size_t outlen, const void *in, size_t inlen, + const void *key, size_t keylen); + +/* Argon2 Team - Begin Code */ +ARGON2_LOCAL int blake2b_long(void *out, size_t outlen, const void *in, size_t inlen); +/* Argon2 Team - End Code */ + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/0x0017a_ctf/third_party/argon2/src/blake2/blake2b.c b/0x0017a_ctf/third_party/argon2/src/blake2/blake2b.c new file mode 100644 index 0000000..3b519dd --- /dev/null +++ b/0x0017a_ctf/third_party/argon2/src/blake2/blake2b.c @@ -0,0 +1,390 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#include +#include +#include + +#include "blake2.h" +#include "blake2-impl.h" + +static const uint64_t blake2b_IV[8] = { + UINT64_C(0x6a09e667f3bcc908), UINT64_C(0xbb67ae8584caa73b), + UINT64_C(0x3c6ef372fe94f82b), UINT64_C(0xa54ff53a5f1d36f1), + UINT64_C(0x510e527fade682d1), UINT64_C(0x9b05688c2b3e6c1f), + UINT64_C(0x1f83d9abfb41bd6b), UINT64_C(0x5be0cd19137e2179)}; + +static const unsigned int blake2b_sigma[12][16] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, + {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, + {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, + {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, +}; + +static BLAKE2_INLINE void blake2b_set_lastnode(blake2b_state *S) { + S->f[1] = (uint64_t)-1; +} + +static BLAKE2_INLINE void blake2b_set_lastblock(blake2b_state *S) { + if (S->last_node) { + blake2b_set_lastnode(S); + } + S->f[0] = (uint64_t)-1; +} + +static BLAKE2_INLINE void blake2b_increment_counter(blake2b_state *S, + uint64_t inc) { + S->t[0] += inc; + S->t[1] += (S->t[0] < inc); +} + +static BLAKE2_INLINE void blake2b_invalidate_state(blake2b_state *S) { + clear_internal_memory(S, sizeof(*S)); /* wipe */ + blake2b_set_lastblock(S); /* invalidate for further use */ +} + +static BLAKE2_INLINE void blake2b_init0(blake2b_state *S) { + memset(S, 0, sizeof(*S)); + memcpy(S->h, blake2b_IV, sizeof(S->h)); +} + +int blake2b_init_param(blake2b_state *S, const blake2b_param *P) { + const unsigned char *p = (const unsigned char *)P; + unsigned int i; + + if (NULL == P || NULL == S) { + return -1; + } + + blake2b_init0(S); + /* IV XOR Parameter Block */ + for (i = 0; i < 8; ++i) { + S->h[i] ^= load64(&p[i * sizeof(S->h[i])]); + } + S->outlen = P->digest_length; + return 0; +} + +/* Sequential blake2b initialization */ +int blake2b_init(blake2b_state *S, size_t outlen) { + blake2b_param P; + + if (S == NULL) { + return -1; + } + + if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) { + blake2b_invalidate_state(S); + return -1; + } + + /* Setup Parameter Block for unkeyed BLAKE2 */ + P.digest_length = (uint8_t)outlen; + P.key_length = 0; + P.fanout = 1; + P.depth = 1; + P.leaf_length = 0; + P.node_offset = 0; + P.node_depth = 0; + P.inner_length = 0; + memset(P.reserved, 0, sizeof(P.reserved)); + memset(P.salt, 0, sizeof(P.salt)); + memset(P.personal, 0, sizeof(P.personal)); + + return blake2b_init_param(S, &P); +} + +int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, + size_t keylen) { + blake2b_param P; + + if (S == NULL) { + return -1; + } + + if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) { + blake2b_invalidate_state(S); + return -1; + } + + if ((key == 0) || (keylen == 0) || (keylen > BLAKE2B_KEYBYTES)) { + blake2b_invalidate_state(S); + return -1; + } + + /* Setup Parameter Block for keyed BLAKE2 */ + P.digest_length = (uint8_t)outlen; + P.key_length = (uint8_t)keylen; + P.fanout = 1; + P.depth = 1; + P.leaf_length = 0; + P.node_offset = 0; + P.node_depth = 0; + P.inner_length = 0; + memset(P.reserved, 0, sizeof(P.reserved)); + memset(P.salt, 0, sizeof(P.salt)); + memset(P.personal, 0, sizeof(P.personal)); + + if (blake2b_init_param(S, &P) < 0) { + blake2b_invalidate_state(S); + return -1; + } + + { + uint8_t block[BLAKE2B_BLOCKBYTES]; + memset(block, 0, BLAKE2B_BLOCKBYTES); + memcpy(block, key, keylen); + blake2b_update(S, block, BLAKE2B_BLOCKBYTES); + /* Burn the key from stack */ + clear_internal_memory(block, BLAKE2B_BLOCKBYTES); + } + return 0; +} + +static void blake2b_compress(blake2b_state *S, const uint8_t *block) { + uint64_t m[16]; + uint64_t v[16]; + unsigned int i, r; + + for (i = 0; i < 16; ++i) { + m[i] = load64(block + i * sizeof(m[i])); + } + + for (i = 0; i < 8; ++i) { + v[i] = S->h[i]; + } + + v[8] = blake2b_IV[0]; + v[9] = blake2b_IV[1]; + v[10] = blake2b_IV[2]; + v[11] = blake2b_IV[3]; + v[12] = blake2b_IV[4] ^ S->t[0]; + v[13] = blake2b_IV[5] ^ S->t[1]; + v[14] = blake2b_IV[6] ^ S->f[0]; + v[15] = blake2b_IV[7] ^ S->f[1]; + +#define G(r, i, a, b, c, d) \ + do { \ + a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \ + d = rotr64(d ^ a, 32); \ + c = c + d; \ + b = rotr64(b ^ c, 24); \ + a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \ + d = rotr64(d ^ a, 16); \ + c = c + d; \ + b = rotr64(b ^ c, 63); \ + } while ((void)0, 0) + +#define ROUND(r) \ + do { \ + G(r, 0, v[0], v[4], v[8], v[12]); \ + G(r, 1, v[1], v[5], v[9], v[13]); \ + G(r, 2, v[2], v[6], v[10], v[14]); \ + G(r, 3, v[3], v[7], v[11], v[15]); \ + G(r, 4, v[0], v[5], v[10], v[15]); \ + G(r, 5, v[1], v[6], v[11], v[12]); \ + G(r, 6, v[2], v[7], v[8], v[13]); \ + G(r, 7, v[3], v[4], v[9], v[14]); \ + } while ((void)0, 0) + + for (r = 0; r < 12; ++r) { + ROUND(r); + } + + for (i = 0; i < 8; ++i) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } + +#undef G +#undef ROUND +} + +int blake2b_update(blake2b_state *S, const void *in, size_t inlen) { + const uint8_t *pin = (const uint8_t *)in; + + if (inlen == 0) { + return 0; + } + + /* Sanity check */ + if (S == NULL || in == NULL) { + return -1; + } + + /* Is this a reused state? */ + if (S->f[0] != 0) { + return -1; + } + + if (S->buflen + inlen > BLAKE2B_BLOCKBYTES) { + /* Complete current block */ + size_t left = S->buflen; + size_t fill = BLAKE2B_BLOCKBYTES - left; + memcpy(&S->buf[left], pin, fill); + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress(S, S->buf); + S->buflen = 0; + inlen -= fill; + pin += fill; + /* Avoid buffer copies when possible */ + while (inlen > BLAKE2B_BLOCKBYTES) { + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress(S, pin); + inlen -= BLAKE2B_BLOCKBYTES; + pin += BLAKE2B_BLOCKBYTES; + } + } + memcpy(&S->buf[S->buflen], pin, inlen); + S->buflen += (unsigned int)inlen; + return 0; +} + +int blake2b_final(blake2b_state *S, void *out, size_t outlen) { + uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; + unsigned int i; + + /* Sanity checks */ + if (S == NULL || out == NULL || outlen < S->outlen) { + return -1; + } + + /* Is this a reused state? */ + if (S->f[0] != 0) { + return -1; + } + + blake2b_increment_counter(S, S->buflen); + blake2b_set_lastblock(S); + memset(&S->buf[S->buflen], 0, BLAKE2B_BLOCKBYTES - S->buflen); /* Padding */ + blake2b_compress(S, S->buf); + + for (i = 0; i < 8; ++i) { /* Output full hash to temp buffer */ + store64(buffer + sizeof(S->h[i]) * i, S->h[i]); + } + + memcpy(out, buffer, S->outlen); + clear_internal_memory(buffer, sizeof(buffer)); + clear_internal_memory(S->buf, sizeof(S->buf)); + clear_internal_memory(S->h, sizeof(S->h)); + return 0; +} + +int blake2b(void *out, size_t outlen, const void *in, size_t inlen, + const void *key, size_t keylen) { + blake2b_state S; + int ret = -1; + + /* Verify parameters */ + if (NULL == in && inlen > 0) { + goto fail; + } + + if (NULL == out || outlen == 0 || outlen > BLAKE2B_OUTBYTES) { + goto fail; + } + + if ((NULL == key && keylen > 0) || keylen > BLAKE2B_KEYBYTES) { + goto fail; + } + + if (keylen > 0) { + if (blake2b_init_key(&S, outlen, key, keylen) < 0) { + goto fail; + } + } else { + if (blake2b_init(&S, outlen) < 0) { + goto fail; + } + } + + if (blake2b_update(&S, in, inlen) < 0) { + goto fail; + } + ret = blake2b_final(&S, out, outlen); + +fail: + clear_internal_memory(&S, sizeof(S)); + return ret; +} + +/* Argon2 Team - Begin Code */ +int blake2b_long(void *pout, size_t outlen, const void *in, size_t inlen) { + uint8_t *out = (uint8_t *)pout; + blake2b_state blake_state; + uint8_t outlen_bytes[sizeof(uint32_t)] = {0}; + int ret = -1; + + if (outlen > UINT32_MAX) { + goto fail; + } + + /* Ensure little-endian byte order! */ + store32(outlen_bytes, (uint32_t)outlen); + +#define TRY(statement) \ + do { \ + ret = statement; \ + if (ret < 0) { \ + goto fail; \ + } \ + } while ((void)0, 0) + + if (outlen <= BLAKE2B_OUTBYTES) { + TRY(blake2b_init(&blake_state, outlen)); + TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); + TRY(blake2b_update(&blake_state, in, inlen)); + TRY(blake2b_final(&blake_state, out, outlen)); + } else { + uint32_t toproduce; + uint8_t out_buffer[BLAKE2B_OUTBYTES]; + uint8_t in_buffer[BLAKE2B_OUTBYTES]; + TRY(blake2b_init(&blake_state, BLAKE2B_OUTBYTES)); + TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); + TRY(blake2b_update(&blake_state, in, inlen)); + TRY(blake2b_final(&blake_state, out_buffer, BLAKE2B_OUTBYTES)); + memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2); + out += BLAKE2B_OUTBYTES / 2; + toproduce = (uint32_t)outlen - BLAKE2B_OUTBYTES / 2; + + while (toproduce > BLAKE2B_OUTBYTES) { + memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES); + TRY(blake2b(out_buffer, BLAKE2B_OUTBYTES, in_buffer, + BLAKE2B_OUTBYTES, NULL, 0)); + memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2); + out += BLAKE2B_OUTBYTES / 2; + toproduce -= BLAKE2B_OUTBYTES / 2; + } + + memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES); + TRY(blake2b(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL, + 0)); + memcpy(out, out_buffer, toproduce); + } +fail: + clear_internal_memory(&blake_state, sizeof(blake_state)); + return ret; +#undef TRY +} +/* Argon2 Team - End Code */ diff --git a/0x0017a_ctf/third_party/argon2/src/blake2/blamka-round-opt.h b/0x0017a_ctf/third_party/argon2/src/blake2/blamka-round-opt.h new file mode 100644 index 0000000..3127f2a --- /dev/null +++ b/0x0017a_ctf/third_party/argon2/src/blake2/blamka-round-opt.h @@ -0,0 +1,471 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef BLAKE_ROUND_MKA_OPT_H +#define BLAKE_ROUND_MKA_OPT_H + +#include "blake2-impl.h" + +#include +#if defined(__SSSE3__) +#include /* for _mm_shuffle_epi8 and _mm_alignr_epi8 */ +#endif + +#if defined(__XOP__) && (defined(__GNUC__) || defined(__clang__)) +#include +#endif + +#if !defined(__AVX512F__) +#if !defined(__AVX2__) +#if !defined(__XOP__) +#if defined(__SSSE3__) +#define r16 \ + (_mm_setr_epi8(2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9)) +#define r24 \ + (_mm_setr_epi8(3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10)) +#define _mm_roti_epi64(x, c) \ + (-(c) == 32) \ + ? _mm_shuffle_epi32((x), _MM_SHUFFLE(2, 3, 0, 1)) \ + : (-(c) == 24) \ + ? _mm_shuffle_epi8((x), r24) \ + : (-(c) == 16) \ + ? _mm_shuffle_epi8((x), r16) \ + : (-(c) == 63) \ + ? _mm_xor_si128(_mm_srli_epi64((x), -(c)), \ + _mm_add_epi64((x), (x))) \ + : _mm_xor_si128(_mm_srli_epi64((x), -(c)), \ + _mm_slli_epi64((x), 64 - (-(c)))) +#else /* defined(__SSE2__) */ +#define _mm_roti_epi64(r, c) \ + _mm_xor_si128(_mm_srli_epi64((r), -(c)), _mm_slli_epi64((r), 64 - (-(c)))) +#endif +#else +#endif + +static BLAKE2_INLINE __m128i fBlaMka(__m128i x, __m128i y) { + const __m128i z = _mm_mul_epu32(x, y); + return _mm_add_epi64(_mm_add_epi64(x, y), _mm_add_epi64(z, z)); +} + +#define G1(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + A0 = fBlaMka(A0, B0); \ + A1 = fBlaMka(A1, B1); \ + \ + D0 = _mm_xor_si128(D0, A0); \ + D1 = _mm_xor_si128(D1, A1); \ + \ + D0 = _mm_roti_epi64(D0, -32); \ + D1 = _mm_roti_epi64(D1, -32); \ + \ + C0 = fBlaMka(C0, D0); \ + C1 = fBlaMka(C1, D1); \ + \ + B0 = _mm_xor_si128(B0, C0); \ + B1 = _mm_xor_si128(B1, C1); \ + \ + B0 = _mm_roti_epi64(B0, -24); \ + B1 = _mm_roti_epi64(B1, -24); \ + } while ((void)0, 0) + +#define G2(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + A0 = fBlaMka(A0, B0); \ + A1 = fBlaMka(A1, B1); \ + \ + D0 = _mm_xor_si128(D0, A0); \ + D1 = _mm_xor_si128(D1, A1); \ + \ + D0 = _mm_roti_epi64(D0, -16); \ + D1 = _mm_roti_epi64(D1, -16); \ + \ + C0 = fBlaMka(C0, D0); \ + C1 = fBlaMka(C1, D1); \ + \ + B0 = _mm_xor_si128(B0, C0); \ + B1 = _mm_xor_si128(B1, C1); \ + \ + B0 = _mm_roti_epi64(B0, -63); \ + B1 = _mm_roti_epi64(B1, -63); \ + } while ((void)0, 0) + +#if defined(__SSSE3__) +#define DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + __m128i t0 = _mm_alignr_epi8(B1, B0, 8); \ + __m128i t1 = _mm_alignr_epi8(B0, B1, 8); \ + B0 = t0; \ + B1 = t1; \ + \ + t0 = C0; \ + C0 = C1; \ + C1 = t0; \ + \ + t0 = _mm_alignr_epi8(D1, D0, 8); \ + t1 = _mm_alignr_epi8(D0, D1, 8); \ + D0 = t1; \ + D1 = t0; \ + } while ((void)0, 0) + +#define UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + __m128i t0 = _mm_alignr_epi8(B0, B1, 8); \ + __m128i t1 = _mm_alignr_epi8(B1, B0, 8); \ + B0 = t0; \ + B1 = t1; \ + \ + t0 = C0; \ + C0 = C1; \ + C1 = t0; \ + \ + t0 = _mm_alignr_epi8(D0, D1, 8); \ + t1 = _mm_alignr_epi8(D1, D0, 8); \ + D0 = t1; \ + D1 = t0; \ + } while ((void)0, 0) +#else /* SSE2 */ +#define DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + __m128i t0 = D0; \ + __m128i t1 = B0; \ + D0 = C0; \ + C0 = C1; \ + C1 = D0; \ + D0 = _mm_unpackhi_epi64(D1, _mm_unpacklo_epi64(t0, t0)); \ + D1 = _mm_unpackhi_epi64(t0, _mm_unpacklo_epi64(D1, D1)); \ + B0 = _mm_unpackhi_epi64(B0, _mm_unpacklo_epi64(B1, B1)); \ + B1 = _mm_unpackhi_epi64(B1, _mm_unpacklo_epi64(t1, t1)); \ + } while ((void)0, 0) + +#define UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + __m128i t0, t1; \ + t0 = C0; \ + C0 = C1; \ + C1 = t0; \ + t0 = B0; \ + t1 = D0; \ + B0 = _mm_unpackhi_epi64(B1, _mm_unpacklo_epi64(B0, B0)); \ + B1 = _mm_unpackhi_epi64(t0, _mm_unpacklo_epi64(B1, B1)); \ + D0 = _mm_unpackhi_epi64(D0, _mm_unpacklo_epi64(D1, D1)); \ + D1 = _mm_unpackhi_epi64(D1, _mm_unpacklo_epi64(t1, t1)); \ + } while ((void)0, 0) +#endif + +#define BLAKE2_ROUND(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + G1(A0, B0, C0, D0, A1, B1, C1, D1); \ + G2(A0, B0, C0, D0, A1, B1, C1, D1); \ + \ + DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1); \ + \ + G1(A0, B0, C0, D0, A1, B1, C1, D1); \ + G2(A0, B0, C0, D0, A1, B1, C1, D1); \ + \ + UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1); \ + } while ((void)0, 0) +#else /* __AVX2__ */ + +#include + +#define rotr32(x) _mm256_shuffle_epi32(x, _MM_SHUFFLE(2, 3, 0, 1)) +#define rotr24(x) _mm256_shuffle_epi8(x, _mm256_setr_epi8(3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10, 3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10)) +#define rotr16(x) _mm256_shuffle_epi8(x, _mm256_setr_epi8(2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9, 2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9)) +#define rotr63(x) _mm256_xor_si256(_mm256_srli_epi64((x), 63), _mm256_add_epi64((x), (x))) + +#define G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + __m256i ml = _mm256_mul_epu32(A0, B0); \ + ml = _mm256_add_epi64(ml, ml); \ + A0 = _mm256_add_epi64(A0, _mm256_add_epi64(B0, ml)); \ + D0 = _mm256_xor_si256(D0, A0); \ + D0 = rotr32(D0); \ + \ + ml = _mm256_mul_epu32(C0, D0); \ + ml = _mm256_add_epi64(ml, ml); \ + C0 = _mm256_add_epi64(C0, _mm256_add_epi64(D0, ml)); \ + \ + B0 = _mm256_xor_si256(B0, C0); \ + B0 = rotr24(B0); \ + \ + ml = _mm256_mul_epu32(A1, B1); \ + ml = _mm256_add_epi64(ml, ml); \ + A1 = _mm256_add_epi64(A1, _mm256_add_epi64(B1, ml)); \ + D1 = _mm256_xor_si256(D1, A1); \ + D1 = rotr32(D1); \ + \ + ml = _mm256_mul_epu32(C1, D1); \ + ml = _mm256_add_epi64(ml, ml); \ + C1 = _mm256_add_epi64(C1, _mm256_add_epi64(D1, ml)); \ + \ + B1 = _mm256_xor_si256(B1, C1); \ + B1 = rotr24(B1); \ + } while((void)0, 0); + +#define G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + __m256i ml = _mm256_mul_epu32(A0, B0); \ + ml = _mm256_add_epi64(ml, ml); \ + A0 = _mm256_add_epi64(A0, _mm256_add_epi64(B0, ml)); \ + D0 = _mm256_xor_si256(D0, A0); \ + D0 = rotr16(D0); \ + \ + ml = _mm256_mul_epu32(C0, D0); \ + ml = _mm256_add_epi64(ml, ml); \ + C0 = _mm256_add_epi64(C0, _mm256_add_epi64(D0, ml)); \ + B0 = _mm256_xor_si256(B0, C0); \ + B0 = rotr63(B0); \ + \ + ml = _mm256_mul_epu32(A1, B1); \ + ml = _mm256_add_epi64(ml, ml); \ + A1 = _mm256_add_epi64(A1, _mm256_add_epi64(B1, ml)); \ + D1 = _mm256_xor_si256(D1, A1); \ + D1 = rotr16(D1); \ + \ + ml = _mm256_mul_epu32(C1, D1); \ + ml = _mm256_add_epi64(ml, ml); \ + C1 = _mm256_add_epi64(C1, _mm256_add_epi64(D1, ml)); \ + B1 = _mm256_xor_si256(B1, C1); \ + B1 = rotr63(B1); \ + } while((void)0, 0); + +#define DIAGONALIZE_1(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + B0 = _mm256_permute4x64_epi64(B0, _MM_SHUFFLE(0, 3, 2, 1)); \ + C0 = _mm256_permute4x64_epi64(C0, _MM_SHUFFLE(1, 0, 3, 2)); \ + D0 = _mm256_permute4x64_epi64(D0, _MM_SHUFFLE(2, 1, 0, 3)); \ + \ + B1 = _mm256_permute4x64_epi64(B1, _MM_SHUFFLE(0, 3, 2, 1)); \ + C1 = _mm256_permute4x64_epi64(C1, _MM_SHUFFLE(1, 0, 3, 2)); \ + D1 = _mm256_permute4x64_epi64(D1, _MM_SHUFFLE(2, 1, 0, 3)); \ + } while((void)0, 0); + +#define DIAGONALIZE_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + __m256i tmp1 = _mm256_blend_epi32(B0, B1, 0xCC); \ + __m256i tmp2 = _mm256_blend_epi32(B0, B1, 0x33); \ + B1 = _mm256_permute4x64_epi64(tmp1, _MM_SHUFFLE(2,3,0,1)); \ + B0 = _mm256_permute4x64_epi64(tmp2, _MM_SHUFFLE(2,3,0,1)); \ + \ + tmp1 = C0; \ + C0 = C1; \ + C1 = tmp1; \ + \ + tmp1 = _mm256_blend_epi32(D0, D1, 0xCC); \ + tmp2 = _mm256_blend_epi32(D0, D1, 0x33); \ + D0 = _mm256_permute4x64_epi64(tmp1, _MM_SHUFFLE(2,3,0,1)); \ + D1 = _mm256_permute4x64_epi64(tmp2, _MM_SHUFFLE(2,3,0,1)); \ + } while(0); + +#define UNDIAGONALIZE_1(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + B0 = _mm256_permute4x64_epi64(B0, _MM_SHUFFLE(2, 1, 0, 3)); \ + C0 = _mm256_permute4x64_epi64(C0, _MM_SHUFFLE(1, 0, 3, 2)); \ + D0 = _mm256_permute4x64_epi64(D0, _MM_SHUFFLE(0, 3, 2, 1)); \ + \ + B1 = _mm256_permute4x64_epi64(B1, _MM_SHUFFLE(2, 1, 0, 3)); \ + C1 = _mm256_permute4x64_epi64(C1, _MM_SHUFFLE(1, 0, 3, 2)); \ + D1 = _mm256_permute4x64_epi64(D1, _MM_SHUFFLE(0, 3, 2, 1)); \ + } while((void)0, 0); + +#define UNDIAGONALIZE_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + __m256i tmp1 = _mm256_blend_epi32(B0, B1, 0xCC); \ + __m256i tmp2 = _mm256_blend_epi32(B0, B1, 0x33); \ + B0 = _mm256_permute4x64_epi64(tmp1, _MM_SHUFFLE(2,3,0,1)); \ + B1 = _mm256_permute4x64_epi64(tmp2, _MM_SHUFFLE(2,3,0,1)); \ + \ + tmp1 = C0; \ + C0 = C1; \ + C1 = tmp1; \ + \ + tmp1 = _mm256_blend_epi32(D0, D1, 0x33); \ + tmp2 = _mm256_blend_epi32(D0, D1, 0xCC); \ + D0 = _mm256_permute4x64_epi64(tmp1, _MM_SHUFFLE(2,3,0,1)); \ + D1 = _mm256_permute4x64_epi64(tmp2, _MM_SHUFFLE(2,3,0,1)); \ + } while((void)0, 0); + +#define BLAKE2_ROUND_1(A0, A1, B0, B1, C0, C1, D0, D1) \ + do{ \ + G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + DIAGONALIZE_1(A0, B0, C0, D0, A1, B1, C1, D1) \ + \ + G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + UNDIAGONALIZE_1(A0, B0, C0, D0, A1, B1, C1, D1) \ + } while((void)0, 0); + +#define BLAKE2_ROUND_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do{ \ + G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + DIAGONALIZE_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + UNDIAGONALIZE_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + } while((void)0, 0); + +#endif /* __AVX2__ */ + +#else /* __AVX512F__ */ + +#include + +#define ror64(x, n) _mm512_ror_epi64((x), (n)) + +static __m512i muladd(__m512i x, __m512i y) +{ + __m512i z = _mm512_mul_epu32(x, y); + return _mm512_add_epi64(_mm512_add_epi64(x, y), _mm512_add_epi64(z, z)); +} + +#define G1(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + A0 = muladd(A0, B0); \ + A1 = muladd(A1, B1); \ +\ + D0 = _mm512_xor_si512(D0, A0); \ + D1 = _mm512_xor_si512(D1, A1); \ +\ + D0 = ror64(D0, 32); \ + D1 = ror64(D1, 32); \ +\ + C0 = muladd(C0, D0); \ + C1 = muladd(C1, D1); \ +\ + B0 = _mm512_xor_si512(B0, C0); \ + B1 = _mm512_xor_si512(B1, C1); \ +\ + B0 = ror64(B0, 24); \ + B1 = ror64(B1, 24); \ + } while ((void)0, 0) + +#define G2(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + A0 = muladd(A0, B0); \ + A1 = muladd(A1, B1); \ +\ + D0 = _mm512_xor_si512(D0, A0); \ + D1 = _mm512_xor_si512(D1, A1); \ +\ + D0 = ror64(D0, 16); \ + D1 = ror64(D1, 16); \ +\ + C0 = muladd(C0, D0); \ + C1 = muladd(C1, D1); \ +\ + B0 = _mm512_xor_si512(B0, C0); \ + B1 = _mm512_xor_si512(B1, C1); \ +\ + B0 = ror64(B0, 63); \ + B1 = ror64(B1, 63); \ + } while ((void)0, 0) + +#define DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + B0 = _mm512_permutex_epi64(B0, _MM_SHUFFLE(0, 3, 2, 1)); \ + B1 = _mm512_permutex_epi64(B1, _MM_SHUFFLE(0, 3, 2, 1)); \ +\ + C0 = _mm512_permutex_epi64(C0, _MM_SHUFFLE(1, 0, 3, 2)); \ + C1 = _mm512_permutex_epi64(C1, _MM_SHUFFLE(1, 0, 3, 2)); \ +\ + D0 = _mm512_permutex_epi64(D0, _MM_SHUFFLE(2, 1, 0, 3)); \ + D1 = _mm512_permutex_epi64(D1, _MM_SHUFFLE(2, 1, 0, 3)); \ + } while ((void)0, 0) + +#define UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + B0 = _mm512_permutex_epi64(B0, _MM_SHUFFLE(2, 1, 0, 3)); \ + B1 = _mm512_permutex_epi64(B1, _MM_SHUFFLE(2, 1, 0, 3)); \ +\ + C0 = _mm512_permutex_epi64(C0, _MM_SHUFFLE(1, 0, 3, 2)); \ + C1 = _mm512_permutex_epi64(C1, _MM_SHUFFLE(1, 0, 3, 2)); \ +\ + D0 = _mm512_permutex_epi64(D0, _MM_SHUFFLE(0, 3, 2, 1)); \ + D1 = _mm512_permutex_epi64(D1, _MM_SHUFFLE(0, 3, 2, 1)); \ + } while ((void)0, 0) + +#define BLAKE2_ROUND(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + G1(A0, B0, C0, D0, A1, B1, C1, D1); \ + G2(A0, B0, C0, D0, A1, B1, C1, D1); \ +\ + DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1); \ +\ + G1(A0, B0, C0, D0, A1, B1, C1, D1); \ + G2(A0, B0, C0, D0, A1, B1, C1, D1); \ +\ + UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1); \ + } while ((void)0, 0) + +#define SWAP_HALVES(A0, A1) \ + do { \ + __m512i t0, t1; \ + t0 = _mm512_shuffle_i64x2(A0, A1, _MM_SHUFFLE(1, 0, 1, 0)); \ + t1 = _mm512_shuffle_i64x2(A0, A1, _MM_SHUFFLE(3, 2, 3, 2)); \ + A0 = t0; \ + A1 = t1; \ + } while((void)0, 0) + +#define SWAP_QUARTERS(A0, A1) \ + do { \ + SWAP_HALVES(A0, A1); \ + A0 = _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 1, 4, 5, 2, 3, 6, 7), A0); \ + A1 = _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 1, 4, 5, 2, 3, 6, 7), A1); \ + } while((void)0, 0) + +#define UNSWAP_QUARTERS(A0, A1) \ + do { \ + A0 = _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 1, 4, 5, 2, 3, 6, 7), A0); \ + A1 = _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 1, 4, 5, 2, 3, 6, 7), A1); \ + SWAP_HALVES(A0, A1); \ + } while((void)0, 0) + +#define BLAKE2_ROUND_1(A0, C0, B0, D0, A1, C1, B1, D1) \ + do { \ + SWAP_HALVES(A0, B0); \ + SWAP_HALVES(C0, D0); \ + SWAP_HALVES(A1, B1); \ + SWAP_HALVES(C1, D1); \ + BLAKE2_ROUND(A0, B0, C0, D0, A1, B1, C1, D1); \ + SWAP_HALVES(A0, B0); \ + SWAP_HALVES(C0, D0); \ + SWAP_HALVES(A1, B1); \ + SWAP_HALVES(C1, D1); \ + } while ((void)0, 0) + +#define BLAKE2_ROUND_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + SWAP_QUARTERS(A0, A1); \ + SWAP_QUARTERS(B0, B1); \ + SWAP_QUARTERS(C0, C1); \ + SWAP_QUARTERS(D0, D1); \ + BLAKE2_ROUND(A0, B0, C0, D0, A1, B1, C1, D1); \ + UNSWAP_QUARTERS(A0, A1); \ + UNSWAP_QUARTERS(B0, B1); \ + UNSWAP_QUARTERS(C0, C1); \ + UNSWAP_QUARTERS(D0, D1); \ + } while ((void)0, 0) + +#endif /* __AVX512F__ */ +#endif /* BLAKE_ROUND_MKA_OPT_H */ diff --git a/0x0017a_ctf/third_party/argon2/src/blake2/blamka-round-ref.h b/0x0017a_ctf/third_party/argon2/src/blake2/blamka-round-ref.h new file mode 100644 index 0000000..16cfc1c --- /dev/null +++ b/0x0017a_ctf/third_party/argon2/src/blake2/blamka-round-ref.h @@ -0,0 +1,56 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef BLAKE_ROUND_MKA_H +#define BLAKE_ROUND_MKA_H + +#include "blake2.h" +#include "blake2-impl.h" + +/* designed by the Lyra PHC team */ +static BLAKE2_INLINE uint64_t fBlaMka(uint64_t x, uint64_t y) { + const uint64_t m = UINT64_C(0xFFFFFFFF); + const uint64_t xy = (x & m) * (y & m); + return x + y + 2 * xy; +} + +#define G(a, b, c, d) \ + do { \ + a = fBlaMka(a, b); \ + d = rotr64(d ^ a, 32); \ + c = fBlaMka(c, d); \ + b = rotr64(b ^ c, 24); \ + a = fBlaMka(a, b); \ + d = rotr64(d ^ a, 16); \ + c = fBlaMka(c, d); \ + b = rotr64(b ^ c, 63); \ + } while ((void)0, 0) + +#define BLAKE2_ROUND_NOMSG(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, \ + v12, v13, v14, v15) \ + do { \ + G(v0, v4, v8, v12); \ + G(v1, v5, v9, v13); \ + G(v2, v6, v10, v14); \ + G(v3, v7, v11, v15); \ + G(v0, v5, v10, v15); \ + G(v1, v6, v11, v12); \ + G(v2, v7, v8, v13); \ + G(v3, v4, v9, v14); \ + } while ((void)0, 0) + +#endif diff --git a/0x0017a_ctf/third_party/argon2/src/core.c b/0x0017a_ctf/third_party/argon2/src/core.c new file mode 100644 index 0000000..e697882 --- /dev/null +++ b/0x0017a_ctf/third_party/argon2/src/core.c @@ -0,0 +1,648 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +/*For memory wiping*/ +#ifdef _WIN32 +#include +#include /* For SecureZeroMemory */ +#endif +#if defined __STDC_LIB_EXT1__ +#define __STDC_WANT_LIB_EXT1__ 1 +#endif +#define VC_GE_2005(version) (version >= 1400) + +/* for explicit_bzero() on glibc */ +#define _DEFAULT_SOURCE + +#include +#include +#include + +#include "core.h" +#include "thread.h" +#include "blake2/blake2.h" +#include "blake2/blake2-impl.h" + +#ifdef GENKAT +#include "genkat.h" +#endif + +#if defined(__clang__) +#if __has_attribute(optnone) +#define NOT_OPTIMIZED __attribute__((optnone)) +#endif +#elif defined(__GNUC__) +#define GCC_VERSION \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#if GCC_VERSION >= 40400 +#define NOT_OPTIMIZED __attribute__((optimize("O0"))) +#endif +#endif +#ifndef NOT_OPTIMIZED +#define NOT_OPTIMIZED +#endif + +/***************Instance and Position constructors**********/ +void init_block_value(block *b, uint8_t in) { memset(b->v, in, sizeof(b->v)); } + +void copy_block(block *dst, const block *src) { + memcpy(dst->v, src->v, sizeof(uint64_t) * ARGON2_QWORDS_IN_BLOCK); +} + +void xor_block(block *dst, const block *src) { + int i; + for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) { + dst->v[i] ^= src->v[i]; + } +} + +static void load_block(block *dst, const void *input) { + unsigned i; + for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) { + dst->v[i] = load64((const uint8_t *)input + i * sizeof(dst->v[i])); + } +} + +static void store_block(void *output, const block *src) { + unsigned i; + for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) { + store64((uint8_t *)output + i * sizeof(src->v[i]), src->v[i]); + } +} + +/***************Memory functions*****************/ + +int allocate_memory(const argon2_context *context, uint8_t **memory, + size_t num, size_t size) { + size_t memory_size = num*size; + if (memory == NULL) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + /* 1. Check for multiplication overflow */ + if (size != 0 && memory_size / size != num) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + /* 2. Try to allocate with appropriate allocator */ + if (context->allocate_cbk) { + (context->allocate_cbk)(memory, memory_size); + } else { + *memory = malloc(memory_size); + } + + if (*memory == NULL) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + return ARGON2_OK; +} + +void free_memory(const argon2_context *context, uint8_t *memory, + size_t num, size_t size) { + size_t memory_size = num*size; + clear_internal_memory(memory, memory_size); + if (context->free_cbk) { + (context->free_cbk)(memory, memory_size); + } else { + free(memory); + } +} + +#if defined(__OpenBSD__) +#define HAVE_EXPLICIT_BZERO 1 +#elif defined(__GLIBC__) && defined(__GLIBC_PREREQ) +#if __GLIBC_PREREQ(2,25) +#define HAVE_EXPLICIT_BZERO 1 +#endif +#endif + +void NOT_OPTIMIZED secure_wipe_memory(void *v, size_t n) { +#if defined(_MSC_VER) && VC_GE_2005(_MSC_VER) || defined(__MINGW32__) + SecureZeroMemory(v, n); +#elif defined memset_s + memset_s(v, n, 0, n); +#elif defined(HAVE_EXPLICIT_BZERO) + explicit_bzero(v, n); +#else + static void *(*const volatile memset_sec)(void *, int, size_t) = &memset; + memset_sec(v, 0, n); +#endif +} + +/* Memory clear flag defaults to true. */ +int FLAG_clear_internal_memory = 1; +void clear_internal_memory(void *v, size_t n) { + if (FLAG_clear_internal_memory && v) { + secure_wipe_memory(v, n); + } +} + +void finalize(const argon2_context *context, argon2_instance_t *instance) { + if (context != NULL && instance != NULL) { + block blockhash; + uint32_t l; + + copy_block(&blockhash, instance->memory + instance->lane_length - 1); + + /* XOR the last blocks */ + for (l = 1; l < instance->lanes; ++l) { + uint32_t last_block_in_lane = + l * instance->lane_length + (instance->lane_length - 1); + xor_block(&blockhash, instance->memory + last_block_in_lane); + } + + /* Hash the result */ + { + uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE]; + store_block(blockhash_bytes, &blockhash); + blake2b_long(context->out, context->outlen, blockhash_bytes, + ARGON2_BLOCK_SIZE); + /* clear blockhash and blockhash_bytes */ + clear_internal_memory(blockhash.v, ARGON2_BLOCK_SIZE); + clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE); + } + +#ifdef GENKAT + print_tag(context->out, context->outlen); +#endif + + free_memory(context, (uint8_t *)instance->memory, + instance->memory_blocks, sizeof(block)); + } +} + +uint32_t index_alpha(const argon2_instance_t *instance, + const argon2_position_t *position, uint32_t pseudo_rand, + int same_lane) { + /* + * Pass 0: + * This lane : all already finished segments plus already constructed + * blocks in this segment + * Other lanes : all already finished segments + * Pass 1+: + * This lane : (SYNC_POINTS - 1) last segments plus already constructed + * blocks in this segment + * Other lanes : (SYNC_POINTS - 1) last segments + */ + uint32_t reference_area_size; + uint64_t relative_position; + uint32_t start_position, absolute_position; + + if (0 == position->pass) { + /* First pass */ + if (0 == position->slice) { + /* First slice */ + reference_area_size = + position->index - 1; /* all but the previous */ + } else { + if (same_lane) { + /* The same lane => add current segment */ + reference_area_size = + position->slice * instance->segment_length + + position->index - 1; + } else { + reference_area_size = + position->slice * instance->segment_length + + ((position->index == 0) ? (-1) : 0); + } + } + } else { + /* Second pass */ + if (same_lane) { + reference_area_size = instance->lane_length - + instance->segment_length + position->index - + 1; + } else { + reference_area_size = instance->lane_length - + instance->segment_length + + ((position->index == 0) ? (-1) : 0); + } + } + + /* 1.2.4. Mapping pseudo_rand to 0.. and produce + * relative position */ + relative_position = pseudo_rand; + relative_position = relative_position * relative_position >> 32; + relative_position = reference_area_size - 1 - + (reference_area_size * relative_position >> 32); + + /* 1.2.5 Computing starting position */ + start_position = 0; + + if (0 != position->pass) { + start_position = (position->slice == ARGON2_SYNC_POINTS - 1) + ? 0 + : (position->slice + 1) * instance->segment_length; + } + + /* 1.2.6. Computing absolute position */ + absolute_position = (start_position + relative_position) % + instance->lane_length; /* absolute position */ + return absolute_position; +} + +/* Single-threaded version for p=1 case */ +static int fill_memory_blocks_st(argon2_instance_t *instance) { + uint32_t r, s, l; + + for (r = 0; r < instance->passes; ++r) { + for (s = 0; s < ARGON2_SYNC_POINTS; ++s) { + for (l = 0; l < instance->lanes; ++l) { + argon2_position_t position = {r, l, (uint8_t)s, 0}; + fill_segment(instance, position); + } + } +#ifdef GENKAT + internal_kat(instance, r); /* Print all memory blocks */ +#endif + } + return ARGON2_OK; +} + +#if !defined(ARGON2_NO_THREADS) + +#ifdef _WIN32 +static unsigned __stdcall fill_segment_thr(void *thread_data) +#else +static void *fill_segment_thr(void *thread_data) +#endif +{ + argon2_thread_data *my_data = thread_data; + fill_segment(my_data->instance_ptr, my_data->pos); + argon2_thread_exit(); + return 0; +} + +/* Multi-threaded version for p > 1 case */ +static int fill_memory_blocks_mt(argon2_instance_t *instance) { + uint32_t r, s; + argon2_thread_handle_t *thread = NULL; + argon2_thread_data *thr_data = NULL; + int rc = ARGON2_OK; + + /* 1. Allocating space for threads */ + thread = calloc(instance->lanes, sizeof(argon2_thread_handle_t)); + if (thread == NULL) { + rc = ARGON2_MEMORY_ALLOCATION_ERROR; + goto fail; + } + + thr_data = calloc(instance->lanes, sizeof(argon2_thread_data)); + if (thr_data == NULL) { + rc = ARGON2_MEMORY_ALLOCATION_ERROR; + goto fail; + } + + for (r = 0; r < instance->passes; ++r) { + for (s = 0; s < ARGON2_SYNC_POINTS; ++s) { + uint32_t l, ll; + + /* 2. Calling threads */ + for (l = 0; l < instance->lanes; ++l) { + argon2_position_t position; + + /* 2.1 Join a thread if limit is exceeded */ + if (l >= instance->threads) { + if (argon2_thread_join(thread[l - instance->threads])) { + rc = ARGON2_THREAD_FAIL; + goto fail; + } + } + + /* 2.2 Create thread */ + position.pass = r; + position.lane = l; + position.slice = (uint8_t)s; + position.index = 0; + thr_data[l].instance_ptr = + instance; /* preparing the thread input */ + memcpy(&(thr_data[l].pos), &position, + sizeof(argon2_position_t)); + if (argon2_thread_create(&thread[l], &fill_segment_thr, + (void *)&thr_data[l])) { + /* Wait for already running threads */ + for (ll = 0; ll < l; ++ll) + argon2_thread_join(thread[ll]); + rc = ARGON2_THREAD_FAIL; + goto fail; + } + + /* fill_segment(instance, position); */ + /*Non-thread equivalent of the lines above */ + } + + /* 3. Joining remaining threads */ + for (l = instance->lanes - instance->threads; l < instance->lanes; + ++l) { + if (argon2_thread_join(thread[l])) { + rc = ARGON2_THREAD_FAIL; + goto fail; + } + } + } + +#ifdef GENKAT + internal_kat(instance, r); /* Print all memory blocks */ +#endif + } + +fail: + if (thread != NULL) { + free(thread); + } + if (thr_data != NULL) { + free(thr_data); + } + return rc; +} + +#endif /* ARGON2_NO_THREADS */ + +int fill_memory_blocks(argon2_instance_t *instance) { + if (instance == NULL || instance->lanes == 0) { + return ARGON2_INCORRECT_PARAMETER; + } +#if defined(ARGON2_NO_THREADS) + return fill_memory_blocks_st(instance); +#else + return instance->threads == 1 ? + fill_memory_blocks_st(instance) : fill_memory_blocks_mt(instance); +#endif +} + +int validate_inputs(const argon2_context *context) { + if (NULL == context) { + return ARGON2_INCORRECT_PARAMETER; + } + + if (NULL == context->out) { + return ARGON2_OUTPUT_PTR_NULL; + } + + /* Validate output length */ + if (ARGON2_MIN_OUTLEN > context->outlen) { + return ARGON2_OUTPUT_TOO_SHORT; + } + + if (ARGON2_MAX_OUTLEN < context->outlen) { + return ARGON2_OUTPUT_TOO_LONG; + } + + /* Validate password (required param) */ + if (NULL == context->pwd) { + if (0 != context->pwdlen) { + return ARGON2_PWD_PTR_MISMATCH; + } + } + + if (ARGON2_MIN_PWD_LENGTH > context->pwdlen) { + return ARGON2_PWD_TOO_SHORT; + } + + if (ARGON2_MAX_PWD_LENGTH < context->pwdlen) { + return ARGON2_PWD_TOO_LONG; + } + + /* Validate salt (required param) */ + if (NULL == context->salt) { + if (0 != context->saltlen) { + return ARGON2_SALT_PTR_MISMATCH; + } + } + + if (ARGON2_MIN_SALT_LENGTH > context->saltlen) { + return ARGON2_SALT_TOO_SHORT; + } + + if (ARGON2_MAX_SALT_LENGTH < context->saltlen) { + return ARGON2_SALT_TOO_LONG; + } + + /* Validate secret (optional param) */ + if (NULL == context->secret) { + if (0 != context->secretlen) { + return ARGON2_SECRET_PTR_MISMATCH; + } + } else { + if (ARGON2_MIN_SECRET > context->secretlen) { + return ARGON2_SECRET_TOO_SHORT; + } + if (ARGON2_MAX_SECRET < context->secretlen) { + return ARGON2_SECRET_TOO_LONG; + } + } + + /* Validate associated data (optional param) */ + if (NULL == context->ad) { + if (0 != context->adlen) { + return ARGON2_AD_PTR_MISMATCH; + } + } else { + if (ARGON2_MIN_AD_LENGTH > context->adlen) { + return ARGON2_AD_TOO_SHORT; + } + if (ARGON2_MAX_AD_LENGTH < context->adlen) { + return ARGON2_AD_TOO_LONG; + } + } + + /* Validate memory cost */ + if (ARGON2_MIN_MEMORY > context->m_cost) { + return ARGON2_MEMORY_TOO_LITTLE; + } + + if (ARGON2_MAX_MEMORY < context->m_cost) { + return ARGON2_MEMORY_TOO_MUCH; + } + + if (context->m_cost < 8 * context->lanes) { + return ARGON2_MEMORY_TOO_LITTLE; + } + + /* Validate time cost */ + if (ARGON2_MIN_TIME > context->t_cost) { + return ARGON2_TIME_TOO_SMALL; + } + + if (ARGON2_MAX_TIME < context->t_cost) { + return ARGON2_TIME_TOO_LARGE; + } + + /* Validate lanes */ + if (ARGON2_MIN_LANES > context->lanes) { + return ARGON2_LANES_TOO_FEW; + } + + if (ARGON2_MAX_LANES < context->lanes) { + return ARGON2_LANES_TOO_MANY; + } + + /* Validate threads */ + if (ARGON2_MIN_THREADS > context->threads) { + return ARGON2_THREADS_TOO_FEW; + } + + if (ARGON2_MAX_THREADS < context->threads) { + return ARGON2_THREADS_TOO_MANY; + } + + if (NULL != context->allocate_cbk && NULL == context->free_cbk) { + return ARGON2_FREE_MEMORY_CBK_NULL; + } + + if (NULL == context->allocate_cbk && NULL != context->free_cbk) { + return ARGON2_ALLOCATE_MEMORY_CBK_NULL; + } + + return ARGON2_OK; +} + +void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance) { + uint32_t l; + /* Make the first and second block in each lane as G(H0||0||i) or + G(H0||1||i) */ + uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE]; + for (l = 0; l < instance->lanes; ++l) { + + store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 0); + store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH + 4, l); + blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash, + ARGON2_PREHASH_SEED_LENGTH); + load_block(&instance->memory[l * instance->lane_length + 0], + blockhash_bytes); + + store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 1); + blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash, + ARGON2_PREHASH_SEED_LENGTH); + load_block(&instance->memory[l * instance->lane_length + 1], + blockhash_bytes); + } + clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE); +} + +void initial_hash(uint8_t *blockhash, argon2_context *context, + argon2_type type) { + blake2b_state BlakeHash; + uint8_t value[sizeof(uint32_t)]; + + if (NULL == context || NULL == blockhash) { + return; + } + + blake2b_init(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH); + + store32(&value, context->lanes); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->outlen); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->m_cost); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->t_cost); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->version); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, (uint32_t)type); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->pwdlen); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + if (context->pwd != NULL) { + blake2b_update(&BlakeHash, (const uint8_t *)context->pwd, + context->pwdlen); + + if (context->flags & ARGON2_FLAG_CLEAR_PASSWORD) { + secure_wipe_memory(context->pwd, context->pwdlen); + context->pwdlen = 0; + } + } + + store32(&value, context->saltlen); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + if (context->salt != NULL) { + blake2b_update(&BlakeHash, (const uint8_t *)context->salt, + context->saltlen); + } + + store32(&value, context->secretlen); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + if (context->secret != NULL) { + blake2b_update(&BlakeHash, (const uint8_t *)context->secret, + context->secretlen); + + if (context->flags & ARGON2_FLAG_CLEAR_SECRET) { + secure_wipe_memory(context->secret, context->secretlen); + context->secretlen = 0; + } + } + + store32(&value, context->adlen); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + if (context->ad != NULL) { + blake2b_update(&BlakeHash, (const uint8_t *)context->ad, + context->adlen); + } + + blake2b_final(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH); +} + +int initialize(argon2_instance_t *instance, argon2_context *context) { + uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; + int result = ARGON2_OK; + + if (instance == NULL || context == NULL) + return ARGON2_INCORRECT_PARAMETER; + instance->context_ptr = context; + + /* 1. Memory allocation */ + result = allocate_memory(context, (uint8_t **)&(instance->memory), + instance->memory_blocks, sizeof(block)); + if (result != ARGON2_OK) { + return result; + } + + /* 2. Initial hashing */ + /* H_0 + 8 extra bytes to produce the first blocks */ + /* uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; */ + /* Hashing all inputs */ + initial_hash(blockhash, context, instance->type); + /* Zeroing 8 extra bytes */ + clear_internal_memory(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, + ARGON2_PREHASH_SEED_LENGTH - + ARGON2_PREHASH_DIGEST_LENGTH); + +#ifdef GENKAT + initial_kat(blockhash, context, instance->type); +#endif + + /* 3. Creating first blocks, we always have at least two blocks in a slice + */ + fill_first_blocks(blockhash, instance); + /* Clearing the hash */ + clear_internal_memory(blockhash, ARGON2_PREHASH_SEED_LENGTH); + + return ARGON2_OK; +} diff --git a/0x0017a_ctf/third_party/argon2/src/core.h b/0x0017a_ctf/third_party/argon2/src/core.h new file mode 100644 index 0000000..59e2564 --- /dev/null +++ b/0x0017a_ctf/third_party/argon2/src/core.h @@ -0,0 +1,228 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef ARGON2_CORE_H +#define ARGON2_CORE_H + +#include "argon2.h" + +#define CONST_CAST(x) (x)(uintptr_t) + +/**********************Argon2 internal constants*******************************/ + +enum argon2_core_constants { + /* Memory block size in bytes */ + ARGON2_BLOCK_SIZE = 1024, + ARGON2_QWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 8, + ARGON2_OWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 16, + ARGON2_HWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 32, + ARGON2_512BIT_WORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 64, + + /* Number of pseudo-random values generated by one call to Blake in Argon2i + to + generate reference block positions */ + ARGON2_ADDRESSES_IN_BLOCK = 128, + + /* Pre-hashing digest length and its extension*/ + ARGON2_PREHASH_DIGEST_LENGTH = 64, + ARGON2_PREHASH_SEED_LENGTH = 72 +}; + +/*************************Argon2 internal data types***********************/ + +/* + * Structure for the (1KB) memory block implemented as 128 64-bit words. + * Memory blocks can be copied, XORed. Internal words can be accessed by [] (no + * bounds checking). + */ +typedef struct block_ { uint64_t v[ARGON2_QWORDS_IN_BLOCK]; } block; + +/*****************Functions that work with the block******************/ + +/* Initialize each byte of the block with @in */ +void init_block_value(block *b, uint8_t in); + +/* Copy block @src to block @dst */ +void copy_block(block *dst, const block *src); + +/* XOR @src onto @dst bytewise */ +void xor_block(block *dst, const block *src); + +/* + * Argon2 instance: memory pointer, number of passes, amount of memory, type, + * and derived values. + * Used to evaluate the number and location of blocks to construct in each + * thread + */ +typedef struct Argon2_instance_t { + block *memory; /* Memory pointer */ + uint32_t version; + uint32_t passes; /* Number of passes */ + uint32_t memory_blocks; /* Number of blocks in memory */ + uint32_t segment_length; + uint32_t lane_length; + uint32_t lanes; + uint32_t threads; + argon2_type type; + int print_internals; /* whether to print the memory blocks */ + argon2_context *context_ptr; /* points back to original context */ +} argon2_instance_t; + +/* + * Argon2 position: where we construct the block right now. Used to distribute + * work between threads. + */ +typedef struct Argon2_position_t { + uint32_t pass; + uint32_t lane; + uint8_t slice; + uint32_t index; +} argon2_position_t; + +/*Struct that holds the inputs for thread handling FillSegment*/ +typedef struct Argon2_thread_data { + argon2_instance_t *instance_ptr; + argon2_position_t pos; +} argon2_thread_data; + +/*************************Argon2 core functions********************************/ + +/* Allocates memory to the given pointer, uses the appropriate allocator as + * specified in the context. Total allocated memory is num*size. + * @param context argon2_context which specifies the allocator + * @param memory pointer to the pointer to the memory + * @param size the size in bytes for each element to be allocated + * @param num the number of elements to be allocated + * @return ARGON2_OK if @memory is a valid pointer and memory is allocated + */ +int allocate_memory(const argon2_context *context, uint8_t **memory, + size_t num, size_t size); + +/* + * Frees memory at the given pointer, uses the appropriate deallocator as + * specified in the context. Also cleans the memory using clear_internal_memory. + * @param context argon2_context which specifies the deallocator + * @param memory pointer to buffer to be freed + * @param size the size in bytes for each element to be deallocated + * @param num the number of elements to be deallocated + */ +void free_memory(const argon2_context *context, uint8_t *memory, + size_t num, size_t size); + +/* Function that securely cleans the memory. This ignores any flags set + * regarding clearing memory. Usually one just calls clear_internal_memory. + * @param mem Pointer to the memory + * @param s Memory size in bytes + */ +void secure_wipe_memory(void *v, size_t n); + +/* Function that securely clears the memory if FLAG_clear_internal_memory is + * set. If the flag isn't set, this function does nothing. + * @param mem Pointer to the memory + * @param s Memory size in bytes + */ +void clear_internal_memory(void *v, size_t n); + +/* + * Computes absolute position of reference block in the lane following a skewed + * distribution and using a pseudo-random value as input + * @param instance Pointer to the current instance + * @param position Pointer to the current position + * @param pseudo_rand 32-bit pseudo-random value used to determine the position + * @param same_lane Indicates if the block will be taken from the current lane. + * If so we can reference the current segment + * @pre All pointers must be valid + */ +uint32_t index_alpha(const argon2_instance_t *instance, + const argon2_position_t *position, uint32_t pseudo_rand, + int same_lane); + +/* + * Function that validates all inputs against predefined restrictions and return + * an error code + * @param context Pointer to current Argon2 context + * @return ARGON2_OK if everything is all right, otherwise one of error codes + * (all defined in + */ +int validate_inputs(const argon2_context *context); + +/* + * Hashes all the inputs into @a blockhash[PREHASH_DIGEST_LENGTH], clears + * password and secret if needed + * @param context Pointer to the Argon2 internal structure containing memory + * pointer, and parameters for time and space requirements. + * @param blockhash Buffer for pre-hashing digest + * @param type Argon2 type + * @pre @a blockhash must have at least @a PREHASH_DIGEST_LENGTH bytes + * allocated + */ +void initial_hash(uint8_t *blockhash, argon2_context *context, + argon2_type type); + +/* + * Function creates first 2 blocks per lane + * @param instance Pointer to the current instance + * @param blockhash Pointer to the pre-hashing digest + * @pre blockhash must point to @a PREHASH_SEED_LENGTH allocated values + */ +void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance); + +/* + * Function allocates memory, hashes the inputs with Blake, and creates first + * two blocks. Returns the pointer to the main memory with 2 blocks per lane + * initialized + * @param context Pointer to the Argon2 internal structure containing memory + * pointer, and parameters for time and space requirements. + * @param instance Current Argon2 instance + * @return Zero if successful, -1 if memory failed to allocate. @context->state + * will be modified if successful. + */ +int initialize(argon2_instance_t *instance, argon2_context *context); + +/* + * XORing the last block of each lane, hashing it, making the tag. Deallocates + * the memory. + * @param context Pointer to current Argon2 context (use only the out parameters + * from it) + * @param instance Pointer to current instance of Argon2 + * @pre instance->state must point to necessary amount of memory + * @pre context->out must point to outlen bytes of memory + * @pre if context->free_cbk is not NULL, it should point to a function that + * deallocates memory + */ +void finalize(const argon2_context *context, argon2_instance_t *instance); + +/* + * Function that fills the segment using previous segments also from other + * threads + * @param context current context + * @param instance Pointer to the current instance + * @param position Current position + * @pre all block pointers must be valid + */ +void fill_segment(const argon2_instance_t *instance, + argon2_position_t position); + +/* + * Function that fills the entire memory t_cost times based on the first two + * blocks in each lane + * @param instance Pointer to the current instance + * @return ARGON2_OK if successful, @context->state + */ +int fill_memory_blocks(argon2_instance_t *instance); + +#endif diff --git a/0x0017a_ctf/third_party/argon2/src/encoding.c b/0x0017a_ctf/third_party/argon2/src/encoding.c new file mode 100644 index 0000000..771a440 --- /dev/null +++ b/0x0017a_ctf/third_party/argon2/src/encoding.c @@ -0,0 +1,463 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#include +#include +#include +#include +#include "encoding.h" +#include "core.h" + +/* + * Example code for a decoder and encoder of "hash strings", with Argon2 + * parameters. + * + * This code comprises three sections: + * + * -- The first section contains generic Base64 encoding and decoding + * functions. It is conceptually applicable to any hash function + * implementation that uses Base64 to encode and decode parameters, + * salts and outputs. It could be made into a library, provided that + * the relevant functions are made public (non-static) and be given + * reasonable names to avoid collisions with other functions. + * + * -- The second section is specific to Argon2. It encodes and decodes + * the parameters, salts and outputs. It does not compute the hash + * itself. + * + * The code was originally written by Thomas Pornin , + * to whom comments and remarks may be sent. It is released under what + * should amount to Public Domain or its closest equivalent; the + * following mantra is supposed to incarnate that fact with all the + * proper legal rituals: + * + * --------------------------------------------------------------------- + * This file is provided under the terms of Creative Commons CC0 1.0 + * Public Domain Dedication. To the extent possible under law, the + * author (Thomas Pornin) has waived all copyright and related or + * neighboring rights to this file. This work is published from: Canada. + * --------------------------------------------------------------------- + * + * Copyright (c) 2015 Thomas Pornin + */ + +/* ==================================================================== */ +/* + * Common code; could be shared between different hash functions. + * + * Note: the Base64 functions below assume that uppercase letters (resp. + * lowercase letters) have consecutive numerical codes, that fit on 8 + * bits. All modern systems use ASCII-compatible charsets, where these + * properties are true. If you are stuck with a dinosaur of a system + * that still defaults to EBCDIC then you already have much bigger + * interoperability issues to deal with. + */ + +/* + * Some macros for constant-time comparisons. These work over values in + * the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true". + */ +#define EQ(x, y) ((((0U - ((unsigned)(x) ^ (unsigned)(y))) >> 8) & 0xFF) ^ 0xFF) +#define GT(x, y) ((((unsigned)(y) - (unsigned)(x)) >> 8) & 0xFF) +#define GE(x, y) (GT(y, x) ^ 0xFF) +#define LT(x, y) GT(y, x) +#define LE(x, y) GE(y, x) + +/* + * Convert value x (0..63) to corresponding Base64 character. + */ +static int b64_byte_to_char(unsigned x) { + return (LT(x, 26) & (x + 'A')) | + (GE(x, 26) & LT(x, 52) & (x + ('a' - 26))) | + (GE(x, 52) & LT(x, 62) & (x + ('0' - 52))) | (EQ(x, 62) & '+') | + (EQ(x, 63) & '/'); +} + +/* + * Convert character c to the corresponding 6-bit value. If character c + * is not a Base64 character, then 0xFF (255) is returned. + */ +static unsigned b64_char_to_byte(int c) { + unsigned x; + + x = (GE(c, 'A') & LE(c, 'Z') & (c - 'A')) | + (GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) | + (GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '+') & 62) | + (EQ(c, '/') & 63); + return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF)); +} + +/* + * Convert some bytes to Base64. 'dst_len' is the length (in characters) + * of the output buffer 'dst'; if that buffer is not large enough to + * receive the result (including the terminating 0), then (size_t)-1 + * is returned. Otherwise, the zero-terminated Base64 string is written + * in the buffer, and the output length (counted WITHOUT the terminating + * zero) is returned. + */ +static size_t to_base64(char *dst, size_t dst_len, const void *src, + size_t src_len) { + size_t olen; + const unsigned char *buf; + unsigned acc, acc_len; + + olen = (src_len / 3) << 2; + switch (src_len % 3) { + case 2: + olen++; + /* fall through */ + case 1: + olen += 2; + break; + } + if (dst_len <= olen) { + return (size_t)-1; + } + acc = 0; + acc_len = 0; + buf = (const unsigned char *)src; + while (src_len-- > 0) { + acc = (acc << 8) + (*buf++); + acc_len += 8; + while (acc_len >= 6) { + acc_len -= 6; + *dst++ = (char)b64_byte_to_char((acc >> acc_len) & 0x3F); + } + } + if (acc_len > 0) { + *dst++ = (char)b64_byte_to_char((acc << (6 - acc_len)) & 0x3F); + } + *dst++ = 0; + return olen; +} + +/* + * Decode Base64 chars into bytes. The '*dst_len' value must initially + * contain the length of the output buffer '*dst'; when the decoding + * ends, the actual number of decoded bytes is written back in + * '*dst_len'. + * + * Decoding stops when a non-Base64 character is encountered, or when + * the output buffer capacity is exceeded. If an error occurred (output + * buffer is too small, invalid last characters leading to unprocessed + * buffered bits), then NULL is returned; otherwise, the returned value + * points to the first non-Base64 character in the source stream, which + * may be the terminating zero. + */ +static const char *from_base64(void *dst, size_t *dst_len, const char *src) { + size_t len; + unsigned char *buf; + unsigned acc, acc_len; + + buf = (unsigned char *)dst; + len = 0; + acc = 0; + acc_len = 0; + for (;;) { + unsigned d; + + d = b64_char_to_byte(*src); + if (d == 0xFF) { + break; + } + src++; + acc = (acc << 6) + d; + acc_len += 6; + if (acc_len >= 8) { + acc_len -= 8; + if ((len++) >= *dst_len) { + return NULL; + } + *buf++ = (acc >> acc_len) & 0xFF; + } + } + + /* + * If the input length is equal to 1 modulo 4 (which is + * invalid), then there will remain 6 unprocessed bits; + * otherwise, only 0, 2 or 4 bits are buffered. The buffered + * bits must also all be zero. + */ + if (acc_len > 4 || (acc & (((unsigned)1 << acc_len) - 1)) != 0) { + return NULL; + } + *dst_len = len; + return src; +} + +/* + * Decode decimal integer from 'str'; the value is written in '*v'. + * Returned value is a pointer to the next non-decimal character in the + * string. If there is no digit at all, or the value encoding is not + * minimal (extra leading zeros), or the value does not fit in an + * 'unsigned long', then NULL is returned. + */ +static const char *decode_decimal(const char *str, unsigned long *v) { + const char *orig; + unsigned long acc; + + acc = 0; + for (orig = str;; str++) { + int c; + + c = *str; + if (c < '0' || c > '9') { + break; + } + c -= '0'; + if (acc > (ULONG_MAX / 10)) { + return NULL; + } + acc *= 10; + if ((unsigned long)c > (ULONG_MAX - acc)) { + return NULL; + } + acc += (unsigned long)c; + } + if (str == orig || (*orig == '0' && str != (orig + 1))) { + return NULL; + } + *v = acc; + return str; +} + +/* ==================================================================== */ +/* + * Code specific to Argon2. + * + * The code below applies the following format: + * + * $argon2[$v=]$m=,t=,p=$$ + * + * where is either 'd', 'id', or 'i', is a decimal integer (positive, + * fits in an 'unsigned long'), and is Base64-encoded data (no '=' padding + * characters, no newline or whitespace). + * + * The last two binary chunks (encoded in Base64) are, in that order, + * the salt and the output. Both are required. The binary salt length and the + * output length must be in the allowed ranges defined in argon2.h. + * + * The ctx struct must contain buffers large enough to hold the salt and pwd + * when it is fed into decode_string. + */ + +int decode_string(argon2_context *ctx, const char *str, argon2_type type) { + +/* check for prefix */ +#define CC(prefix) \ + do { \ + size_t cc_len = strlen(prefix); \ + if (strncmp(str, prefix, cc_len) != 0) { \ + return ARGON2_DECODING_FAIL; \ + } \ + str += cc_len; \ + } while ((void)0, 0) + +/* optional prefix checking with supplied code */ +#define CC_opt(prefix, code) \ + do { \ + size_t cc_len = strlen(prefix); \ + if (strncmp(str, prefix, cc_len) == 0) { \ + str += cc_len; \ + { code; } \ + } \ + } while ((void)0, 0) + +/* Decoding prefix into decimal */ +#define DECIMAL(x) \ + do { \ + unsigned long dec_x; \ + str = decode_decimal(str, &dec_x); \ + if (str == NULL) { \ + return ARGON2_DECODING_FAIL; \ + } \ + (x) = dec_x; \ + } while ((void)0, 0) + + +/* Decoding prefix into uint32_t decimal */ +#define DECIMAL_U32(x) \ + do { \ + unsigned long dec_x; \ + str = decode_decimal(str, &dec_x); \ + if (str == NULL || dec_x > UINT32_MAX) { \ + return ARGON2_DECODING_FAIL; \ + } \ + (x) = (uint32_t)dec_x; \ + } while ((void)0, 0) + + +/* Decoding base64 into a binary buffer */ +#define BIN(buf, max_len, len) \ + do { \ + size_t bin_len = (max_len); \ + str = from_base64(buf, &bin_len, str); \ + if (str == NULL || bin_len > UINT32_MAX) { \ + return ARGON2_DECODING_FAIL; \ + } \ + (len) = (uint32_t)bin_len; \ + } while ((void)0, 0) + + size_t maxsaltlen = ctx->saltlen; + size_t maxoutlen = ctx->outlen; + int validation_result; + const char* type_string; + + /* We should start with the argon2_type we are using */ + type_string = argon2_type2string(type, 0); + if (!type_string) { + return ARGON2_INCORRECT_TYPE; + } + + CC("$"); + CC(type_string); + + /* Reading the version number if the default is suppressed */ + ctx->version = ARGON2_VERSION_10; + CC_opt("$v=", DECIMAL_U32(ctx->version)); + + CC("$m="); + DECIMAL_U32(ctx->m_cost); + CC(",t="); + DECIMAL_U32(ctx->t_cost); + CC(",p="); + DECIMAL_U32(ctx->lanes); + ctx->threads = ctx->lanes; + + CC("$"); + BIN(ctx->salt, maxsaltlen, ctx->saltlen); + CC("$"); + BIN(ctx->out, maxoutlen, ctx->outlen); + + /* The rest of the fields get the default values */ + ctx->secret = NULL; + ctx->secretlen = 0; + ctx->ad = NULL; + ctx->adlen = 0; + ctx->allocate_cbk = NULL; + ctx->free_cbk = NULL; + ctx->flags = ARGON2_DEFAULT_FLAGS; + + /* On return, must have valid context */ + validation_result = validate_inputs(ctx); + if (validation_result != ARGON2_OK) { + return validation_result; + } + + /* Can't have any additional characters */ + if (*str == 0) { + return ARGON2_OK; + } else { + return ARGON2_DECODING_FAIL; + } +#undef CC +#undef CC_opt +#undef DECIMAL +#undef BIN +} + +int encode_string(char *dst, size_t dst_len, argon2_context *ctx, + argon2_type type) { +#define SS(str) \ + do { \ + size_t pp_len = strlen(str); \ + if (pp_len >= dst_len) { \ + return ARGON2_ENCODING_FAIL; \ + } \ + memcpy(dst, str, pp_len + 1); \ + dst += pp_len; \ + dst_len -= pp_len; \ + } while ((void)0, 0) + +#define SX(x) \ + do { \ + char tmp[30]; \ + sprintf(tmp, "%lu", (unsigned long)(x)); \ + SS(tmp); \ + } while ((void)0, 0) + +#define SB(buf, len) \ + do { \ + size_t sb_len = to_base64(dst, dst_len, buf, len); \ + if (sb_len == (size_t)-1) { \ + return ARGON2_ENCODING_FAIL; \ + } \ + dst += sb_len; \ + dst_len -= sb_len; \ + } while ((void)0, 0) + + const char* type_string = argon2_type2string(type, 0); + int validation_result = validate_inputs(ctx); + + if (!type_string) { + return ARGON2_ENCODING_FAIL; + } + + if (validation_result != ARGON2_OK) { + return validation_result; + } + + + SS("$"); + SS(type_string); + + SS("$v="); + SX(ctx->version); + + SS("$m="); + SX(ctx->m_cost); + SS(",t="); + SX(ctx->t_cost); + SS(",p="); + SX(ctx->lanes); + + SS("$"); + SB(ctx->salt, ctx->saltlen); + + SS("$"); + SB(ctx->out, ctx->outlen); + return ARGON2_OK; + +#undef SS +#undef SX +#undef SB +} + +size_t b64len(uint32_t len) { + size_t olen = ((size_t)len / 3) << 2; + + switch (len % 3) { + case 2: + olen++; + /* fall through */ + case 1: + olen += 2; + break; + } + + return olen; +} + +size_t numlen(uint32_t num) { + size_t len = 1; + while (num >= 10) { + ++len; + num = num / 10; + } + return len; +} + diff --git a/0x0017a_ctf/third_party/argon2/src/encoding.h b/0x0017a_ctf/third_party/argon2/src/encoding.h new file mode 100644 index 0000000..5b8b2dd --- /dev/null +++ b/0x0017a_ctf/third_party/argon2/src/encoding.h @@ -0,0 +1,57 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef ENCODING_H +#define ENCODING_H +#include "argon2.h" + +#define ARGON2_MAX_DECODED_LANES UINT32_C(255) +#define ARGON2_MIN_DECODED_SALT_LEN UINT32_C(8) +#define ARGON2_MIN_DECODED_OUT_LEN UINT32_C(12) + +/* +* encode an Argon2 hash string into the provided buffer. 'dst_len' +* contains the size, in characters, of the 'dst' buffer; if 'dst_len' +* is less than the number of required characters (including the +* terminating 0), then this function returns ARGON2_ENCODING_ERROR. +* +* on success, ARGON2_OK is returned. +*/ +int encode_string(char *dst, size_t dst_len, argon2_context *ctx, + argon2_type type); + +/* +* Decodes an Argon2 hash string into the provided structure 'ctx'. +* The only fields that must be set prior to this call are ctx.saltlen and +* ctx.outlen (which must be the maximal salt and out length values that are +* allowed), ctx.salt and ctx.out (which must be buffers of the specified +* length), and ctx.pwd and ctx.pwdlen which must hold a valid password. +* +* Invalid input string causes an error. On success, the ctx is valid and all +* fields have been initialized. +* +* Returned value is ARGON2_OK on success, other ARGON2_ codes on error. +*/ +int decode_string(argon2_context *ctx, const char *str, argon2_type type); + +/* Returns the length of the encoded byte stream with length len */ +size_t b64len(uint32_t len); + +/* Returns the length of the encoded number num */ +size_t numlen(uint32_t num); + +#endif diff --git a/0x0017a_ctf/third_party/argon2/src/genkat.h b/0x0017a_ctf/third_party/argon2/src/genkat.h new file mode 100644 index 0000000..3a7162a --- /dev/null +++ b/0x0017a_ctf/third_party/argon2/src/genkat.h @@ -0,0 +1,51 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef ARGON2_KAT_H +#define ARGON2_KAT_H + +#include "core.h" + +/* + * Initial KAT function that prints the inputs to the file + * @param blockhash Array that contains pre-hashing digest + * @param context Holds inputs + * @param type Argon2 type + * @pre blockhash must point to INPUT_INITIAL_HASH_LENGTH bytes + * @pre context member pointers must point to allocated memory of size according + * to the length values + */ +void initial_kat(const uint8_t *blockhash, const argon2_context *context, + argon2_type type); + +/* + * Function that prints the output tag + * @param out output array pointer + * @param outlen digest length + * @pre out must point to @a outlen bytes + **/ +void print_tag(const void *out, uint32_t outlen); + +/* + * Function that prints the internal state at given moment + * @param instance pointer to the current instance + * @param pass current pass number + * @pre instance must have necessary memory allocated + **/ +void internal_kat(const argon2_instance_t *instance, uint32_t pass); + +#endif diff --git a/0x0017a_ctf/third_party/argon2/src/ref.c b/0x0017a_ctf/third_party/argon2/src/ref.c new file mode 100644 index 0000000..10e45eb --- /dev/null +++ b/0x0017a_ctf/third_party/argon2/src/ref.c @@ -0,0 +1,194 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#include +#include +#include + +#include "argon2.h" +#include "core.h" + +#include "blake2/blamka-round-ref.h" +#include "blake2/blake2-impl.h" +#include "blake2/blake2.h" + + +/* + * Function fills a new memory block and optionally XORs the old block over the new one. + * @next_block must be initialized. + * @param prev_block Pointer to the previous block + * @param ref_block Pointer to the reference block + * @param next_block Pointer to the block to be constructed + * @param with_xor Whether to XOR into the new block (1) or just overwrite (0) + * @pre all block pointers must be valid + */ +static void fill_block(const block *prev_block, const block *ref_block, + block *next_block, int with_xor) { + block blockR, block_tmp; + unsigned i; + + copy_block(&blockR, ref_block); + xor_block(&blockR, prev_block); + copy_block(&block_tmp, &blockR); + /* Now blockR = ref_block + prev_block and block_tmp = ref_block + prev_block */ + if (with_xor) { + /* Saving the next block contents for XOR over: */ + xor_block(&block_tmp, next_block); + /* Now blockR = ref_block + prev_block and + block_tmp = ref_block + prev_block + next_block */ + } + + /* Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then + (16,17,..31)... finally (112,113,...127) */ + for (i = 0; i < 8; ++i) { + BLAKE2_ROUND_NOMSG( + blockR.v[16 * i], blockR.v[16 * i + 1], blockR.v[16 * i + 2], + blockR.v[16 * i + 3], blockR.v[16 * i + 4], blockR.v[16 * i + 5], + blockR.v[16 * i + 6], blockR.v[16 * i + 7], blockR.v[16 * i + 8], + blockR.v[16 * i + 9], blockR.v[16 * i + 10], blockR.v[16 * i + 11], + blockR.v[16 * i + 12], blockR.v[16 * i + 13], blockR.v[16 * i + 14], + blockR.v[16 * i + 15]); + } + + /* Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then + (2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127) */ + for (i = 0; i < 8; i++) { + BLAKE2_ROUND_NOMSG( + blockR.v[2 * i], blockR.v[2 * i + 1], blockR.v[2 * i + 16], + blockR.v[2 * i + 17], blockR.v[2 * i + 32], blockR.v[2 * i + 33], + blockR.v[2 * i + 48], blockR.v[2 * i + 49], blockR.v[2 * i + 64], + blockR.v[2 * i + 65], blockR.v[2 * i + 80], blockR.v[2 * i + 81], + blockR.v[2 * i + 96], blockR.v[2 * i + 97], blockR.v[2 * i + 112], + blockR.v[2 * i + 113]); + } + + copy_block(next_block, &block_tmp); + xor_block(next_block, &blockR); +} + +static void next_addresses(block *address_block, block *input_block, + const block *zero_block) { + input_block->v[6]++; + fill_block(zero_block, input_block, address_block, 0); + fill_block(zero_block, address_block, address_block, 0); +} + +void fill_segment(const argon2_instance_t *instance, + argon2_position_t position) { + block *ref_block = NULL, *curr_block = NULL; + block address_block, input_block, zero_block; + uint64_t pseudo_rand, ref_index, ref_lane; + uint32_t prev_offset, curr_offset; + uint32_t starting_index; + uint32_t i; + int data_independent_addressing; + + if (instance == NULL) { + return; + } + + data_independent_addressing = + (instance->type == Argon2_i) || + (instance->type == Argon2_id && (position.pass == 0) && + (position.slice < ARGON2_SYNC_POINTS / 2)); + + if (data_independent_addressing) { + init_block_value(&zero_block, 0); + init_block_value(&input_block, 0); + + input_block.v[0] = position.pass; + input_block.v[1] = position.lane; + input_block.v[2] = position.slice; + input_block.v[3] = instance->memory_blocks; + input_block.v[4] = instance->passes; + input_block.v[5] = instance->type; + } + + starting_index = 0; + + if ((0 == position.pass) && (0 == position.slice)) { + starting_index = 2; /* we have already generated the first two blocks */ + + /* Don't forget to generate the first block of addresses: */ + if (data_independent_addressing) { + next_addresses(&address_block, &input_block, &zero_block); + } + } + + /* Offset of the current block */ + curr_offset = position.lane * instance->lane_length + + position.slice * instance->segment_length + starting_index; + + if (0 == curr_offset % instance->lane_length) { + /* Last block in this lane */ + prev_offset = curr_offset + instance->lane_length - 1; + } else { + /* Previous block */ + prev_offset = curr_offset - 1; + } + + for (i = starting_index; i < instance->segment_length; + ++i, ++curr_offset, ++prev_offset) { + /*1.1 Rotating prev_offset if needed */ + if (curr_offset % instance->lane_length == 1) { + prev_offset = curr_offset - 1; + } + + /* 1.2 Computing the index of the reference block */ + /* 1.2.1 Taking pseudo-random value from the previous block */ + if (data_independent_addressing) { + if (i % ARGON2_ADDRESSES_IN_BLOCK == 0) { + next_addresses(&address_block, &input_block, &zero_block); + } + pseudo_rand = address_block.v[i % ARGON2_ADDRESSES_IN_BLOCK]; + } else { + pseudo_rand = instance->memory[prev_offset].v[0]; + } + + /* 1.2.2 Computing the lane of the reference block */ + ref_lane = ((pseudo_rand >> 32)) % instance->lanes; + + if ((position.pass == 0) && (position.slice == 0)) { + /* Can not reference other lanes yet */ + ref_lane = position.lane; + } + + /* 1.2.3 Computing the number of possible reference block within the + * lane. + */ + position.index = i; + ref_index = index_alpha(instance, &position, pseudo_rand & 0xFFFFFFFF, + ref_lane == position.lane); + + /* 2 Creating a new block */ + ref_block = + instance->memory + instance->lane_length * ref_lane + ref_index; + curr_block = instance->memory + curr_offset; + if (ARGON2_VERSION_10 == instance->version) { + /* version 1.2.1 and earlier: overwrite, not XOR */ + fill_block(instance->memory + prev_offset, ref_block, curr_block, 0); + } else { + if(0 == position.pass) { + fill_block(instance->memory + prev_offset, ref_block, + curr_block, 0); + } else { + fill_block(instance->memory + prev_offset, ref_block, + curr_block, 1); + } + } + } +} diff --git a/0x0017a_ctf/third_party/argon2/src/thread.c b/0x0017a_ctf/third_party/argon2/src/thread.c new file mode 100644 index 0000000..3ae2fb2 --- /dev/null +++ b/0x0017a_ctf/third_party/argon2/src/thread.c @@ -0,0 +1,57 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#if !defined(ARGON2_NO_THREADS) + +#include "thread.h" +#if defined(_WIN32) +#include +#endif + +int argon2_thread_create(argon2_thread_handle_t *handle, + argon2_thread_func_t func, void *args) { + if (NULL == handle || func == NULL) { + return -1; + } +#if defined(_WIN32) + *handle = _beginthreadex(NULL, 0, func, args, 0, NULL); + return *handle != 0 ? 0 : -1; +#else + return pthread_create(handle, NULL, func, args); +#endif +} + +int argon2_thread_join(argon2_thread_handle_t handle) { +#if defined(_WIN32) + if (WaitForSingleObject((HANDLE)handle, INFINITE) == WAIT_OBJECT_0) { + return CloseHandle((HANDLE)handle) != 0 ? 0 : -1; + } + return -1; +#else + return pthread_join(handle, NULL); +#endif +} + +void argon2_thread_exit(void) { +#if defined(_WIN32) + _endthreadex(0); +#else + pthread_exit(NULL); +#endif +} + +#endif /* ARGON2_NO_THREADS */ diff --git a/0x0017a_ctf/third_party/argon2/src/thread.h b/0x0017a_ctf/third_party/argon2/src/thread.h new file mode 100644 index 0000000..d4ca10c --- /dev/null +++ b/0x0017a_ctf/third_party/argon2/src/thread.h @@ -0,0 +1,67 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef ARGON2_THREAD_H +#define ARGON2_THREAD_H + +#if !defined(ARGON2_NO_THREADS) + +/* + Here we implement an abstraction layer for the simpĺe requirements + of the Argon2 code. We only require 3 primitives---thread creation, + joining, and termination---so full emulation of the pthreads API + is unwarranted. Currently we wrap pthreads and Win32 threads. + + The API defines 2 types: the function pointer type, + argon2_thread_func_t, + and the type of the thread handle---argon2_thread_handle_t. +*/ +#if defined(_WIN32) +#include +typedef unsigned(__stdcall *argon2_thread_func_t)(void *); +typedef uintptr_t argon2_thread_handle_t; +#else +#include +typedef void *(*argon2_thread_func_t)(void *); +typedef pthread_t argon2_thread_handle_t; +#endif + +/* Creates a thread + * @param handle pointer to a thread handle, which is the output of this + * function. Must not be NULL. + * @param func A function pointer for the thread's entry point. Must not be + * NULL. + * @param args Pointer that is passed as an argument to @func. May be NULL. + * @return 0 if @handle and @func are valid pointers and a thread is successfully + * created. + */ +int argon2_thread_create(argon2_thread_handle_t *handle, + argon2_thread_func_t func, void *args); + +/* Waits for a thread to terminate + * @param handle Handle to a thread created with argon2_thread_create. + * @return 0 if @handle is a valid handle, and joining completed successfully. +*/ +int argon2_thread_join(argon2_thread_handle_t handle); + +/* Terminate the current thread. Must be run inside a thread created by + * argon2_thread_create. +*/ +void argon2_thread_exit(void); + +#endif /* ARGON2_NO_THREADS */ +#endif diff --git a/0x0017a_ctf/uf2families.json b/0x0017a_ctf/uf2families.json new file mode 100644 index 0000000..91d99ad --- /dev/null +++ b/0x0017a_ctf/uf2families.json @@ -0,0 +1,22 @@ +[ + { + "short_name": "RP2040", + "id": "0xe48bff56", + "description": "Raspberry Pi RP2040" + }, + { + "short_name": "RP2350-ARM-S", + "id": "0xe48bff59", + "description": "Raspberry Pi RP2350, ARM, Secure" + }, + { + "short_name": "RP2350-ARM-NS", + "id": "0xe48bff5a", + "description": "Raspberry Pi RP2350, ARM, Non-Secure" + }, + { + "short_name": "RP2350-RISCV", + "id": "0xe48bff5b", + "description": "Raspberry Pi RP2350, RISC-V" + } +] diff --git a/0x001a_operators/.gitignore b/0x001a_operators/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x001a_operators/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x001a_operators/.vscode/c_cpp_properties.json b/0x001a_operators/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c429d6d --- /dev/null +++ b/0x001a_operators/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x001a_operators/.vscode/cmake-kits.json b/0x001a_operators/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x001a_operators/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x001a_operators/.vscode/extensions.json b/0x001a_operators/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x001a_operators/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x001a_operators/.vscode/launch.json b/0x001a_operators/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x001a_operators/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x001a_operators/.vscode/settings.json b/0x001a_operators/.vscode/settings.json new file mode 100644 index 0000000..95be83b --- /dev/null +++ b/0x001a_operators/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x001a_operators/.vscode/tasks.json b/0x001a_operators/.vscode/tasks.json new file mode 100644 index 0000000..d1b3193 --- /dev/null +++ b/0x001a_operators/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x001a_operators/0x001a_operators.c b/0x001a_operators/0x001a_operators.c new file mode 100644 index 0000000..1eecc97 --- /dev/null +++ b/0x001a_operators/0x001a_operators.c @@ -0,0 +1,126 @@ +/** + * @file 0x001a_operators.c + * @brief Operators: demonstrate C operators with DHT11 temperature sensor + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates arithmetic, increment, relational, logical, bitwise, and + * assignment operators in C. Also reads humidity and temperature from a + * DHT11 sensor on GPIO4. + * + * Wiring: + * GPIO4 -> DHT11 data pin (with 10k pull-up to 3V3) + * 3V3 -> DHT11 VCC + * GND -> DHT11 GND + */ + +#include +#include "pico/stdlib.h" +#include "dht11.h" + +/** + * @brief Print pre-computed operator results + * + * @details Prints all six operator demonstration values over UART. + * + * @param arith arithmetic operator result + * @param inc increment operator result + * @param rel relational operator result + * @param logic logical operator result + * @param bitw bitwise operator result + * @param assign assignment operator result + * @retval None + */ +static void print_operator_results(int arith, int inc, bool rel, + bool logic, int bitw, int assign) { + printf("arithmetic_operator: %d\r\n", arith); + printf("increment_operator: %d\r\n", inc); + printf("relational_operator: %d\r\n", rel); + printf("logical_operator: %d\r\n", logic); + printf("bitwise_operator: %d\r\n", bitw); + printf("assignment_operator: %d\r\n", assign); +} + +/** + * @brief Compute arithmetic and increment operator results + * + * @param arith pointer to store multiplication result + * @param incr pointer to store post-increment result + * @param x left operand + * @param y right operand + * @retval None + */ +static void compute_arithmetic_ops(int *arith, int *incr, int x, int y) { + *arith = (x * y); + *incr = x++; +} + +/** + * @brief Compute all operator values and print results + * + * @details Performs arithmetic, relational, logical, bitwise, + * and assignment operations, then prints each result. + * + * @retval None + */ +static void compute_operators(void) { + int x = 5, y = 10; + int arith, incr; + compute_arithmetic_ops(&arith, &incr, x, y); + bool rel = (x > y); + bool logic = (x > y) && (y > x); + int bits = (x << 1); + int assign = (x += 5); + print_operator_results(arith, incr, rel, logic, bits, assign); +} + +/** + * @brief Read and print DHT11 humidity and temperature + * + * @details Attempts a DHT11 read; on success prints humidity and + * temperature, on failure prints an error message. + * + * @retval None + */ +static void print_dht11_reading(void) { + float hum, temp; + if (dht11_read(&hum, &temp)) { + printf("Humidity: %.1f%%, Temperature: %.1f°C\r\n", hum, temp); + } else { + printf("DHT11 read failed\r\n"); + } +} + +int main(void) { + stdio_init_all(); + dht11_init(4); + while (true) { + compute_operators(); + print_dht11_reading(); + sleep_ms(2000); + } +} \ No newline at end of file diff --git a/0x001a_operators/CMakeLists.txt b/0x001a_operators/CMakeLists.txt new file mode 100644 index 0000000..f6ef427 --- /dev/null +++ b/0x001a_operators/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x001a_operators C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x001a_operators 0x001a_operators.c dht11.c ) + +pico_set_program_name(0x001a_operators "0x001a_operators") +pico_set_program_version(0x001a_operators "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x001a_operators 1) +pico_enable_stdio_usb(0x001a_operators 0) + +# Add the standard library to the build +target_link_libraries(0x001a_operators + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x001a_operators PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x001a_operators) + diff --git a/0x001a_operators/dht11.c b/0x001a_operators/dht11.c new file mode 100644 index 0000000..28f147f --- /dev/null +++ b/0x001a_operators/dht11.c @@ -0,0 +1,140 @@ +/** + * @file dht11.c + * @brief Implementation of DHT11 temperature and humidity sensor driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "dht11.h" +#include "hardware/gpio.h" +#include "pico/time.h" + +/** @brief GPIO pin connected to the DHT11 sensor */ +static uint dht_pin; + +/** + * @brief Send the DHT11 start signal on the data pin + * + * Drives the pin LOW for 18 ms then HIGH for 40 us before switching + * the pin to input mode to listen for the sensor response. + */ +static void send_start_signal(void) { + gpio_set_dir(dht_pin, GPIO_OUT); + gpio_put(dht_pin, 0); + sleep_ms(18); + gpio_put(dht_pin, 1); + sleep_us(40); + gpio_set_dir(dht_pin, GPIO_IN); +} + +/** + * @brief Wait for the pin to leave a given logic level + * + * Spins until the pin no longer reads the specified level, returning + * false if a timeout of 10 000 iterations is exceeded. + * + * @param level Logic level to wait through (0 or 1) + * @return bool true once the level changed, false on timeout + */ +static bool wait_for_level(int level) { + uint32_t timeout = 10000; + while (gpio_get(dht_pin) == level) + if (--timeout == 0) return false; + return true; +} + +/** + * @brief Wait for the DHT11 response after the start signal + * + * The sensor pulls LOW then HIGH then LOW again; each transition + * is awaited with a timeout. + * + * @return bool true if the full response was received, false on timeout + */ +static bool wait_response(void) { + if (!wait_for_level(1)) return false; + if (!wait_for_level(0)) return false; + if (!wait_for_level(1)) return false; + return true; +} + +/** + * @brief Read a single bit from the DHT11 data stream + * + * Waits for the low-period to end, measures the high-period duration, + * and shifts the result into the appropriate byte of the data array. + * + * @param data 5-byte array accumulating the received bits + * @param i Bit index (0-39) + * @return bool true on success, false on timeout + */ +static bool read_bit(uint8_t *data, int i) { + if (!wait_for_level(0)) return false; + uint32_t start = time_us_32(); + if (!wait_for_level(1)) return false; + uint32_t duration = time_us_32() - start; + data[i / 8] <<= 1; + if (duration > 40) data[i / 8] |= 1; + return true; +} + +/** + * @brief Read all 40 data bits from the DHT11 + * + * @param data 5-byte array filled with the received data + * @return bool true if all 40 bits were read, false on timeout + */ +static bool read_40_bits(uint8_t *data) { + for (int i = 0; i < 40; i++) + if (!read_bit(data, i)) return false; + return true; +} + +/** + * @brief Verify the DHT11 checksum byte + * + * @param data 5-byte received data (bytes 0-3 plus checksum in byte 4) + * @return bool true if the checksum matches, false otherwise + */ +static bool validate_checksum(const uint8_t *data) { + return data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF); +} + +void dht11_init(uint8_t pin) { + dht_pin = pin; + gpio_init(pin); + gpio_pull_up(pin); +} + +bool dht11_read(float *humidity, float *temperature) { + uint8_t data[5] = {0}; + send_start_signal(); + if (!wait_response()) return false; + if (!read_40_bits(data)) return false; + if (!validate_checksum(data)) return false; + *humidity = data[0] + data[1] * 0.1f; + *temperature = data[2] + data[3] * 0.1f; + return true; +} diff --git a/0x001a_operators/dht11.h b/0x001a_operators/dht11.h new file mode 100644 index 0000000..30e298c --- /dev/null +++ b/0x001a_operators/dht11.h @@ -0,0 +1,57 @@ +/** + * @file dht11.h + * @brief Header for DHT11 temperature and humidity sensor driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef DHT11_H +#define DHT11_H + +#include +#include + +/** + * @brief Initialize the DHT11 driver + * + * Configures the GPIO pin for the DHT11 sensor. This must be called before + * using dht11_read(). + * + * @param pin GPIO pin number connected to DHT11 signal + */ +void dht11_init(uint8_t pin); + +/** + * @brief Read temperature and humidity from DHT11 sensor + * + * Performs the DHT11 communication protocol to read sensor data. + * + * @param humidity Pointer to store humidity value (0-100%) + * @param temperature Pointer to store temperature value in Celsius + * @return true if read successful, false on error or timeout + */ +bool dht11_read(float *humidity, float *temperature); + +#endif // DHT11_H diff --git a/0x001a_operators/hack-temp.py b/0x001a_operators/hack-temp.py new file mode 100644 index 0000000..8973073 --- /dev/null +++ b/0x001a_operators/hack-temp.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python3 + +""" +FILE: hack-temp.py + +DESCRIPTION: +Incremental firmware patcher with minimal, conservative steps to bias readings. + +BRIEF: +Provides three modes: +- pool_only: change only the literal pool constant (scaling effect; least invasive) +- temp_only_vadd: convert temperature path to vadd.f32 and set a small negative pool +- both_vadd: convert both paths to vadd.f32 and set a small negative pool + +Intended to let you test changes incrementally, keeping humidity stable while +you evaluate temperature bias first. + +AUTHOR: Kevin Thomas +CREATION DATE: November 1, 2025 +UPDATE DATE: November 1, 2025 +""" + +import sys +import os +import struct + +BIN_PATH = "build/0x001a_operators.bin" + +# Offsets +OFF_410 = 0x410 +OFF_414 = 0x414 +OFF_POOL = 0x42C + +# Original encodings (for visibility only) +ORIG_410 = bytes.fromhex("a6ee257a") # vfma.f32 s14, s12, s11 +ORIG_414 = bytes.fromhex("e6eea57a") # vfma.f32 s15, s13, s11 +ORIG_POOL = bytes.fromhex("cccccc3d") # 0.1f + +# Encodings for vadd.f32 s14, s14, s11 and vadd.f32 s15, s15, s11 +VADD_S14_S11 = struct.pack(" 1 else "pool_only").strip().lower() + if mode == "pool_only": + patch_pool_only(new_pool_float=-1.0) + elif mode == "temp_only_vadd": + patch_temp_only_vadd(new_pool_float=-2.0) + elif mode == "both_vadd": + patch_both_vadd(new_pool_float=-2.0) + else: + print(f"Unknown mode: {mode}") + print("Use: pool_only | temp_only_vadd | both_vadd") + sys.exit(2) + + +if __name__ == "__main__": + main() + print("Patch complete. Convert to UF2 and flash. Test each mode incrementally.") diff --git a/0x001a_operators/pico_sdk_import.cmake b/0x001a_operators/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x001a_operators/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x001d_static-conditionals/.gitignore b/0x001d_static-conditionals/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x001d_static-conditionals/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x001d_static-conditionals/.vscode/c_cpp_properties.json b/0x001d_static-conditionals/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c429d6d --- /dev/null +++ b/0x001d_static-conditionals/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x001d_static-conditionals/.vscode/cmake-kits.json b/0x001d_static-conditionals/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x001d_static-conditionals/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x001d_static-conditionals/.vscode/extensions.json b/0x001d_static-conditionals/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x001d_static-conditionals/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x001d_static-conditionals/.vscode/launch.json b/0x001d_static-conditionals/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x001d_static-conditionals/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x001d_static-conditionals/.vscode/settings.json b/0x001d_static-conditionals/.vscode/settings.json new file mode 100644 index 0000000..95be83b --- /dev/null +++ b/0x001d_static-conditionals/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x001d_static-conditionals/.vscode/tasks.json b/0x001d_static-conditionals/.vscode/tasks.json new file mode 100644 index 0000000..d1b3193 --- /dev/null +++ b/0x001d_static-conditionals/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x001d_static-conditionals/0x001d_static-conditionals.c b/0x001d_static-conditionals/0x001d_static-conditionals.c new file mode 100644 index 0000000..6db9a5e --- /dev/null +++ b/0x001d_static-conditionals/0x001d_static-conditionals.c @@ -0,0 +1,106 @@ +/** + * @file 0x001d_static-conditionals.c + * @brief Static conditionals: if/else and switch with servo sweep + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates static (compile-time known) conditionals using if/else if/else + * and switch/case statements. Also sweeps a servo on GPIO6 between 0 and + * 180 degrees each iteration. + * + * Wiring: + * GPIO6 -> Servo signal wire (orange/white) + * 5V -> Servo VCC (red) + * GND -> Servo GND (brown/black) + */ + +#include +#include "pico/stdlib.h" +#include "servo.h" + +/** @brief GPIO pin number for the servo */ +#define SERVO_GPIO 6 + +/** + * @brief Print choice value using if/else if/else conditional + * + * @details Prints "1", "2", or "?" depending on the choice value. + * + * @param choice integer value to evaluate + * @retval None + */ +static void print_if_else(int choice) { + if (choice == 1) { + printf("1\r\n"); + } else if (choice == 2) { + printf("2\r\n"); + } else { + printf("?\r\n"); + } +} + +/** + * @brief Print choice value using switch/case conditional + * + * @details Prints "one", "two", or "??" depending on the choice value. + * + * @param choice integer value to evaluate + * @retval None + */ +static void print_switch(int choice) { + switch (choice) { + case 1: printf("one\r\n"); break; + case 2: printf("two\r\n"); break; + default: printf("??\r\n"); + } +} + +/** + * @brief Sweep servo between 0 and 180 degrees + * + * @details Sets the servo to 0 degrees, waits 500ms, then sets + * to 180 degrees and waits 500ms. + * + * @retval None + */ +static void sweep_servo(void) { + servo_set_angle(0.0f); + sleep_ms(500); + servo_set_angle(180.0f); + sleep_ms(500); +} + +int main(void) { + stdio_init_all(); + int choice = 1; + servo_init(SERVO_GPIO); + while (true) { + print_if_else(choice); + print_switch(choice); + sweep_servo(); + } +} \ No newline at end of file diff --git a/0x001d_static-conditionals/CMakeLists.txt b/0x001d_static-conditionals/CMakeLists.txt new file mode 100644 index 0000000..7b03b00 --- /dev/null +++ b/0x001d_static-conditionals/CMakeLists.txt @@ -0,0 +1,59 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x001d_static-conditionals C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x001d_static-conditionals 0x001d_static-conditionals.c servo.c) + +pico_set_program_name(0x001d_static-conditionals "0x001d_static-conditionals") +pico_set_program_version(0x001d_static-conditionals "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x001d_static-conditionals 1) +pico_enable_stdio_usb(0x001d_static-conditionals 0) + +# Add the standard library to the build +target_link_libraries(0x001d_static-conditionals + pico_stdlib + hardware_pwm + hardware_clocks) + +# Add the standard include files to the build +target_include_directories(0x001d_static-conditionals PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x001d_static-conditionals) + diff --git a/0x001d_static-conditionals/pico_sdk_import.cmake b/0x001d_static-conditionals/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x001d_static-conditionals/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x001d_static-conditionals/servo.c b/0x001d_static-conditionals/servo.c new file mode 100644 index 0000000..d0fefbd --- /dev/null +++ b/0x001d_static-conditionals/servo.c @@ -0,0 +1,107 @@ +/** + * @file servo.c + * @brief Implementation of a simple SG90 servo driver using PWM (50Hz) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "servo.h" +#include "pico/stdlib.h" +#include "hardware/pwm.h" +#include "hardware/clocks.h" + +/** @brief Default minimum pulse width in microseconds */ +static const uint16_t SERVO_DEFAULT_MIN_US = 1000; +/** @brief Default maximum pulse width in microseconds */ +static const uint16_t SERVO_DEFAULT_MAX_US = 2000; + +/** @brief GPIO pin assigned to the servo */ +static uint8_t servo_pin = 0; +/** @brief PWM hardware slice for the servo pin */ +static uint servo_slice = 0; +/** @brief PWM channel within the servo slice */ +static uint servo_chan = 0; +/** @brief PWM counter wrap value for 50 Hz servo */ +static uint32_t servo_wrap = 20000 - 1; +/** @brief Servo PWM frequency in Hz */ +static float servo_hz = 50.0f; +/** @brief Flag indicating servo has been initialized */ +static bool servo_initialized = false; + +/** + * @brief Convert a pulse width in microseconds to a PWM counter level + * + * Uses the configured PWM wrap and servo frequency to map pulse time + * into the channel compare value expected by the PWM hardware. + * + * @param pulse_us Pulse width in microseconds + * @return uint32_t PWM level suitable for pwm_set_chan_level() + */ +static uint32_t pulse_us_to_level(uint32_t pulse_us) { + const float period_us = 1000000.0f / servo_hz; + float counts_per_us = (servo_wrap + 1) / period_us; + return (uint32_t)(pulse_us * counts_per_us + 0.5f); +} + +/** + * @brief Build and apply the PWM slice configuration for 50 Hz servo + * + * Computes the clock divider from the system clock to achieve the + * target servo frequency with the chosen wrap value, then starts + * the PWM slice. + */ +static void apply_servo_config(void) { + pwm_config config = pwm_get_default_config(); + const uint32_t sys_clock_hz = clock_get_hz(clk_sys); + float clock_div = (float)sys_clock_hz / (servo_hz * (servo_wrap + 1)); + pwm_config_set_clkdiv(&config, clock_div); + pwm_config_set_wrap(&config, servo_wrap); + pwm_init(servo_slice, &config, true); +} + +void servo_init(uint8_t pin) { + servo_pin = pin; + gpio_set_function(servo_pin, GPIO_FUNC_PWM); + servo_slice = pwm_gpio_to_slice_num(servo_pin); + servo_chan = pwm_gpio_to_channel(servo_pin); + apply_servo_config(); + servo_initialized = true; +} + +void servo_set_pulse_us(uint16_t pulse_us) { + if (!servo_initialized) return; + if (pulse_us < SERVO_DEFAULT_MIN_US) pulse_us = SERVO_DEFAULT_MIN_US; + if (pulse_us > SERVO_DEFAULT_MAX_US) pulse_us = SERVO_DEFAULT_MAX_US; + uint32_t level = pulse_us_to_level(pulse_us); + pwm_set_chan_level(servo_slice, servo_chan, level); +} + +void servo_set_angle(float degrees) { + if (degrees < 0.0f) degrees = 0.0f; + if (degrees > 180.0f) degrees = 180.0f; + float ratio = degrees / 180.0f; + uint16_t pulse = (uint16_t)(SERVO_DEFAULT_MIN_US + ratio * (SERVO_DEFAULT_MAX_US - SERVO_DEFAULT_MIN_US) + 0.5f); + servo_set_pulse_us(pulse); +} \ No newline at end of file diff --git a/0x001d_static-conditionals/servo.h b/0x001d_static-conditionals/servo.h new file mode 100644 index 0000000..878d029 --- /dev/null +++ b/0x001d_static-conditionals/servo.h @@ -0,0 +1,62 @@ +/** + * @file servo.h + * @brief Header for SG90 servo driver (PWM) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef SERVO_H +#define SERVO_H + +#include +#include + +/** + * @brief Initialize servo driver on a given GPIO pin + * + * Configures PWM for a 50 Hz servo (SG90). Call once before using other API. + * + * @param pin GPIO pin number to use for servo PWM + */ +void servo_init(uint8_t pin); + +/** + * @brief Set servo pulse width in microseconds (typical 1000-2000) + * + * @param pulse_us Pulse width in microseconds + */ +void servo_set_pulse_us(uint16_t pulse_us); + +/** + * @brief Set servo angle in degrees (0 to 180) + * + * Maps 0..180 degrees to the configured pulse range (default 1000..2000 us). + * Values outside [0,180] will be clamped. + * + * @param degrees Angle in degrees + */ +void servo_set_angle(float degrees); + +#endif // SERVO_H diff --git a/0x0020_dynamic-conditionals/.gitignore b/0x0020_dynamic-conditionals/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0020_dynamic-conditionals/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0020_dynamic-conditionals/.vscode/c_cpp_properties.json b/0x0020_dynamic-conditionals/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c429d6d --- /dev/null +++ b/0x0020_dynamic-conditionals/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0020_dynamic-conditionals/.vscode/cmake-kits.json b/0x0020_dynamic-conditionals/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0020_dynamic-conditionals/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0020_dynamic-conditionals/.vscode/extensions.json b/0x0020_dynamic-conditionals/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0020_dynamic-conditionals/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0020_dynamic-conditionals/.vscode/launch.json b/0x0020_dynamic-conditionals/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0020_dynamic-conditionals/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0020_dynamic-conditionals/.vscode/settings.json b/0x0020_dynamic-conditionals/.vscode/settings.json new file mode 100644 index 0000000..95be83b --- /dev/null +++ b/0x0020_dynamic-conditionals/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0020_dynamic-conditionals/.vscode/tasks.json b/0x0020_dynamic-conditionals/.vscode/tasks.json new file mode 100644 index 0000000..d1b3193 --- /dev/null +++ b/0x0020_dynamic-conditionals/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0020_dynamic-conditionals/0x0020_dynamic-conditionals.c b/0x0020_dynamic-conditionals/0x0020_dynamic-conditionals.c new file mode 100644 index 0000000..028fcd4 --- /dev/null +++ b/0x0020_dynamic-conditionals/0x0020_dynamic-conditionals.c @@ -0,0 +1,111 @@ +/** + * @file 0x0020_dynamic-conditionals.c + * @brief Dynamic conditionals: UART input drives if/else and switch with servo + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates dynamic (runtime) conditionals using UART input via getchar(). + * Reads a character, evaluates it with if/else and switch/case, and controls + * a servo on GPIO6 based on the input ('1' or '2'). + * + * Wiring: + * GPIO6 -> Servo signal wire (orange/white) + * 5V -> Servo VCC (red) + * GND -> Servo GND (brown/black) + */ + +#include +#include "pico/stdlib.h" +#include "servo.h" + +/** @brief GPIO pin number for the servo */ +#define SERVO_GPIO 6 + +/** + * @brief Evaluate choice with if/else conditional and print result + * + * @details Compares against hex ASCII values 0x31 ('1') and 0x32 ('2'). + * + * @param choice character value received from UART + * @retval None + */ +static void eval_if_else(uint8_t choice) { + if (choice == 0x31) { + printf("1\r\n"); + } else if (choice == 0x32) { + printf("2\r\n"); + } else { + printf("??\r\n"); + } +} + +/** + * @brief Sweep servo from start angle to end angle with a pause + * + * @details Sets the servo to start, waits 500 ms, sets to end, + * waits 500 ms. + * + * @param label text to print before sweeping + * @param start starting angle in degrees + * @param end ending angle in degrees + * @retval None + */ +static void sweep_servo(const char *label, float start, float end) { + printf("%s\r\n", label); + servo_set_angle(start); + sleep_ms(500); + servo_set_angle(end); + sleep_ms(500); +} + +/** + * @brief Process choice with switch/case and drive servo accordingly + * + * @details On '1', sweeps servo 0->180; on '2', sweeps 180->0; + * otherwise prints unknown. + * + * @param choice character value received from UART + * @retval None + */ +static void process_servo_command(uint8_t choice) { + switch (choice) { + case '1': sweep_servo("one", 0.0f, 180.0f); break; + case '2': sweep_servo("two", 180.0f, 0.0f); break; + default: printf("??\r\n"); + } +} + +int main(void) { + stdio_init_all(); + uint8_t choice = 0; + servo_init(SERVO_GPIO); + while (true) { + choice = getchar(); + eval_if_else(choice); + process_servo_command(choice); + } +} diff --git a/0x0020_dynamic-conditionals/CMakeLists.txt b/0x0020_dynamic-conditionals/CMakeLists.txt new file mode 100644 index 0000000..4859d4e --- /dev/null +++ b/0x0020_dynamic-conditionals/CMakeLists.txt @@ -0,0 +1,59 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0020_dynamic-conditionals C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0020_dynamic-conditionals 0x0020_dynamic-conditionals.c servo.c) + +pico_set_program_name(0x0020_dynamic-conditionals "0x0020_dynamic-conditionals") +pico_set_program_version(0x0020_dynamic-conditionals "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0020_dynamic-conditionals 1) +pico_enable_stdio_usb(0x0020_dynamic-conditionals 0) + +# Add the standard library to the build +target_link_libraries(0x0020_dynamic-conditionals + pico_stdlib + hardware_pwm + hardware_clocks) + +# Add the standard include files to the build +target_include_directories(0x0020_dynamic-conditionals PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0020_dynamic-conditionals) + diff --git a/0x0020_dynamic-conditionals/pico_sdk_import.cmake b/0x0020_dynamic-conditionals/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0020_dynamic-conditionals/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0020_dynamic-conditionals/servo.c b/0x0020_dynamic-conditionals/servo.c new file mode 100644 index 0000000..d0fefbd --- /dev/null +++ b/0x0020_dynamic-conditionals/servo.c @@ -0,0 +1,107 @@ +/** + * @file servo.c + * @brief Implementation of a simple SG90 servo driver using PWM (50Hz) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "servo.h" +#include "pico/stdlib.h" +#include "hardware/pwm.h" +#include "hardware/clocks.h" + +/** @brief Default minimum pulse width in microseconds */ +static const uint16_t SERVO_DEFAULT_MIN_US = 1000; +/** @brief Default maximum pulse width in microseconds */ +static const uint16_t SERVO_DEFAULT_MAX_US = 2000; + +/** @brief GPIO pin assigned to the servo */ +static uint8_t servo_pin = 0; +/** @brief PWM hardware slice for the servo pin */ +static uint servo_slice = 0; +/** @brief PWM channel within the servo slice */ +static uint servo_chan = 0; +/** @brief PWM counter wrap value for 50 Hz servo */ +static uint32_t servo_wrap = 20000 - 1; +/** @brief Servo PWM frequency in Hz */ +static float servo_hz = 50.0f; +/** @brief Flag indicating servo has been initialized */ +static bool servo_initialized = false; + +/** + * @brief Convert a pulse width in microseconds to a PWM counter level + * + * Uses the configured PWM wrap and servo frequency to map pulse time + * into the channel compare value expected by the PWM hardware. + * + * @param pulse_us Pulse width in microseconds + * @return uint32_t PWM level suitable for pwm_set_chan_level() + */ +static uint32_t pulse_us_to_level(uint32_t pulse_us) { + const float period_us = 1000000.0f / servo_hz; + float counts_per_us = (servo_wrap + 1) / period_us; + return (uint32_t)(pulse_us * counts_per_us + 0.5f); +} + +/** + * @brief Build and apply the PWM slice configuration for 50 Hz servo + * + * Computes the clock divider from the system clock to achieve the + * target servo frequency with the chosen wrap value, then starts + * the PWM slice. + */ +static void apply_servo_config(void) { + pwm_config config = pwm_get_default_config(); + const uint32_t sys_clock_hz = clock_get_hz(clk_sys); + float clock_div = (float)sys_clock_hz / (servo_hz * (servo_wrap + 1)); + pwm_config_set_clkdiv(&config, clock_div); + pwm_config_set_wrap(&config, servo_wrap); + pwm_init(servo_slice, &config, true); +} + +void servo_init(uint8_t pin) { + servo_pin = pin; + gpio_set_function(servo_pin, GPIO_FUNC_PWM); + servo_slice = pwm_gpio_to_slice_num(servo_pin); + servo_chan = pwm_gpio_to_channel(servo_pin); + apply_servo_config(); + servo_initialized = true; +} + +void servo_set_pulse_us(uint16_t pulse_us) { + if (!servo_initialized) return; + if (pulse_us < SERVO_DEFAULT_MIN_US) pulse_us = SERVO_DEFAULT_MIN_US; + if (pulse_us > SERVO_DEFAULT_MAX_US) pulse_us = SERVO_DEFAULT_MAX_US; + uint32_t level = pulse_us_to_level(pulse_us); + pwm_set_chan_level(servo_slice, servo_chan, level); +} + +void servo_set_angle(float degrees) { + if (degrees < 0.0f) degrees = 0.0f; + if (degrees > 180.0f) degrees = 180.0f; + float ratio = degrees / 180.0f; + uint16_t pulse = (uint16_t)(SERVO_DEFAULT_MIN_US + ratio * (SERVO_DEFAULT_MAX_US - SERVO_DEFAULT_MIN_US) + 0.5f); + servo_set_pulse_us(pulse); +} \ No newline at end of file diff --git a/0x0020_dynamic-conditionals/servo.h b/0x0020_dynamic-conditionals/servo.h new file mode 100644 index 0000000..878d029 --- /dev/null +++ b/0x0020_dynamic-conditionals/servo.h @@ -0,0 +1,62 @@ +/** + * @file servo.h + * @brief Header for SG90 servo driver (PWM) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef SERVO_H +#define SERVO_H + +#include +#include + +/** + * @brief Initialize servo driver on a given GPIO pin + * + * Configures PWM for a 50 Hz servo (SG90). Call once before using other API. + * + * @param pin GPIO pin number to use for servo PWM + */ +void servo_init(uint8_t pin); + +/** + * @brief Set servo pulse width in microseconds (typical 1000-2000) + * + * @param pulse_us Pulse width in microseconds + */ +void servo_set_pulse_us(uint16_t pulse_us); + +/** + * @brief Set servo angle in degrees (0 to 180) + * + * Maps 0..180 degrees to the configured pulse range (default 1000..2000 us). + * Values outside [0,180] will be clamped. + * + * @param degrees Angle in degrees + */ +void servo_set_angle(float degrees); + +#endif // SERVO_H diff --git a/0x0023_structures/.gitignore b/0x0023_structures/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0023_structures/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0023_structures/.vscode/c_cpp_properties.json b/0x0023_structures/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c429d6d --- /dev/null +++ b/0x0023_structures/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0023_structures/.vscode/cmake-kits.json b/0x0023_structures/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0023_structures/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0023_structures/.vscode/extensions.json b/0x0023_structures/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0023_structures/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0023_structures/.vscode/launch.json b/0x0023_structures/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0023_structures/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0023_structures/.vscode/settings.json b/0x0023_structures/.vscode/settings.json new file mode 100644 index 0000000..95be83b --- /dev/null +++ b/0x0023_structures/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0023_structures/.vscode/tasks.json b/0x0023_structures/.vscode/tasks.json new file mode 100644 index 0000000..d1b3193 --- /dev/null +++ b/0x0023_structures/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0023_structures/0x0023_structures.c b/0x0023_structures/0x0023_structures.c new file mode 100644 index 0000000..657dc5f --- /dev/null +++ b/0x0023_structures/0x0023_structures.c @@ -0,0 +1,144 @@ +/** + * @file 0x0023_structures.c + * @brief Structures: IR remote controls LEDs via a struct-based controller + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates C structures by defining a simple_led_ctrl_t to manage three + * LEDs. An IR receiver on GPIO5 decodes NEC commands and activates the + * corresponding LED (0x0C -> GPIO16, 0x18 -> GPIO17, 0x5E -> GPIO18). + * + * Wiring: + * GPIO5 -> IR receiver OUT + * GPIO16 -> LED1 anode (with current-limiting resistor to GND) + * GPIO17 -> LED2 anode (with current-limiting resistor to GND) + * GPIO18 -> LED3 anode (with current-limiting resistor to GND) + */ + +#include +#include +#include "pico/stdlib.h" +#include "ir.h" + +/** @brief GPIO pin number for the IR receiver */ +#define IR_PIN 5 + +typedef struct { + uint8_t led1_pin; + uint8_t led2_pin; + uint8_t led3_pin; + bool led1_state; + bool led2_state; + bool led3_state; +} simple_led_ctrl_t; + +/** + * @brief Initialize all three LED GPIO pins as outputs + * + * @details Configures led1_pin, led2_pin, and led3_pin from the + * structure as GPIO outputs. + * + * @param leds pointer to the LED controller structure + * @retval None + */ +static void init_led_gpios(simple_led_ctrl_t *leds) { + gpio_init(leds->led1_pin); + gpio_set_dir(leds->led1_pin, GPIO_OUT); + gpio_init(leds->led2_pin); + gpio_set_dir(leds->led2_pin, GPIO_OUT); + gpio_init(leds->led3_pin); + gpio_set_dir(leds->led3_pin, GPIO_OUT); +} + +/** + * @brief Process an NEC IR key and update LED states + * + * @details Maps NEC command codes to LEDs: 0x0C -> LED1, 0x18 -> LED2, + * 0x5E -> LED3. Turns off all LEDs first, then activates the match. + * + * @param leds pointer to the LED controller structure + * @param key NEC command code from IR receiver + * @retval None + */ +static void process_ir_key(simple_led_ctrl_t *leds, int key) { + printf("NEC command: 0x%02X\n", key); + leds->led1_state = (key == 0x0C); + leds->led2_state = (key == 0x18); + leds->led3_state = (key == 0x5E); + gpio_put(leds->led1_pin, leds->led1_state); + gpio_put(leds->led2_pin, leds->led2_state); + gpio_put(leds->led3_pin, leds->led3_state); + sleep_ms(10); +} + +/** + * @brief Poll IR receiver and dispatch key to handler + * + * @details Reads one IR key; if valid, processes it; otherwise + * yields with a short sleep. + * + * @param leds pointer to the LED controller structure + * @retval None + */ +static void poll_ir(simple_led_ctrl_t *leds) { + int key = ir_getkey(); + if (key >= 0) { + process_ir_key(leds, key); + } else { + sleep_ms(1); + } +} + +/** + * @brief Build the default LED controller structure + * + * @details Initializes pins 16-18 with all LEDs off. + * + * @return simple_led_ctrl_t initialized structure + */ +static simple_led_ctrl_t make_default_leds(void) { + simple_led_ctrl_t leds = { + .led1_pin = 16, + .led2_pin = 17, + .led3_pin = 18, + .led1_state = false, + .led2_state = false, + .led3_state = false + }; + return leds; +} + +int main(void) { + stdio_init_all(); + simple_led_ctrl_t leds = make_default_leds(); + init_led_gpios(&leds); + ir_init(IR_PIN); + printf("IR receiver on GPIO %d ready\n", IR_PIN); + while (true) { + poll_ir(&leds); + } +} diff --git a/0x0023_structures/CMakeLists.txt b/0x0023_structures/CMakeLists.txt new file mode 100644 index 0000000..d171bb7 --- /dev/null +++ b/0x0023_structures/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0023_structures C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0023_structures 0x0023_structures.c ir.c) + +pico_set_program_name(0x0023_structures "0x0023_structures") +pico_set_program_version(0x0023_structures "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0023_structures 1) +pico_enable_stdio_usb(0x0023_structures 0) + +# Add the standard library to the build +target_link_libraries(0x0023_structures + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0023_structures PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0023_structures) + diff --git a/0x0023_structures/ir.c b/0x0023_structures/ir.c new file mode 100644 index 0000000..9d5b42a --- /dev/null +++ b/0x0023_structures/ir.c @@ -0,0 +1,130 @@ +/** + * @file ir.c + * @brief Implementation of NEC IR receiver (decoder) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ir.h" +#include "pico/stdlib.h" +#include "pico/time.h" +#include "hardware/gpio.h" + +/** @brief GPIO pin connected to the IR receiver */ +static unsigned int ir_pin = 0; + +/** + * @brief Wait for a GPIO pin to reach a given logic level + * + * Spins until the pin matches the requested level or a microsecond timeout + * is exceeded. Returns the elapsed time in microseconds, or -1 on timeout. + * + * @param gpio GPIO pin to monitor + * @param level Desired logic level (true = HIGH, false = LOW) + * @param timeout_us Maximum wait in microseconds + * @return int64_t Elapsed microseconds, or -1 on timeout + */ +static int64_t wait_for_level(unsigned int gpio, bool level, uint32_t timeout_us) { + absolute_time_t start = get_absolute_time(); + while (gpio_get(gpio) != level) { + if (absolute_time_diff_us(start, get_absolute_time()) > (int64_t)timeout_us) + return -1; + } + return absolute_time_diff_us(start, get_absolute_time()); +} + +/** + * @brief Wait for the NEC 9 ms leader pulse and 4.5 ms space + * + * @return bool true if a valid leader was detected, false on timeout + */ +static bool wait_leader(void) { + if (wait_for_level(ir_pin, 0, 150000) < 0) return false; + int64_t t = wait_for_level(ir_pin, 1, 12000); + if (t < 8000 || t > 10000) return false; + t = wait_for_level(ir_pin, 0, 7000); + if (t < 3500 || t > 5000) return false; + return true; +} + +/** + * @brief Read a single NEC-encoded bit from the IR receiver + * + * Measures the mark/space timing and shifts the result into the + * appropriate byte of the data array. + * + * @param data 4-byte array accumulating received bits + * @param i Bit index (0-31) + * @return bool true on success, false on timeout or protocol error + */ +static bool read_nec_bit(uint8_t *data, int i) { + if (wait_for_level(ir_pin, 1, 1000) < 0) return false; + int64_t t = wait_for_level(ir_pin, 0, 2500); + if (t < 200) return false; + int byte_idx = i / 8; + int bit_idx = i % 8; + if (t > 1200) data[byte_idx] |= (1 << bit_idx); + return true; +} + +/** + * @brief Read all 32 data bits of an NEC frame + * + * @param data 4-byte array filled with the received address and command + * @return bool true if all 32 bits were read, false on timeout + */ +static bool read_32_bits(uint8_t *data) { + for (int i = 0; i < 32; ++i) + if (!read_nec_bit(data, i)) return false; + return true; +} + +/** + * @brief Validate an NEC frame and extract the command byte + * + * Checks that the address and command pairs are bitwise-inverted. + * + * @param data 4-byte NEC frame (addr, ~addr, cmd, ~cmd) + * @return int Command byte (0-255) on success, -1 on validation failure + */ +static int validate_nec_frame(const uint8_t *data) { + if ((uint8_t)(data[0] + data[1]) == 0xFF && (uint8_t)(data[2] + data[3]) == 0xFF) + return data[2]; + return -1; +} + +void ir_init(uint8_t pin) { + ir_pin = pin; + gpio_init(pin); + gpio_set_dir(pin, GPIO_IN); + gpio_pull_up(pin); +} + +int ir_getkey(void) { + if (!wait_leader()) return -1; + uint8_t data[4] = {0, 0, 0, 0}; + if (!read_32_bits(data)) return -1; + return validate_nec_frame(data); +} diff --git a/0x0023_structures/ir.h b/0x0023_structures/ir.h new file mode 100644 index 0000000..84aa67f --- /dev/null +++ b/0x0023_structures/ir.h @@ -0,0 +1,63 @@ +/** + * @file ir.h + * @brief Header for NEC IR receiver (decoder) API + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef IR_H +#define IR_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize the IR receiver GPIO + * + * Configures the given `pin` as an input with pull-up for the NEC IR receiver. + * Call this once during board initialization before calling `ir_getkey()`. + * + * @param pin GPIO pin number connected to IR receiver output + */ +void ir_init(uint8_t pin); + +/** + * @brief Blocking NEC IR decoder + * + * Blocks while waiting for a complete NEC frame. On success returns the + * decoded command byte (0..255). On timeout or protocol error returns -1. + * + * @return decoded command byte (0..255) or -1 on failure + */ +int ir_getkey(void); + +#ifdef __cplusplus +} +#endif + +#endif // IR_H diff --git a/0x0023_structures/pico_sdk_import.cmake b/0x0023_structures/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0023_structures/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0026_functions/.gitignore b/0x0026_functions/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0026_functions/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0026_functions/.vscode/c_cpp_properties.json b/0x0026_functions/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c429d6d --- /dev/null +++ b/0x0026_functions/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0026_functions/.vscode/cmake-kits.json b/0x0026_functions/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0026_functions/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0026_functions/.vscode/extensions.json b/0x0026_functions/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0026_functions/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0026_functions/.vscode/launch.json b/0x0026_functions/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0026_functions/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0026_functions/.vscode/settings.json b/0x0026_functions/.vscode/settings.json new file mode 100644 index 0000000..95be83b --- /dev/null +++ b/0x0026_functions/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0026_functions/.vscode/tasks.json b/0x0026_functions/.vscode/tasks.json new file mode 100644 index 0000000..d1b3193 --- /dev/null +++ b/0x0026_functions/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0026_functions/0x0026_functions.c b/0x0026_functions/0x0026_functions.c new file mode 100644 index 0000000..210c7cc --- /dev/null +++ b/0x0026_functions/0x0026_functions.c @@ -0,0 +1,237 @@ +/** + * @file 0x0026_functions.c + * @brief Functions: IR remote controls LEDs with helper function decomposition + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates function decomposition by breaking IR-controlled LED logic + * into small, focused helper functions. An IR receiver on GPIO5 decodes + * NEC commands and activates the corresponding LED with a blink effect. + * + * Wiring: + * GPIO5 -> IR receiver OUT + * GPIO16 -> LED1 anode (with current-limiting resistor to GND) + * GPIO17 -> LED2 anode (with current-limiting resistor to GND) + * GPIO18 -> LED3 anode (with current-limiting resistor to GND) + */ + +#include +#include +#include "pico/stdlib.h" +#include "ir.h" + +/** @brief GPIO pin number for the IR receiver */ +#define IR_PIN 5 + +typedef struct { + uint8_t led1_pin; + uint8_t led2_pin; + uint8_t led3_pin; + bool led1_state; + bool led2_state; + bool led3_state; +} simple_led_ctrl_t; + +/** + * @brief Map NEC IR command code to LED number + * + * @details Translates a received NEC IR command code to a logical + * LED number. Supports three button mappings. + * + * @param ir_command NEC command code from IR receiver + * @retval 1-3 for matched LED, 0 if no match + */ +static int ir_to_led_number(int ir_command) { + if (ir_command == 0x0C) return 1; + if (ir_command == 0x18) return 2; + if (ir_command == 0x5E) return 3; + return 0; +} + +/** + * @brief Get GPIO pin number for a given LED number + * + * @details Retrieves the GPIO pin associated with a logical LED + * number from the LED controller structure. + * + * @param leds pointer to LED controller structure + * @param led_num LED number (1-3) + * @retval GPIO pin number or 0 if invalid + */ +static uint8_t get_led_pin(simple_led_ctrl_t *leds, int led_num) { + if (led_num == 1) return leds->led1_pin; + if (led_num == 2) return leds->led2_pin; + if (led_num == 3) return leds->led3_pin; + return 0; +} + +/** + * @brief Turn off all LEDs in the controller + * + * @details Sets all three LED GPIO outputs to low. + * + * @param leds pointer to LED controller structure + * @retval None + */ +static void leds_all_off(simple_led_ctrl_t *leds) { + gpio_put(leds->led1_pin, false); + gpio_put(leds->led2_pin, false); + gpio_put(leds->led3_pin, false); +} + +/** + * @brief Blink an LED pin a specified number of times + * + * @details Toggles the specified GPIO pin on and off for the given + * count, with configurable delay between transitions. + * + * @param pin GPIO pin number to blink + * @param count number of blink cycles + * @param delay_ms delay in milliseconds for on/off periods + * @retval None + */ +static void blink_led(uint8_t pin, uint8_t count, uint32_t delay_ms) { + for (uint8_t i = 0; i < count; i++) { + gpio_put(pin, true); + sleep_ms(delay_ms); + gpio_put(pin, false); + sleep_ms(delay_ms); + } +} + +/** + * @brief Process IR command and activate corresponding LED + * + * @details Turns off all LEDs, maps the command to an LED number, + * blinks it, then holds it steady. + * + * @param ir_command NEC command code from IR receiver + * @param leds pointer to LED controller structure + * @param blink_count number of blinks before steady state + * @retval LED number activated (1-3), 0 if none, -1 if invalid + */ +static int process_ir_led_command(int ir_command, simple_led_ctrl_t *leds, uint8_t blink_count) { + if (!leds || ir_command < 0) return -1; + leds_all_off(leds); + int led_num = ir_to_led_number(ir_command); + if (led_num == 0) return 0; + uint8_t pin = get_led_pin(leds, led_num); + blink_led(pin, blink_count, 50); + gpio_put(pin, true); + return led_num; +} + +/** + * @brief Initialize all three LED GPIO pins as outputs + * + * @details Configures led1_pin, led2_pin, and led3_pin from the + * structure as GPIO outputs. + * + * @param leds pointer to the LED controller structure + * @retval None + */ +static void init_led_gpios(simple_led_ctrl_t *leds) { + gpio_init(leds->led1_pin); + gpio_set_dir(leds->led1_pin, GPIO_OUT); + gpio_init(leds->led2_pin); + gpio_set_dir(leds->led2_pin, GPIO_OUT); + gpio_init(leds->led3_pin); + gpio_set_dir(leds->led3_pin, GPIO_OUT); +} + +/** + * @brief Poll IR and handle a single received key + * + * @details Reads an IR key, processes the command with 3 blinks, + * and prints the result. + * + * @param leds pointer to the LED controller structure + * @retval None + */ +/** + * @brief Handle a valid IR key press + * + * @details Prints the NEC command, processes it with 3 blinks, + * and reports the activated LED. + * + * @param leds pointer to the LED controller structure + * @param key NEC command code + * @retval None + */ +static void handle_ir_key(simple_led_ctrl_t *leds, int key) { + printf("NEC command: 0x%02X\n", key); + int activated_led = process_ir_led_command(key, leds, 3); + if (activated_led > 0) { + printf("LED %d activated on GPIO %d\n", activated_led, + get_led_pin(leds, activated_led)); + } + sleep_ms(10); +} + +/** + * @brief Poll IR and handle a single received key + * + * @details Reads an IR key; if valid dispatches to handler, + * otherwise yields with a short sleep. + * + * @param leds pointer to the LED controller structure + * @retval None + */ +static void poll_and_handle_ir(simple_led_ctrl_t *leds) { + int key = ir_getkey(); + if (key >= 0) { + handle_ir_key(leds, key); + } else { + sleep_ms(1); + } +} + +/** + * @brief Build the default LED controller structure + * + * @details Initializes pins 16-18 with all LEDs off. + * + * @return simple_led_ctrl_t initialized structure + */ +static simple_led_ctrl_t make_default_leds(void) { + simple_led_ctrl_t leds = { + .led1_pin = 16, .led2_pin = 17, .led3_pin = 18, + .led1_state = false, .led2_state = false, .led3_state = false + }; + return leds; +} + +int main(void) { + stdio_init_all(); + simple_led_ctrl_t leds = make_default_leds(); + init_led_gpios(&leds); + ir_init(IR_PIN); + printf("IR receiver on GPIO %d ready\n", IR_PIN); + while (true) { + poll_and_handle_ir(&leds); + } +} diff --git a/0x0026_functions/CMakeLists.txt b/0x0026_functions/CMakeLists.txt new file mode 100644 index 0000000..b6ea0f9 --- /dev/null +++ b/0x0026_functions/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0026_functions C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0026_functions 0x0026_functions.c ir.c) + +pico_set_program_name(0x0026_functions "0x0026_functions") +pico_set_program_version(0x0026_functions "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0026_functions 1) +pico_enable_stdio_usb(0x0026_functions 0) + +# Add the standard library to the build +target_link_libraries(0x0026_functions + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0026_functions PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0026_functions) + diff --git a/0x0026_functions/ir.c b/0x0026_functions/ir.c new file mode 100644 index 0000000..9d5b42a --- /dev/null +++ b/0x0026_functions/ir.c @@ -0,0 +1,130 @@ +/** + * @file ir.c + * @brief Implementation of NEC IR receiver (decoder) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ir.h" +#include "pico/stdlib.h" +#include "pico/time.h" +#include "hardware/gpio.h" + +/** @brief GPIO pin connected to the IR receiver */ +static unsigned int ir_pin = 0; + +/** + * @brief Wait for a GPIO pin to reach a given logic level + * + * Spins until the pin matches the requested level or a microsecond timeout + * is exceeded. Returns the elapsed time in microseconds, or -1 on timeout. + * + * @param gpio GPIO pin to monitor + * @param level Desired logic level (true = HIGH, false = LOW) + * @param timeout_us Maximum wait in microseconds + * @return int64_t Elapsed microseconds, or -1 on timeout + */ +static int64_t wait_for_level(unsigned int gpio, bool level, uint32_t timeout_us) { + absolute_time_t start = get_absolute_time(); + while (gpio_get(gpio) != level) { + if (absolute_time_diff_us(start, get_absolute_time()) > (int64_t)timeout_us) + return -1; + } + return absolute_time_diff_us(start, get_absolute_time()); +} + +/** + * @brief Wait for the NEC 9 ms leader pulse and 4.5 ms space + * + * @return bool true if a valid leader was detected, false on timeout + */ +static bool wait_leader(void) { + if (wait_for_level(ir_pin, 0, 150000) < 0) return false; + int64_t t = wait_for_level(ir_pin, 1, 12000); + if (t < 8000 || t > 10000) return false; + t = wait_for_level(ir_pin, 0, 7000); + if (t < 3500 || t > 5000) return false; + return true; +} + +/** + * @brief Read a single NEC-encoded bit from the IR receiver + * + * Measures the mark/space timing and shifts the result into the + * appropriate byte of the data array. + * + * @param data 4-byte array accumulating received bits + * @param i Bit index (0-31) + * @return bool true on success, false on timeout or protocol error + */ +static bool read_nec_bit(uint8_t *data, int i) { + if (wait_for_level(ir_pin, 1, 1000) < 0) return false; + int64_t t = wait_for_level(ir_pin, 0, 2500); + if (t < 200) return false; + int byte_idx = i / 8; + int bit_idx = i % 8; + if (t > 1200) data[byte_idx] |= (1 << bit_idx); + return true; +} + +/** + * @brief Read all 32 data bits of an NEC frame + * + * @param data 4-byte array filled with the received address and command + * @return bool true if all 32 bits were read, false on timeout + */ +static bool read_32_bits(uint8_t *data) { + for (int i = 0; i < 32; ++i) + if (!read_nec_bit(data, i)) return false; + return true; +} + +/** + * @brief Validate an NEC frame and extract the command byte + * + * Checks that the address and command pairs are bitwise-inverted. + * + * @param data 4-byte NEC frame (addr, ~addr, cmd, ~cmd) + * @return int Command byte (0-255) on success, -1 on validation failure + */ +static int validate_nec_frame(const uint8_t *data) { + if ((uint8_t)(data[0] + data[1]) == 0xFF && (uint8_t)(data[2] + data[3]) == 0xFF) + return data[2]; + return -1; +} + +void ir_init(uint8_t pin) { + ir_pin = pin; + gpio_init(pin); + gpio_set_dir(pin, GPIO_IN); + gpio_pull_up(pin); +} + +int ir_getkey(void) { + if (!wait_leader()) return -1; + uint8_t data[4] = {0, 0, 0, 0}; + if (!read_32_bits(data)) return -1; + return validate_nec_frame(data); +} diff --git a/0x0026_functions/ir.h b/0x0026_functions/ir.h new file mode 100644 index 0000000..84aa67f --- /dev/null +++ b/0x0026_functions/ir.h @@ -0,0 +1,63 @@ +/** + * @file ir.h + * @brief Header for NEC IR receiver (decoder) API + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef IR_H +#define IR_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize the IR receiver GPIO + * + * Configures the given `pin` as an input with pull-up for the NEC IR receiver. + * Call this once during board initialization before calling `ir_getkey()`. + * + * @param pin GPIO pin number connected to IR receiver output + */ +void ir_init(uint8_t pin); + +/** + * @brief Blocking NEC IR decoder + * + * Blocks while waiting for a complete NEC frame. On success returns the + * decoded command byte (0..255). On timeout or protocol error returns -1. + * + * @return decoded command byte (0..255) or -1 on failure + */ +int ir_getkey(void); + +#ifdef __cplusplus +} +#endif + +#endif // IR_H diff --git a/0x0026_functions/pico_sdk_import.cmake b/0x0026_functions/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0026_functions/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/Embedded Hacking.png b/Embedded Hacking.png new file mode 100644 index 0000000..81c55fd Binary files /dev/null and b/Embedded Hacking.png differ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..48c09e7 --- /dev/null +++ b/README.md @@ -0,0 +1,1245 @@ +![image](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded%20Hacking.png?raw=true) + +
+ +--- +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** +--- + +
+ +## FREE Reverse Engineering Self-Study Course [HERE](https://github.com/mytechnotalent/Reverse-Engineering) + +
+ +# Today's Tutorial [October 6, 2026] +## Lesson 317: Embedded Hacking Course (Chapter 35: Structures) +This chapter covers structures as well as an intro to infrared basics as we work a infrared receiver and infrared remote controller as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +
+ +# Embedded Hacking +A FREE comprehensive step-by-step embedded hacking course covering Embedded Software Development to Reverse Engineering. + +VIDEO PROMO [HERE](https://www.youtube.com/watch?v=aD7X9sXirF8) + +
+ +# FREE Book [Download](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) + +
+ +# Required Skills +Students must have a working understanding of the following items: + +- **Intermediate C Programming:** In particular, you must understand how pointers reference memory addresses which we will use to perform live variable hijacking. + - Training Link [HERE](https://www.learn-c.org) +- **Bare-Metal Embedded Systems Assembly Basics:** You must have experience with bare-metal assembly as we will be using this extensively throughout the entire course specifically with GDB and Ghidra. + - Training Link [HERE](https://azeria-labs.com/writing-arm-assembly-part-1) +- **Computer Architecture Basics (Registers & Stack):** You need a working mental model of how a CPU/MCU uses registers and the stack to follow the boot process and function calls. + - Training Link [HERE](https://www.geeksforgeeks.org/computer-science-fundamentals/what-is-register-memory) + - Training Link [HERE](https://www.geeksforgeeks.org/computer-organization-architecture/memory-stack-organization-in-computer-architecture) +- **Command Line Interface (CLI) Proficiency:** You must be comfortable navigating directories and executing commands in a terminal to run the necessary OpenOCD and GDB tools. + - Training Link [HERE](https://www.freecodecamp.org/news/command-line-commands-cli-tutorial) +- **Breadboarding Proficiency:** You must be comfortable demonstrating breadboard proficiency by efficiently prototyping circuits and integrating diverse peripherals, sensors, and microcontrollers. + - Training Video [HERE](https://www.youtube.com/watch?v=fq6U5Y14oM4) + +
+ +# Hardware + +### Reference Diagrams & Schematics +- [Fritzing Project](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/hardware/EHP2.fzz) +- [Breadboard Diagram](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/hardware/EHP2_bb.png) +- [Pico 2 Pinout](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/hardware/pico-2-r4-pinout.svg) +- [Debug Probe Wiring](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/hardware/dp.png) + +### Required Components & Sensors +The following hardware parts and sensors are used throughout the course experiments, projects, and live CTFs: +- [1x Full-size Breadboard (Long)](https://www.amazon.com/s?k=full+size+breadboard) +- [1x Assorted Jumper Wires (Male-to-Male, Male-to-Female, Female-to-Female)](https://www.amazon.com/s?k=breadboard+jumper+wires+assortment) +- [1x Raspberry Pi Pico 2 w/ Header](https://www.amazon.com/s?k=raspberry+pi+pico+2+with+pre-soldered+header) +- [1x Raspberry Pi Pico Debug Probe](https://www.amazon.com/s?k=raspberry+pi+debug+probe) +- [2x USB A-Male to USB Micro-B Cables (1 for Pico 2, 1 for Debug Probe)](https://www.amazon.com/s?k=micro+usb+cable+2+pack) +- [3x 5mm LEDs (1 Red, 1 Green, 1 Yellow)](https://www.amazon.com/s?k=5mm+led+kit) +- [3x 100, 220 or 330 Ohm Resistors (for LEDs)](https://www.amazon.com/s?k=resistor+assortment+kit) +- [1x Push Button (Tactile switch)](https://www.amazon.com/s?k=tactile+push+button+assortment) +- [1x 1602 LCD (with PCF8574 I2C backpack)](https://www.amazon.com/s?k=1602+lcd+i2c+module) +- [1x DHT11 Temperature & Humidity Sensor](https://www.amazon.com/s?k=dht11+temperature+and+humidity+sensor) +- [1x SG90 Servo Motor](https://www.amazon.com/s?k=sg90+micro+servo+motor) +- [1x 1000uF 25V Capacitor](https://www.amazon.com/Cionyce-Capacitor-Electrolytic-CapacitorsMicrowave/dp/B0B63CCQ2N) +- [1x 100pcs 100nF DIP Ceramic Disc Capacitor](https://www.amazon.com/dp/B09XV2H8JQ) +- [1x Infrared (IR) Receiver (VS1838B)](https://www.amazon.com/s?k=vs1838b+ir+receiver+module) +- [1x Infrared (IR) Remote Controller (NEC-compatible)](https://www.amazon.com/s?k=arduino+ir+remote+control) +- [1x u-blox NEO-6M GPS Receiver Module (with Active Antenna)](https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B084MK8BS2) +- [2x REYAX RYLR998 868/915MHz LoRa Transceiver Modules (1x Pico 2, 1x Computer)](https://www.amazon.com/dp/B099RM1XMG) +- [1x FT232RL USB to TTL Serial Adapter Module](https://www.amazon.com/FT232RL-Serial-Adapter-Module-Arduino/dp/B0FHP71BCQ) + +

+ +# Breadboard Design +![image](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/hardware/EHP2_bb.png?raw=true) + +
+ +# Development Environment Setup + +Everything installs natively on Windows, macOS (Apple Silicon), and Linux x64 — **no VM required**. The core toolchain (Pico SDK, the full Arm GNU Toolchain — `arm-none-eabi-gcc`, `arm-none-eabi-gdb`, `arm-none-eabi-nm`, `objdump` — `picotool`, and OpenOCD) comes from the **Raspberry Pi Pico VS Code extension**, which installs it all under `~/.pico-sdk` on every platform. You then add two reverse-engineering tools and a serial monitor. `arm-none-eabi-gdb` is what the GDB and Binary Ninja (GDB MI) debugging labs use. + +## Windows x64 + +1. Install [VS Code](https://code.visualstudio.com/), [Git for Windows](https://git-scm.com/download/win), and [Python 3](https://www.python.org/downloads/) (check **Add python.exe to PATH**). +2. In VS Code open **Extensions**, search **Raspberry Pi Pico**, install it, and choose **Pico 2** as the board when prompted. This pulls the Pico SDK, Arm GNU Toolchain (includes `arm-none-eabi-gdb`), `picotool`, and OpenOCD. +3. Install [Binary Ninja Personal](https://binary.ninja/) and activate the license. +4. Install [Ghidra](https://github.com/NationalSecurityAgency/ghidra/releases) and [Eclipse Temurin JDK 21](https://adoptium.net/temurin/releases/?version=21). +5. Serial monitor: [PuTTY](https://www.putty.org/). + +## macOS Apple Silicon + +```bash +brew install cmake ninja +``` + +1. Install [VS Code](https://code.visualstudio.com/), then the **Raspberry Pi Pico** extension (choose **Pico 2**). This installs the Pico SDK, Arm GNU Toolchain (includes `arm-none-eabi-gdb`), `picotool`, and OpenOCD. +2. Install [Binary Ninja Personal](https://binary.ninja/) and activate the license. +3. Install Ghidra and JDK 21: `brew install --cask temurin@21`, then the [Ghidra release](https://github.com/NationalSecurityAgency/ghidra/releases). +4. Serial monitor: `screen` (built in). + +> Use the Apple Silicon Homebrew at `/opt/homebrew`. If `file "$(which cmake)"` reports `x86_64`, an Intel Homebrew at `/usr/local` is shadowing it — put `/opt/homebrew/bin` first on your `PATH`. Do **not** fix this with Rosetta; it produces debugger failures that look like bugs. + +## Linux x64 + +```bash +sudo apt update +sudo apt install git python3 cmake ninja-build build-essential screen +``` + +1. Install [VS Code](https://code.visualstudio.com/), then the **Raspberry Pi Pico** extension (choose **Pico 2**). This installs the Pico SDK, Arm GNU Toolchain (includes `arm-none-eabi-gdb`), `picotool`, and OpenOCD. +2. Install [Binary Ninja Personal](https://binary.ninja/) and activate the license. +3. Install Ghidra and JDK 21 (`sudo apt install openjdk-21-jdk`, then the [Ghidra release](https://github.com/NationalSecurityAgency/ghidra/releases)). +4. Serial monitor: `screen` or `minicom`. + +## Example: Compile and Flash `0x0001 hello, world` + +The **build** step is identical on all three platforms — CMake and Ninja are cross-platform, and the Pico extension's `~/.pico-sdk/cmake/pico-vscode.cmake` supplies the SDK, toolchain, and `picotool` paths on every OS. Only the **flash** and **serial** steps differ, so each platform is shown in full below. All three flash over the Debug Probe with SWD (`program 0x10000000 verify reset exit`) — no BOOTSEL, no UF2. + +### Windows x64 + +```powershell +cd 0x0001_hello-world +cmake -B build -G Ninja -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 -DCMAKE_BUILD_TYPE=Release +cmake --build build +..\flash.ps1 build\0x0001_hello-world.bin +``` +Serial monitor: **PuTTY** -> *Serial* -> the Debug Probe's COM port -> **115200**. + +### macOS Apple Silicon + +```bash +cd 0x0001_hello-world +cmake -B build -G Ninja -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 -DCMAKE_BUILD_TYPE=Release +cmake --build build +../flash.sh build/0x0001_hello-world.bin +screen /dev/cu.usbmodem* 115200 +``` + +### Linux x64 + +```bash +cd 0x0001_hello-world +cmake -B build -G Ninja -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 -DCMAKE_BUILD_TYPE=Release +cmake --build build +../flash.sh build/0x0001_hello-world.bin +screen /dev/ttyACM0 115200 +``` + +You should see `hello, world` repeat at 115200. + +> **If CMake cannot find the SDK or toolchain** (for example you installed the toolchain yourself instead of via the VS Code extension), pass them explicitly: +> ```bash +> cmake -B build -G Ninja -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 -DCMAKE_BUILD_TYPE=Release \ +> -DPICO_SDK_PATH="$HOME/.pico-sdk/sdk/2.2.0" \ +> -DPICO_TOOLCHAIN_PATH="$HOME/.pico-sdk/toolchain/14_2_Rel1" +> ``` +> On Windows use forward slashes, e.g. `C:/Users//.pico-sdk/sdk/2.2.0`. + +Debug Probe wiring is [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/hardware/dp.png). + +
+ +# Datasheets & References + +This repository includes the complete set of authoritative technical reference manuals and datasheets required for professional embedded engineering and reverse engineering on the Raspberry Pi Pico 2: + +### [RP2350 Datasheet](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/datasheets/rp2350-datasheet.pdf) +*RP2350 Datasheet: A microcontroller by Raspberry Pi (1,380 pages)* +- **Core Architecture:** Dual Arm Cortex-M33 / Hazard3 RISC-V processors running up to 150MHz, 520kB on-chip SRAM across 10 striped banks, and 8kB one-time-programmable (OTP) storage. +- **System Memory Map (Chapter 2):** Complete physical address map covering external XIP Flash (`0x10000000`), SRAM (`0x20000000`), APB/AHB peripherals (`0x40000000`), and core-local Single-Cycle I/O (`0xd0000000`). +- **Peripheral Register Definitions:** Exhaustive register layouts, reset states, and bitfield descriptions for all 52 peripherals: `IO_BANK0` (pin multiplexing & `FUNCSEL`), `PADS_BANK0` (electrical drive strength, pulls, and Schmitt triggers), `UART0`/`UART1`, `SPI0`/`SPI1`, `I2C0`/`I2C1`, `PWM`, `DMA`, `CLOCKS`, and `WATCHDOG`. +- **Programmable I/O (PIO):** Comprehensive architectural guide for the 3 on-chip PIO blocks (`PIO0`, `PIO1`, `PIO2`) with 12 state machines for deterministic, high-speed custom hardware protocols. +- **Fast GPIO Coprocessor:** Hardware specification for single-cycle atomic GPIO output and direction control via dedicated ARM coprocessor instructions (`mcrr p0`). +- **Bootrom & Hardware Security:** Covers the internal mask ROM boot flow, secure boot signature verification, SHA-256 cryptographic accelerator, and OTP key protection. + +### [Raspberry Pi Pico C/C++ SDK](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/datasheets/raspberry-pi-pico-c-sdk.pdf) +*Raspberry Pi Pico-series C/C++ SDK: Libraries and tools for development (821 pages)* +- **SDK Architecture & Build System:** CMake configuration, target library linking (`pico_stdlib`, `hardware_gpio`), compiler flags, and the UF2 binary packaging workflow. +- **Hardware Drivers (`hardware_*`):** Low-level driver API documentation: + - `hardware_gpio`: Functions like `gpio_init()`, `gpio_set_dir()`, `gpio_put()`, `gpio_get()`, and hardware pin interrupt handling. + - `hardware_uart`: Serial communication initialization (`uart_init()`, baud rate dividers, FIFO handling). + - `hardware_dma`: Channel configuration, transfer sizing, and background memory pacing. + - `hardware_clocks` & `hardware_resets`: PLL frequency configuration and peripheral unreset sequencing. +- **High-Level Runtimes (`pico_*`):** Standard I/O stream redirection (`stdio_init_all()` over UART/USB CDC), multicore core 1 launching (`pico_multicore`), sleep timers, and thread synchronization. +- **Hardware Structs (`hardware_structs`):** C struct definitions matching chip MMIO registers 1-to-1, used by reverse engineers to reconstruct decompiled peripheral accesses. + +### [Arm Cortex-M33 Technical Reference Manual](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/datasheets/arm_cortex_m33_trm_100230_0100_03_en.pdf) +*Arm® Cortex®-M33 Processor Technical Reference Manual (Doc ID: 100230_0100_03_en, 168 pages)* +- **Core Microarchitecture:** 3-stage in-order pipeline, Harvard bus architecture (instruction and data buses), integer execution core, and optional IEEE 754 single/double-precision floating-point unit (FPU). +- **Nested Vectored Interrupt Controller (NVIC):** Interrupt priorities, exception vector table mapping, low-latency interrupt entry, and hardware tail-chaining mechanics. +- **Memory Protection & TrustZone:** Memory Protection Unit (MPU) supporting up to 16 configurable regions, and Security Attribution Unit (SAU) for hardware-enforced Secure vs Non-secure domain isolation. +- **CoreSight Debug & Trace:** Debug Access Port (DAP) used by OpenOCD and the Raspberry Pi Debug Probe, Flash Patch and Breakpoint unit (FPB), Data Watchpoint and Trace (DWT), and Instrumentation Trace Macrocell (ITM). +- **Coprocessor Interface:** Bus architecture connecting hardware accelerators directly to the core (utilized by the RP2350 for single-cycle GPIO and math acceleration). + +### [Armv8-M Architecture Reference Manual (DDI0553B)](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/datasheets/DDI0553B_y_armv8m_arm.pdf) +*Armv8-M Architecture Reference Manual (Doc ID: DDI0553B, 2,149 pages)* +- **Authoritative Instruction Set Reference (Section C2.4, Pages 527–1454):** Complete alphabetical encyclopedia of every assembly instruction supported by the RP2350 Cortex-M33: + - Data processing: `MOV`, `MOVS`, `MVN`, `ADD`, `SUB`, `MUL`, `SDIV`, `UDIV`, `AND`, `ORR`, `EOR`, `BIC`. + - Memory load/store: `LDR`, `STR`, `LDRB`, `STRB`, `LDRH`, `STRH`, `LDRD`, `STRD`, `LDM`, `STM`, `PUSH`, `POP`. + - Control flow: `B`, `BL`, `BX`, `BLX`, `CBZ`, `CBNZ`, `TBB`, `TBH`. + - Coprocessor instructions: `MCR`, `MRC`, `MCRR`, `MRRC` (used by the RP2350 fast GPIO block). + - Floating-point instructions: `VMOV`, `VADD`, `VSUB`, `VMUL`, `VDIV`, `VCMP`, `VMRS`, `VMSR`. +- **Machine Opcode Encodings:** Exact 16-bit Thumb and 32-bit Thumb-2 binary bit patterns for every instruction, essential for binary patching and shellcode analysis. +- **Programmer's Model:** General-purpose registers (`r0`–`r12`), Stack Pointers (`SP`/`MSP`/`PSP`), Link Register (`LR`), Program Counter (`PC`), Status Registers (`APSR`, `IPSR`, `EPSR`, `xPSR`), and Special Registers (`CONTROL`, `PRIMASK`). +- **Exception Mechanics:** Hardware state stacking upon exception entry (`r0`-`r3`, `r12`, `lr`, `pc`, `xPSR`), EXC_RETURN values, and fault analysis (HardFault, MemManage, BusFault, UsageFault). + +### [Procedure Call Standard for the Arm Architecture (AAPCS32)](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/datasheets/aapcs32.pdf) +*Procedure Call Standard for the Arm® Architecture (AAPCS32 / ABI, 41 pages)* +- **Register Allocation Contract:** Defines caller and callee responsibilities across function calls: + - Parameter passing: `r0`–`r3` pass the first four 32-bit parameters (or 64-bit pairs like `r0`-`r1` and `r2`-`r3`). Additional parameters are pushed to the stack. + - Return values: `r0` returns 32-bit scalar values; `r0`-`r1` returns 64-bit integers and 64-bit IEEE 754 `double` floats. + - Scratch / Caller-Saved registers: `r0`–`r3`, `r12` (`IP`), and `r14` (`LR`) can be freely overwritten by called functions. + - Preserved / Callee-Saved registers: `r4`–`r11` must be preserved across function calls (saved via `push` in prologue and restored via `pop` in epilogue). +- **Floating-Point ABI:** Register allocation for hardware VFP registers (`s0`–`s15`, `d0`–`d7`) vs software floating-point library calls. +- **Data Alignment & Struct Packing:** Alignment rules (1, 2, 4, 8 bytes), structure member padding, and composite type memory layouts. + +### [ARM Application Note 132 (advnote132)](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/datasheets/advnote132.pdf) +*ABI Advisory Note – SP must be 8-byte aligned on entry to AAPCS-conforming functions (10 pages)* +- **The 8-Byte Stack Alignment Rule:** Mandates that the Stack Pointer (`SP`) must be aligned to an 8-byte (doubleword) boundary at all public function call interfaces. +- **Why Unaligned Stacks Fail:** 64-bit memory operations (`LDRD`, `STRD`) and double-precision floating-point operations require 8-byte alignment; misaligned stacks trigger hardware alignment faults or severe memory bus performance penalties. +- **Prologue Disassembly Deconstruction:** Explains why compiler-generated assembly frequently emits `push {r4, lr}` (saving two 32-bit registers) even when `r4` is completely unused—the extra register push serves as deliberate padding to keep `SP` strictly 8-byte aligned! + +
+ +# Syllabus + +## Week 1 +Introduction and Overview of Embedded Reverse Engineering: Ethics, Scoping, and Basic Concepts + +### Week 1 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK01/WEEK01-SLIDES.pdf) + +### Week 1 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK01/WEEK01.md) + +### Week 1a Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK01/WEEK01a.md) + +### Chapter 1: hello, world +This chapter covers the basics of setting up a dev environment and basic template firmware for the Pico 2 MCU in addition to printing hello, world. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 2: Debugging hello, world +This chapter covers the debugging of our firmware for the Pico 2 MCU hello, world program. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 2 +Hello, World - Debugging and Hacking Basics: Debugging and Hacking a Basic Program for the Pico 2 + +### Week 2 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK02/WEEK02-SLIDES.pdf) + +### Week 2 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK02/WEEK02.md) + +### Chapter 3: Hacking hello, world +This chapter covers the hacking of our firmware for the Pico 2 MCU hello, world program. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 3 +Embedded System Analysis: Understanding the RP2350 Architecture w/ Comprehensive Firmware Analysis + +### Week 3 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK03/WEEK03-SLIDES.pdf) + +### Week 3 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK03/WEEK03.md) + +### Ghidra Patching Tutorial [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK03/GHIDRA_PATCHING_TUTORIAL.md) + +### CTF-01 Instructions [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/0x0001b_ctf/CTF-01-I.md) + +### CTF-01 Rubric [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/0x0001b_ctf/CTF-01-R.md) + +### CTF-01 Solution [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/0x0001b_ctf/CTF-01-S.md) + +### CTF-01 BIN [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/0x0001b_ctf/CTF-01.bin) + +### CTF-01 UF2 [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/0x0001b_ctf/CTF-01.uf2) + +### Chapter 4: Embedded System Analysis +This chapter covers a comprehensive embedded system analysis reviewing parts of the RP2350 datasheet and helpful firmware analysis tools. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 4 +Variables in Embedded Systems: Debugging and Hacking Variables w/ GPIO Output Basics + +### Week 4 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK04/WEEK04-SLIDES.pdf) + +### Week 4 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK04/WEEK04.md) + +### Week 4a Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK04/WEEK04a.md) + +### Week 4-BN Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK04/WEEK04-BN.md) + +### RP2350 SVD [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK04/rp2350.svd) + +### Chapter 5: Intro To Variables +This chapter covers an introduction to variables as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 6: Debugging Intro To Variables +This chapter covers debugging an introduction to variables as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 7: Hacking Intro To Variables +This chapter covers hacking an introduction to variables as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 8: Uninitialized Variables +This chapter covers uninitialized variables as well as an intro to GPIO outputs as we blink an LED as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 9: Debugging Uninitialized Variables +This chapter covers debugging uninitialized variables as well as an intro to GPIO outputs as we blink an LED as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 10: Hacking Uninitialized Variables +This chapter covers hacking uninitialized variables as well as an intro to GPIO outputs as we blink an LED as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 5 +Integers and Floats in Embedded Systems: Debugging and Hacking Integers and Floats w/ Intermediate GPIO Output Assembler Analysis + +### Week 5 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK05/WEEK05-SLIDES.pdf) + +### Week 5 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK05/WEEK05.md) + +### Week 5a Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK05/WEEK05a.md) + +### Week 5-BN Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK05/WEEK05-BN.md) + +### Float/Hex Converter Tool [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK05/float_hex_converter.py) + +### Chapter 11: Integer Data Type +This chapter covers the integer data type in addition to a deeper assembler dive into GPIO outputs as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 12: Debugging Integer Data Type +This chapter covers debugging the integer data type in addition to a deeper assembler dive into GPIO outputs as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 13: Hacking Integer Data Type +This chapter covers hacking the integer data type in addition to a deeper assembler dive into GPIO outputs as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 14: Floating-Point Data Type +This chapter covers the floating-point data type as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 15: Debugging Floating-Point Data Type +This chapter covers debugging the floating-point data type as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 16: Hacking Floating-Point Data Type +This chapter covers hacking the floating-point data type as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 17: Double Floating-Point Data Type +This chapter covers the double floating-point data type as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 18: Debugging Double Floating-Point Data Type +This chapter covers debugging the double floating-point data type as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 19: Hacking Double Floating-Point Data Type +This chapter covers hacking the double floating-point data type as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 6 +Static Variables in Embedded Systems: Debugging and Hacking Static Variables w/ GPIO Input Basics + +### Classified Brief 0x01 [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/0x0011a_cb/CLASSIFIED-BRIEF-0x01.md) + +### Classified Brief 0x01 BIN [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/0x0011a_cb/0x0011a_cb.bin) + +### Classified Brief 0x01 UF2 [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/0x0011a_cb/0x0011a_cb.uf2) + +### Week 6 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK06/WEEK06-SLIDES.pdf) + +### Week 6 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK06/WEEK06.md) + +### Week 6-BN Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK06/WEEK06-BN.md) + +### Chapter 20: Static Variables +This chapter covers static variables as well as an intro to GPIO inputs as we work with push buttons as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 21: Debugging Static Variables +This chapter covers debugging static variables as well as an intro to GPIO inputs as we work with push buttons as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 22: Hacking Static Variables +This chapter covers hacking static variables as well as an intro to GPIO inputs as we work with push buttons as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 7 +Constants in Embedded Systems: Debugging and Hacking Constants w/ 1602 LCD I2C Basics + +### Week 7 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK07/WEEK07-SLIDES.pdf) + +### Week 7 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK07/WEEK07.md) + +### Week 7-BN Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK07/WEEK07-BN.md) + +### Chapter 23: Constants +This chapter covers constants as well as an intro to I2C as we work a 1602 LCD as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 24: Debugging Constants +This chapter covers debugging constants as well as an intro to I2C as we work a 1602 LCD as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 25: Hacking Constants +This chapter covers hacking constants as well as an intro to I2C as we work a 1602 LCD as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 8 +### Midterm Exam + +### CTF-02 Instructions [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/0x0017a_ctf/CTF-02-I.md) + +### CTF-02 Rubric [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/0x0017a_ctf/CTF-02-R.md) + +### CTF-02 Solution [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/0x0017a_ctf/CTF-02-S.md) + +### CTF-02 BIN [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/0x0017a_ctf/CTF-02.bin) + +### CTF-02 UF2 [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/0x0017a_ctf/CTF-02.uf2) + +## Week 9 +Operators in Embedded Systems: Debugging and Hacking Operators w/ DHT11 Temperature & Humidity Sensor Single-Wire Protocol Basics + +### Week 9 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK09/WEEK09-SLIDES.pdf) + +### Week 9 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK09/WEEK09.md) + +### Week 9-BN Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK09/WEEK09-BN.md) + +### Chapter 26: Operators +This chapter covers operators as well as an intro to single-wire protocol as we work a DHT11 temperature and humidity sensor as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 27: Debugging Operators +This chapter covers debugging operators as well as an intro to single-wire protocol as we work a DHT11 temperature and humidity sensor as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 28: Hacking Operators +This chapter covers hacking operators as well as an intro to single-wire protocol as we work a DHT11 temperature and humidity sensor as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 10 +Conditionals in Embedded Systems: Debugging and Hacking Static & Dynamic Conditionals w/ SG90 Servo Motor PWM Basics + +### Week 10 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK10/WEEK10-SLIDES.pdf) + +### Week 10 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK10/WEEK10.md) + +### Week 10-BN Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK10/WEEK10-BN.md) + +### Chapter 29: Static Conditionals +This chapter covers static conditionals as well as an intro to PWM as we work a SG90 servo motor as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 30: Debugging Static Conditionals +This chapter covers debugging static conditionals as well as an intro to PWM as we work a SG90 servo motor as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 31: Hacking Static Conditionals +This chapter covers hacking static conditionals as well as an intro to PWM as we work a SG90 servo motor as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 32: Dynamic Conditionals +This chapter covers dynamic conditionals as well as additional PWM examples as we work a SG90 servo motor as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 33: Debugging Dynamic Conditionals +This chapter covers debugging dynamic conditionals as well as additional PWM examples as we work a SG90 servo motor as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 34: Hacking Dynamic Conditionals +This chapter covers hacking dynamic conditionals as well as additional PWM examples as we work a SG90 servo motor as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 11 +Structures and Functions in Embedded Systems: Debugging and Hacking w/ IR Remote Control and NEC Protocol Basics + +### Week 11 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK11/WEEK11-SLIDES.pdf) + +### Week 11 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK11/WEEK11.md) + +### Week 11-BN Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK11/WEEK11-BN.md) + +### Chapter 35: Structures +This chapter covers structures as well as an intro to infrared basics as we work a infrared receiver and infrared remote controller as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 36: Debugging Structures +This chapter covers debugging structures as well as an intro to infrared basics as we work a infrared receiver and infrared remote controller as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 37: Hacking Structures +This chapter covers hacking structures as well as an intro to infrared basics as we work a infrared receiver and infrared remote controller as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 38: Functions, w/ Param, w/ Return +This chapter covers functions, w/ params and w/ a return value as well as additional infrared examples as we work a infrared receiver and infrared remote controller it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 39: Debugging Functions, w/ Param, w/ Return +This chapter covers debugging functions, w/ params and w/ a return value as well as additional infrared examples as we work a infrared receiver and infrared remote controller as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 40: Hacking Functions, w/ Param, w/ Return +This chapter covers hacking functions, w/ params and w/ a return value as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/textbook/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 12 +Unknown Firmware Debugging and Hacking + +## Week 13 +Final Review – Embedded Debugging and Hacking Techniques w/ Advanced Firmware Analysis Q&A + +## Week 14 +### Final Projects + +### Final Project Option 1: The InfuSafe Pro Incident +In the aftermath of a catastrophic medical device failure, you are thrust into the role of an FDA forensic investigator facing an impossible crisis: 23 patients dead, 100 million recalled insulin pumps sitting in warehouses worldwide, and 2.3 million lives hanging in the balance all while the only evidence remaining is raw binary firmware after a rogue engineer destroyed every line of source code before fleeing to Montenegro. Armed only with GDB, Ghidra, and the reverse engineering skills honed over the first seven weeks of this course, you must excavate the truth from machine code, identify the lethal bugs spawned by an AI code generator called "OopsieGPT," and determine whether these devices can be salvaged to save millions in underserved communities or if $4.7 billion in humanitarian medical technology must be incinerated. This is not a simulation; this is triage at the intersection of embedded systems security and human survival. + +### Final Project Option 2: Operation Dark Eclipse +Forty-two stories beneath frozen tundra, a shadow intelligence alliance called Dark Eyes operates centrifuges enriching weapons-grade material for a first strike against Washington, D.C. and Agent NIGHTINGALE gave her life to extract the single firmware file that now sits before you. Conventional warfare cannot reach this fortress buried beneath rock and concrete, but you can: as the architect of a precision cyber weapon in the tradition of Stuxnet, you must reverse engineer the RP2350-based centrifuge controller, craft binary patches that double the spin speed while falsifying every sensor readout to show nominal operation, and execute the sabotage that will cascade-destroy their enrichment program and set their nuclear ambitions back a decade. Every skill from the entire semester ARM assembly, Ghidra analysis, IEEE-754 floating-point manipulation, branch modification, log desynchronization converges in this final mission. Agent NIGHTINGALE's seven-year-old daughter still watches the driveway, waiting for a mother who will never return. Honor that sacrifice. Complete the mission. Do not fail. + +
+ +# Supplemental Material (Beyond the Scope of the Course) + +## Pico 2 IoT Projects & CTFs + +### Act I of OPERATION COLD IRON [HERE](https://github.com/mytechnotalent/cold-chain-monitor) + +### Act I of OPERATION COLD IRON CTF [HERE](https://github.com/mytechnotalent/CTF_cold-chain-monitor) + +### Act II of OPERATION COLD IRON [HERE](https://github.com/mytechnotalent/access-gate) + +### Act II of OPERATION COLD IRON CTF [HERE](https://github.com/mytechnotalent/CTF_access-gate) + +### Act III of OPERATION COLD IRON [HERE](https://github.com/mytechnotalent/pipeline-valve-controller) + +### Act III of OPERATION COLD IRON CTF [HERE](https://github.com/mytechnotalent/CTF_pipeline-valve-controller) + +### Act IV of OPERATION COLD IRON [HERE](https://github.com/mytechnotalent/hvac-automation-node) + +### Act IV of OPERATION COLD IRON CTF [HERE](https://github.com/mytechnotalent/CTF_hvac-automation-node) + +### Act V of OPERATION COLD IRON [HERE](https://github.com/mytechnotalent/industrial-tamper-system) + +### Act V of OPERATION COLD IRON CTF [HERE](https://github.com/mytechnotalent/CTF_industrial-tamper-system) + +### Act VI of OPERATION COLD IRON [HERE](https://github.com/mytechnotalent/smart-logistics-dropbox) + +### Act VI of OPERATION COLD IRON CTF [HERE](https://github.com/mytechnotalent/CTF_smart-logistics-dropbox) + +### Act VII of OPERATION COLD IRON [HERE](https://github.com/mytechnotalent/factory-andon-station) + +### Act VII of OPERATION COLD IRON CTF [HERE](https://github.com/mytechnotalent/CTF_factory-andon-station) + +### Act VIII of OPERATION COLD IRON [HERE](https://github.com/mytechnotalent/datacenter-vent-controller) + +### Act VIII of OPERATION COLD IRON CTF [HERE](https://github.com/mytechnotalent/CTF_datacenter-vent-controller) + +### Act IX of OPERATION COLD IRON [HERE](https://github.com/mytechnotalent/smart-parking-barrier) + +### Act IX of OPERATION COLD IRON CTF [HERE](https://github.com/mytechnotalent/CTF_smart-parking-barrier) + +### Act X of OPERATION COLD IRON [HERE](https://github.com/mytechnotalent/chemical-warning-terminal) + +### Act X of OPERATION COLD IRON CTF [HERE](https://github.com/mytechnotalent/CTF_chemical-warning-terminal) + +
+ +## Pi 4B/5 Embedded Linux C IoT Project & CTF + +### OPERATION TELESCREEN [HERE](https://github.com/mytechnotalent/telescreen) + +### OPERATION TELESCREEN CTF [HERE](https://github.com/mytechnotalent/CTF_telescreen) + +
+ +## Pico 2 W C MeshCore Project + +### MeshCore Bare RP2350 [HERE](https://github.com/mytechnotalent/meshcore-bare-rp2350) + +
+ +## Pico 2 Rust Tutorial + +### Chapter 1: What Is Embedded Rust? +This lesson will teach what embedded Rust is within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-01.md) to read the lesson and see the code. + +### Chapter 2: Number Systems and Memory +This lesson will teach number systems and memory within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-02.md) to read the lesson and see the code. + +### Chapter 3: Rust Essentials +This lesson will teach the Rust essentials within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-03.md) to read the lesson and see the code. + +### Chapter 4: Ownership, Borrowing, and Lifetimes +This lesson will teach ownership, borrowing and lifetimes within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-04.md) to read the lesson and see the code. + +### Chapter 5: Structs, Enums, and Pattern Matching +This lesson will teach structs, enums and pattern matching within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-05.md) to read the lesson and see the code. + +### Chapter 6: Traits and Generics +This lesson will teach traits and generics within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-06.md) to read the lesson and see the code. + +### Chapter 7: no_std and no_main +This lesson will teach no_std and no_main within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-07.md) to read the lesson and see the code. + +### Chapter 8: Cargo, Targets, and the Toolchain +This lesson will teach cargo, targets and the toolchain within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-08.md) to read the lesson and see the code. + +### Chapter 9: memory.x and the Linker Script +This lesson will teach the memory.x linker script within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-09.md) to read the lesson and see the code. + +### Chapter 10: build.rs, Makefile, and Flashing +This lesson will teach the build.rs, Makefile and flashing process within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-10.md) to read the lesson and see the code. + +### Chapter 11: Memory-Mapped I/O +This lesson will teach memory-mapped I/O within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-11.md) to read the lesson and see the code. + +### Chapter 12: Real-Time and Concurrency +This lesson will teach real-time and concurrency within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-12.md) to read the lesson and see the code. + +### Chapter 13: Futures and async/await +This lesson will teach futures and async/await within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-13.md) to read the lesson and see the code. + +### Chapter 14: The Embassy Executor +This lesson will teach the Embassy executor within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-14.md) to read the lesson and see the code. + +### Chapter 15: embassy-time +This lesson will teach embassy-time within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-15.md) to read the lesson and see the code. + +### Chapter 16: The embassy-rp HAL +This lesson will teach the embassy-rp HAL within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-16.md) to read the lesson and see the code. + +### Chapter 17: GPIO with embassy-rp +This lesson will teach GPIO with embassy-rp within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-17.md) to read the lesson and see the code. + +### Chapter 18: Driver Architecture +This lesson will teach the driver architecture within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-18.md) to read the lesson and see the code. + +### Chapter 19: config.rs — Blink Configuration +This lesson will teach the config.rs blink configuration within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-19.md) to read the lesson and see the code. + +### Chapter 20: led.rs — The LED State Machine +This lesson will teach the led.rs LED state machine within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-20.md) to read the lesson and see the code. + +### Chapter 21: main.rs — The Async Blink Loop +This lesson will teach the main.rs async blink loop within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-21.md) to read the lesson and see the code. + +### Chapter 22: Button Hardware and Debouncing +This lesson will teach button hardware and debouncing within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-22.md) to read the lesson and see the code. + +### Chapter 23: button.rs — The Button Controller +This lesson will teach the button.rs button controller within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-23.md) to read the lesson and see the code. + +### Chapter 24: main.rs — The Button Polling Loop +This lesson will teach the main.rs button polling loop within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-24.md) to read the lesson and see the code. + +### Chapter 25: Host Testing with cargo test +This lesson will teach host testing with cargo test within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-25.md) to read the lesson and see the code. + +### Chapter 26: UART Fundamentals and the Echo Protocol +This lesson will teach UART fundamentals and the echo protocol within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-26.md) to read the lesson and see the code. + +### Chapter 27: uart.rs — The Echo State Machine +This lesson will teach the uart.rs echo state machine within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-27.md) to read the lesson and see the code. + +### Chapter 28: Interrupts and DMA — Interrupt-Driven UART +This lesson will teach interrupts and DMA interrupt-driven UART within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-28.md) to read the lesson and see the code. + +### Chapter 29: main.rs — The UART Echo Loop +This lesson will teach the main.rs UART echo loop within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-29.md) to read the lesson and see the code. + +### Chapter 30: The Complete Integration +This lesson will teach the complete integration within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-Rust-Tutorial/blob/main/CHAPTER-30.md) to read the lesson and see the code. + +
+ +## Pico 2 Rust Drivers + +### UART Driver [HERE](https://github.com/mytechnotalent/RP2350_Rust_UART_Driver) + +### Blink Driver [HERE](https://github.com/mytechnotalent/RP2350_Rust_Blink_Driver) + +### Button Driver [HERE](https://github.com/mytechnotalent/RP2350_Rust_Button_Driver) + +
+ +## Pico 2 ARM Assembler Tutorial + +### Chapter 1: What Is a Computer? +This lesson will teach you what is a computer with the fundamental model of computation that every computer shares with an intro to the RP2350 and ARM Cortex-M33. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-01.md) to read the lesson and see the code. + +### Chapter 2: Number Systems — Binary, Hexadecimal, and Decimal +This lesson will teach the basics of the three main number systems which are decimal, binary and hexadecimal. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-02.md) to read the lesson and see the code. + +### Chapter 3: Memory — Addresses, Bytes, Words, and Endianness +This lesson will teach the basics addresses, bytes, words and endianness within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-03.md) to read the lesson and see the code. + +### Chapter 4: What Is a Register? +This lesson will teach the general purpose registers within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-04.md) to read the lesson and see the code. + +### Chapter 5: Load-Store Architecture — How ARM Accesses Memory +This lesson will teach how ARM accesses memory with load and store architecture within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-05.md) to read the lesson and see the code. + +### Chapter 6: The Fetch-Decode-Execute Cycle in Detail +This lesson will teach the fetch and decode cycle in more detail within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-06.md) to read the lesson and see the code. + +### Chapter 7: ARM Cortex-M33 ISA Overview +This lesson will teach the ARM Cortex-M33 ISA overview within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-07.md) to read the lesson and see the code. + +### Chapter 8: ARM Immediate and Move Instructions +This lesson will teach ARM immediate and move instructions within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-08.md) to read the lesson and see the code. + +### Chapter 9: ARM Arithmetic and Logic Instructions +This lesson will teach ARM arithmetic and logic instructions within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-09.md) to read the lesson and see the code. + +### Chapter 10: ARM Memory Access Instructions +This lesson will teach ARM memory access instructions within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-10.md) to read the lesson and see the code. + +### Chapter 11: ARM Branch Instructions +This lesson will teach ARM branch instructions within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-11.md) to read the lesson and see the code. + +### Chapter 12: ARM Calls, Returns, and the Stack Frame +This lesson will teach ARM calls, returns and the stack frame within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-12.md) to read the lesson and see the code. + +### Chapter 13: Assembler Directives +This lesson will teach assembler directives within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-13.md) to read the lesson and see the code. + +### Chapter 14: Labels, Symbols, and the Symbol Table +This lesson will teach labels, symbols and the symbol table within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-14.md) to read the lesson and see the code. + +### Chapter 15: Sections, Memory Layout, and the Linker Script +This lesson will teach sections, memory layout, and the linker script within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-15.md) to read the lesson and see the code. + +### Chapter 16: System Registers and Coprocessor Interface +This lesson will teach system registers and coprocessor interface within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-16.md) to read the lesson and see the code. + +### Chapter 17: Bit Manipulation Patterns +This lesson will teach bit manipulation patterns within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-17.md) to read the lesson and see the code. + +### Chapter 18: RP2350 Hardware Architecture +This lesson will teach RP2350 hardware architecture within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-18.md) to read the lesson and see the code. + +### Chapter 19: The Linker Script +This lesson will teach the linker script within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-19.md) to read the lesson and see the code. + +### Chapter 20: The Build System +This lesson will teach the build system within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-20.md) to read the lesson and see the code. + +### Chapter 21: image_def.s — The PICOBIN Boot Block +This lesson will teach the PICOBIN boot block within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-21.md) to read the lesson and see the code. + +### Chapter 22: constants.s — Memory Addresses and Constants +This lesson will teach memory addresses and constants within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-22.md) to read the lesson and see the code. + +### Chapter 23: vector_table.s and stack.s — Boot Foundation +This lesson will teach the boot foundation within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-23.md) to read the lesson and see the code. + +### Chapter 24: reset_handler.s — The Boot Sequence +This lesson will teach the boot sequence within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-24.md) to read the lesson and see the code. + +### Chapter 25: xosc.s — Crystal Oscillator and Clock Configuration +This lesson will teach the crystal oscillator and clock configuration within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-25.md) to read the lesson and see the code. + +### Chapter 26: reset.s — Releasing Peripherals from Reset +This lesson will teach releasing peripherals from reset within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-26.md) to read the lesson and see the code. + +### Chapter 27: gpio.s Part 1 — GPIO_Config +This lesson will teach GPIO config within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-27.md) to read the lesson and see the code. + +### Chapter 28: gpio.s Part 2, delay.s, and coprocessor.s — Output Control and Timing +This lesson will teach output control and timing within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-28.md) to read the lesson and see the code. + +### Chapter 29: main.s — The Blink Loop +This lesson will teach the blink loop within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-29.md) to read the lesson and see the code. + +### Chapter 30: Full Integration — From Source to Blinking LED +This lesson will teach the full source to blinking LED within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-ARM-ASM-Tutorial/blob/main/CHAPTER-30.md) to read the lesson and see the code. + +
+ +## Pico 2 ARM Assembler Drivers + +### UART Driver [HERE](https://github.com/mytechnotalent/RP2350_UART_Driver) + +### Blink Driver [HERE](https://github.com/mytechnotalent/RP2350_Blink_Driver) + +### Button Driver [HERE](https://github.com/mytechnotalent/RP2350_Button_Driver) + +
+ +## Pico 2 RISC-V Assembler Tutorial + +### Chapter 1: What Is a Computer? +This lesson will teach you what is a computer with the fundamental model of computation that every computer shares with an intro to the RP2350 and RISC-V. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-01.md) to read the lesson and see the code. + +### Chapter 2: Number Systems — Binary, Hexadecimal, and Decimal +This lesson will teach the basics of the three main number systems which are decimal, binary and hexadecimal. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-02.md) to read the lesson and see the code. + +### Chapter 3: Memory — Addresses, Bytes, Words, and Endianness +This lesson will teach the basics of addresses, bytes, words and endianness within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-03.md) to read the lesson and see the code. + +### Chapter 4: What Is a Register? +This lesson will teach the general purpose registers within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-04.md) to read the lesson and see the code. + +### Chapter 5: Load-Store Architecture — How RISC-V Accesses Memory +This lesson will teach how RISC-V accesses memory with load and store architecture within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-05.md) to read the lesson and see the code. + +### Chapter 6: The Fetch-Decode-Execute Cycle in Detail +This lesson will teach the fetch, decode and execute cycle in detail within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-06.md) to read the lesson and see the code. + +### Chapter 7: RISC-V Hazard3 ISA Overview +This lesson will teach the RISC-V Hazard3 ISA overview within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-07.md) to read the lesson and see the code. + +### Chapter 8: RISC-V Immediate and Upper-Immediate Instructions +This lesson will teach RISC-V immediate and upper-immediate instructions within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-08.md) to read the lesson and see the code. + +### Chapter 9: RISC-V Arithmetic and Logic Instructions +This lesson will teach RISC-V arithmetic and logic instructions within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-09.md) to read the lesson and see the code. + +### Chapter 10: RISC-V Memory Access — Load and Store Deep Dive +This lesson will teach RISC-V memory access load and store instructions in deep detail within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-10.md) to read the lesson and see the code. + +### Chapter 11: RISC-V Branch Instructions +This lesson will teach RISC-V branch instructions within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-11.md) to read the lesson and see the code. + +### Chapter 12: RISC-V Jumps, Calls, and Returns +This lesson will teach RISC-V jumps, calls and returns within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-12.md) to read the lesson and see the code. + +### Chapter 13: RISC-V Pseudo-Instructions +This lesson will teach RISC-V pseudo-instructions within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-13.md) to read the lesson and see the code. + +### Chapter 14: Assembler Directives +This lesson will teach assembler directives within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-14.md) to read the lesson and see the code. + +### Chapter 15: Calling Convention and Stack Frames +This lesson will teach the calling convention and stack frames within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-15.md) to read the lesson and see the code. + +### Chapter 16: Bitwise Operations for Hardware Programming +This lesson will teach bitwise operations for hardware programming within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-16.md) to read the lesson and see the code. + +### Chapter 17: Memory-Mapped I/O +This lesson will teach memory-mapped I/O within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-17.md) to read the lesson and see the code. + +### Chapter 18: The RP2350 — Architecture and Hardware +This lesson will teach the RP2350 architecture and hardware within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-18.md) to read the lesson and see the code. + +### Chapter 19: The Linker Script — Placing Code in Memory +This lesson will teach the linker script and how code is placed in memory within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-19.md) to read the lesson and see the code. + +### Chapter 20: The Build Pipeline — From Assembly to Flashable Binary +This lesson will teach the build pipeline from assembly to flashable binary within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-20.md) to read the lesson and see the code. + +### Chapter 21: Boot Metadata — image_def.s +This lesson will teach the image_def.s boot metadata within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-21.md) to read the lesson and see the code. + +### Chapter 22: The Constants File — constants.s +This lesson will teach the constants.s file within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-22.md) to read the lesson and see the code. + +### Chapter 23: Stack and Vector Table — stack.s and vector_table.s +This lesson will teach the stack and vector table within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-23.md) to read the lesson and see the code. + +### Chapter 24: Boot Sequence — reset_handler.s +This lesson will teach the reset_handler.s boot sequence within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-24.md) to read the lesson and see the code. + +### Chapter 25: Oscillator Initialization — xosc.s +This lesson will teach the xosc.s oscillator initialization within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-25.md) to read the lesson and see the code. + +### Chapter 26: Reset Controller — reset.s +This lesson will teach the reset.s reset controller within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-26.md) to read the lesson and see the code. + +### Chapter 27: GPIO Configuration — gpio.s Part 1 +This lesson will teach the gpio.s GPIO configuration part 1 within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-27.md) to read the lesson and see the code. + +### Chapter 28: GPIO Set/Clear, Delay, and Coprocessor — gpio.s Part 2, delay.s, coprocessor.s +This lesson will teach the gpio.s GPIO set/clear, delay.s and coprocessor.s within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-28.md) to read the lesson and see the code. + +### Chapter 29: Application Entry Point — main.s +This lesson will teach the main.s application entry point within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-29.md) to read the lesson and see the code. + +### Chapter 30: Full Integration — Build, Flash, Wire, and Test +This lesson will teach the full build, flash, wire and test integration within the MCU. + +-> Click [HERE](https://github.com/mytechnotalent/RP2350-RISCV-ASM-Tutorial/blob/main/CHAPTER-30.md) to read the lesson and see the code. + +
+ +## Pico 2 RISC-V Assembler Drivers + +### UART Driver [HERE](https://github.com/mytechnotalent/RP2350_UART_Driver_RISCV) + +### Blink Driver [HERE](https://github.com/mytechnotalent/RP2350_Blink_Driver_RISCV) + +### Button Driver [HERE](https://github.com/mytechnotalent/RP2350_Button_Driver_RISCV) + +
+ +## Pico 2 Developer Projects + +### LED Chase and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-01-blink) + +### Traffic Light Phases and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-02-traffic-light) + +### Hardware PWM Breathing and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-03-led-pwm-breathing) + +### 1602 I2C LCD Text and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-04-lcd-hello) + +### Live LCD Counter and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-05-lcd-live) + +### SG90 Servo Sweep and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-06-servo-sweep) + +### SG90 Servo Position and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-07-servo-position) + +### SG90 Gauge Needle and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-08-servo-gauge) + +### Tactile Button Poll and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-09-button-poll) + +### Debounced Button Edges and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-10-button-debounce) + +### Short, Long, and Double Press Events and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-11-button-events) + +### DHT11 Temperature and Humidity Read and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-12-dht11-read) + +### DHT11 Reading on the 1602 I2C LCD and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-13-dht11-lcd) + +### DHT11 Comfort LEDs and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-14-dht11-leds) + +### VS1838B NEC Decode and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-15-ir-decode) + +### Remote Key Actions on the LEDs and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-16-ir-keys) + +### Remote Keypad Digits on the LEDs and 1602 I2C LCD and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-17-ir-keypad) + +### Remote Key Lamp Selection and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-18-ir-control-led) + +### Remote Key Preset Angles on the SG90 Servo and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-19-ir-control-servo) + +### Remote Key Status Pages on the 1602 I2C LCD and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-20-ir-lcd-status) + +### Two Way Token Echo and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-21-lora-echo) + +### DHT11 Telemetry and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-22-lora-telemetry) + +### Remote Command LEDs and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-23-lora-command) + +### ACK Timeout and Retry and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-24-lora-ack) + +### Address Filter and Sender Attribution and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-25-lora-addressing) + +### Signal Quality on the LCD and LEDs and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-26-lora-rssi) + +### Buffered Readings and Flush on Link Recovery and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-27-lora-store-forward) + +### Menu Navigation with the GP15 Button and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-28-lcd-menu-button) + +### Menu Navigation with the IR Remote and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-29-lcd-menu-remote) + +### Button and Remote Status Pages on the 1602 I2C LCD and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-30-status-pages) + +### Hysteresis Vent Control with an IR Setpoint and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-31-thermostat) + +### Latched Band Breach, Door Latch Servo, and an Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-32-cold-chain-alarm) + +### Buffered Readings, Batched Flush, and an Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-33-data-logger) + +### Gateway Command, Servo Position, and an Authenticated ACK [HERE](https://github.com/mytechnotalent/picokit-34-remote-actuator) + +### Latching Alarm LED, Local Acknowledge, and an Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-35-annunciator-ack) + +### Timed Vent and LED Actions with an Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-36-scheduler) + +### Random-Light Reaction Game, Button Time, and an Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-37-reaction-timer) + +### Timed Intersection Phases, a Pedestrian Request, and an Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-38-traffic-controller) + +### Sealed Telemetry, Loopback Verify, and a Forged-Frame Reject [HERE](https://github.com/mytechnotalent/picokit-39-authenticated-telemetry) + +### Per-Node Monotonic Sequence, Replay Window, and a Rejected Replay [HERE](https://github.com/mytechnotalent/picokit-40-anti-replay) + +### Per-Device Key from a Device ID and Salt, Two Keys Proven Different [HERE](https://github.com/mytechnotalent/picokit-41-provisioning) + +### BLAKE2b Hash-Chained Event Log and Recompute on Read [HERE](https://github.com/mytechnotalent/picokit-42-tamper-log) + +### Debug Probe Breakpoints and SWD Variable Reads [HERE](https://github.com/mytechnotalent/picokit-43-swd-breakpoints) + +### Debug Probe Data Watchpoint on a Status Variable [HERE](https://github.com/mytechnotalent/picokit-44-swd-watchpoints) + +### Controlled Fault and Cortex-M33 Fault Status Registers [HERE](https://github.com/mytechnotalent/picokit-45-fault-analysis) + +### ELF and UF2 Inspection of a Known Constant [HERE](https://github.com/mytechnotalent/picokit-46-binary-recon) + +### Two-Node Mesh Relay and Authenticated Heartbeat [HERE](https://github.com/mytechnotalent/picokit-47-two-node-mesh) + +### Sensor Node and Charted Gateway [HERE](https://github.com/mytechnotalent/picokit-48-gateway-dashboard) + +### Fleet and Config Table with Multi-Node Gateway [HERE](https://github.com/mytechnotalent/picokit-49-fleet) + +### Full Peripheral Capstone and Authenticated Telemetry [HERE](https://github.com/mytechnotalent/picokit-50-finale) + +
+ +# License +[Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) diff --git a/WEEK01/WEEK01-SLIDES.pdf b/WEEK01/WEEK01-SLIDES.pdf new file mode 100644 index 0000000..935ca43 Binary files /dev/null and b/WEEK01/WEEK01-SLIDES.pdf differ diff --git a/WEEK01/WEEK01.md b/WEEK01/WEEK01.md new file mode 100644 index 0000000..c42b57e --- /dev/null +++ b/WEEK01/WEEK01.md @@ -0,0 +1,755 @@ +# Week 1: Introduction and Overview of Embedded Reverse Engineering: Ethics, Scoping, and Basic Concepts + +*** +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +By the end of this week, you will be able to: + +- Understand what a microcontroller is and how it works +- Know the basic registers of the ARM Cortex-M33 processor +- Understand memory layout (Flash vs RAM) and why it matters +- Understand how the stack works in embedded systems +- Set up and connect GDB to your Pico 2 for debugging +- Use Ghidra for static analysis of your binary +- Read basic ARM assembly instructions and understand what they do + +--- + +## Part 1: Understanding the Basics + +### What is a Microcontroller? + +Think of a microcontroller as a tiny computer on a single chip. Just like your laptop has a processor, memory, and storage, a microcontroller has all of these packed into one small chip. The **RP2350** is the microcontroller chip that powers the **Raspberry Pi Pico 2**. + +### What is the ARM Cortex-M33? + +The RP2350 has two "brains" inside it - we call these **cores**. One brain uses ARM Cortex-M33 instructions, and the other can use RISC-V instructions. In this course, we'll focus on the **ARM Cortex-M33** core because it's more commonly used in the industry. + +### What is Reverse Engineering? + +Reverse engineering is like being a detective for code. Instead of writing code and compiling it, we take compiled code (the 1s and 0s that the computer actually runs) and figure out what it does. This is useful for: + +- Understanding how things work +- Finding bugs or security issues +- Learning how software interacts with hardware + +--- + +## Part 2: Understanding Processor Registers + +### What is a Register? + +A **register** is like a tiny, super-fast storage box inside the processor. The processor uses registers to hold numbers while it's doing calculations. Think of them like the short-term memory your brain uses when doing math in your head. + +### The ARM Cortex-M33 Registers + +The ARM Cortex-M33 has several important registers: + +| Register | Also Called | Purpose | +| ------------ | -------------------- | ------------------------------------------- | +| `r0` - `r12` | General Purpose | Store numbers, pass data between functions | +| `r13` | SP (Stack Pointer) | Keeps track of where we are in the stack | +| `r14` | LR (Link Register) | Remembers where to go back after a function | +| `r15` | PC (Program Counter) | Points to the next instruction to run | + +##### General Purpose Registers (`r0` - `r12`) + +These 13 registers are your "scratch paper." When the processor needs to add two numbers, subtract, or do any calculation, it uses these registers to hold the values. + +**Example:** If you want to add 5 + 3: + +1. Put 5 in `r0` +2. Put 3 in `r1` +3. Add them and store the result (8) in `r2` + +##### The Stack Pointer (`r13` / SP) + +The **stack** is a special area of memory that works like a stack of plates: + +- When you add something, you put it on top (called a **PUSH**) +- When you remove something, you take it from the top (called a **POP**) + +The Stack Pointer always points to the top of this stack. On ARM systems, the stack **grows downward** in memory. This means when you push something onto the stack, the address number gets smaller! + +The two Arm ABI documents we verified give the formal proof for these rules. In AAPCS32, page 17 defines the core register roles used by the base procedure call standard: `r13` is `SP`, `r14` is `LR`, `r15` is `PC`, `r0`-`r3` are argument and scratch registers, and `r4`-`r11` are the longer-lived variable registers. In Advisory Note 132, page 7 states that `SP` must be aligned to a multiple of 8 at every conforming call site and must already be 8-byte aligned when control first enters conforming code. That is why compiler-generated prologues often push an even number of registers, such as `push {r3, lr}`, to preserve both saved state and the required ABI stack alignment. + +``` +Higher Memory Address (0x20082000) ++------------------+ +| | ← Stack starts here (empty) ++------------------+ +| Pushed Item 1 | ← SP points here after 1 push ++------------------+ +| Pushed Item 2 | ← SP points here after 2 pushes ++------------------+ +Lower Memory Address (0x20081FF8) +``` + +##### The Link Register (`r14` / LR) + +When you call a function, the processor needs to remember where to come back to. The Link Register stores this "return address." + +**Example:** +``` +main() calls print_hello() + ↓ +LR = address right after the call in main() + ↓ +print_hello() runs + ↓ +print_hello() finishes, looks at LR + ↓ +Jumps back to main() at the address stored in LR +``` + +##### The Program Counter (`r15` / PC) + +The Program Counter always points to the **next instruction** the processor will execute. It's like your finger following along as you read a book - it always points to where you are. + +--- + +## Part 3: Understanding Memory Layout + +### XIP - Execute In Place + +The RP2350 uses something called **XIP (Execute In Place)**. This means the processor can run code directly from the flash memory (where your program is stored) without copying it to RAM first. + +**Key Memory Address:** `0x10000000` + +This is where your program code starts in flash memory. Remember this address - we'll use it a lot! + +### Memory Map Overview + +``` ++-------------------------------------+ +| Flash Memory (XIP) | +| Starts at: 0x10000000 | +| Contains: Your program code | ++-------------------------------------+ +| RAM | +| Starts at: 0x20000000 | +| Contains: Stack, Heap, Variables | ++-------------------------------------+ +``` + +### Stack vs Heap + +| Stack | Heap | +| ---------------------------------------- | ---------------------------------- | +| Automatic memory management | Manual memory management | +| Fast | Slower | +| Limited size | More flexible size | +| Used for function calls, local variables | Used for dynamic memory allocation | +| Grows downward | Grows upward | + +--- + +## Part 3.5: Reviewing Our Hello World Code + +Before we start debugging, let's understand the code we'll be working with. Here's our `0x0001_hello-world.c` program: + +```c +#include +#include "pico/stdlib.h" + +int main(void) { + stdio_init_all(); + + while (true) + printf("hello, world\r\n"); +} +``` + +### Breaking Down the Code + +##### The Includes + +```c +#include +#include "pico/stdlib.h" +``` + +- **``** - This is the standard input/output library. It gives us access to the `printf()` function that lets us print text. +- **`"pico/stdlib.h"`** - This is the Pico SDK's standard library. It provides essential functions for working with the Raspberry Pi Pico hardware. + +##### The Main Function + +```c +int main(void) { +``` + +Every C program starts running from the `main()` function. The `void` means it takes no arguments, and `int` means it returns an integer (though our program never actually returns). + +##### Initializing Standard I/O + +```c +stdio_init_all(); +``` + +This function initializes all the standard I/O (input/output) for the Pico. It sets up: + +- **USB CDC** (so you can see output when connected to a computer via USB) +- **UART** (serial communication pins) + +Without this line, `printf()` wouldn't have anywhere to send its output! + +##### The Infinite Loop + +```c +while (true) + printf("hello, world\r\n"); +``` + +- **`while (true)`** - This creates an infinite loop. The program will keep running forever (or until you reset/power off the Pico). +- **`printf("hello, world\r\n")`** - This prints the text "hello, world" followed by a carriage return (`\r`) and newline (`\n`). + +> Tip: **Why `\r\n` instead of just `\n`?** +> +> In embedded systems, we often use both carriage return (`\r`) and newline (`\n`) together. The `\r` moves the cursor back to the beginning of the line, and `\n` moves to the next line. This ensures proper display across different terminal programs. + +### What Happens When This Runs? + +1. **Power on** - The Pico boots up and starts executing code from flash memory +2. **`stdio_init_all()`** - Sets up USB and/or UART for communication +3. **Infinite loop begins** - The program enters the `while(true)` loop +4. **Print forever** - "hello, world" is sent over and over as fast as possible + +### Why This Code is Perfect for Learning + +This simple program is ideal for reverse engineering practice because: + +- It has a clear, recognizable function call (`printf`) +- It has an infinite loop we can observe +- It's small enough to understand completely +- It demonstrates real hardware interaction (USB/UART output) + +When we debug this code, we'll be able to see how the C code translates to ARM assembly instructions! + +### Compiling and Flashing to the Pico 2 + +Now that we understand the code, let's get it running on our hardware: + +##### Step 1: Compile the Code + +In VS Code, look for the **Compile** button in the status bar at the bottom of the window. This is provided by the Raspberry Pi Pico extension. Click it to compile your project. + +The extension will run CMake and build your code, creating a `.uf2` file that can be loaded onto the Pico 2. + +##### Step 2: Put the Pico 2 in Flash Loading Mode + +To flash new code to your Pico 2, you need to put it into **BOOTSEL mode**: + +1. **Press and hold** the right-most button on your breadboard (the BOOTSEL button) +2. **While holding BOOTSEL**, press the white **Reset** button +3. **Release the Reset button** first +4. **Then release the BOOTSEL button** + +When done correctly, your Pico 2 will appear as a USB mass storage device (like a flash drive) on your computer. This means it's ready to receive new firmware! + +> Tip: **Tip:** You'll see a drive called "RP2350" appear in your file explorer when the Pico 2 is in flash loading mode. + +##### Step 3: Flash and Run + +Back in VS Code, click the **Run** button in the status bar. The extension will: + +1. Copy the compiled `.uf2` file to the Pico 2 +2. The Pico 2 will automatically reboot and start running your code + +Once flashed, your Pico 2 will immediately start executing the hello-world program, printing "hello, world" continuously when we open PuTTY! + +--- + +## Part 4: Dynamic Analysis with GDB + +### Prerequisites + +Before we start, make sure you have: + +1. A Raspberry Pi Pico 2 board +2. GDB (GNU Debugger) installed +3. OpenOCD or another debug probe connection +4. The sample "hello-world" binary loaded on your Pico 2 + +### Connecting to Your Pico 2 with OpenOCD + +Open a terminal and start OpenOCD: + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +### Connecting to Your Pico 2 with GDB + +Open another terminal and start GDB with your binary: + +```cmd +arm-none-eabi-gdb build\0x0001_hello-world.elf +``` + +Connect to your target: + +```cmd +(gdb) target extended-remote :3333 +(gdb) monitor reset halt +``` + +### Basic GDB Commands: Your First Steps + +Now that we're connected, let's learn three essential GDB commands that you'll use constantly in embedded reverse engineering. + +##### Setting a Breakpoint with `b main` + +A **breakpoint** tells the debugger to pause execution when it reaches a specific point. Let's set one at our `main` function: + +```gdb +(gdb) b main +Breakpoint 1 at 0x10000234: file ../0x0001_hello-world.c, line 5. +``` + +**What this tells us:** + +- GDB found our `main` function +- It's located at address `0x10000234` in flash memory +- The source file and line number are shown (because we have debug symbols) + +Now let's run to that breakpoint: + +```gdb +(gdb) c +Continuing. + +Breakpoint 1, main () at ../0x0001_hello-world.c:5 +5 stdio_init_all(); +``` + +The program has stopped right at the beginning of `main`! + +##### Disassembling with `disas` + +The `disas` (disassemble) command shows us the assembly instructions for the current function: + +```gdb +(gdb) disas +Dump of assembler code for function main: +=> 0x10000234 <+0>: push {r3, lr} + 0x10000236 <+2>: bl 0x1000156c + 0x1000023a <+6>: ldr r0, [pc, #8] @ (0x10000244 ) + 0x1000023c <+8>: bl 0x100015fc <__wrap_puts> + 0x10000240 <+12>: b.n 0x1000023a + 0x10000242 <+14>: nop + 0x10000244 <+16>: adds r4, r1, r7 + 0x10000246 <+18>: asrs r0, r0, #32 +End of assembler dump. +``` + +**Understanding the output:** + +- The `=>` arrow shows where we're currently stopped +- Each line shows: `address : instruction operands` +- We can see the calls to `stdio_init_all` and `__wrap_puts` (printf was optimized to puts) +- The `b.n 0x1000023a` at the end is our infinite loop - it jumps back to reload the string! + +##### Viewing ELF Sections with `info files` and `maintenance info sections` + +To see how the ELF is laid out in memory, use: + +```gdb +(gdb) info files +Symbols from "C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0001_hello-world\build\0x0001_hello-world.elf". +Extended remote target using gdb-specific protocol: + `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0001_hello-world\build\0x0001_hello-world.elf', file type elf32-littlearm. + Entry point: 0x1000014c + 0x10000000 - 0x100019cc is .text + 0x100019cc - 0x10001b18 is .rodata + 0x10001b18 - 0x10001b20 is .ARM.exidx + 0x10001b20 - 0x10001b4c is .binary_info + 0x20000000 - 0x20000110 is .ram_vector_table + 0x20000110 - 0x200002ac is .data + 0x200002ac - 0x200002ac is .tdata + 0x200002ac - 0x200002ac is .tbss + 0x200002b0 - 0x200004d8 is .bss + 0x200004d8 - 0x20000cd8 is .heap + 0x20081000 - 0x20081800 is .stack_dummy + 0x10001ce8 - 0x10001cfc is .flash_end + While running this, GDB does not access memory from... +Local exec file: + `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0001_hello-world\build\0x0001_hello-world.elf', file type elf32-littlearm. + Entry point: 0x1000014c + 0x10000000 - 0x100019cc is .text + 0x100019cc - 0x10001b18 is .rodata + 0x10001b18 - 0x10001b20 is .ARM.exidx + 0x10001b20 - 0x10001b4c is .binary_info + 0x20000000 - 0x20000110 is .ram_vector_table + 0x20000110 - 0x200002ac is .data + 0x200002ac - 0x200002ac is .tdata + 0x200002ac - 0x200002ac is .tbss + 0x200002b0 - 0x200004d8 is .bss + 0x200004d8 - 0x20000cd8 is .heap + 0x20081000 - 0x20081800 is .stack_dummy + 0x10001ce8 - 0x10001cfc is .flash_end +(gdb) maintenance info sections +Exec file: `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0001_hello-world\build\0x0001_hello-world.elf', file type elf32-littlearm. + [0] 0x10000000->0x100019cc at 0x00001000: .text ALLOC LOAD READONLY CODE HAS_CONTENTS + [1] 0x100019cc->0x10001b18 at 0x000029cc: .rodata ALLOC LOAD READONLY DATA HAS_CONTENTS + [2] 0x10001b18->0x10001b20 at 0x00002b18: .ARM.exidx ALLOC LOAD READONLY DATA HAS_CONTENTS + [3] 0x10001b20->0x10001b4c at 0x00002b20: .binary_info ALLOC LOAD READONLY DATA HAS_CONTENTS + [4] 0x20000000->0x20000110 at 0x00004000: .ram_vector_table ALLOC + [5] 0x20000110->0x20000110 at 0x00003cfc: .uninitialized_data HAS_CONTENTS + [6] 0x20000110->0x200002ac at 0x00003110: .data ALLOC LOAD READONLY CODE HAS_CONTENTS + [7] 0x200002ac->0x200002ac at 0x00003cfc: .tdata ALLOC LOAD DATA HAS_CONTENTS + [8] 0x200002ac->0x200002ac at 0x00000000: .tbss ALLOC + [9] 0x200002b0->0x200004d8 at 0x000042b0: .bss ALLOC + [10] 0x200004d8->0x20000cd8 at 0x000044d8: .heap ALLOC READONLY + [11] 0x20080000->0x20080000 at 0x00003cfc: .scratch_x HAS_CONTENTS + [12] 0x20081000->0x20081000 at 0x00003cfc: .scratch_y HAS_CONTENTS + [13] 0x20081000->0x20081800 at 0x00004000: .stack_dummy ALLOC READONLY + [14] 0x10001ce8->0x10001cfc at 0x00003ce8: .flash_end ALLOC LOAD READONLY DATA HAS_CONTENTS + [15] 0x0000->0x0034 at 0x00003cfc: .ARM.attributes READONLY HAS_CONTENTS + [16] 0x0000->0x0045 at 0x00003d30: .comment READONLY HAS_CONTENTS + [17] 0x0000->0x2069a at 0x00003d75: .debug_info READONLY HAS_CONTENTS + [18] 0x0000->0x54ff at 0x0002440f: .debug_abbrev READONLY HAS_CONTENTS + [19] 0x0000->0x0af0 at 0x00029910: .debug_aranges READONLY HAS_CONTENTS + [20] 0x0000->0x2f86 at 0x0002a400: .debug_rnglists READONLY HAS_CONTENTS + [21] 0x0000->0x15526 at 0x0002d386: .debug_line READONLY HAS_CONTENTS + [22] 0x0000->0x56a7 at 0x000428ac: .debug_str READONLY HAS_CONTENTS + [23] 0x0000->0x1ed4 at 0x00047f54: .debug_frame READONLY HAS_CONTENTS + [24] 0x0000->0xffd1 at 0x00049e28: .debug_loclists READONLY HAS_CONTENTS + [25] 0x0000->0x0178 at 0x00059df9: .debug_line_str READONLY HAS_CONTENTS +``` + +**What each section means:** + +| Section | Purpose | +| ------- | ------- | +| `.text` | Executable machine code (your functions/instructions). | +| `.rodata` | Read-only constants (strings like `"hello, world"`, lookup tables, const data). | +| `.ARM.exidx` | ARM exception unwind index used for stack unwinding/backtraces. | +| `.binary_info` | Pico metadata used by tools (program identity/build information). | +| `.ram_vector_table` | Interrupt vector table copied/placed in RAM for runtime use. | +| `.uninitialized_data` | Deliberately non-zeroed RAM region that can survive certain reset paths. | +| `.data` | Initialized global/static variables in RAM (initial values come from flash). | +| `.tdata` | Initialized thread-local storage data (often empty in simple bare-metal apps). | +| `.tbss` | Zero-initialized thread-local storage (often empty). | +| `.bss` | Zero-initialized global/static variables in RAM. | +| `.heap` | Heap allocation region (`malloc/new`) reserved in RAM. | +| `.scratch_x` | RP2 scratch RAM bank X section (core-local/low-contention placement). | +| `.scratch_y` | RP2 scratch RAM bank Y section (core-local/low-contention placement). | +| `.stack_dummy` | Linker-reserved stack range marker used to size/place the stack. | +| `.flash_end` | Marker/metadata near the logical end of flash image region. | +| `.ARM.attributes` | ARM build attributes (ABI/architecture metadata for tools/linkers). | +| `.comment` | Compiler/build comment strings (toolchain identification). | +| `.debug_info` | Main DWARF debug database (types, variables, symbols, scopes). | +| `.debug_abbrev` | Abbreviation table referenced by `.debug_info`. | +| `.debug_aranges` | Address-to-compilation-unit lookup acceleration data. | +| `.debug_rnglists` | DWARF range lists for non-contiguous code/data ranges. | +| `.debug_line` | Address-to-source-line mapping used for stepping/breakpoints. | +| `.debug_str` | Shared string pool used by DWARF debug sections. | +| `.debug_frame` | Call frame information used for unwinding stack frames. | +| `.debug_loclists` | Variable location lists (where variables live over PC ranges). | +| `.debug_line_str` | Extra string pool used by `.debug_line` data. | + +> Tip: **Practical rule:** For reverse engineering runtime behavior, focus first on `.text`, `.rodata`, `.data`, `.bss`, heap/stack regions, and the vector table. Debug sections are for source-level mapping and symbol intelligence. + +**Fast interpretation checklist (use this every time):** + +1. **Find where code executes**: Verify `.text` starts at `0x10000000` (XIP flash) and note its end. +2. **Find immutable constants**: Use `.rodata` for strings/tables; cross-reference these addresses in disassembly. +3. **Find initialized RAM state**: `.data` lives in RAM at runtime, but its initial bytes come from flash. +4. **Find zeroed runtime state**: `.bss` is RAM that startup code clears to zero before `main`. +5. **Find interrupt control point**: Confirm `.ram_vector_table` location for exception/IRQ handler mapping. +6. **Bound dynamic memory**: Note `.heap` range so you can classify allocator activity vs static data. +7. **Bound call-stack activity**: Use `.stack_dummy` as linker stack reservation, then track live stack with `$sp`. +8. **Separate runtime vs debug-only sections**: `.debug_*`, `.comment`, and `.ARM.attributes` help tooling, not execution. +9. **Correlate any suspicious address quickly**: Flash/XIP (`0x100...`) usually code/const; SRAM (`0x200...`) usually data/stack/heap. +10. **Validate in memory**: After identifying a section, inspect it with `x` in GDB to confirm actual bytes/instructions. + +##### Viewing Registers with `i r` + +The `i r` (info registers) command shows the current state of all CPU registers: + +```gdb +(gdb) i r +r0 0x0 0 +r1 0x10000235 268436021 +r2 0x80808080 -2139062144 +r3 0xe000ed08 -536810232 +r4 0x100001d0 268435920 +r5 0x88526891 -2007865199 +r6 0x4f54710 83183376 +r7 0x400e0014 1074659348 +r8 0x43280035 1126694965 +r9 0x0 0 +r10 0x10000000 268435456 +r11 0x62707361 1651536737 +r12 0xed07f600 -318245376 +sp 0x20082000 0x20082000 +lr 0x1000018f 268435855 +pc 0x10000234 0x10000234
+xpsr 0x69000000 1761607680 +``` + +**Key registers to watch:** + +| Register | Value | Meaning | +| -------- | ------------ | ----------------------------------------------- | +| `pc` | `0x10000234` | Program Counter - we're at the start of `main` | +| `sp` | `0x20081fc8` | Stack Pointer - top of our stack in RAM | +| `lr` | `0x100002d5` | Link Register - where we return after `main` | +| `r0-r3` | Various | Will hold function arguments and return values | + +> Tip: **Tip:** You can also use `i r pc sp lr` to show only specific registers you care about. + +### Quick Reference: Essential GDB Commands + +| Command | Short Form | What It Does | +| --------------------- | ---------- | ------------------------------------ | +| `break main` | `b main` | Set a breakpoint at main | +| `continue` | `c` | Continue execution until breakpoint | +| `disassemble` | `disas` | Show assembly for current function | +| `info registers` | `i r` | Show all register values | +| `stepi` | `si` | Execute one assembly instruction | +| `nexti` | `ni` | Execute one instruction (skip calls) | +| `x/10i $pc` | | Examine 10 instructions at PC | +| `monitor reset halt` | | Reset the target and halt | + +### Watching the Stack Change After `push {r3, lr}` + +The first instruction in `main` is `push {r3, lr}`. Before we step it, `SP` is `0x20082000`. After a single `si`, `SP` becomes `0x20081ff8`, which tells us the processor reserved 8 bytes on the stack for two 32-bit values. The first word at the new top of stack is `0xe000ed08`, which is the old value of `r3`, and the second word is `0x1000018f`, which is the saved `lr` return address. This matches the ABI rule we discussed earlier: the compiler pushes an even number of registers so the stack stays 8-byte aligned at the next call site before `stdio_init_all()` runs. + +Notice the difference between inspecting memory at `$sp` and inspecting `$lr`. `x/4x $sp` is enough here to show the relevant stack words in RAM, while `x/x $lr` shows the instruction word stored at the flash address held in the link register. In other words, `$sp` points to saved data on the stack, but `$lr` points to code that execution will return to later. + +```gdb +(gdb) x/x $sp +0x20082000: 0x00000000 +(gdb) si +0x10000236 5 stdio_init_all(); + +(gdb) x/x $sp +0x20081ff8: 0xe000ed08 +(gdb) x/4x $sp +0x20081ff8: 0xe000ed08 0x1000018f 0x00000000 0x00000000 +(gdb) x/x $lr +0x1000018f : 0x00478849 +``` + +> Tip: **What's Next?** In Week 2, we'll put these GDB commands to work with hands-on debugging exercises! We'll step through code, examine the stack, watch registers change, and ultimately use these skills to modify a running program. The commands you learned here are the foundation for everything that follows. + +--- + +## Part 5: Static Analysis with Ghidra + +### Setting Up Your First Ghidra Project + +Before we dive into GDB debugging, let's set up Ghidra to analyze our hello-world binary. Ghidra is a powerful reverse engineering tool that will help us visualize the disassembly and decompiled code. + +##### Step 1: Create a New Project + +1. Launch Ghidra +2. A window will appear - select **File -> New Project** +3. Choose **Non-Shared Project** and click **Next** +4. Enter the Project Name: `0x0001_hello-world` +5. Click **Finish** + +##### Step 2: Import the Binary + +1. Open your file explorer and navigate to the `Embedded-Hacking` folder +2. **Drag and drop** the `0x0001_hello-world.elf` file into the folder panel within the Ghidra application + +##### Step 3: Understand the Import Dialog + +In the small window that appears, you will see the file identified as an **ELF** (Executable and Linkable Format). + +> Tip: **What is an ELF file?** +> +> ELF stands for **Executable and Linkable Format**. This format includes **symbols** - human-readable names for functions and variables. These symbols make reverse engineering much easier because you can see function names like `main` and `printf` instead of just memory addresses. +> +> In future weeks, we will work with **stripped binaries** (`.bin` files) that do not contain these symbols. This is more realistic for real-world reverse engineering scenarios where symbols have been removed to make analysis harder. + +3. Click **Ok** to import the file +4. **Double-click** on the file within the project window to open it in the CodeBrowser + +##### Step 4: Auto-Analyze the Binary + +When prompted, click **Yes** to auto-analyze the binary. Accept the default analysis options and click **Analyze**. + +Ghidra will now process the binary, identifying functions, strings, and cross-references. This may take a moment. + +### Reviewing the Main Function in Ghidra + +Once analysis is complete, let's find our `main` function: + +1. In the **Symbol Tree** panel on the left, expand **Functions** +2. Look for `main` in the list (you can also use **Search -> For Address or Label** and type "main") +3. Click on `main` to navigate to it + +##### What You'll See + +Ghidra shows you two views of the code: + +**Listing View (Center Panel)** - The disassembled ARM assembly: +``` + ************************************************************* + * FUNCTION + ************************************************************* + int main (void ) + assume LRset = 0x0 + assume TMode = 0x1 + int r0:4 + main XREF[3]: Entry Point (*) , + _reset_handler:1000018c (c) , + .debug_frame::00000018 (*) + 0x0001_hello-world.c:4 (2) + 0x0001_hello-world.c:5 (2) + 10000234 08 b5 push {r3,lr} + 0x0001_hello-world.c:5 (4) + 10000236 01 f0 99 f9 bl stdio_init_all _Bool stdio_init_all(void) + LAB_1000023a XREF[1]: 10000240 (j) + 0x0001_hello-world.c:7 (6) + 0x0001_hello-world.c:8 (6) + 1000023a 02 48 ldr r0=>__EH_FRAME_BEGIN__ ,[DAT_10000244 ] = "hello, world\r" + = 100019CCh + 1000023c 01 f0 de f9 bl __wrap_puts int __wrap_puts(char * s) + 0x0001_hello-world.c:7 (8) + 10000240 fb e7 b LAB_1000023a + 10000242 00 ?? 00h + 10000243 bf ?? BFh + DAT_10000244 XREF[1]: main:1000023a (R) + 10000244 cc 19 00 10 undefine 100019CCh ? -> 100019cc + +``` + +**Decompile View (Right Panel)** - The reconstructed C code: +```c +int main(void) + +{ + stdio_init_all(); + do { + __wrap_puts("hello, world\r"); + } while( true ); +} +``` + +> **Notice how Ghidra reconstructed our original C code!** The decompiler recognized the infinite loop and the `puts` call (the compiler optimized `printf` to `puts` since we're just printing a simple string). + +##### Why We Start with .elf Files + +We're using the `.elf` file because it contains symbols that help us learn: + +- Function names are visible (`main`, `stdio_init_all`, `puts`) +- Variable names may be preserved +- The structure of the code is easier to understand + +In future weeks, we'll work with `.bin` files that have been stripped of symbols. This will teach you how to identify functions and understand code when you don't have these helpful hints! + +--- + +## Part 6: Summary and Review + +### What We Learned + +1. **Registers**: The ARM Cortex-M33 has 13 general-purpose registers (`r0`-`r12`), plus special registers for the stack pointer (`r13`/SP), link register (`r14`/LR), and program counter (`r15`/PC). + +2. **The Stack**: + - Grows downward in memory + - PUSH adds items (SP decreases) + - POP removes items (SP increases) + - Used to save return addresses and register values + +3. **Memory Layout**: + - Code lives in flash memory starting at `0x10000000` + - Stack lives in RAM around `0x20080000` + +4. **GDB Basics**: We learned the essential commands for connecting to hardware and examining code: + +| Command | What It Does | +| --------------------- | -------------------------------------- | +| `target extended-remote :3333` | Connect to OpenOCD debug server | +| `monitor reset halt` | Reset and halt the processor | +| `b main` | Set breakpoint at main function | +| `c` | Continue running until breakpoint | +| `disas` | Disassemble current function | +| `i r` | Show all register values | + +5. **Ghidra Static Analysis**: We set up a Ghidra project and analyzed our binary: + - Imported the ELF file with symbols + - Found the `main` function + - Saw the decompiled C code + - Understood how assembly maps to C + +6. **Little-Endian**: The RP2350 stores multi-byte values with the least significant byte at the lowest address, making them appear "backwards" when viewed as a single value. + +### The Program Flow + +``` ++-----------------------------------------------------+ +| 1. push {r3, lr} | +| Save registers to stack | ++-----------------------------------------------------+ +| 2. bl stdio_init_all | +| Initialize standard I/O | ++-----------------------------------------------------+ +| 3. ldr r0, [pc, #8] ----------------+ | +| Load address of "hello, world" into r0| | ++-----------------------------------------------------+ +| 4. bl __wrap_puts | | +| Print the string | | ++-----------------------------------------------------+ +| 5. b.n (back to step 3) ----------------+ | +| Infinite loop! | ++-----------------------------------------------------+ +``` + +--- + +> **Note:** The detailed hands-on GDB debugging (stepping through code, watching the stack, examining memory) will be covered in Week 2! + +--- + +## Key Takeaways + +1. **Reverse engineering combines static and dynamic analysis** - we look at the code (static with Ghidra) and run it to see what happens (dynamic with GDB). + +2. **The stack is fundamental** - understanding how push/pop work is essential for following function calls. + +3. **GDB and Ghidra work together** - Ghidra helps you understand the big picture, GDB lets you watch it happen live. + +4. **Assembly isn't scary** - each instruction does one simple thing. Put them together and you understand the whole program! + +5. **Everything is just numbers** - whether it's code, data, or addresses, it's all stored as numbers in memory. + +--- + +## Glossary + +| Term | Definition | +| ------------------- | --------------------------------------------------------- | +| **Assembly** | Human-readable representation of machine code | +| **Breakpoint** | A marker that tells the debugger to pause execution | +| **GDB** | GNU Debugger - a tool for examining running programs | +| **Hex/Hexadecimal** | Base-16 number system (0-9, A-F) | +| **Little-Endian** | Storing the least significant byte at the lowest address | +| **Microcontroller** | A small computer on a single chip | +| **Program Counter** | Register that points to the next instruction | +| **Register** | Fast storage inside the processor | +| **Stack** | Memory region for temporary storage during function calls | +| **Stack Pointer** | Register that points to the top of the stack | +| **XIP** | Execute In Place - running code directly from flash | + + + diff --git a/WEEK01/WEEK01.pdf b/WEEK01/WEEK01.pdf new file mode 100644 index 0000000..a342dfd Binary files /dev/null and b/WEEK01/WEEK01.pdf differ diff --git a/WEEK01/WEEK01a.md b/WEEK01/WEEK01a.md new file mode 100644 index 0000000..7a2bae8 --- /dev/null +++ b/WEEK01/WEEK01a.md @@ -0,0 +1,718 @@ +# Week 1a: Understanding the ARM Stack: Inline Assembly and Live Debugging + +*** +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +By the end of this week, you will be able to: + +- Understand how the RP2350 Cortex-M33 stack grows in SRAM. +- Identify the ARM registers used by this stack experiment. +- Build and flash a Pico 2 ELF through a Debug Probe. +- Connect OpenOCD and GDB to live hardware. +- Step one assembly instruction at a time with `si`. +- Examine the exact stack words written by each multi-register instruction. +- Prove that an ARM register list is ordered by register number, not source-list spelling. + +--- + +## Part 1: Understanding the Basics + +### What is a Microcontroller? + +A microcontroller is a complete small computer on one chip. It contains processor cores, memory controllers, peripherals, and interfaces for hardware such as GPIO, UART, timers, and SPI. The Raspberry Pi Pico 2 uses the **RP2350** microcontroller. + +### What is the ARM Cortex-M33? + +The RP2350 can run Arm Cortex-M33 cores. The program in this folder is built for that Arm target. We will use the Debug Probe, OpenOCD, and GDB to stop a core and inspect its registers and memory while it executes the program. + +### What is Dynamic Analysis? + +Dynamic analysis means observing a program while it runs on real hardware. In this lesson we will: + +- Stop the processor at `main`. +- View the instructions produced by the compiler. +- Execute one instruction with `si`. +- Read the stack pointer and the memory it points to. + +--- + +## Part 2: Understanding Processor Registers + +### What is a Register? + +A register is very fast storage inside the CPU. Instructions use registers for values, addresses, temporary results, and control flow. + +### The ARM Cortex-M33 Registers + +| Register | Also Called | Purpose | +| --- | --- | --- | +| `r0` - `r12` | General purpose | Hold values and addresses while instructions run. | +| `r13` | SP | Points to the current top of the stack. | +| `r14` | LR | Holds the return address after a function call. | +| `r15` | PC | Points to the next instruction to execute. | + +##### General-Purpose Registers (`r0` - `r12`) + +This lesson uses `r2`, `r3`, `r4`, `r6`, `r9`, and `r10`. The assembly saves their current values to SRAM, then restores them before the loop repeats. + +##### The Stack Pointer (`r13` / SP) + +The stack is a region of SRAM used for temporary values, saved registers, return addresses, and local variables. On Cortex-M, the standard stack grows toward lower addresses. + +- A `push` lowers `sp` and writes values below the old stack pointer. +- A `pop` reads values at `sp` and raises `sp`. +- Each saved register occupies 4 bytes. + +```text +Higher addresses ++------------------+ +| Old SP location | ++------------------+ +| Saved value | ++------------------+ +| Saved value | <- SP after a multi-register push ++------------------+ +Lower addresses +``` + +##### The Link Register (`r14` / LR) + +A `bl` instruction calls a function and stores the return address in `lr`. The compiler-generated prologue for `main` saves `lr` on the stack before calling `stdio_init_all`. + +##### The Program Counter (`r15` / PC) + +The Program Counter identifies the next instruction. In GDB, the `=>` marker in `disas main` points to the instruction that will run when you type `si`. + +--- + +## Part 3: Understanding Memory Layout + +### XIP - Execute In Place + +The Pico 2 executes this firmware directly from external flash through XIP. The executable code normally begins at `0x10000000`. + +### Memory Map Overview + +```text ++-------------------------------------+ +| Flash Memory (XIP) | +| Starts at: 0x10000000 | +| Contains: program instructions | ++-------------------------------------+ +| SRAM | +| Starts at: 0x20000000 | +| Contains: stack, heap, variables | ++-------------------------------------+ +``` + +### Why the Stack Is in SRAM + +The stack changes on every function call and return, so it must be writable. When GDB displays `$sp`, it should show an address in the `0x200...` SRAM range. `x/wx $sp` reads the 32-bit value currently at the top of that stack. + +--- + +## Part 3.5: Reviewing Our Stack Code + +The file `0x0001a_stack.c` initializes standard I/O, then repeats this assembly block forever: + +```c +__asm volatile( + "push {r4, lr}\n" + "push {r3, r2, r6}\n" + "stmdb sp!, {r9, r10}\n" + "ldmia sp!, {r9, r10}\n" + "pop {r2, r3, r6}\n" + "pop {r4, lr}\n" + ::: "memory"); +``` + +### Breaking Down the Code + +##### First Push: `push {r4, lr}` + +This lowers `sp` by 8 bytes. At the new stack pointer, the saved values are: + +```text +[sp + 4] = lr (higher address, visual top) +[sp] = r4 (lower address) <- SP <- TOP OF STACK +``` + +##### Second Push: `push {r3, r2, r6}` + +This is deliberately written in a confusing order. The source says `r3` first, but an ARM register list is a set of registers, not an ordered sequence of operations. The assembler encodes the same register mask as `{r2, r3, r6}` and warns that the list is not ascending. + +After one `si`, the stack layout proves the actual rule: + +```text +[sp + 16] = lr (highest address, visual top) +[sp + 12] = r4 +[sp + 8] = r6 +[sp + 4] = r3 +[sp] = r2 (lowest address) <- SP <- TOP OF STACK +``` + +The first three words were written by this instruction; `r4` and `lr` remain from +the preceding `push {r4, lr}`. The lowest register number in this push is stored +at the lowest address. Because the stack grows down, `[sp]` is the lower address +and the older `lr` at `[sp + 16]` is the higher address at the visual top. + +##### High Registers: `stmdb sp!, {r9, r10}` + +The 16-bit Thumb `push` encoding cannot encode high registers `r8` through `r12`. +`stmdb sp!` is the general full-descending stack instruction used for `r9` and +`r10`. + +```text +[sp + 4] = r10 (higher address, visual top) +[sp] = r9 (lower address) <- SP <- TOP OF STACK +``` + +##### The Restore Instructions + +The next instructions restore the same groups in reverse stack-group order. This matters because the last group saved is at the current top of the stack: + +```text +ldmia sp!, {r9, r10} +pop {r2, r3, r6} +pop {r4, lr} +``` + +The stack pointer ends at the same value it had before the inline assembly, so the infinite loop does not consume stack space. + +### Compiling and Flashing to the Pico 2 + +##### Step 1: Compile the Code + +From the project folder, build the program: + +```powershell +& "$env:USERPROFILE\.pico-sdk\ninja\v1.13.2\ninja.exe" -C build +``` + +The expected artifact is `build\0x0001a_stack.elf`. The assembler reports `register range not in ascending order` for the deliberately unordered source list. That warning is expected for this experiment. + +##### Step 2: Flash and Verify + +Use the Debug Probe to flash the ELF and compare target flash with the built image: + +```powershell +& "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\openocd.exe" -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c 'adapter speed 5000; targets rp2350.dap.core1; cortex_m reset_config sysresetreq; targets rp2350.dap.core0; program "build/0x0001a_stack.elf" verify reset exit' +``` + +You should see: + +```text +** Programming Finished ** +** Verify Started ** +** Verified OK ** +** Resetting Target ** +shutdown command invoked +``` + +`Verified OK` proves that the programmed bytes match the ELF. `shutdown command invoked` is normal because `exit` ends OpenOCD after flashing. + +--- + +## Part 4: Dynamic Analysis with GDB + +### Prerequisites + +Before starting, you need: + +1. A Pico 2 with the Debug Probe connected. +2. OpenOCD from the installed Pico SDK. +3. `arm-none-eabi-gdb`. +4. The verified `build\0x0001a_stack.elf` on the Pico 2. + +### Connecting to Your Pico 2 with OpenOCD + +Open a terminal and start the debug server. Leave it running: + +```powershell +& "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\openocd.exe" -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000; init" +``` + +OpenOCD listens for GDB connections on port `3333`. + +##### VM Command + +If the VM has `openocd` on its `PATH`, use the same command without the full executable path: + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000; init" +``` + +The VM needs USB access to the Debug Probe. Nothing else about the build, ELF, or GDB sequence changes. + +### Connecting to Your Pico 2 with GDB + +Open a second terminal in the project folder: + +```powershell +arm-none-eabi-gdb build\0x0001a_stack.elf +``` + +Connect, reset, halt, and stop at `main`: + +```gdb +target extended-remote :3333 +monitor reset halt +b main +c +disas main +``` + +You should see this instruction pattern from the current `build\0x0001a_stack.elf`. +The instruction addresses are determined by the ELF. The register contents shown +later are live target state and are not determined by the ELF. + +```text +=> main: push {r3, lr} + main+2: bl stdio_init_all + main+6: push {r4, lr} + main+8: push {r2, r3, r6} + main+10: stmdb sp!, {r9, r10} + main+14: ldmia.w sp!, {r9, r10} + main+18: pop {r2, r3, r6} + main+20: ldmia.w sp!, {r4, lr} + main+24: b.n main+6 +``` + +Notice that GDB displays `{r2, r3, r6}`, not the source spelling `{r3, r2, r6}`. That is the encoded register set in canonical order. Some disassemblers display `r10` as its conventional alias, `sl`; `{r9, sl}` and `{r9, r10}` name the same register set. + +### Basic GDB Commands: Your First Steps + +| Command | Short Form | What It Does | +| --- | --- | --- | +| `break main` | `b main` | Set a breakpoint at `main`. | +| `continue` | `c` | Run until a breakpoint. | +| `disassemble` | `disas` | Show the current function's assembly. | +| `info registers` | `i r` | Display CPU registers. | +| `stepi` | `si` | Execute exactly one instruction. | +| `nexti` | `ni` | Execute one instruction without entering a call. | +| `x/wx ADDRESS` | | Examine one 32-bit hexadecimal word. | +| `monitor reset halt` | | Ask OpenOCD to reset and halt the target. | + +### Watching the Stack Change + +> **Important: the instruction addresses and stack offsets below come from the +> current ELF. The Step 4 register capture is from the live GDB session shown +> here. Register contents and old SRAM words are target state, not constants +> that can be recovered from the ELF. The deterministic rule is that `push +> {r4, lr}` decreases `sp` by 8 bytes, stores `r4` at the new `[sp]`, and +> stores `lr` at `[sp + 4]`. + +##### Step 1: Inspect the Stack Before the Compiler Prologue + +At the breakpoint, GDB is paused before `push {r3, lr}`. Inspect the current stack pointer and the two words below it: + +```gdb +(gdb) p/x $sp +$1 = 0x20082000 +(gdb) x/2wx $sp-8 +0x20081ff8: 0x88526891 0x10000187 +``` + +The stack pointer is at `0x20082000`. The two words below it contain previous values (before the prologue). + +##### Step 2: Execute One Instruction + +Execute the compiler prologue `push {r3, lr}`: + +```gdb +(gdb) si +0x100001e2 7 stdio_init_all(); +(gdb) p/x $sp +$2 = 0x20081ff8 +(gdb) p/x $r3 +$3 = 0xe000ed08 +(gdb) x/wx $sp +0x20081ff8: 0xe000ed08 +(gdb) p/x $lr +$4 = 0x1000018b +(gdb) x/wx $sp+4 +0x20081ffc: 0x1000018b +``` + +The prologue saved two values to the stack: +```text +[sp + 4] = 0x1000018b (lr, higher address, visual top) +[sp] = 0xe000ed08 (r3, lower address) <- SP <- TOP OF STACK +``` + +**Stack layout after prologue** (memory grows downward; **higher hex addresses = deeper in stack = older data**): + +```text +Address Pointer Value Label +0x20082000 ---------- ---------- (previous data — HIGHEST address, visual top) +0x20081ffc ---------- 0x1000018b (lr, higher address) +0x20081ff8 <- [sp] 0xe000ed08 (r3, lowest address) <- SP <- TOP OF STACK + +MEMORY ORDER: 0x20081ff8 < 0x20081ffc < 0x20082000 + (visual bottom) (visual top) +``` + +**Memory address explanation for newcomers:** +- Hex address `0x20081ffc` is **LARGER** than `0x20081ff8` (compare the last hex digits: `ffc` > `ff8`) +- **Larger addresses = higher in memory = deeper in the stack** +- When we PUSH, sp **DECREASES** (goes to a smaller address, moving DOWN on the page) +- When we POP, sp **INCREASES** (goes to a larger address, moving UP on the page) + +The stack pointer dropped 8 bytes: from `0x20082000` to `0x20081ff8` (it went DOWN, to a smaller/lower address). + +##### Step 3: Step Over `stdio_init_all` + +Do not step into the library initialization code. Use `ni` to execute it and return: + +```gdb +(gdb) ni +0x100001e6 15 __asm volatile( +(gdb) disas main +Dump of assembler code for function main: + 0x100001e0 <+0>: push {r3, lr} + 0x100001e2 <+2>: bl 0x10001594 +=> 0x100001e6 <+6>: push {r4, lr} + 0x100001e8 <+8>: push {r2, r3, r6} + ... +``` + +The arrow now points at `push {r4, lr}`, the first inline assembly instruction. + +##### Step 4: Prove the First Inline Push + +Read the registers and stack pointer before the first inline push: + +```gdb +(gdb) p/x $r4 +$3 = 0x100001cc +(gdb) p/x $lr +$4 = 0x1000159b +(gdb) p/x $sp +$5 = 0x20081ff8 +``` + +Now execute the first inline push: + +```gdb +(gdb) si +0x100001e8 16 "push {r4, lr}\n" +(gdb) p/x $sp +$6 = 0x20081ff0 +(gdb) x/wx $sp +0x20081ff0: 0x100001cc +(gdb) x/wx $sp+4 +0x20081ff4: 0x1000159b +``` + +**After first inline push `push {r4, lr}`:** +- SP = `0x20081ff0` (**new TOP of stack, sp decreased**) +- `r4` = `0x100001cc` (now saved on stack) +- `lr` = `0x1000159b` (now saved on stack) + +The first push saved: +```text +[sp + 4] = 0x1000159b (lr, higher address, visual top) +[sp] = 0x100001cc (r4, lower address) <- SP <- TOP OF STACK +``` + +**Stack layout after first inline push**: + +```text +Address Pointer Value Label +0x20081ffc ---------- 0x1000018b (lr from prologue — highest address, visual top) +0x20081ff8 ---------- 0xe000ed08 (r3 from prologue — higher address) +0x20081ff4 ---------- 0x1000159b (lr — higher address) +0x20081ff0 <- [sp] 0x100001cc (r4, lowest address) <- SP <- TOP OF STACK + +MEMORY ORDER: 0x20081ff0 < 0x20081ff4 < 0x20081ff8 < 0x20081ffc + (visual bottom) (visual top) + Most recent Least recent +``` + +**The stack pointer dropped from `0x20081ff8` to `0x20081ff0` — that's 8 bytes down (toward lower addresses).** +**The value at `[sp]` (the TOP) is now `0x100001cc` (r4).** +**Address `0x20081ff0` is SMALLER than `0x20081ff8`, so sp moved DOWN.** + + +##### Step 5: Prove Register-List Ordering + +Read the three registers before executing the deliberately unordered list `push {r3, r2, r6}`: + +```gdb +(gdb) p/x $r2 +$18 = 0x200005cc +(gdb) p/x $r3 +$19 = 0xe000ed08 +(gdb) p/x $r6 +$20 = 0x04f54710 +(gdb) p/x $sp +$21 = 0x20081ff0 +``` + +Now execute the second inline push: + +```gdb +(gdb) si +0x100001ea <+10>: stmdb sp!, {r9, sl} +(gdb) p/x $sp +$22 = 0x20081fe4 +(gdb) x/wx $sp +0x20081fe4: 0x200005cc +(gdb) x/wx $sp+4 +0x20081fe8: 0xe000ed08 +(gdb) x/wx $sp+8 +0x20081fec: 0x04f54710 +``` + +The second push saved three registers in **numeric order** despite the source spelling `{r3, r2, r6}`: +```text +[sp + 8] = 0x04f54710 (r6, higher address, visual top) +[sp + 4] = 0xe000ed08 (r3, higher address) +[sp] = 0x200005cc (r2, lower address) <- SP <- TOP OF STACK +``` + +**Stack layout after second inline push** (**lower hex addresses = most recent = TOP**): + +```text +Address Pointer Value Label +0x20081ffc ---------- 0x1000018b (lr — HIGHEST address, OLDEST data, visual top) +0x20081ff8 ---------- 0xe000ed08 (r3 — higher address) +0x20081ff4 ---------- 0x1000159b (lr — higher address) +0x20081ff0 ---------- 0x100001cc (r4 — higher address) +0x20081fec ---------- 0x04f54710 (r6 — higher address) +0x20081fe8 ---------- 0xe000ed08 (r3 — higher address) +0x20081fe4 <- [sp] 0x200005cc (r2, lowest address) <- SP <- TOP OF STACK + +ADDRESS ORDERING: 0x20081fe4 < 0x20081fe8 < ... < 0x20081ffc + (visual bottom) (visual top) + Hex comparison: fe4 < fe8 < fec < ff0 < ff4 < ff8 < ffc + Most recent data at the visual bottom +``` + +**The stack pointer dropped from `0x20081ff0` to `0x20081fe4` — that's 12 bytes down (three 4-byte registers).** +**When comparing hex: `0x20081fe4` is SMALLER than `0x20081ff0`, so sp moved to a LOWER address.** +**The value at `[sp]` (the TOP) is now `0x200005cc` (r2).** + +Notice: the source wrote `r3` first, but the CPU pushed `r2` first because `r2` has a lower register number. The encoded register mask is `{r2, r3, r6}`, and the stack layout proves it. + +##### Step 6: Prove the High-Register Save + +Read the high registers before the `stmdb sp!, {r9, r10}` instruction: + +```gdb +(gdb) p/x $r9 +$23 = 0x00000000 +(gdb) p/x $r10 +$24 = 0x10000000 +(gdb) p/x $sp +$25 = 0x20081fe4 +``` + +Execute the high-register save: + +```gdb +(gdb) si +0x100001ee <+14>: ldmia.w sp!, {r9, sl} +(gdb) p/x $sp +$26 = 0x20081fdc +(gdb) p/x $r9 +$27 = 0x00000000 +(gdb) p/x $r10 +$28 = 0x10000000 +(gdb) x/wx $sp +0x20081fdc: 0x00000000 +(gdb) x/wx $sp+4 +0x20081fe0: 0x10000000 +(gdb) x/wx $sp+8 +0x20081fe4: 0x200005cc +(gdb) x/wx $sp+12 +0x20081fe8: 0xe000ed08 +(gdb) x/wx $sp+16 +0x20081fec: 0x04f54710 +(gdb) x/wx $sp+20 +0x20081ff0: 0x100001cc +(gdb) x/wx $sp+24 +0x20081ff4: 0x1000159b +(gdb) x/wx $sp+28 +0x20081ff8: 0xe000ed08 +``` + +**After high-register save `stmdb sp!, {r9, r10}`:** +- SP = `0x20081fdc` (**new TOP of stack, sp decreased further**) +- `r9` = `0x00000000` (now saved on stack) +- `r10` = `0x10000000` (now saved on stack) + +The `stmdb sp!, {r9, r10}` instruction saved: +```text +[sp + 4] = 0x10000000 (r10, higher address, visual top) +[sp] = 0x00000000 (r9, lower address) <- SP <- TOP OF STACK +``` + +**The stack pointer dropped from `0x20081fe4` to `0x20081fdc` — that's 8 more bytes down.** +**The value at `[sp]` (the TOP) is now `0x00000000` (r9).** +**This is the DEEPEST point of the stack during inline assembly.** + +```text +Address Pointer Value Label +0x20081ff8 ---------- 0xe000ed08 (saved prologue r3, highest address, visual top) +0x20081ff4 ---------- 0x1000159b (lr, higher address) +0x20081ff0 ---------- 0x100001cc (r4, higher address) +0x20081fec ---------- 0x04f54710 (r6, higher address) +0x20081fe8 ---------- 0xe000ed08 (r3, higher address) +0x20081fe4 ---------- 0x200005cc (r2, higher address) +0x20081fe0 ---------- 0x10000000 (r10, higher address) +0x20081fdc <- [sp] 0x00000000 (r9, lowest address) <- SP <- TOP OF STACK +``` + +##### Step 7: Watch the Restores + +At this point, the stack is at maximum depth. Now execute the restore instructions one by one: + +**Restore 1: ldmia sp!, {r9, r10}** + +```gdb +(gdb) si +0x100001f2 <+18>: pop {r2, r3, r6} +(gdb) p/x $sp +$29 = 0x20081fe4 +(gdb) p/x $r9 +$30 = 0x00000000 +(gdb) p/x $r10 +$31 = 0x10000000 +``` + +`ldmia sp!` restored `r9` and `r10` from the stack and raised `sp` by 8 bytes (from `0x20081fdc` to `0x20081fe4`). +**SP is now `0x20081fe4`; `[sp]` contains r2, so `SP <- TOP OF STACK` at the next group.** + +**Restore 2: pop {r2, r3, r6}** + +```gdb +(gdb) si +0x100001f4 <+20>: ldmia.w sp!, {r4, lr} +(gdb) p/x $sp +$32 = 0x20081ff0 +(gdb) p/x $r2 +$33 = 0x200005cc +(gdb) p/x $r3 +$34 = 0xe000ed08 +(gdb) p/x $r6 +$35 = 0x04f54710 +``` + +`pop {r2, r3, r6}` restored the three registers from SRAM and raised `sp` by 12 bytes (from `0x20081fe4` to `0x20081ff0`). +**SP is now `0x20081ff0`; `[sp]` contains r4, so `SP <- TOP OF STACK` at the final group.** + +**Restore 3: ldmia.w sp!, {r4, lr}** + +```gdb +(gdb) si +0x100001f8 <+24>: b.n 0x100001e6 +(gdb) p/x $sp +$36 = 0x20081ff8 +(gdb) p/x $r4 +$37 = 0x100001cc +(gdb) p/x $lr +$38 = 0x1000159b +``` + +`ldmia.w sp!, {r4, lr}` restored `r4` and `lr` from SRAM and raised `sp` by 8 bytes (from `0x20081ff0` to `0x20081ff8`). +**SP is now `0x20081ff8`; `[sp]` contains the saved prologue r3, so `SP <- TOP OF STACK` for the remaining prologue stack data.** + +**Result:** After all three restore instructions, `sp` is back at `0x20081ff8`, exactly where it was before the inline assembly block started. The loop branches back to `push {r4, lr}` at address `0x100001e6` and repeats forever without consuming stack space. + +### Understanding the Stack Diagram + +At the deepest point, after `stmdb sp!, {r9, r10}`, the inline assembly has saved 28 bytes. The `old SP` below is the stack pointer after the separate compiler prologue, not the stack pointer at entry to `main`: + +```text +Before inline assembly: At maximum inline stack depth: + +old SP <- SP old SP + [old SP - 4] lr + [old SP - 8] r4 + [old SP - 12] r6 + [old SP - 16] r3 + [old SP - 20] r2 + [old SP - 24] r10 + [old SP - 28] r9 <- SP +``` + +The restoration sequence removes the top group first: `r9/r10`, then `r2/r3/r6`, then `r4/lr`. + +--- + +## Part 5: Summary and Review + +### What We Learned + +1. **Registers**: `sp`, `lr`, and `pc` control stack location, returns, and the next instruction. +2. **The stack**: It grows down in SRAM. Pushes decrease `sp`; pops increase it. +3. **Multi-register instructions**: Register-list source order is not the stack-memory order. ARM stores lower register numbers at lower addresses. +4. **OpenOCD and GDB**: OpenOCD connects to the Debug Probe; GDB connects to OpenOCD on port `3333`. +5. **Live evidence**: `si` executes one instruction, and `x/wx $sp` shows the exact 32-bit word that instruction placed at the stack pointer. + +### The Program Flow + +```text ++-----------------------------------------------------+ +| 1. push {r3, lr} | +| Compiler saves its prologue registers | ++-----------------------------------------------------+ +| 2. bl stdio_init_all | +| Initialize standard I/O | ++-----------------------------------------------------+ +| 3. push {r4, lr} | +| Save the first inline group | ++-----------------------------------------------------+ +| 4. push {r3, r2, r6} | +| Source order differs from stack-memory order | ++-----------------------------------------------------+ +| 5. stmdb / ldmia / pop / pop | +| Save and restore all groups | ++-----------------------------------------------------+ +| 6. b.n main+6 | +| Repeat with the original stack pointer | ++-----------------------------------------------------+ +``` + +--- + +## Key Takeaways + +1. **The stack grows downward**: a push decreases the numeric value in `sp`. +2. **A register list is not a sequence**: `{r3, r2, r6}` and `{r2, r3, r6}` encode the same register set. +3. **Memory is the proof**: after `si`, inspect `[sp]`, `[sp+4]`, and `[sp+8]` with GDB. +4. **The restore order matters**: always restore the most recently saved group first. +5. **Balanced stack operations are required**: the loop returns `sp` to its starting value on every iteration. + +--- + +## Glossary + +| Term | Definition | +| --- | --- | +| **Assembly** | Human-readable form of processor instructions. | +| **Breakpoint** | A debugger stop point. | +| **Debug Probe** | Hardware interface that lets OpenOCD communicate with the target over SWD. | +| **GDB** | GNU Debugger, used to inspect and control the running target. | +| **LR** | Link Register, holding a function return address. | +| **OpenOCD** | Debug server that bridges GDB and the Debug Probe. | +| **PC** | Program Counter, pointing to the next instruction. | +| **SP** | Stack Pointer, pointing to the top of the stack. | +| **SRAM** | Writable memory used for runtime data and the stack. | +| **XIP** | Execute In Place, executing program code directly from flash. | diff --git a/WEEK01/WEEK01a.pdf b/WEEK01/WEEK01a.pdf new file mode 100644 index 0000000..fd62f23 Binary files /dev/null and b/WEEK01/WEEK01a.pdf differ diff --git a/WEEK01/slides/WEEK01-IMG00.svg b/WEEK01/slides/WEEK01-IMG00.svg new file mode 100644 index 0000000..e666d03 --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 01 + + +Introduction and Overview of +Embedded Reverse Engineering: +Ethics, Scoping, and Basic Concepts + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK01/slides/WEEK01-IMG01.svg b/WEEK01/slides/WEEK01-IMG01.svg new file mode 100644 index 0000000..58888b0 --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG01.svg @@ -0,0 +1,112 @@ + + + + + +ARM Cortex-M33 Regs +ARM Architecture & Registers + + + + Key Registers + + + + + r0 + Arg 1 / Return + + + + r1 + Arg 2 + + + + r2 + Arg 3 + + + + r3 + Arg 4 + + + + r0-r3 Caller-saved + r4-r11 Callee-saved + + + + SP (r13) + Stack Ptr + + + + LR (r14) + Return Addr + + + + PC (r15) + Next Instr + + + + xPSR + Status Flags + + + + Function Call Flow + + + + + main() + + + + + BL + + + + func() + + + + + BX LR + + + + + + How It Works + + r0 = first argument + puts(r0) passes the + string address in r0 + + + + LR saves return addr + BL: PC+4 stored in LR + BX LR: jump back + + + + All registers: 32 bits wide + \ No newline at end of file diff --git a/WEEK01/slides/WEEK01-IMG02.svg b/WEEK01/slides/WEEK01-IMG02.svg new file mode 100644 index 0000000..3345c63 --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG02.svg @@ -0,0 +1,101 @@ + + + + + +Stack Growth Direction +ARM Stack Mechanics + + + + Before PUSH + + + 0x20082000 + SP here + + + (empty) + + + (empty) + + + (empty) + + + (empty) + + 0x20080000 + + + Grows DOWN + + + + + + After PUSH + + + 0x20082000 + + + LR value + + + r3 value + + + (empty) + + + (empty) + + SP here now = 0x20081FF8 + + SP moved down + by 8 bytes + + 0x20080000 + + + + Key Concepts + + + Full Descending + SP points to the + last pushed item + + + + PUSH: SP -= 4 + Each 32-bit val + drops SP by 4 + Two vals = -8 + + + + POP: SP += 4 + Restores values + SP moves back up + + + + Initial SP + Set by vector + table at 0x00 + StackTop=0x20082000 + \ No newline at end of file diff --git a/WEEK01/slides/WEEK01-IMG03.svg b/WEEK01/slides/WEEK01-IMG03.svg new file mode 100644 index 0000000..b684520 --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG03.svg @@ -0,0 +1,98 @@ + + + + + +RP2350 Memory Map +RP2350 Address Space + + + + Address Space + + + + + ROM Boot + 0x0000_0000 + + + + XIP Flash + 16MB max + 0x1000_0000 + + + + SRAM + 520KB total + 0x2000_0000 + + + + APB Periph + 0x4000_0000 + + + + AHB Periph + 0x5000_0000 + + + + SIO + 0xD000_0000 + + + + PPB Cortex + 0xE000_0000 + + + addr+ + + + + Key Details + + + + XIP Flash + Code runs directly + from flash via cache + Execute-In-Place + + + + + SRAM Banks + SRAM0-7: 8x64KB + SRAM8-9: 2x4KB + Stack + Heap here + + + + + Peripherals + GPIO, UART, SPI + I2C, PWM, ADC + Memory-mapped I/O + + + + + SIO + PPB + Single-cycle I/O + Debug + NVIC + \ No newline at end of file diff --git a/WEEK01/slides/WEEK01-IMG04.svg b/WEEK01/slides/WEEK01-IMG04.svg new file mode 100644 index 0000000..d6c7ad5 --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG04.svg @@ -0,0 +1,90 @@ + + + + + +Stack vs Heap in RAM +RP2350 SRAM: Data, BSS, Heap, Stack + + + + SRAM Layout + + + + 0x2008_2000 (top) + + + + STACK + Grows DOWN + SP = top of stack + + + + + + + + FREE SPACE + + + + + + + + HEAP + Grows UP + heap_base = __end__ + + + + .data + .bss + + + 0x2000_0000 (base) + + + + How To Read Sizes Quickly + + + + 1) .data size + __data_end__ - __data_start__ + init non-zero globals/statics + + + + 2) .bss size + __bss_end__ - __bss_start__ + zero/uninit globals/statics + + + + 3) Heap starts at + __end__ (aka __HeapBase) + + + + 4) Stack starts at + vector_table[0] (initial SP) + typical: 0x20082000 on RP2350 + + + + 5) Safety rule + Heap must never collide with stack + \ No newline at end of file diff --git a/WEEK01/slides/WEEK01-IMG05.svg b/WEEK01/slides/WEEK01-IMG05.svg new file mode 100644 index 0000000..7941c08 --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG05.svg @@ -0,0 +1,86 @@ + + + + + +Link Register & Return +ARM Function Calls + + + + BL Call Flow + + + + Step 1: caller + + main: BL add + + + + + + + Step 2: hardware saves LR + + LR = return addr + + + + + + + Step 3: run function + + add: ADD r0, r1 + + + + + + + Step 4: return to caller + + BX LR (jump back) + + + + Key Concepts + + + BL instruction + Branch with Link + Saves return addr + in LR (r14) + + + + BX LR + Branch to addr + stored in LR + Returns to caller + + + + Nested Calls + Must PUSH LR first + PUSH {r3, lr} + POP {r3, pc} + POP into PC = return + + + + r14 = LR + Always check LR in GDB + \ No newline at end of file diff --git a/WEEK01/slides/WEEK01-IMG06.svg b/WEEK01/slides/WEEK01-IMG06.svg new file mode 100644 index 0000000..3d6d502 --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG06.svg @@ -0,0 +1,101 @@ + + + + + +Program Counter Flow +Instruction Execution + + + + PC Execution + + + + ADDR + INSTRUCTION + + + + 0x1000 + MOV r0, #5 + PC + + + + 0x1002 + MOV r1, #3 + + + + 0x1004 + ADD r0, r1 + + + + 0x1006 + BL func + + + + 0x1010 + func: PUSH {lr} + + + + 0x1012 + SUB r0, #1 + + + + 0x1014 + POP {pc} + + + + + BL = jump to function target + POP {pc} = return to caller + + + + Key Concepts + + + r15 = PC + Points to current + instruction + 4 + Prefetch pipeline + + + + Sequential + Thumb next = +2 or +4 + depends on instruction encoding + Cortex-M33 executes Thumb only + + + + Branch + B = unconditional + BL = save LR, jump + BX = branch reg + BEQ = branch if Z=1 + not linear +2 stepping + + + + GDB tip + stepi = step 1 instr + \ No newline at end of file diff --git a/WEEK01/slides/WEEK01-IMG07.svg b/WEEK01/slides/WEEK01-IMG07.svg new file mode 100644 index 0000000..0acc44a --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG07.svg @@ -0,0 +1,110 @@ + + + + + +Little-Endian Bytes +Byte Ordering + + + + Byte Ordering + + + + 0xDEADBEEF + + + Big-Endian + MSB first + + + DE + + + AD + + + BE + + + EF + + + +0 + +1 + +2 + +3 + + + vs + + + Little-Endian + LSB first + + + EF + + + BE + + + AD + + + DE + + + +0 + +1 + +2 + +3 + + + Bytes are reversed! + Lowest address holds + least significant byte + ARM uses little-endian + + + + Key Concepts + + + ARM = Little-Endian + Cortex-M33 uses LE + by default + Also: x86, RISC-V + + + + Why It Matters + Memory dumps show + raw byte order + Must mentally flip + to get true value + + + + GDB Example + x/4xb 0x2000 + EF BE AD DE + = 0xDEADBEEF + + + + x/xw = word view + GDB auto-corrects + \ No newline at end of file diff --git a/WEEK01/slides/WEEK01-IMG08.svg b/WEEK01/slides/WEEK01-IMG08.svg new file mode 100644 index 0000000..6b7faad --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG08.svg @@ -0,0 +1,90 @@ + + + + + +XIP Flash Model +Execute-in-Place + + + + Execute-In-Place + + + + + QSPI Flash + External 16MB + + + + + + + + QSPI Controller + + + + + + + + XIP Cache + 16KB cache + + + + + + + + Cortex-M33 CPU + Fetches via PC + + + Mapped at + 0x1000_0000 + + + + Key Details + + + What is XIP? + Code stays in flash + CPU reads it as if + it were normal memory + No copy to SRAM needed + + + + QSPI Interface + 4 data lines + Fast serial flash + CLK, CS, IO0-IO3 + + + + Cache Behavior + Used for XIP flash reads + (code + const data at 0x1000....) + Not used for SRAM/peripherals + or during flash program/erase + + + + RE Insight + Dump flash via SWD + \ No newline at end of file diff --git a/WEEK01/slides/WEEK01-IMG09.svg b/WEEK01/slides/WEEK01-IMG09.svg new file mode 100644 index 0000000..7fc15e0 --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG09.svg @@ -0,0 +1,92 @@ + + + + + +ELF File Structure +Binary Format + + + + ELF File Layout (Offsets) + + Higher file offsets + + + + Section Headers + + + + .symtab + + + + .bss + + + + .data + + + + .rodata + + + + .text + Machine code + + + + Program Headers + + + + ELF Header + Magic: 7f 45 4c 46 (off 0x0000) + + + + Section Details + Mapping + + + ELF Header + Arch: ARM 32-bit + Entry point addr + Type: executable + + + + .text = code + All instructions + Maps to XIP flash + Disassemble this! + + + + .data / .rodata + .data = initialized + .rodata = constants + .bss = zeroed vars + + + + .symtab = symbols + Function names + ELF header is file metadata + not runtime address 0x1000.... + Use section VMA/LMA for memory map + \ No newline at end of file diff --git a/WEEK01/slides/WEEK01-IMG10.svg b/WEEK01/slides/WEEK01-IMG10.svg new file mode 100644 index 0000000..ecffd4b --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG10.svg @@ -0,0 +1,94 @@ + + + + + +GDB-OpenOCD Chain +Debug Toolchain + + + + Debug Toolchain + + + + + GDB + Client + + + + + TCP + :3333 + + + + OpenOCD + Server + + + + + USB + + + + CMSIS-DAP + Probe + + + + + SWD + + + + RP2350 + Target + + + + GDB Commands + + + target remote :3333 + Connect to OpenOCD + + monitor reset halt + Reset + stop at entry + + break main + Set breakpoint + + info registers + Dump all regs + + + + SWD Protocol + + + 2 wires only + SWCLK = clock + SWDIO = data + + + + Capabilities + Read/write memory + Read/write regs + Set breakpoints + Full chip control + \ No newline at end of file diff --git a/WEEK02/WEEK02-SLIDES.pdf b/WEEK02/WEEK02-SLIDES.pdf new file mode 100644 index 0000000..592fbf1 Binary files /dev/null and b/WEEK02/WEEK02-SLIDES.pdf differ diff --git a/WEEK02/WEEK02.md b/WEEK02/WEEK02.md new file mode 100644 index 0000000..ea41551 --- /dev/null +++ b/WEEK02/WEEK02.md @@ -0,0 +1,1646 @@ +# Week 2: Hello, World - Debugging and Hacking Basics: Debugging and Hacking a Basic Program for the Pico 2 + +*** +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: + +- Connect to a live embedded system using OpenOCD and GDB +- Step through code instruction by instruction and watch the stack change +- Examine memory, registers, and decode little-endian values +- Set strategic breakpoints to pause execution at key moments +- Understand why direct string assignment fails in bare-metal systems +- Write custom data directly to SRAM memory +- Hijack register values to redirect program behavior +- Modify a running program's output in real-time + +## Review from Week 1 +This week builds directly on Week 1 concepts. You should already be comfortable with: + +- **Registers** (`r0`-`r12`, SP, LR, PC) - We'll watch them change and manipulate `r0` to change program behavior +- **Memory Layout** (Flash at `0x10000000`, RAM at `0x20000000`) - Critical for understanding where we can write +- **The Stack** and how `push`/`pop` work - We'll watch this in action +- **Little-Endian** byte ordering - We'll decode values live +- **GDB basics** from Week 1's dynamic analysis section +- **Ghidra basics** from Week 1's static analysis section + +--- + +## Part 1: Understanding Live Hacking + +#### What is Live Hacking? + +**Live hacking** means modifying a program *while it's running* on real hardware. Instead of changing the source code and recompiling, we intercept the program mid-execution and change what it does on the fly. + +Think of it like this: imagine a train is heading to New York City. Live hacking is like switching the tracks while the train is moving so it goes to Los Angeles instead! + +#### Why is This Important? + +Live hacking techniques are used for: + +- **Security Research**: Finding vulnerabilities in embedded systems +- **Penetration Testing**: Testing if systems can be compromised +- **Malware Analysis**: Understanding how malicious code works +- **Debugging**: Fixing bugs in systems that can't be easily reprogrammed + +#### Real-World Application + +> **"With great power comes great responsibility!"** + +Imagine you're a security researcher testing an industrial control system at a power plant. You need to verify that an attacker couldn't: + +1. Change the values being displayed to engineers +2. Make dangerous equipment appear safe +3. Hide malicious activity from monitoring systems + +The techniques you'll learn today are *exactly* how this would be done. Understanding these attacks helps us build better defenses! + +--- + +## Part 2: Review - Memory Layout (from Week 1) + +> **REVIEW:** In Week 1, we learned about the RP2350's memory layout. This knowledge is essential for our hack! + +Before we hack, let's remember where things live in memory on the RP2350: + +#### The Code We're Hacking + +Remember our `0x0001_hello-world.c` program from Week 1: + +```c +#include +#include "pico/stdlib.h" + +int main(void) { + stdio_init_all(); + + while (true) + printf("hello, world\r\n"); +} +``` + +This simple program: + +1. Initializes I/O with `stdio_init_all()` +2. Enters an infinite `while(true)` loop +3. Prints `"hello, world\r\n"` forever + +Our goal: **Make it print something else WITHOUT changing the source code!** + +#### Memory Map + +``` ++-----------------------------------------------------+ +| Flash Memory (XIP) - READ ONLY | +| Starts at: 0x10000000 | +| Contains: Program code, constant strings | +| NOTE: We CANNOT write to flash during runtime! | ++-----------------------------------------------------+ +| SRAM - READ/WRITE | +| Starts at: 0x20000000 | +| Contains: Stack, Heap, Variables | +| NOTE: We CAN write to SRAM during runtime! | ++-----------------------------------------------------+ +``` + +> **REVIEW:** In Week 1, we saw SP values in the `0x20081xxx` range (for example `0x20081fc8`) - that's in the SRAM region. In this run you may see values like `0x20081ff8` depending on where execution is paused. The stack "grows downward" from the top of SRAM. + +#### Why This Matters for Our Hack + +The string `"hello, world"` is stored in **flash memory** (around `0x100019cc`). Flash memory is **read-only** during normal operation - we can't just overwrite it. + +But SRAM (starting at `0x20000000`) is **read-write**! This is where we'll create our hacked string. + +--- + +## Part 3: The Attack Plan + +Here's our step-by-step attack strategy: + +``` ++-----------------------------------------------------+ +| STEP 1: Start the debug server (OpenOCD) | ++-----------------------------------------------------+ +| STEP 2: Connect with GDB and halt the program | ++-----------------------------------------------------+ +| STEP 3: Set a breakpoint right before puts() | ++-----------------------------------------------------+ +| STEP 4: When we hit the breakpoint, r0 contains | +| the address of "hello, world" | ++-----------------------------------------------------+ +| STEP 5: Create our malicious string in SRAM | ++-----------------------------------------------------+ +| STEP 6: Change r0 to point to OUR string | ++-----------------------------------------------------+ +| STEP 7: Continue execution - HACKED! | ++-----------------------------------------------------+ +``` + +--- + +## Part 4: Setting Up Your Environment + +#### Prerequisites + +Before we start, make sure you have: + +1. A Raspberry Pi Pico 2 board with debug probe connected +2. OpenOCD installed and configured +3. GDB (arm-none-eabi-gdb) installed +4. A serial monitor application (like PuTTY, minicom, or screen) +5. The "hello-world" binary loaded on your Pico 2 + +#### What You'll Need Open + +You will need **THREE** terminal windows: + +1. **Terminal 1**: Running OpenOCD (the debug server) +2. **Terminal 2**: Running GDB (where we do the hacking) +3. **PuTTY**: Running your serial monitor (to see output) + +--- + +## Part 5: GDB Deep Dive - Exploring the Binary + +Before we start hacking, let's use GDB to thoroughly understand our program. This hands-on tutorial will teach you to examine memory, step through code, and watch the stack in action. + +#### Starting the Debug Environment + +##### Step 0a: Start OpenOCD (Terminal 1) + +OpenOCD is the bridge between your computer and the Pico 2's debug interface. It creates a server that GDB can connect to. + +**Open Terminal 1 and type:** + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +**What this command means:** + +- `openocd` = the OpenOCD program +- `-s ...` = path to OpenOCD scripts folder +- `-f interface/cmsis-dap.cfg` = use the CMSIS-DAP debug probe configuration +- `-f target/rp2350.cfg` = configure for the RP2350 chip +- `-c "adapter speed 5000"` = set the debug speed to 5000 kHz + +**You should see output like:** + +``` +Open On-Chip Debugger 0.12.0 +Licensed under GNU GPL v2 +Info : Listening on port 3333 for gdb connections +Info : CMSIS-DAP: SWD supported +Info : CMSIS-DAP: Interface ready +``` + +**Important:** Leave this terminal running! Don't close it. + +##### Step 0b: Start Your Serial Monitor - PuTTY (Terminal 2) + +PuTTY will show us the output from our Pico 2. When we hack the program, we'll see the results here! + +**To set up PuTTY:** + +1. Open **PuTTY** +2. In the **Session** category: + - **Connection type**: Select **Serial** + - **Serial line**: Enter your COM port (e.g., `COM3` - check Device Manager to find yours) + - **Speed**: Enter `115200` +3. Click **Open** + +> Tip: **Finding your COM port:** Open Device Manager -> Ports (COM & LPT) -> Look for "USB Serial Device" or "Pico" - note the COM number. + +**You should see:** + +``` +hello, world +hello, world +hello, world +hello, world +... +``` + +The program is running and printing `"hello, world"` in an infinite loop! + +**Important:** Leave PuTTY running! We'll watch it change when we hack the system. + +##### Step 0c: Start GDB (Terminal 3) + +**Open Terminal 3** and start GDB with your binary: + +```cmd +arm-none-eabi-gdb build\0x0001_hello-world.elf +``` + +**What this command means:** + +- `arm-none-eabi-gdb` = the ARM version of GDB +- `build\0x0001_hello-world.elf` = our compiled program with debug symbols + +**You should see:** + +``` +GNU gdb (Arm GNU Toolchain 13.2) 13.2 +Reading symbols from build\0x0001_hello-world.elf... +(gdb) +``` + +The `(gdb)` prompt means GDB is ready for commands! + +##### Step 0d: Connect GDB to OpenOCD + +Now we need to connect GDB to OpenOCD. OpenOCD is listening on port `3333`. + +**Type this command:** + +```gdb +(gdb) target extended-remote :3333 +``` + +**You should see:** + +``` +Remote debugging using :3333 +main () + at C:/Users/assem.KEVINTHOMAS/OneDrive/Documents/Embedded-Hacking/0x0001_hello-world/0x0001_hello-world.c:5 +5 stdio_init_all(); +``` + +We're connected! GDB shows us the program is currently in the `main` function. + +##### Step 0e: Halt the Running Program + +The program is still running (you can see "hello, world" still printing in PuTTY). Let's stop it: + +**Type this command:** + +```gdb +(gdb) monitor reset halt +``` + +**What this command means:** + +- `monitor` = send a command to OpenOCD (not GDB) +- `reset` = reset the processor +- `halt` = stop execution immediately + +**You should see:** + +``` +[rp2350.cm0] halted due to debug-request, current mode: Thread +xPSR: 0xf9000000 pc: 0x00000088 msp: 0xf0000000 +[rp2350.cm1] halted due to debug-request, current mode: Thread +xPSR: 0xf9000000 pc: 0x00000088 msp: 0xf0000000 +``` + +**Check PuTTY:** The "hello, world" messages should have stopped! The processor is now frozen, waiting for our commands. + +--- + +#### Exploring the Binary + +Now that we're connected and the processor is halted, let's explore! + +##### Step 1: Examine Memory Starting at XIP Base Address + +Our program code starts at address `0x10000000`. Let's look at the first 1000 instructions to find our `main` function. + +**Type this command in GDB:** + +```gdb +(gdb) x/1000i 0x10000000 +``` + +**What this command means:** + +- `x` = examine memory +- `/1000i` = show 1000 instructions +- `0x10000000` = starting address + +**What you're looking for:** + +Scroll through the output and look for something like this: + +``` +0x10000234
: push {r3, lr} +``` + +The `
` label tells us we found our main function! The address `0x10000234` is where main starts. + +##### Step 2: Examine the Main Function in Detail + +Now let's look at just the main function. We'll examine 5 instructions starting at the main function address: + +**Type this command:** + +```gdb +(gdb) x/5i 0x10000234 +``` + +**You should see:** + +``` +(gdb) x/5i 0x10000234 +=> 0x10000234
: push {r3, lr} + 0x10000236 : bl 0x1000156c + 0x1000023a : ldr r0, [pc, #8] @ (0x10000244 ) + 0x1000023c : bl 0x100015fc <__wrap_puts> + 0x10000240 : b.n 0x1000023a +``` + +**Let's understand each instruction:** + +| Address | Instruction | What It Does | +| ------------ | -------------------------------- | --------------------------------------------------- | +| `0x10000234` | `push {r3, lr}` | Save `r3` and the return address onto the stack | +| `0x10000236` | `bl 0x1000156c ` | Call the `stdio_init_all` function | +| `0x1000023a` | `ldr r0, [pc, #8]` | Load the address of our string into `r0` | +| `0x1000023c` | `bl 0x100015fc <__wrap_puts>` | Call `puts` to print our string | +| `0x10000240` | `b.n 0x1000023a` | Jump back to the `ldr` instruction (infinite loop!) | + +##### Step 3: Set a Breakpoint at Main + +A **breakpoint** is like a stop sign for your program. When the processor reaches that address, it will pause and let us examine things. + +**Type this command:** + +```gdb +(gdb) b *0x10000234 +``` + +**You should see:** + +``` +Breakpoint 1 at 0x10000234: file C:/Users/.../0x0001_hello-world.c, line 5. +Note: automatically using hardware breakpoints for read-only addresses. +``` + +**What this means:** + +- `b` = set breakpoint +- `*0x10000234` = at this exact memory address +- GDB confirms the breakpoint is set and even tells us which line of C code this corresponds to! + +##### Step 4: Continue Execution Until Breakpoint + +Now let's run the program until it hits our breakpoint: + +**Type this command:** + +```gdb +(gdb) c +``` + +**You should see:** + +``` +Continuing. + +Thread 1 "rp2350.cm0" hit Breakpoint 1, main () + at C:/Users/.../0x0001_hello-world.c:5 +5 stdio_init_all(); +``` + +**What happened:** + +- The processor ran until it reached address `0x10000234` +- It stopped right before executing the instruction at that address +- GDB shows us we're at line 5 of our C source code + +##### Step 5: Examine Instructions with Arrow + +Let's look at our instructions again: + +**Type this command:** + +```gdb +(gdb) x/5i 0x10000234 +``` + +**You should see:** + +``` +(gdb) x/5i 0x10000234 +=> 0x10000234
: push {r3, lr} + 0x10000236 : bl 0x1000156c + 0x1000023a : ldr r0, [pc, #8] @ (0x10000244 ) + 0x1000023c : bl 0x100015fc <__wrap_puts> + 0x10000240 : b.n 0x1000023a +``` + +**Notice the arrow `=>`!** This arrow shows which instruction we're about to execute. We haven't executed it yet - we're paused right before it. + +--- + +#### Understanding the Stack in Action + +##### Step 6: Examine the Stack Before Push + +Before we execute the `push` instruction, let's see what's on the stack: + +**Type this command:** + +```gdb +(gdb) x/10x $sp +``` + +**What this command means:** + +- `x` = examine memory +- `/10x` = show 10 values in hexadecimal +- `$sp` = starting at the stack pointer address + +**You should see:** + +``` +0x20082000: 0x00000000 0x00000000 0x00000000 0x00000000 +0x20082010: 0x00000000 0x00000000 0x00000000 0x00000000 +0x20082020: 0x00000000 0x00000000 +``` + +**What this shows:** + +- The stack pointer is at address `0x20082000` +- The stack is empty (all zeros) +- This is the "top" of our stack in RAM + +**Verify where this value came from (the Vector Table):** + +Now let's trace back where this initial `0x20082000` value came from. It comes from the first entry in the vector table at `0x10000000`: + +```gdb +(gdb) x/x $sp +0x20082000: 0x00000000 +(gdb) x/x 0x10000000 +0x10000000 <__vectors>: 0x20082000 +``` + +**What this shows:** + +- The first command `x/x $sp` reads one word at the stack pointer (currently `0x00000000`) +- The second command `x/x 0x10000000` reads the **first entry in the vector table** at address `0x10000000` +- That vector table entry contains `0x20082000` - this is the **initial stack pointer value**! +- The Boot ROM reads this value and puts it into the SP register when the chip starts +- This is why our SP register already has `0x20082000` before `main()` runs + +##### Step 7: Execute One Instruction (Step Into) + +Now let's execute just ONE assembly instruction: + +**Type this command:** + +```gdb +(gdb) si +``` + +**What this command means:** + +- `si` = step instruction (execute one assembly instruction) + +**You should see:** + +``` +0x10000236 5 stdio_init_all(); +``` + +Let's verify where we are: + +**Type this command:** + +```gdb +(gdb) x/5i 0x10000234 +``` + +**You should see:** + +``` +(gdb) x/5i 0x10000234 + 0x10000234
: push {r3, lr} +=> 0x10000236 : bl 0x1000156c + 0x1000023a : ldr r0, [pc, #8] @ (0x10000244 ) + 0x1000023c : bl 0x100015fc <__wrap_puts> + 0x10000240 : b.n 0x1000023a +``` + +**Notice:** The arrow `=>` has moved! We've executed the `push` instruction and are now about to execute the `bl` (branch with link) instruction. + +##### Step 8: Examine the Stack After Push + +Now let's see what the push instruction did to our stack: + +**Type this command:** + +```gdb +(gdb) x/10x $sp +``` + +**You should see:** + +``` +0x20081ff8: 0xe000ed08 0x1000018f 0x00000000 0x00000000 +0x20082008: 0x00000000 0x00000000 0x00000000 0x00000000 +0x20082018: 0x00000000 0x00000000 +``` + +**What changed:** + +- The stack pointer moved from `0x20082000` to `0x20081ff8` +- That's 8 bytes lower (2 * 4-byte values) +- Two new values appeared: `0xe000ed08` and `0x1000018f` + +##### Step 9: Verify What Was Pushed + +Let's prove that these values came from `r3` and `lr`: + +**Check `r3`:** + +```gdb +(gdb) x/x $r3 +``` + +**You should see:** + +``` +0xe000ed08: Cannot access memory at address 0xe000ed08 +``` + +This error is expected! The value `0xe000ed08` is in `r3`, and when we try to examine it as an address, that memory location isn't accessible. But we can see the value matches what's on the stack! + +**Check `lr` (Link Register):** + +```gdb +(gdb) x/x $lr +``` + +**You should see:** + +``` +0x1000018f : 0x00478849 +``` + +The value `0x1000018f` is in `lr` - this is the return address! This matches the second value on our stack. + +##### Step 10: Verify Stack Layout + +Let's look at each pushed value individually: + +**First pushed value (`r3`):** + +```gdb +(gdb) x/x $sp +``` + +**You should see:** + +``` +0x20081ff8: 0xe000ed08 +``` + +This is the value from `r3`, pushed first. + +**Second pushed value (`lr`):** + +```gdb +(gdb) x/x $sp+4 +``` + +**You should see:** + +``` +0x20081ffc: 0x1000018f +``` + +This is the value from `lr` (the return address), pushed second. + +### Understanding the Stack Diagram + +``` +Before push {r3, lr}: After push {r3, lr}: + +Address Value Address Value +--------------------- --------------------- +0x20082000 (empty) ← SP 0x20082000 (old SP location) + 0x20081ffc 0x1000018f (lr) + 0x20081ff8 0xe000ed08 (r3) ← SP +``` + +**Key Points:** + +1. The stack grows DOWNWARD (addresses get smaller) +2. The SP always points to the last item pushed +3. `r3` was pushed first, then `lr` was pushed on top of it + +--- + +#### Continuing Through the Program + +##### Step 11: Step Over the stdio_init_all Function + +We don't need to examine every instruction inside `stdio_init_all` - it's just setup code. Let's "step over" it: + +**First, verify where we are:** + +```gdb +(gdb) x/5i 0x10000234 +``` + +**You should see:** + +```gdb +(gdb) x/5i 0x10000234 + 0x10000234
: push {r3, lr} +=> 0x10000236 : bl 0x1000156c + 0x1000023a : ldr r0, [pc, #8] @ (0x10000244 ) + 0x1000023c : bl 0x100015fc <__wrap_puts> + 0x10000240 : b.n 0x1000023a +``` + +**Now step over the function call:** + +```gdb +(gdb) n +``` + +**What this command means:** + +- `n` = next (step over function calls, don't go inside them) + +**You should see:** + +``` +8 printf("hello, world\r\n"); +``` + +**Verify where we are now:** + +```gdb +(gdb) x/5i 0x10000234 +``` + +**You should see:** + +```gdb +(gdb) x/5i 0x10000234 + 0x10000234
: push {r3, lr} + 0x10000236 : bl 0x1000156c +=> 0x1000023a : ldr r0, [pc, #8] @ (0x10000244 ) + 0x1000023c : bl 0x100015fc <__wrap_puts> + 0x10000240 : b.n 0x1000023a +``` + +The arrow has moved past the function call! + +#### Understanding the LDR Instruction + +We're now at: +``` +ldr r0, [pc, #8] @ (0x10000244 ) +``` + +**What does this instruction do?** + +1. Take the current Program Counter (PC) value +2. Add 8 to it +3. Go to that memory address (`0x10000244`) +4. Load the value stored there into `r0` + +This is loading a **pointer** - the address of our "hello, world" string! + +##### Step 13: Execute the LDR and Examine `r0` + +**Execute one instruction:** + +```gdb +(gdb) si +``` + +**You should see:** + +``` +0x1000023c 8 printf("hello, world\r\n"); +``` + +**Now examine what's in `r0`:** + +```gdb +(gdb) x/x $r0 +``` + +**You should see:** + +``` +0x100019cc: 0x6c6c6568 +``` + +##### Step 14: Decoding the Mystery Value + +The value `0x6c6c6568` looks strange, but it's actually ASCII characters! Let's decode it: + +**ASCII Table Reference:** + +| Hex | Character | +| ------ | --------- | +| `0x68` | h | +| `0x65` | e | +| `0x6c` | l | +| `0x6c` | l | + +So `0x6c6c6568` = "lleh" backwards! + +**Why is it backwards?** + +This is called **little-endian** byte order. The RP2350 stores bytes in reverse order in memory. When we read them as a 32-bit value, they appear reversed. + +##### Step 15: View the Full String + +Let's tell GDB to show this as a string instead of a hex number: + +```gdb +(gdb) x/s $r0 +``` + +**What this command means:** + +- `x` = examine memory +- `/s` = show as a string +- `$r0` = at the address stored in `r0` + +**You should see:** + +``` +0x100019cc: "hello, world\r" +``` + +There's our string! The `\r` is a carriage return character (part of `\r\n`). + +> **Key Discovery:** The string `"hello, world"` is stored at address `0x100019cc` in flash memory. This is the value that gets loaded into `r0` before calling `puts()`. We'll use this knowledge in our hack! + +--- + +## Part 6: Continuing the Debug Session for the Hack + +You're right where you need to be from Part 5, so we **do not restart** OpenOCD or GDB here. + +Use this as a quick checkpoint before the hack: + +- OpenOCD is still running and listening on `:3333` +- GDB is still connected (`target extended-remote :3333` already done) +- The target is halted at a known point (or easy to re-halt) + +If your session is still live, continue directly to Part 7. + +If you got disconnected, run only this minimal recovery in GDB: + +```gdb +(gdb) target extended-remote :3333 +(gdb) monitor reset halt +``` + +After that, continue to the analysis steps below. + +--- + +## Part 7: Analyzing the Target + +> **REVIEW:** We're using the same GDB commands we learned earlier. The `x` command examines memory, and `/5i` shows 5 instructions. + +##### Step 6: Examine the Main Function + +Let's look at the main function to understand what we're dealing with: + +**Type this command:** + +```gdb +(gdb) x/5i 0x10000234 +``` + +**What this command means:** + +- `x` = examine memory (Week 1 review!) +- `/5i` = show 5 instructions +- `0x10000234` = the address of main (we found this in Week 1!) + +**You should see:** + +```gdb +(gdb) x/5i 0x10000234 + 0x10000234
: push {r3, lr} + 0x10000236 : bl 0x1000156c + 0x1000023a : ldr r0, [pc, #8] @ (0x10000244 ) +=> 0x1000023c : bl 0x100015fc <__wrap_puts> + 0x10000240 : b.n 0x1000023a +``` + +> **REVIEW:** This is the same disassembly we analyzed in Week 1! Remember: +> +> - `push {r3, lr}` saves registers to the stack +> - `bl` is "branch with link" - it calls a function and saves the return address in LR +> - `b.n` is the infinite loop that jumps back to the `ldr` instruction + +#### Understanding the Code Flow + +> **REVIEW:** In Week 1, we learned that `r0`-`r3` are used to pass arguments to functions. The first argument always goes in `r0`! + +Let's break down what happens each time through the loop: + +| Address | Instruction | What Happens | +| ------------ | ------------------ | ------------------------------------------------------ | +| `0x1000023a` | `ldr r0, [pc, #8]` | Load the address of `"hello, world"` into `r0` | +| `0x1000023c` | `bl __wrap_puts` | Call `puts()` - it reads the string address from `r0`! | +| `0x10000240` | `b.n 0x1000023a` | Jump back to the `ldr` instruction (loop forever) | + +#### How This Maps to Our C Code + +```c +while (true) + printf("hello, world\r\n"); // The compiler optimized this to puts() +``` + +The compiler: + +1. Loads the string address into `r0` (first argument) +2. Calls `puts()` (optimized from `printf()` since we're just printing a string) +3. Loops back forever with `b.n` + +**The Key Insight:** Right before the `bl __wrap_puts` instruction (at `0x1000023c`), the register `r0` contains the address of the string to print! + +If we can change what `r0` points to, we can make it print **anything we want**! + +--- + +## Part 8: Setting the Trap + +> **REVIEW:** In Week 1, we used `b main` and `b *0x10000234` to set breakpoints. Now we'll use the same technique at a more strategic location! + +##### Step 7: Set a Strategic Breakpoint + +We want to stop the program RIGHT BEFORE it calls `puts()`. That's at address `0x1000023c`. + +**Type this command:** + +```gdb +(gdb) b *0x1000023c +``` + +**What this command means:** + +- `b` = set a breakpoint (same as Week 1!) +- `*0x1000023c` = at this exact memory address (the asterisk means "address") + +**You should see:** + +``` +Breakpoint 2 at 0x1000023c: file C:/Users/assem.KEVINTHOMAS/OneDrive/Documents/Embedded-Hacking/0x0001_hello-world/0x0001_hello-world.c, line 8 +``` + +**What does "hardware breakpoints" mean?** + +Because our code is in flash memory (read-only), GDB can't insert a software breakpoint by modifying the code. Instead, it uses a special feature of the ARM processor called a **hardware breakpoint**. The processor has a limited number of these (usually 4-8), but they work on any memory type. + +##### Step 8: Continue Execution and Hit the Breakpoint + +Now let's run the program until it hits our breakpoint: + +**Type this command:** + +```gdb +(gdb) c +``` + +**What this command means:** + +- `c` = continue (run until something stops us) + +**You should see:** + +```gdb +Continuing. + +Thread 1 "rp2350.cm0" hit Breakpoint 2, 0x1000023c in main () + at C:/Users/assem.KEVINTHOMAS/OneDrive/Documents/Embedded-Hacking/0x0001_hello-world/0x0001_hello-world.c:8 +8 printf("hello, world\r\n"); +``` + +The program has stopped RIGHT BEFORE calling `puts()`! The string address is loaded into `r0`, but the function hasn't been called yet. + +##### Step 9: Verify Our Position with Disassembly + +Let's double-check where we are using the `disas` command: + +**Type this command:** + +```gdb +(gdb) disas +``` + +**What this command means:** + +- `disas` = disassemble the current function + +**You should see:** + +```gdb +(gdb) disas +Dump of assembler code for function main: + 0x10000234 <+0>: push {r3, lr} + 0x10000236 <+2>: bl 0x1000156c + 0x1000023a <+6>: ldr r0, [pc, #8] @ (0x10000244 ) +=> 0x1000023c <+8>: bl 0x100015fc <__wrap_puts> + 0x10000240 <+12>: b.n 0x1000023a + 0x10000242 <+14>: nop + 0x10000244 <+16>: adds r4, r1, r7 + 0x10000246 <+18>: asrs r0, r0, #32 +``` + +**Notice the arrow `=>`** pointing to `0x1000023c`! This confirms we're about to execute the `bl __wrap_puts` instruction. Perfect! + +--- + +## Part 9: Examining the Current State + +> **REVIEW:** In Week 1, we used `x/s $r0` to view the "hello, world" string. We also learned about **little-endian** byte ordering - remember how `0x6c6c6568` spelled "lleh" backwards? + +##### Step 10: Examine What's in r0 + +Let's see what string `r0` is currently pointing to: + +**Type this command:** + +```gdb +(gdb) x/s $r0 +``` + +**What this command means:** + +- `x` = examine memory (Week 1 review!) +- `/s` = display as a string +- `$r0` = the address stored in register `r0` + +**You should see:** + +```gdb +0x100019cc: "hello, world\r" +``` + +There it is! The register `r0` contains `0x100019cc`, which is the address of our `"hello, world"` string in flash memory. + +> **REVIEW:** This is the same address `0x100019cc` we discovered in Week 1, Step 15 when we used `x/s $r0` after executing the `ldr` instruction! + +--- + +## Part 10: The Failed Hack Attempt (Learning Why) + +##### Step 11: Try to Directly Change the String (This Will Fail!) + +Your first instinct might be to just assign a new string to `r0`. Let's try it and see what happens: + +**Type this command:** + +```gdb +(gdb) set $r0 = "hacky, world\r" +``` + +**You should see an error:** + +``` +evaluation of this expression requires the program to have a function "malloc". +``` + +**Oh no! It didn't work!** + +#### Why Did This Fail? + +This is a very important lesson! Here's what happened: + +1. When you type `"hacky, world\r"` in GDB, GDB interprets this as: "Create a new string and give me its address" + +2. To create a new string at runtime, GDB tries to allocate target memory using `malloc()`. + +3. If the target program exposed a working `malloc()` that GDB could call, this style of assignment can work. + +4. In our case, this is a bare-metal firmware image and there is no usable `malloc()` path for GDB expression evaluation, so GDB cannot create temporary storage for that string literal. + +5. That is why this command fails here, and why we switch to writing bytes directly into SRAM ourselves. + +**Let's verify nothing changed:** + +```gdb +(gdb) x/s $r0 +``` + +**You should see:** + +``` +0x100019cc: "hello, world\r" +``` + +The original string is still there. Our hack attempt failed... but we're not giving up! + +--- + +## Part 11: The Real Hack - Writing to SRAM + +##### Step 12: Understanding the Solution + +Since we can't use `malloc()`, we need to manually create our string somewhere in memory. Remember our memory map? + +- Flash (`0x10000000`): **Read-only** - can't write here +- SRAM (`0x20000000`): **Read-write** - we CAN write here! + +> **REVIEW:** In Week 1 we observed SP in the `0x20081xxx` range (for example `0x20081fc8`). In this run, after `push {r3, lr}`, you are seeing `0x20081ff8`, which is also correct and still near the top of SRAM. We'll write our string at a safer SRAM address (`0x20040000`) to avoid vector-table and stack conflicts. + +We'll write our malicious string directly to SRAM, then point `r0` to it. + +##### Step 13: Create Our Malicious String in SRAM + +We need to write 13 bytes (12 characters + null terminator) to SRAM: + +| Character | ASCII Hex | +| --------- | --------- | +| h | - | +| a | - | +| c | - | +| k | - | +| y | - | +| , | - | +| (space) | - | +| w | - | +| o | - | +| r | - | +| l | - | +| d | - | +| `\r` | - | +| \0 | - | + +**Type this command:** + +```gdb +(gdb) set {char[13]} 0x20040000 = "hacky, world" +``` + +**What this command means:** + +- `set` = modify memory +- `{char[13]}` = treat the target as an array of 13 characters +- `0x20040000` = the address where we're writing (safe SRAM offset) +- `= "hacky, world"` = the string bytes to write + +**No output means success!** + +##### Step 14: Verify Our String Was Written + +Let's confirm our malicious string is in SRAM: + +**Type this command:** + +```gdb +(gdb) x/s 0x20040000 +``` + +**You should see:** + +```gdb +0x20040000: "hacky, world" +``` + +**OUR STRING IS IN MEMORY!** + +The important thing is our string is in writable SRAM at a safe offset and ready to use. + +--- + +## Part 12: Hijacking the Register + +> **REVIEW:** In Week 1, we learned that `r0` holds the first argument to a function. When `puts()` is called, it expects `r0` to contain a pointer to the string it should print. By changing `r0`, we change what gets printed! + +##### Step 15: Change r0 to Point to Our String + +Now for the magic moment! We'll change `r0` from pointing to the original string to pointing to OUR string: + +**Type this command:** + +```gdb +(gdb) set $r0 = 0x20040000 +``` + +**What this command means:** + +- `set` = modify a value +- `$r0` = the `r0` register +- `= 0x20040000` = change it to this address (where our string is) + +**No output means success!** + +##### Step 16: Verify the Register Was Changed + +Let's confirm `r0` now points to our malicious string: + +**First, check one byte (explicit byte view):** + +```gdb +(gdb) x/bx $r0 +``` + +**You should see:** + +``` +0x20040000: 0x68 +``` + +The value `0x68` is ASCII `'h'`, the first byte of `"hacky, world"`. + +**Now check one 32-bit word (explicit word view):** + +```gdb +(gdb) x/wx $r0 +``` + +**You should see:** + +``` +0x20040000: 0x6b636168 +``` + +This is the first 4 bytes (`h a c k`) packed into one 32-bit little-endian word. + +**Now check it as a string:** + +```gdb +(gdb) x/s $r0 +``` + +**You should see:** + +``` +0x20040000: "hacky, world" +``` + +**THE HIJACK IS COMPLETE!** When `puts()` runs, it will read the string address from `r0` - which now points to our malicious string! + +--- + +## Part 13: Executing the Hack + +##### Step 17: Continue Execution + +This is the moment of truth! Let's continue the program and watch our hack take effect: + +**Type this command:** + +```gdb +(gdb) c +``` + +**You should see:** + +```gdb +Continuing. + +Thread 1 "rp2350.cm0" hit Breakpoint 2, 0x1000023c in main () + at C:/Users/assem.KEVINTHOMAS/OneDrive/Documents/Embedded-Hacking/0x0001_hello-world/0x0001_hello-world.c:8 +8 printf("hello, world\r\n"); +``` + +The program ran through one loop iteration and hit our breakpoint again. + +##### Step 18: Check Your Serial Monitor! + +**Look at Terminal 2 (your serial monitor)!** + +**You should see:** + +``` +hello, world +hello, world +hello, world +hacky, world <-- OUR HACK! +``` + + **BOOM! WE DID IT!** + +You just modified a running program on real hardware! The processor executed code that was supposed to print "hello, world" but instead printed "hacky, world" because we hijacked the data it was using! + +--- + +## Part 14: Static Analysis with Ghidra - Understanding the Hack + +Now that we've performed the hack dynamically with GDB, let's use Ghidra to understand the same concepts through static analysis. This shows how you could plan such an attack without even connecting to the hardware! + +#### Opening the Project in Ghidra + +If you haven't already set up the Ghidra project from Week 1: + +1. Launch Ghidra +2. Select **File -> New Project** -> **Non-Shared Project** +3. Name it `0x0001_hello-world` +4. Drag and drop `0x0001_hello-world.elf` into the project +5. Double-click to open in CodeBrowser +6. Click **Yes** to auto-analyze + +##### Step 1: Navigate to Main + +1. In the **Symbol Tree** panel (left side), expand **Functions** +2. Find and click on `main` + +**What you'll see in the Listing View:** + +``` + ************************************************************* + * FUNCTION + ************************************************************* + int main (void ) + assume LRset = 0x0 + assume TMode = 0x1 + int r0:4 + main XREF[3]: Entry Point (*) , + _reset_handler:1000018c (c) , + .debug_frame::00000018 (*) + 0x0001_hello-world.c:4 (2) + 0x0001_hello-world.c:5 (2) + 10000234 08 b5 push {r3,lr} + 0x0001_hello-world.c:5 (4) + 10000236 01 f0 99 f9 bl stdio_init_all _Bool stdio_init_all(void) + LAB_1000023a XREF[1]: 10000240 (j) + 0x0001_hello-world.c:7 (6) + 0x0001_hello-world.c:8 (6) + 1000023a 02 48 ldr r0=>__EH_FRAME_BEGIN__ ,[DAT_10000244 ] = "hello, world\r" + = 100019CCh + 1000023c 01 f0 de f9 bl __wrap_puts int __wrap_puts(char * s) + 0x0001_hello-world.c:7 (8) + 10000240 fb e7 b LAB_1000023a + 10000242 00 ?? 00h + 10000243 bf ?? BFh + DAT_10000244 XREF[1]: main:1000023a (R) + 10000244 cc 19 00 10 undefine 100019CCh ? -> 100019cc +``` + +**What you'll see in the Decompile View:** + +```c +int main(void) + +{ + stdio_init_all(); + do { + __wrap_puts("hello, world\r"); + } while( true ); +} +``` + +##### Step 2: Identify the Attack Point + +In our GDB hack, we set a breakpoint at `0x1000023c` - right before `bl __wrap_puts`. Let's understand why this was the perfect attack point: + +**Click on address `0x1000023c` in the Listing view.** + +Notice: + +- The instruction is `bl __wrap_puts` - a function call +- The previous instruction at `0x1000023a` loaded `r0` with the string address +- Ghidra shows `= "hello, world\r"` right in the listing! + +> **Key Insight:** Ghidra already tells us the string value! In the Listing, you can see `= "hello, world\r"` and `= 100019CCh`. This is the exact address we discovered through GDB! + +##### Step 3: Find the String in Memory + +Let's trace where the string actually lives: + +1. In the Listing view, look at address `0x1000023a`: + ``` + LAB_1000023a XREF[1]: 10000240 (j) + 0x0001_hello-world.c:7 (6) + 0x0001_hello-world.c:8 (6) + 1000023a 02 48 ldr r0=>__EH_FRAME_BEGIN__ ,[DAT_10000244 ] = "hello, world\r" + = 100019CCh + ``` + +2. **Double-click on `DAT_10000244`** to go to the data reference + +3. You'll see: + ``` + DAT_10000244 XREF[1]: main:1000023a (R) + 10000244 cc 19 00 10 undefine 100019CCh ? -> 100019cc + ``` + +4. **Double-click on `100019CCh`** to navigate to the actual string + +**You'll arrive at the string data:** + +``` + // + // .rodata + // SHT_PROGBITS [0x100019cc - 0x10001b17] + // ram:100019cc-ram:10001b17 + // + __init_array_end XREF[5]: Entry Point (*) , + __boot2_start__ frame_dummy:10000218 (*) , + __boot2_end__ main:1000023a (*) , + __EH_FRAME_BEGIN__ runtime_init:1000138a (R) , + _elfSectionHeaders::0000005c (*) + 100019cc 68 65 6c ds "hello, world\r" + 6c 6f 2c + 20 77 6f +``` + +##### Step 4: Understand Why We Needed SRAM + +Look at the string address: `0x100019cc` + +This starts with `0x10...` which means it's in **Flash memory (XIP region)**! + +| Address Range | Memory Type | Writable? | +| ------------- | ----------- | --------- | +| `0x10000000`+ | Flash (XIP) | **NO** - Read Only | +| `0x20000000`+ | SRAM | **YES** - Read/Write | + +> **This is why our direct string modification failed in GDB!** The string lives in flash memory, which is read-only at runtime. We had to create our malicious string in SRAM at a safe address (`0x20040000`) instead. + +##### Step 5: Examine Cross-References + +Ghidra's cross-reference feature shows everywhere a value is used: + +1. Navigate back to `main` (press **G**, type `main`, press Enter) +2. Click on `__wrap_puts` at address `0x1000023c` +3. Right-click and select **References -> Show References to __wrap_puts** + +This shows every place that calls `puts()`. In a larger program, you could find ALL the print statements and potentially modify any of them! + +##### Step 6: Use the Decompiler to Plan Attacks + +The Decompile view makes attack planning easy: + +```c +int main(void) + +{ + stdio_init_all(); + do { + __wrap_puts("hello, world\r"); + } while( true ); +} +``` + +From this view, you can immediately see: + +- The program loops forever (`do { } while (true)`) +- It calls `__wrap_puts()` with a string argument +- To change the output, you need to change what's passed to `puts()` + +##### Step 7: Viewing the String in the .rodata Section + +When you navigate to the string address `0x100019cc`, you'll see the string stored in the `.rodata` (read-only data) section: + +``` + // + // .rodata + // SHT_PROGBITS [0x100019cc - 0x10001b17] + // ram:100019cc-ram:10001b17 + // + __init_array_end XREF[5]: Entry Point (*) , + __boot2_start__ frame_dummy:10000218 (*) , + __boot2_end__ main:1000023a (*) , + __EH_FRAME_BEGIN__ runtime_init:1000138a (R) , + _elfSectionHeaders::0000005c (*) + 100019cc 68 65 6c ds "hello, world\r" + 6c 6f 2c + 20 77 6f +``` + +This shows the raw bytes of our string: `68 65 6c 6c 6f 2c 20 77 6f...` which spell out `"hello, world\r"` in ASCII. + +##### Step 8: Patching Data in Ghidra (Preview) + +Ghidra allows you to modify data directly in the binary! Here's how to patch the string: + +1. **Navigate to the string** at address `0x100019cc` +2. **Right-click** on the string `"hello, world\r"` in the Listing view +3. **Select** **Patch Data** from the context menu +4. **Type** your new string: `"hacky, world\r"` +5. **Press Enter** to apply the patch + +> **Important:** The new string must be the **same length or shorter** than the original! If your new string is longer, it will overwrite adjacent data and likely crash the program. + +| Original String | Patched String | Result | +| --------------- | -------------- | ------ | +| `hello, world\r` (14 bytes) | `hacky, world\r` (14 bytes) | Works perfectly | +| `hello, world\r` (14 bytes) | `PWNED!\r` (7 bytes) | Works (shorter is OK) | +| `hello, world\r` (14 bytes) | `this is a much longer string\r` | Overwrites other data! | + +After patching, you'll see the change reflected in the Listing view: + +``` + 100019cc 68 61 63 ds "hacky, world\r" + 6b 79 2c + 20 77 6f +``` + +Notice how the bytes changed: `68 65 6c 6c 6f` ("hello") became `68 61 63 6b 79` ("hacky")! + +#### Looking Ahead: Persistent Binary Patching + +> **Coming in Future Lessons:** What we've done in Ghidra so far is just a **preview** of the patch - it modifies the data in Ghidra's view, but doesn't save it back to the actual binary file. + +In future lessons, we will learn how to: + +1. **Export the patched binary** from Ghidra to create a modified `.elf` or `.bin` file +2. **Flash the patched binary** to the Pico 2, making the hack **persistent** + +The key difference: + +| Technique | Persistence | When It's Useful | +| --------- | ----------- | ---------------- | +| **GDB Live Hacking** (this week) | Temporary - lost on reset | Testing, debugging, quick exploitation | +| **Ghidra Patch Preview** (this step) | None - just visualization | Planning and verifying patches | +| **Binary Patching** (future lessons) | **Permanent** - survives reboot | Persistent backdoors, firmware mods | + +This step helps you understand the mechanics of modifying binary data. Once you're comfortable with this concept, you'll be ready to create truly persistent modifications! + +#### Comparing GDB and Ghidra Approaches + +| Task | GDB (Dynamic) | Ghidra (Static) | +| ---- | ------------- | --------------- | +| Find main address | `x/1000i 0x10000000` + search | Symbol Tree -> Functions -> main | +| Find string address | Step through `ldr`, examine `$r0` | Click on `ldr` - shows `= 100019CCh` | +| See string content | `x/s $r0` | Double-click address -> see `ds "hello, world"` | +| Identify attack point | Set breakpoints, step, observe | Read decompiled code, find function calls | +| Verify memory type | Know address ranges | Check address prefix (`0x10...` vs `0x20...`) | + +#### Why Use Both Tools? + +- **Ghidra** helps you **plan** the attack by understanding code structure +- **GDB** lets you **execute** the attack and modify live values +- Together, they form a complete reverse engineering workflow! + +#### Ghidra Tips for Attack Planning + +1. **Use the Decompiler** - It shows you the high-level logic without decoding assembly +2. **Follow Cross-References** - Find all places a function or variable is used +3. **Check Address Ranges** - Quickly identify Flash vs SRAM locations +4. **Add Comments** - Press `;` to annotate what you discover for later +5. **Rename Variables** - Right-click -> Rename to give meaningful names + +--- + +## Part 15: Summary and Review + +#### What We Accomplished + +We successfully performed a **live memory injection attack**: + +1. **Connected** to a running embedded system using OpenOCD and GDB +2. **Analyzed** the program flow to find the perfect attack point +3. **Set a breakpoint** right before the critical function call +4. **Discovered** that direct string assignment doesn't work without `malloc()` +5. **Wrote** our malicious data directly to SRAM +6. **Hijacked** the `r0` register to point to our data +7. **Executed** the hack and watched the output change! + +#### Week 1 Concepts We Applied + +| Week 1 Concept | How We Used It This Week | +| -------------- | ------------------------ | +| Memory Layout (Flash vs RAM) | We knew flash is read-only, so we wrote to SRAM | +| Registers (`r0`) | We hijacked `r0` to point to our malicious string | +| GDB `x` command | We examined memory and verified our injected string | +| GDB breakpoints (`b`) | We set a strategic breakpoint before `puts()` | +| Disassembly (`disas`) | We found the exact instruction to target | +| Little-endian | We understood how our string bytes are stored | + +#### The Attack Flow Diagram + +``` +BEFORE OUR HACK: ++-----------------+ +------------------------------+ +| r0 = 0x100019cc| ---> | Flash: "hello, world\r" | ++-----------------+ +------------------------------+ + | + ▼ + puts() prints "hello, world" + +AFTER OUR HACK: ++-----------------+ +------------------------------+ +| r0 = 0x20040000| ---> | SRAM: "hacky, world" | ++-----------------+ +------------------------------+ + | + ▼ + puts() prints "hacky, world" +``` + +#### New GDB Commands We Learned + +| Command | What It Does | New/Review | +| ---------------------------- | ----------------------------------- | ---------- | +| `target extended-remote :3333` | Connect to OpenOCD debug server | **New** | +| `monitor reset halt` | Reset and halt the processor | **New** | +| `disas` | Disassemble the current function | Review | +| `x/Ni ADDRESS` | Examine N instructions at ADDRESS | Review | +| `x/s ADDRESS` | Examine memory as a string | Review | +| `b *ADDRESS` | Set breakpoint at exact address | Review | +| `c` | Continue execution | Review | +| `set $r0 = VALUE` | Change a register's value | **New** | +| `set {char[N]} ADDR = {...}` | Write characters directly to memory | **New** | + +#### Key Memory Addresses + +| Address | What's There | Read/Write? | +| ------------ | -------------------------------- | ----------- | +| `0x10000234` | Start of `main()` function | Read-only | +| `0x1000023c` | The `bl __wrap_puts` call | Read-only | +| `0x100019cc` | Original `"hello, world"` string | Read-only | +| `0x20040000` | Safe SRAM location (hack target) | Read-Write | + +--- + +--- + +## Key Takeaways + +#### Building on Week 1 + +1. **Memory layout knowledge is power** - Understanding that flash is read-only and SRAM is read-write was essential for our hack. This directly built on Week 1's memory map lesson. + +2. **Registers control everything** - In Week 1, we watched registers change during execution. This week, we CHANGED them ourselves to alter program behavior. + +3. **GDB is a hacking tool** - The same commands we used for learning (`x`, `b`, `c`, `disas`) are the same commands used for exploitation. + +#### New Concepts + +4. **Flash is Read-Only at Runtime** - You can't modify code or constant strings in flash memory while the program runs. You must use SRAM. + +5. **Bare-Metal Means No Runtime** - Without an operating system, there's no `malloc()`, no dynamic memory allocation. You have to manage memory manually. + +6. **Registers Are the Key** - Function arguments are passed in registers (`r0`, `r1`, etc.). By changing these registers at the right moment, you can change what functions do. + +7. **Timing is Everything** - We had to set our breakpoint at exactly the right instruction. One instruction earlier, and `r0` wouldn't be loaded yet. One instruction later, and `puts()` would already have the wrong address. + +8. **This is Real Hacking** - The techniques you learned today are used by security researchers, penetration testers, and yes, attackers. Understanding these attacks helps us build more secure systems. + +--- + +## Security Implications + +#### How Would This Work in the Real World? + +Imagine an attacker with physical access to an industrial control system: + +| Scenario | Attack | +| ---------------------- | ----------------------------------------------------------------- | +| **Nuclear Centrifuge** | Change the displayed RPM from dangerous (15,000) to safe (10,000) | +| **Medical Device** | Modify dosage readings to hide an overdose | +| **Vehicle ECU** | Alter speedometer reading while car actually speeds | +| **Smart Lock** | Change the "locked" status to "unlocked" | + +#### How Do We Defend Against This? + +1. **Disable Debug Ports** - Production devices should have JTAG/SWD disabled +2. **Secure Boot** - Verify firmware hasn't been tampered with +3. **Memory Protection** - Use ARM's MPU to restrict memory access +4. **Tamper Detection** - Hardware that detects physical intrusion +5. **Encryption** - Keep sensitive data encrypted in memory + +--- + +## Glossary + +#### New Terms This Week + +| Term | Definition | +| ------------------------- | ---------------------------------------------------------------------- | +| **Bare-Metal** | Programming directly on hardware without an operating system | +| **CMSIS-DAP** | A standard debug interface protocol for ARM processors | +| **Hijack** | Taking control of a value or flow that was intended for something else | +| **Hardware Breakpoint** | A breakpoint implemented in CPU hardware, works on any memory | +| **Memory Injection** | Writing attacker-controlled data into a program's memory space | +| **OpenOCD** | Open On-Chip Debugger - software that interfaces with debug hardware | +| **Register Manipulation** | Changing the values stored in CPU registers | +| **SRAM** | Static Random Access Memory - fast, volatile, read-write memory | +| **Software Breakpoint** | A breakpoint implemented by modifying code (requires writable memory) | + +#### Review Terms from Week 1 + +| Term | Definition | Where We Used It | +| ------------------- | --------------------------------------------------------- | ---------------- | +| **Breakpoint** | A marker that pauses program execution at a specific location | Part 7 - Setting the trap | +| **Register** | Fast storage inside the processor | Part 11 - Hijacking `r0` | +| **Stack Pointer** | Register that points to the top of the stack | Part 2 - Memory layout | +| **XIP** | Execute In Place - running code directly from flash | Part 2 - Why we can't write to flash | +| **Little-Endian** | Storing the least significant byte at the lowest address | Part 10 - String storage | + + + diff --git a/WEEK02/WEEK02.pdf b/WEEK02/WEEK02.pdf new file mode 100644 index 0000000..ee6918f Binary files /dev/null and b/WEEK02/WEEK02.pdf differ diff --git a/WEEK02/slides/WEEK02-IMG00.svg b/WEEK02/slides/WEEK02-IMG00.svg new file mode 100644 index 0000000..44adb6f --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 02 + + +Hello, World - Debugging and +Hacking Basics: Debugging and Hacking +a Basic Program for the Pico 2 + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK02/slides/WEEK02-IMG01.svg b/WEEK02/slides/WEEK02-IMG01.svg new file mode 100644 index 0000000..c48c43a --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG01.svg @@ -0,0 +1,73 @@ + + + + + +Live Hacking Overview +Introduction to Live Hacking + + + + What Is Live Hacking? + + + Modify a program + WHILE it is running + on real hardware + + + + The Train Analogy + Train heading to NYC + Switch the tracks + while it moves + Now it goes to LA! + + + + Why It Matters + Security research + Penetration testing + Malware analysis + Hardware debugging + + No recompile needed! + + + + This Week's Goal + + + Target Program + hello-world.c + Prints "hello, world" + in infinite loop + + + + Our Mission + Make it print + something ELSE + without changing + the source code + + + + Tools Used + GDB = live debug + OpenOCD = HW bridge + Ghidra = analysis + + Hack the running binary + \ No newline at end of file diff --git a/WEEK02/slides/WEEK02-IMG02.svg b/WEEK02/slides/WEEK02-IMG02.svg new file mode 100644 index 0000000..718b3de --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG02.svg @@ -0,0 +1,85 @@ + + + + + +GDB Debug Session +GDB Fundamentals + + + + Setup Steps + + + + Step 1: Start OpenOCD + + openocd -s <scripts> + -f interface/cmsis-dap.cfg + -f target/rp2350.cfg + -c "adapter speed 5000" + + + Step 2: Launch GDB + + arm-none-eabi-gdb + build\0x0001_hello-world.elf + + + Step 3: Connect to target + + target extended-remote :3333 + + + Step 4: Reset + halt + + monitor reset halt + + + Step 5: Set breakpoint + + break main + + Then: continue (c) + + + + What Each Does + + + openocd + Loads probe + chip + config files + Then listens on :3333 + + + + arm-none-eabi-gdb + ARM debugger from + the embedded toolchain + + + + target extended-remote + GDB connects to + OpenOCD server + + + + monitor reset halt + Reset chip + stop + at very first instr + Clean starting state + \ No newline at end of file diff --git a/WEEK02/slides/WEEK02-IMG03.svg b/WEEK02/slides/WEEK02-IMG03.svg new file mode 100644 index 0000000..3932205 --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG03.svg @@ -0,0 +1,88 @@ + + + + + +Breakpoints +GDB Breakpoint Types + + + + How They Work + + + + Normal Execution + + + MOV r0, #5 + + + MOV r1, #3 + + + BL printf + + + With Breakpoint + + + MOV r0, #5 + + + MOV r1, #3 + STOP + + + BL printf + paused + + CPU halts BEFORE + executing breakpoint + instruction + + Now you can inspect + + + + GDB Breakpoints + + + break main + Stop at function + By symbol name + + + + break *0x10000340 + Stop at exact addr + By hex address + + + + info break + List all active + breakpoints + + + + continue (c) + Resume running + until next break + + + + delete 1 + Remove breakpoint #1 + \ No newline at end of file diff --git a/WEEK02/slides/WEEK02-IMG04.svg b/WEEK02/slides/WEEK02-IMG04.svg new file mode 100644 index 0000000..494fdda --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG04.svg @@ -0,0 +1,102 @@ + + + + + +Stack in Action +Runtime Stack Analysis + + + + Before Call + + + 0x20082000 + SP here + + + empty (0x20081FFC) + + + empty (0x20081FF8) + + + free stack space + + + unused lower space + + 0x20080000 + + Grows DOWN + + + + + + After PUSH + + + 0x20082000 + PUSH {r4, lr} + + + saved LR + + + saved r4 + + + free stack space + + + free stack space + + SP now = 0x20081FF8 + + SP moved down + by 8 bytes + GDB: x/4xw $sp + saved regs are now visible + + + + Key Points + + + PUSH saves + Preserves regs + before function + body runs + + + + POP restores + Puts values + back when func + returns + + + + Watch in GDB + x/4xw $sp + See stack data + + + + stepi + Step 1 instr + watch stack + change live + \ No newline at end of file diff --git a/WEEK02/slides/WEEK02-IMG05.svg b/WEEK02/slides/WEEK02-IMG05.svg new file mode 100644 index 0000000..83ce46a --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG05.svg @@ -0,0 +1,79 @@ + + + + + +LDR Instruction +ARM Load Instructions + + + + How LDR Works + + + + Instruction: + + LDR r0, [pc, #12] + + + Step 1: Calculate addr + + addr = PC + 12 + + + Step 2: Read memory + + value = *(addr) + + + Step 3: Load into reg + + r0 = value + + + r0 now holds the + address of our + "hello, world" string + + + + Why It Matters + + + String Loading + printf needs addr + of string in r0 + r0 = first argument + + + + PC-Relative + Address computed + relative to current + PC position + Works from any addr + + + + The Attack Point + If we change r0 + AFTER the LDR + printf prints OUR + string instead! + + + + This is the hack! + \ No newline at end of file diff --git a/WEEK02/slides/WEEK02-IMG06.svg b/WEEK02/slides/WEEK02-IMG06.svg new file mode 100644 index 0000000..52029b8 --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG06.svg @@ -0,0 +1,93 @@ + + + + + +The Attack Plan +Exploit Strategy + + + + Attack Flow (4 Steps) + + + + + 1. Break at + printf call + + + + + + + + 2. Write new + string to SRAM + + + + + + + + 3. Set r0 to + SRAM addr + + + + + + + + 4. Continue + execution + + printf reads r0, prints "hacky, world"! + + + + Normal Flow + + + + LDR r0, ="hello" + + + BL printf + + Output: + "hello, world" + + Prints original string + + + + Hacked Flow + + + + LDR r0, ="hello" + + + r0 = 0x20040000 + + + BL printf + + Output: + "hacky, world" + + Prints our string + \ No newline at end of file diff --git a/WEEK02/slides/WEEK02-IMG07.svg b/WEEK02/slides/WEEK02-IMG07.svg new file mode 100644 index 0000000..984e441 --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG07.svg @@ -0,0 +1,80 @@ + + + + + +Failed vs Real Hack +Attack Methodology + + + + Failed Attempt + + + The Bad Idea + Set r0 to point + at a string literal + like "hacky" + + + + Why It Fails + r0 only holds a + 32-bit number + Not a string itself! + + + + set $r0 = "HACK" + GDB interprets this + as an address value + pointing to garbage + + + + Result: CRASH + or prints garbage + + + + Real Hack + + + The Right Way + 1. Write string + bytes to SRAM + 2. Point r0 to + that SRAM addr + + + + GDB Commands + + + set {char[13]}0x20040000 + + + = "hacky, world" + + + set $r0 = 0x20040000 + + + + String exists in + writable SRAM + r0 points to it + + "hacky, world" printed! + \ No newline at end of file diff --git a/WEEK02/slides/WEEK02-IMG08.svg b/WEEK02/slides/WEEK02-IMG08.svg new file mode 100644 index 0000000..f9626eb --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG08.svg @@ -0,0 +1,83 @@ + + + + + +Writing to SRAM +Memory Manipulation + + + + SRAM at 0x20040000 + + + + Before (empty) + + + 00 00 00 00 00 00 00 00 + + + 00 00 00 00 00 00 00 00 + + + After writing + + + 68 61 63 6b 79 2c 20 77 + + h a c k y , w + + + GDB Command: + + + set {char[13]} + 0x20040000 = "hacky, world" + + + Verify with: + + x/s 0x20040000 + + + + Why SRAM? + + + SRAM = writable + RAM at 0x20000000 + We can write any + data here via GDB + + + + Flash = read-only + XIP at 0x10000000 + Cannot write to it + during execution + That's why we use RAM + + + + Choosing Address + 0x20040000 is safe + Far from stack + and heap regions + + + + Null terminator + \0 ends the string + \ No newline at end of file diff --git a/WEEK02/slides/WEEK02-IMG09.svg b/WEEK02/slides/WEEK02-IMG09.svg new file mode 100644 index 0000000..4cf346e --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG09.svg @@ -0,0 +1,77 @@ + + + + + +Register Hijack +Control Flow Attack + + + + Before Hijack + + + r0 loaded by LDR: + + + r0 = 0x10001234 + + Points to flash: + + + "hello, world\r\n" + + printf will read r0 + and print that string + + + + The Hijack Command + + + set $r0 = 0x20040000 + + Now r0 points to + OUR string in SRAM + instead of flash + + + + After Hijack + + + r0 now contains: + + + r0 = 0x20040000 + + Points to SRAM: + + + "hacky, world" + + + + Then: continue + printf reads r0 + Follows pointer + to 0x20040000 + Finds "hacky, world" + Prints it! + + + + Output changed + without touching code + \ No newline at end of file diff --git a/WEEK02/slides/WEEK02-IMG10.svg b/WEEK02/slides/WEEK02-IMG10.svg new file mode 100644 index 0000000..3582678 --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG10.svg @@ -0,0 +1,75 @@ + + + + + +GDB vs Ghidra +Static vs Dynamic Analysis + + + + GDB (Dynamic) + + + Live analysis + Program is running + on real hardware + + + + Capabilities + Set breakpoints + Read/write memory + Modify registers + Step instructions + Watch values change + + + + Best For + Live modification + Runtime behavior + Testing exploits + Verifying attacks + + Needs running target + + + + Ghidra (Static) + + + Offline analysis + Just the binary file + No hardware needed + + + + Capabilities + Disassembly view + Decompile to C + Find functions + Cross-references + String search + + + + Best For + Planning attacks + Understanding code + Finding targets + Mapping functions + + Works with just ELF + \ No newline at end of file diff --git a/WEEK03/GHIDRA_PATCHING_TUTORIAL.md b/WEEK03/GHIDRA_PATCHING_TUTORIAL.md new file mode 100644 index 0000000..5e2801d --- /dev/null +++ b/WEEK03/GHIDRA_PATCHING_TUTORIAL.md @@ -0,0 +1,252 @@ +# Ghidra Binary Patching Tutorial: ARM Cortex-M Thumb-2 Bytes Window Workflow + +``` ++-----------------------------------------------------------------+ +| GHIDRA BYTES WINDOW BINARY PATCHING GUIDE | +| | +| Target Architecture: ARM Cortex-M33 (ARMv8-M Main / Thumb-2) | +| Tool: Ghidra Software Reverse Engineering Suite | +| Core Technique: Listing Clear (C) -> Bytes Window Edit | +| -> Listing Disassemble (D) | +| Target Firmware: Raspberry Pi Pico 2 (RP2350) Raw Binaries | +| Classification: Defensive Firmware Security & Patching | ++-----------------------------------------------------------------+ +``` + +--- + +## 1. Executive Summary & Overview + +Binary patching is a fundamental capability in embedded systems reverse engineering and defensive firmware security. When analyzing compiled firmware images—such as raw binary files (`.bin`) extracted from bare-metal microcontrollers—security analysts and engineers frequently need to modify program logic directly in machine code without access to the original source code or a compilation toolchain. + +Common operational scenarios for embedded binary patching include: +- **Defensive Telemetry Correction:** Rectifying corrupted or spoofed sensor thresholds in mission-critical industrial or SCADA control nodes. +- **Vulnerability Remediation & Hot-Patching:** Neutralizing memory corruption flaws, logic bugs, or insecure dispatch routines in deployed firmware. +- **Hardware Reverse Engineering Challenges:** Solving embedded Capture The Flag (CTF) challenges where firmware logic gates must be manipulated to uncover security flags. + +While high-level desktop reverse engineering tutorials often recommend right-clicking an instruction and selecting **Patch Instruction**, this GUI action consistently fails when targeting **ARM Cortex-M Thumb-2** binaries. This comprehensive guide documents the underlying architectural reason for this failure and provides the official, step-by-step **Bytes Window Workflow** using high-resolution Ghidra screenshots from live analysis of `CTF-01.bin`. + +--- + +## 2. The Architectural Problem: Why GUI Patching Fails in ARM Thumb-2 + +### 2.1 The ARM Cortex-M Thumb-2 Instruction Set & IT Blocks + +The Raspberry Pi Pico 2 is powered by the dual-core **ARM Cortex-M33** microcontroller, which executes the **ARMv8-M Mainline (Thumb-2)** instruction set. In Thumb-2 mode: +1. Instructions are variable-length: either 16 bits (2 bytes) or 32 bits (4 bytes). +2. Conditional execution is handled via **If-Then (`IT`) blocks** (e.g., `it`, `ite`, `itt`). +3. An instruction immediately preceding an `IT` block sets the Condition Code Flags (e.g., `cmp r3, #0x5e`), and the subsequent `ite hi` instruction evaluates those flags to execute conditionally. + +### 2.2 The Context Collision in Ghidra's Disassembler + +When you right-click a compare instruction (such as `cmp r3, #0x5e` at offset `0x100001FC`) and select **Patch Instruction** (or press `Ctrl+Shift+G`): + +``` ++-----------------------------------------------------------------+ +| GUI PATCH INSTRUCTION FAILURE CHAIN | +| | +| 1. User invokes GUI Patch Instruction at compare site | +| 2. Ghidra PatchInstructionAction invokes ReDisassembleCommand | +| 3. ReDisassembleCommand hits subsequent 'ite' block | +| 4. Ghidra attempts to write internal ITBlock context register | +| 5. CodeManager throws: ContextChangeException | +| 6. Ghidra aborts Thumb decoding -> falls back to 32-bit ARM | +| 7. Downstream instructions collapsed into movwcs / ldmdavs | +| 8. Compare Site B (0x1000020A) completely swallowed & lost | ++-----------------------------------------------------------------+ +``` + +Ghidra maintains internal context registers (such as `TMode` for Thumb execution and `ITBlock` for conditional state tracking). When `ReDisassembleCommand` encounters an existing instruction downstream of the patch site, the context register write collides with existing database state, throwing a `ContextChangeException` (`Context register change conflicts with one or more instructions`). + +Because Ghidra's assembler aborts decoding Thumb instructions upon this conflict, it defaults to **32-bit ARM disassembly mode**. It misinterprets the 16-bit Thumb opcode bytes as 32-bit ARM instructions (`movwcs`, `ldmdavs`, `blcs`), which **swallows downstream code** and completely obliterates subsequent compare sites (such as Compare Site B at `0x1000020A`). + +### 2.3 Why 'Locking TMode = 1' (Ctrl+R) Does Not Solve the Issue + +Attempting to highlight the `.text` section and press `Ctrl+R` (**Set Register Values**) to set `TMode = 1` also fails once Ghidra has already performed its initial auto-analysis. Ghidra's database rules forbid setting context register values across memory ranges that already contain disassembled code. + +The clean, permanent, and battle-tested technique is the **Bytes Window Workflow**. + +--- + +## 3. The 5-Step Bytes Window Workflow + +The **Bytes Window Workflow** completely bypasses Ghidra's assembler context conflicts. By clearing the single instruction to undefined raw bytes, editing the underlying byte in the hex view, and then disassembling just that location, Ghidra's disassembler operates cleanly without attempting to rewrite `ITBlock` context registers over existing downstream code. + +### Step 1: Open the Bytes Window + +By default, Ghidra displays the **Listing** view and the **Decompiler** view. To inspect and edit raw hex bytes directly: + +1. Look at the top Ghidra menu bar. +2. Click **Window** in the menu. +3. Select **Bytes: .bin** (for example, **Bytes: CTF-01.bin**). + +![Ghidra Window Menu - Opening Bytes Window](img/fig1.png) +

Figure 1: Navigating to Window -> Bytes: CTF-01.bin in the Ghidra menu bar.

+ +4. Dock the **Bytes** window side-by-side with your **Listing** window so both panels are simultaneously visible on your screen. + +--- + +### Step 2: Locate the Target Instruction in the Listing Window + +Before making any modifications, navigate to the target function and identify the exact instruction bytes: + +1. Click inside the **Listing** window and press key **`G`** (Go To Address). +2. Enter the target address (for example, `0x100001FC`). +3. Observe the disassembled instruction and its opcode encoding: + +```assembly +100001fc 5e 2b cmp r3,#0x5e +``` + +![Target Instruction in Listing View](img/fig2.png) +

Figure 2: Listing view at offset 100001fc showing the initial instruction: cmp r3,#0x5e (bytes 5e 2b).

+ +#### Technical Breakdown of Opcode `5e 2b`: +In ARMv8-M Thumb-16 architecture, the compare-immediate instruction `cmp , #` is encoded as: +- Bit pattern: `0010 1nnn iiiiiiii` +- Opcode field (`00101`): Specifies `CMP` immediate. +- Register `r3` (`nnn = 011` binary): Specifies operand register `r3`. +- Upper byte: `0010 1011` binary = `0x2B`. +- Lower byte: `0x5E` (hex) = `94` decimal (the comparison threshold). +- Because ARM Cortex-M microcontrollers are **little-endian**, the low byte (`5E`) appears first in memory at address `0x100001FC`, followed by the high byte (`2B`) at address `0x100001FD`. +- To change the threshold from $94$ ($0\text{x}5E$) to $59$ ($0\text{x}3B$), we only need to alter byte `0x100001FC` from `5E` to `3B`! + +--- + +### Step 3: Clear the Code Bytes in the Listing Window (`C`) + +This is the critical step that prevents assembler context conflicts. Before editing a byte, the instruction must be cleared from Ghidra's active instruction database: + +1. In the **Listing** window, click directly on address **`100001fc`** (the row containing `cmp r3,#0x5e`). +2. Press the key **`C`** on your keyboard (or right-click and select **Clear Code Bytes**). +3. Notice that the instruction disassembly immediately clears into undefined raw bytes: + +```assembly +100001fc 5e ?? 5Eh ^ +100001fd 2b ?? 2Bh + +``` + +![Listing View after Clearing Code Bytes](img/fig3.png) +

Figure 3: Listing view after pressing 'C': the instruction at 100001fc is cleared to raw undefined bytes.

+ +By clearing the instruction first, Ghidra's database disarms the active context parser for this address. The subsequent `ite hi` instruction downstream is no longer linked to an active parsing state at this address. + +--- + +### Step 4: Enable Edit Mode and Modify the Byte in the Bytes Window + +Now that the address is cleared, modify the raw hex byte using the Bytes window: + +1. In the **Bytes** window toolbar, locate and click the **Pencil Icon** (**Toggle Edit Mode**). The icon highlights to indicate that byte-editing mode is now active. +2. Locate offset row **`100001f0`** in the Bytes window. +3. Move your cursor to column **`C`** (representing address `100001fc`). +4. Click on the byte **`5E`** and type the replacement value **`3B`**. +5. Notice that Ghidra immediately updates the byte to `3B` and highlights it in red to indicate an uncommitted edit. + +![Side-by-Side View of Listing and Bytes Window with Edit Mode Active](img/fig4.png) +

Figure 4: Side-by-side view showing the active pencil icon, the modified byte '3b' highlighted in red in the Bytes window, and the synchronized Listing window.

+ +6. Look at the **Listing** window. Address `100001fc` now reflects the new byte `3b` while remaining undefined: + +```assembly +100001fc 3b ?? 3Bh ; +100001fd 2b ?? 2Bh + +``` + +![Listing View showing Modified Byte before Disassembly](img/fig5.png) +

Figure 5: Listing view showing the modified byte '3b' at 100001fc ready for disassembly.

+ +--- + +### Step 5: Disassemble the Modified Instruction (`D`) + +With the desired byte safely written to memory, restore the disassembly: + +1. Click back into the **Listing** window. +2. Click directly on address **`100001fc`**. +3. Press the key **`D`** on your keyboard (or right-click and select **Disassemble**). +4. Ghidra immediately decodes the bytes `3b 2b` as Thumb-2 machine code: + +```assembly +100001fc 3b 2b cmp r3,#0x3b +``` + +5. **Verify Downstream Disassembly:** Inspect the instructions immediately following `0x100001FC`: + - `100001fe 8c bf ite hi` remains completely intact! + - `10000200 00 23 movhi r3,#0x0` remains intact! + - `10000202 01 23 movls r3,#0x1` remains intact! + - **Compare Site B at `0x1000020A` remains completely intact and visible!** + +No context conflict was triggered, no instructions were swallowed, and no 32-bit ARM decoding errors occurred. + +--- + +### Step 6: Repeat for Secondary Sites & Export Patched Firmware + +#### Repeating on Compare Site B: +In many defensive patches (such as `CTF-01`), redundant checks or dual comparison gates are enforced by the compiler: +1. In the **Listing** window, click address **`1000020a`** (`cmp r3,#0x5e`). +2. Press **`C`** to clear code bytes. +3. In the **Bytes** window at offset `1000020a`, change byte `5E` to **`3B`**. +4. In the **Listing** window, click back on **`1000020a`** and press **`D`** to disassemble. +5. Both Compare Site A and Compare Site B are now patched to `0x3B` (59 decimal)! + +#### Exporting the Patched Binary Image: +1. Click **File** in the Ghidra menu bar. +2. Select **Export Program** (or press key `O`). +3. Set **Format** to **Raw Bytes**. +4. Click the `...` button next to **Output File** and navigate to your project directory. +5. Set the filename (for example, `CTF-01_fixed.bin`). +6. Click **OK** to save the patched binary image to disk. + +#### Converting to UF2 and Flashing: +Convert the exported raw binary to the Raspberry Pi Pico 2 UF2 format using `uf2conv.py`: + +```bash +python3 uf2conv.py CTF-01_fixed.bin \ + --base 0x10000000 \ + --family 0xe48bff59 \ + --output CTF-01_fixed.uf2 +``` + +Hold the `BOOTSEL` button on your Raspberry Pi Pico 2, plug it into your workstation's USB port, and drag-and-drop `CTF-01_fixed.uf2` onto the `RP2350` drive to verify your patched firmware on physical hardware! + +--- + +## 4. Method Comparison & Reference Guide + +``` ++-----------------------------------------------------------------+ +| GHIDRA ARM THUMB-2 PATCHING METHODS MATRIX | +| | +| Method Downstream Safety IT-Block Safe Status | +| ----------------- ----------------- ------------- ------ | +| GUI Patch Inst. Fails (Swallows) No (Conflict) AVOID | +| TMode = 1 (Ctrl+R) Fails (Database) No (Exception) AVOID | +| Bytes Window (C/D) 100% Safe & Clean Yes (Disarmed) OPTIMAL | ++-----------------------------------------------------------------+ +``` + +
+ +### Keystroke Quick Reference Table + +| Step | Action | Window | Shortcut / Control | Result | +| :--- | :--- | :--- | :--- | :--- | +| **1** | Open Bytes Panel | Menu Bar | `Window` $\rightarrow$ `Bytes: ` | Displays raw hex grid | +| **2** | Locate Target | Listing | Press `G` $\rightarrow$ enter address | Cursor at target opcode | +| **3** | Clear Code Bytes | Listing | Press `C` | Instruction cleared to `??` | +| **4** | Enable Edit Mode | Bytes | Click **Pencil Icon** | Enables write mode | +| **5** | Modify Hex Byte | Bytes | Type new hex byte (`3B`) | Byte highlighted in red | +| **6** | Re-Disassemble | Listing | Click address $\rightarrow$ press `D` | Reassembles cleanly as Thumb-2 | +| **7** | Export Program | Menu Bar | `File` $\rightarrow$ `Export Program` | Saves patched `.bin` file | + +--- + +## 5. Key Takeaways & Best Practices + +1. **Thumb-2 Conditional State is Fragile in Ghidra:** The presence of `IT`/`ITE` blocks creates dynamic context register dependencies. High-level assembler dialogs fail because they attempt to re-evaluate downstream context over existing disassembly. +2. **Clear First (`C`), Edit Second, Disassemble Last (`D`):** Always clear the code bytes before modifying machine code in Ghidra. Clearing breaks the active context dependency graph and allows clean byte substitution. +3. **Always Verify Downstream Code:** After pressing `D`, scan the next 10 instructions to ensure downstream labels, branch targets, and conditional blocks were not disrupted. +4. **Export as Raw Bytes:** When flashing embedded microcontrollers like the RP2350, always export as **Raw Bytes** (`.bin`), never as ELF or PE, before converting to UF2. diff --git a/WEEK03/GHIDRA_PATCHING_TUTORIAL.pdf b/WEEK03/GHIDRA_PATCHING_TUTORIAL.pdf new file mode 100644 index 0000000..a3e67ba Binary files /dev/null and b/WEEK03/GHIDRA_PATCHING_TUTORIAL.pdf differ diff --git a/WEEK03/WEEK03-SLIDES.pdf b/WEEK03/WEEK03-SLIDES.pdf new file mode 100644 index 0000000..226ea8f Binary files /dev/null and b/WEEK03/WEEK03-SLIDES.pdf differ diff --git a/WEEK03/WEEK03.md b/WEEK03/WEEK03.md new file mode 100644 index 0000000..e8167db --- /dev/null +++ b/WEEK03/WEEK03.md @@ -0,0 +1,1663 @@ +# Week 3: Embedded System Analysis: Understanding the RP2350 Architecture w/ Comprehensive Firmware Analysis + +*** +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: + +- Understand how the RP2350 boots from the on-chip bootrom +- Know what the vector table is and why it's important +- Trace the complete boot sequence from power-on to `main()` +- Understand XIP (Execute In Place) and how code runs from flash +- Read and analyze the startup assembly code (`crt0.S`) +- Use GDB to examine the boot process step by step +- Use Ghidra to statically analyze the boot sequence +- Understand the difference between Thumb mode addressing and actual addresses + +## Review from Weeks 1-2 +This week builds on your GDB and Ghidra skills from previous weeks: + +- **GDB Commands** (`x`, `b`, `c`, `si`, `disas`, `i r`) - We'll use all of these to trace the boot process +- **Memory Layout** (Flash at `0x10000000`, RAM at `0x20000000`) - Understanding where code and data live +- **Registers** (`r0`-`r12`, SP, LR, PC) - We'll watch how they're initialized during boot +- **Ghidra Analysis** - Decompiling and understanding assembly in a visual tool +- **Thumb Mode** - Remember addresses with LSB=1 indicate Thumb code + +--- + +## The Code We're Analyzing + +Throughout this week, we'll continue working with our `0x0001_hello-world.c` program: + +```c +#include +#include "pico/stdlib.h" + +int main(void) { + stdio_init_all(); + + while (true) + printf("hello, world\r\n"); +} +``` + +But this week, we're going **deeper** - we'll understand everything that happens BEFORE `main()` even runs! How does the chip know where `main()` is? How does the stack get initialized? Let's find out! + +--- + +## Part 1: Understanding the Boot Process + +### What Happens When You Power On? + +When you plug in your Raspberry Pi Pico 2, a lot happens before your `main()` function runs! Think of it like waking up in the morning: + +1. **First, your alarm goes off** (Power is applied to the chip) +2. **You open your eyes** (The bootrom starts running) +3. **You check your phone** (The bootrom looks for valid code in flash) +4. **You get out of bed** (The bootrom jumps to your program) +5. **You brush your teeth, get dressed** (Startup code initializes everything) +6. **Finally, you start your day** (Your `main()` function runs!) + +Each of these steps has a corresponding piece of code. Let's explore them all! + +### The RP2350 Boot Sequence Overview + +``` ++-----------------------------------------------------------------+ +| STEP 1: Power On | +| - The Cortex-M33 core wakes up | +| - Execution begins at address 0x00000000 (Bootrom) | ++-----------------------------------------------------------------+ + ↓ ++-----------------------------------------------------------------+ +| STEP 2: Bootrom Executes (32KB on-chip ROM) | +| - This code is burned into the chip - can't be changed! | +| - It looks for valid firmware in flash memory | +| - It scans the first 4 kB of the image for a valid IMAGE_DEF | +| (Datasheet §4.1, p. 338: 32KB ROM; §5.9.5, p. 429: IMAGE_DEF) | ++-----------------------------------------------------------------+ + ↓ ++-----------------------------------------------------------------+ +| STEP 3: Flash XIP Setup (bootrom-managed) | +| - The bootrom configures the flash interface automatically | +| - Sets up XIP (Execute In Place) mode | +| - NOTE: Unlike RP2040, there is NO separate boot2 in flash! | +| (Datasheet §5.2, p. 375: "removal of a boot2 in the first | +| 256 bytes of the image") | ++-----------------------------------------------------------------+ + ↓ ++-----------------------------------------------------------------+ +| STEP 4: Vector Table & Reset Handler | +| - Bootrom reads the vector table at 0x10000000 | +| - Gets the initial stack pointer from offset 0x00 | +| - Gets the reset handler address from offset 0x04 | +| - Jumps to the reset handler! | ++-----------------------------------------------------------------+ + ↓ ++-----------------------------------------------------------------+ +| STEP 5: C Runtime Startup (crt0.S) | +| - Copies initialized data from flash to RAM | +| - Zeros out the BSS section | +| - Calls runtime_init() | +| - Finally calls main()! | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 2: The Bootrom - Where It All Begins + +### What is the Bootrom? + +The **bootrom** is a 32KB piece of code that is permanently burned into the RP2350 chip at the factory. You cannot change it - it's "mask ROM" (Read Only Memory). + +Think of the bootrom like the BIOS in your computer - it's the first thing that runs and is responsible for finding and loading your actual program. + +### Key Bootrom Facts + +| Property | Value | Description | +| ----------- | ------------- | ---------------------------------- | +| Size | 32 KB | Small but powerful | +| Location | `0x00000000` | The very first address in memory | +| Modifiable? | **NO** | Burned into silicon at the factory | +| Purpose | Boot the chip | Find and load your firmware | + +### What Does the Bootrom Do? + +1. **Initialize Hardware**: Sets up clocks, resets peripherals +2. **Check Boot Sources (Discovery)**: Scans configured boot sources (for this course: flash) to find a candidate firmware image region. +3. **Validate Firmware (Validation)**: Verifies that candidate by finding IMAGE_DEF start/end markers and parsing the block. +4. **Configure Flash**: Sets up the XIP interface +5. **Jump to Your Code**: Reads the vector table and jumps to your reset handler + +### The IMAGE_DEF Structure + +The bootrom looks for a special marker in your firmware called **IMAGE_DEF**. This tells the bootrom "Hey, there's valid code here!" + +Here's what it looks like in the Pico SDK: + +```assembly +.section .picobin_block, "a" // placed in flash +.word 0xffffded3 // PICOBIN_BLOCK_MARKER_START ← ROM looks for this! +.byte 0x42 // PICOBIN_BLOCK_ITEM_1BS_IMAGE_TYPE +.byte 0x1 // item is 1 word in size +.hword 0b0001000000100001 // SECURE mode (0x1021) +.byte 0xff // PICOBIN_BLOCK_ITEM_2BS_LAST +.hword 0x0001 // item is 1 word in size +.byte 0x0 // pad +.word 0x0 // relative pointer to next block (0 = loop to self) +.word 0xab123579 // PICOBIN_BLOCK_MARKER_END +``` + +**The magic numbers:** + +- `0xffffded3` = Start marker ("I'm a valid Pico binary!") +- `0xab123579` = End marker ("End of the header block") + +### See This Exact Block in Your ELF (Commands + Real Output) + +Use these commands to view the IMAGE_DEF bytes directly in the ELF: + +```cmd +arm-none-eabi-objdump -s --start-address=0x1000013c --stop-address=0x10000150 build/0x0001_hello-world.elf +arm-none-eabi-objdump -s --start-address=0x10000130 --stop-address=0x10000154 build/0x0001_hello-world.elf +arm-none-eabi-gdb build/0x0001_hello-world.elf -ex "x/20bx 0x1000013c" -ex quit +``` + +Actual output from this lesson build: + +```text +build/0x0001_hello-world.elf: file format elf32-littlearm + +Contents of section .text: + 1000013c 42012110 ff010000 b01b0000 793512ab B.!.........y5.. + 1000014c 4ff00000 O... + +build/0x0001_hello-world.elf: file format elf32-littlearm + +Contents of section .text: + 10000130 a0010010 90a31ae7 d3deffff 42012110 ............B.!. + 10000140 ff010000 b01b0000 793512ab 4ff00000 ........y5..O... + 10000150 1e490860 .I.` +``` + +Command 1 explained (`--start-address=0x1000013c --stop-address=0x10000150`): + +- Starts at `0x1000013c`, so it does **not** include the start marker at `0x10000138` (`d3deffff`). +- Shows IMAGE_DEF body fields and the end marker: + - `42012110` = `42 01 21 10` (item type/size + secure mode field) + - `ff010000` = last-item marker + size + pad + - `b01b0000` = next word in the block payload for this build + - `793512ab` = `PICOBIN_BLOCK_MARKER_END` (`0xab123579` in little-endian) +- `4ff00000` at `0x1000014c` is already the next instruction word after IMAGE_DEF. + +Command 2 explained (`--start-address=0x10000130 --stop-address=0x10000154`): + +- Starts earlier, so it captures context **and** both IMAGE_DEF markers. +- `a0010010 90a31ae7` = binary-info context before IMAGE_DEF. +- `d3deffff` at `0x10000138` = `PICOBIN_BLOCK_MARKER_START`. +- `793512ab` at `0x10000148` = `PICOBIN_BLOCK_MARKER_END`. +- `4ff00000 1e490860` = code words after the IMAGE_DEF block. +- This command proves the full block location for this build: `0x10000138` to `0x1000014b`. + +Important: IMAGE_DEF offset can vary by build. In this build, the start marker `d3deffff` +is at `0x10000138` (not `0x1000013c`), so always search for the marker bytes instead of +assuming a fixed address. + +--- + +## Part 3: Understanding XIP (Execute In Place) + +> **REVIEW:** In Week 1, we learned that our code lives at `0x10000000` in flash memory. We used `x/1000i 0x10000000` to find our `main` function. Now we'll understand WHY code is at this address! + +### What is XIP? + +**XIP (Execute In Place)** means the processor can run code directly from flash memory without copying it to RAM first. + +Think of it like reading a book: + +- **Without XIP**: You photocopy every page into a notebook, then read from the notebook +- **With XIP**: You just read directly from the book! + +### Why Use XIP? + +| Advantage | Explanation | +| ----------- | ------------------------------------------- | +| Saves RAM | Code stays in flash, RAM is free for data | +| Faster Boot | No need to copy entire program to RAM first | +| Simpler | Less memory management needed | + +### XIP Memory Address + +The XIP flash region starts at address `0x10000000`. This is where your compiled code lives! + +``` ++-----------------------------------------------------+ +| Address: 0x10000000 (XIP Base) | +| +-------------------------------------------------+| +| | Vector Table (first thing here!) || +| | - Stack Pointer at offset 0x00 || +| | - Reset Handler at offset 0x04 || +| | - Other exception handlers... || +| +-------------------------------------------------+| +| | Your Code || +| | - Reset handler || +| | - main() function || +| | - Other functions || +| +-------------------------------------------------+| +| | Read-Only Data || +| | - Strings like "hello, world" || +| | - Constant values || +| +-------------------------------------------------+| ++-----------------------------------------------------+ +``` + +--- + +## Part 4: The Vector Table - The CPU's Instruction Manual + +### What is the Vector Table? + +The **vector table** is a list of addresses at the very beginning of your program. It tells the CPU: + +1. Where to set the stack pointer +2. Where to start executing code (reset handler) +3. Where to go when errors or interrupts happen + +Think of it like the table of contents in a book - it tells you where to find everything! + +### Vector Table Layout + +The vector table lives at `0x10000000` and looks like this: + +| Offset | Address | Content | Description | +| ------ | ------------ | ------------ | --------------------------- | +| `0x00` | `0x10000000` | `0x20082000` | Initial Stack Pointer (SP) | +| `0x04` | `0x10000004` | `0x1000015d` | Reset Handler (entry point) | +| `0x08` | `0x10000008` | `0x1000011b` | NMI Handler | +| `0x0C` | `0x1000000C` | `0x1000011d` | HardFault Handler | + +### Understanding Thumb Mode Addressing + +**Important Concept Alert!** + +Look at the reset handler address: `0x1000015d`. Notice it ends in `d` (an odd number)? + +On ARM Cortex-M processors, all code runs in **Thumb mode**. The processor uses the **least significant bit (LSB)** of an address to indicate this: + +| LSB | Mode | Meaning | +| ---------- | ----- | ----------------------------------------- | +| `1` (odd) | Thumb | "This is Thumb code" | +| `0` (even) | ARM | "This is ARM code" (not used on Cortex-M) | + +So `0x1000015d` means: + +- The actual code is at `0x1000015c` (even address) +- The `+1` tells the processor "use Thumb mode" + +**GDB vs Ghidra:** + +- GDB shows `0x1000015d` (with Thumb bit) +- Ghidra shows `0x1000015c` (actual instruction address) +- Both are correct! They're just displaying it differently. + +--- + +## Part 5: The Linker Script - Memory Mapping + +### What is a Linker Script? + +The **linker script** tells the compiler where to put different parts of your program in memory. It's like an architect's blueprint for memory! + +### Finding the Linker Script + +On Windows with the Pico SDK 2.2.0, you'll find it at: +``` +C:\Users\\.pico-sdk\sdk\2.2.0\src\rp2_common\pico_crt0\rp2350\memmap_default.ld +``` + +### Key Parts of the Linker Script + +```ld +MEMORY +{ + INCLUDE "pico_flash_region.ld" + RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k + SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k + SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k +} +``` + +**What this means:** + +| Region | Start Address | Size | Purpose | +| --------- | ------------- | -------- | -------------------------------------------- | +| Flash | `0x10000000` | (varies) | Your code (XIP) | +| RAM | `0x20000000` | 512 KB | Main striped SRAM | +| SCRATCH_X | `0x20080000` | 4 KB | SRAM8: shared, non-striped SRAM | +| SCRATCH_Y | `0x20081000` | 4 KB | SRAM9: shared, non-striped SRAM | + +`SCRATCH_X` and `SCRATCH_Y` are linker-script names for SRAM8 and SRAM9. They +are **not hardware-assigned to Core 0 or Core 1**: both cores can access both +banks. Software may reserve either bank for per-core data to reduce bank +contention. This particular linker script selects `SCRATCH_Y` for the Core 0 +stack; that is a software allocation choice, not a property of SRAM9. + +### Where Does This Build's Core 0 Stack Come From? + +The linker script calculates the initial stack pointer: + +```ld +__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); +``` + +Let's do the math: + +- `ORIGIN(SCRATCH_Y)` = `0x20081000` +- `LENGTH(SCRATCH_Y)` = `0x1000` (4 KB) +- `__StackTop` = `0x20081000` + `0x1000` = **`0x20082000`** + +This value (`0x20082000`) is what we see at offset `0x00` in the vector table. +It is the initial Core 0 stack pointer for this build because its linker script +chose `SCRATCH_Y`; it does not reserve `SCRATCH_Y` for Core 0 in hardware. + +--- + +## Part 6: Setting Up Your Environment (GDB - Dynamic Analysis) + +> **REVIEW:** This setup is identical to Weeks 1-2. If you need a refresher on OpenOCD and GDB connection, refer back to Week 1 Part 4 or Week 2 Part 5. + +### Prerequisites + +Before we start, make sure you have: + +1. A Raspberry Pi Pico 2 board with debug probe connected +2. OpenOCD installed and configured +3. GDB (`arm-none-eabi-gdb`) installed +4. The "hello-world" binary loaded on your Pico 2 +5. Access to the Pico SDK source files (for reference) + +### Starting the Debug Session + +**Terminal 1 - Start OpenOCD:** + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +**Terminal 2 - Start GDB:** + +```cmd +arm-none-eabi-gdb build\0x0001_hello-world.elf +``` + +**Connect to target:** + +```gdb +(gdb) target extended-remote :3333 +(gdb) monitor reset halt +``` + +--- + +## Part 7: Hands-On GDB Tutorial - Examining the Vector Table + +> **REVIEW:** We're using the same `x` (examine) command from Week 1. Remember: `x/Nx` shows N hex values, `x/Ni` shows N instructions, `x/s` shows strings. + +### Step 1: Examine the Vector Table + +Let's look at the first 4 entries of the vector table at `0x10000000`: + +**Type this command:** + +```gdb +(gdb) x/4x 0x10000000 +``` + +**What this command means:** + +- `x` = examine memory (Week 1 review!) +- `/4x` = show 4 values in hexadecimal +- `0x10000000` = the address of the vector table + +**You should see:** + +``` +0x10000000 <__vectors>: 0x20082000 0x1000015d 0x1000011b 0x1000011d +``` + +### Step 2: Understanding What We See + +> **REVIEW:** In Weeks 1-2, we saw both `sp = 0x20082000` at the clean breakpoint at `main` and lower values like `0x20081fc8` or `0x20081ff8` after additional stack activity. Here we are looking at the *initial* stack pointer from the vector table before any code runs. + +Let's decode each value: + +| Address | Value | Meaning | +| ------------ | ------------ | ---------------------------------------- | +| `0x10000000` | `0x20082000` | Initial Stack Pointer - top of SCRATCH_Y | +| `0x10000004` | `0x1000015d` | Reset Handler + 1 (Thumb bit) | +| `0x10000008` | `0x1000011b` | NMI Handler + 1 (Thumb bit) | +| `0x1000000C` | `0x1000011d` | HardFault Handler + 1 (Thumb bit) | + +**Key Insight:** The stack pointer (`0x20082000`) is exactly what the linker script calculated! And all the handler addresses have their LSB set to `1` for Thumb mode. + +### Step 3: Verify the Stack Pointer Calculation + +Let's confirm our math by examining what's at `0x10000000`: + +**Type this command:** + +```gdb +(gdb) x/x 0x10000000 +``` + +**You should see:** + +``` +0x10000000 <__vectors>: 0x20082000 +``` + +This matches: + +- `SCRATCH_Y` starts at `0x20081000` +- `SCRATCH_Y` is 4 KB (`0x1000` bytes) +- `0x20081000` + `0x1000` = `0x20082000` + +--- + +## Part 8: Examining the Reset Handler + +> **REVIEW:** We used `x/5i` extensively in Weeks 1-2 to examine our `main` function. Now we'll use the same technique to examine the code that runs BEFORE `main`! + +### Step 4: Disassemble the Reset Handler + +The reset handler is where execution begins after the bootrom hands off control. Let's look at it: + +**Type this command:** + +```gdb +(gdb) x/3i 0x1000015c +``` + +**Note:** We use `0x1000015c` (even) not `0x1000015d` (odd) because we want to see the actual instructions! + +**You should see:** + +``` + 0x1000015c <_reset_handler>: mov.w r0, #3489660928 @ 0xd0000000 + 0x10000160 <_reset_handler+4>: ldr r0, [r0, #0] + 0x10000162 <_reset_handler+6>: + cbz r0, 0x1000016a +``` + +### Step 5: Understanding the Reset Handler + +Let's break down what these first three instructions do: + +**Instruction 1: `mov.w r0, #0xd0000000`** + +This loads the address `0xd0000000` into register `r0`. But what's at that address? + +That's the **SIO (Single-cycle I/O) base address**! The SIO block contains a special register called **CPUID** that tells us which core we're running on. + +**Instruction 2: `ldr r0, [r0, #0]`** + +This reads the value at address `0xd0000000` (the CPUID register) into `r0`. + +| Core | CPUID Value | +| ------ | ----------- | +| Core 0 | `0` | +| Core 1 | `1` | + +**Instruction 3: `cbz r0, 0x1000016a`** + +This is "Compare and Branch if Zero". If `r0` is `0` (meaning we're on Core 0), branch to `0x1000016a` to continue with startup. Otherwise, we're on Core 1 and need to handle that differently. + +### Why Check Which Core We're On? + +The RP2350 has **two cores**, but only **Core 0** should run the startup code! If both cores tried to initialize the same memory and peripherals, chaos would ensue. + +So the reset handler checks: + +- **Core 0?** -> Continue with startup +- **Core 1?** -> Go back to the bootrom and wait + +--- + +## Part 9: The Complete Reset Handler Flow + +### Step 6: Examine More of the Reset Handler + +Let's look at more instructions to see the full picture: + +**Type this command:** + +```gdb +(gdb) x/20i 0x1000015c +``` + +**You should see:** + +``` + 0x1000015c <_reset_handler>: mov.w r0, #3489660928 @ 0xd0000000 + 0x10000160 <_reset_handler+4>: ldr r0, [r0, #0] + 0x10000162 <_reset_handler+6>: + cbz r0, 0x1000016a + 0x10000164 : mov.w r0, #0 + 0x10000168 : + b.n 0x10000150 <_enter_vtable_in_r0> + 0x1000016a : + add r4, pc, #52 @ (adr r4, 0x100001a0 ) + 0x1000016c : ldmia r4!, {r1, r2, r3} + 0x1000016e : cmp r1, #0 + 0x10000170 : + beq.n 0x10000178 + 0x10000172 : + bl 0x1000019a + 0x10000176 : + b.n 0x1000016c + 0x10000178 : + ldr r1, [pc, #84] @ (0x100001d0 ) + 0x1000017a : + ldr r2, [pc, #88] @ (0x100001d4 ) + 0x1000017c : movs r0, #0 + 0x1000017e : + b.n 0x10000182 + 0x10000180 : stmia r1!, {r0} + 0x10000182 : cmp r1, r2 + 0x10000184 : bne.n 0x10000180 + 0x10000186 : + ldr r1, [pc, #80] @ (0x100001d8 ) + 0x10000188 : blx r1 +``` + +### Step 7: Understanding the Startup Phases + +The reset handler performs several phases: + +``` ++-----------------------------------------------------------------+ +| PHASE 1: Core Check (0x1000015c - 0x10000168) | +| - Check CPUID to see which core we're on | +| - If not Core 0, go back to bootrom | ++-----------------------------------------------------------------+ + ↓ ++-----------------------------------------------------------------+ +| PHASE 2: Data Copy Setup & Loop (0x1000016a - 0x10000176) | +| - Set up the data_cpy_table pointer and load each copy triplet | +| - Copy initialized variables from flash to RAM | ++-----------------------------------------------------------------+ + ↓ ++-----------------------------------------------------------------+ +| PHASE 3: BSS Setup & Clear (0x10000178 - 0x10000184) | +| - Load the BSS start/end addresses into r1 and r2 | +| - GDB labels those literals as `data_cpy_table+48/+52` | +| - Zero out all uninitialized global variables | ++-----------------------------------------------------------------+ + ↓ ++-----------------------------------------------------------------+ +| PHASE 4: Platform Entry Begins (0x10000186 - 0x10000188 shown) | +| - Load the runtime_init() pointer from the table | +| - Branch to runtime_init() with `blx r1` | +| - `main()` and `exit()` appear a few instructions later | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 10: Understanding the Data Copy Phase + +### What is the Data Copy Phase? + +> **REVIEW:** In Week 2, we learned that flash is read-only and SRAM is read-write. That's why the startup code must COPY initialized variables from flash to RAM - they can't be modified in flash! + +When you write C code like this: + +```c +int my_counter = 42; // Initialized global variable +``` + +The value `42` is stored in flash memory (because flash is non-volatile). But variables need to live in RAM to be modified! So the startup code **copies** these initial values from flash to RAM. + +### Step 8: Find the Data Copy Table + +The data copy table contains entries that describe what to copy where. Let's examine it: + +**Type this command:** + +```gdb +(gdb) x/12x 0x100001a0 +``` + +**You should see something like:** + +``` +0x100001a0 : 0x10001b4c 0x20000110 0x200002ac 0x10001ce8 +0x100001b0 : 0x20080000 0x20080000 0x10001ce8 0x20081000 +0x100001c0 : 0x20081000 0x00000000 0x00004770 0xe000ed08 +``` + +The data_cpy_table contains multiple entries. Each entry has three values: + +1. **Source address** (in flash) +2. **Destination address** (in RAM) +3. **End address** (where to stop copying) + +In the output above, we see: + +- **First entry**: `0x10001b4c` (source), `0x20000110` (dest), `0x200002ac` (end) +- **Second entry starts**: `0x10001ce8` (source of next entry), ... + +The table ends with an entry where the source address is `0x00000000` (which signals "no more entries"). + +### Step 9: Watch the Data Copy Loop + +The data copy loop works like this: + +``` ++---------------------------------------------+ +| 1. Load source, dest, end from table | +| 2. If source == 0, we're done | +| 3. Otherwise, copy word by word | +| 4. Go back to step 1 for next entry | ++---------------------------------------------+ +``` + +The actual code (starting at **`0x1000016c`** in the reset handler): + +```assembly +0x1000016c : ldmia r4!, {r1, r2, r3} +0x1000016e : cmp r1, #0 +0x10000170 : +beq.n 0x10000178 +0x10000172 : +bl 0x1000019a +0x10000176 : +b.n 0x1000016c +``` + +> Tip: **Note:** You can see this code in **Step 6** earlier where we examined the reset handler with `x/20i 0x1000015c`. + +--- + +## Part 11: Understanding the BSS Clear Phase + +### What is BSS? + +**BSS** stands for "Block Started by Symbol" (historical name). It's the section of memory for **uninitialized global variables**. + +When you write: + +```c +int my_counter; // Uninitialized - will be in BSS +``` + +The C standard says this variable **must start at zero**. The BSS clear phase zeros out this entire region. + +### Step 10: Examine the BSS Clear Loop + +**Type this command:** + +```gdb +(gdb) x/5i 0x10000178 +``` + +**You should see:** + +``` +0x10000178 : +ldr r1, [pc, #84] @ (0x100001d0 ) +0x1000017a : +ldr r2, [pc, #88] @ (0x100001d4 ) +0x1000017c : movs r0, #0 +0x1000017e : +b.n 0x10000182 +0x10000180 : stmia r1!, {r0} +``` + +The first two `ldr` instructions are still part of the **BSS clear setup**, even though GDB shows the source words as `data_cpy_table+48` and `data_cpy_table+52`. That label means the two literal words live in the same nearby constant block as the copy-table entries; it does **not** mean the code is still performing `.data` copies. At this point, `r1` becomes the BSS start address, `r2` becomes the BSS end address, and the loop beginning at `0x10000180` zeros that range. + +### Understanding the Loop + +``` ++---------------------------------------------+ +| r1 = start of BSS section | +| r2 = end of BSS section | +| r0 = 0 | +| | +| LOOP: | +| Store 0 at address r1 | +| Increment r1 by 4 bytes | +| If r1 != r2, repeat | ++---------------------------------------------+ +``` + +--- + +## Part 12: Examining Exception Handlers + +### Step 11: Look at the Default Exception Handlers + +What happens if an exception occurs (like a HardFault)? Let's look: + +**Type this command:** + +```gdb +(gdb) x/10i 0x10000110 +``` + +**You should see:** + +``` +0x10000110 : mrs r0, IPSR +0x10000114 : subs r0, #16 +0x10000116 : bkpt 0x0000 +0x10000118 : bkpt 0x0000 +0x1000011a : bkpt 0x0000 +0x1000011c : bkpt 0x0000 +0x1000011e : bkpt 0x0000 +0x10000120 : bkpt 0x0000 +0x10000122 : bkpt 0x0000 +0x10000124 <__default_isrs_end>: + @ instruction: 0xebf27188 +``` + +### What is `bkpt`? + +The `bkpt` instruction is a **breakpoint**. When executed, it stops the processor and triggers the debugger! + +These are the **default** exception handlers - they just stop the program so you can debug. In your own code, you can override these with real handlers. + +### Why So Many Handlers? + +Each type of exception has its own handler: + +| Handler | Purpose | +| --------------- | ------------------------------------------ | +| `isr_nmi` | Non-Maskable Interrupt (can't be disabled) | +| `isr_hardfault` | Serious error (bad memory access, etc.) | +| `isr_svcall` | Supervisor Call (used by RTOSes) | +| `isr_pendsv` | Pendable Supervisor (also for RTOSes) | +| `isr_systick` | System Timer tick interrupt | + +--- + +## Part 13: Finding Where Main is Called + +### Step 12: Trace Reset Handler to `main` + +Start at the **application** Cortex-M vector table in XIP flash. The RP2350 +bootrom occupies `0x00000000`; it is not this firmware's vector table. Word +`0x10000000` is this image's initial stack pointer, and word `0x10000004` is +the **reset-handler pointer** loaded into `pc` when the bootrom enters the +application: + +```gdb +(gdb) x/2wx 0x10000000 +0x10000000 <__vectors>: 0x20082000 0x1000015d +``` + +`0x1000015d` is a Thumb function pointer: bit 0 is set to indicate Thumb +state. Clear that bit before disassembly, so the reset handler's first +instruction address is `0x1000015c`. Then continue through startup until +`platform_entry`: + +```gdb +(gdb) x/10i 0x1000015c +(gdb) x/x 0x10000004 +0x10000004 <__vectors+4>: 0x1000015d +(gdb) b platform_entry +(gdb) c +``` + +At `platform_entry`, the three `ldr r1` / `blx r1` pairs are indirect calls: + +```text +0x10000186 : ldr r1, [pc, #80] +0x10000188 : blx r1 (first call) +0x1000018a : ldr r1, [pc, #80] +0x1000018c : blx r1 (second call) +0x1000018e : ldr r1, [pc, #80] +0x10000190 : blx r1 (third call) +``` + +### Prove That the Second Call Is `main` + +Stop at the second `blx r1`. `r1` holds the target; inspect it and then +disassemble that address: + +```gdb +(gdb) b *0x1000018c +(gdb) c + +Thread 1 "rp2350.dap.core0" hit Breakpoint 1, platform_entry () + at C:/Users/assem.KEVINTHOMAS/.pico-sdk/sdk/2.2.0/src/rp2_common/pico_crt0/crt0.S:515 +515 blx r1 +(gdb) x/x 0x1000018c +0x1000018c : 0x49144788 +(gdb) disas +Dump of assembler code for function platform_entry: + 0x10000186 <+0>: ldr r1, [pc, #80] @ (0x100001d8 ) + 0x10000188 <+2>: blx r1 + 0x1000018a <+4>: ldr r1, [pc, #80] @ (0x100001dc ) +=> 0x1000018c <+6>: blx r1 + 0x1000018e <+8>: ldr r1, [pc, #80] @ (0x100001e0 ) + 0x10000190 <+10>: blx r1 + 0x10000192 <+12>: bkpt 0x0000 + 0x10000194 <+14>: b.n 0x10000192 +End of assembler dump. +(gdb) x/x 0x1000018c +0x1000018c : 0x49144788 +(gdb) x/x $r1 +0x10000235
: 0x99f001b5 +(gdb) disas $r1 +Dump of assembler code for function main: + 0x10000234 <+0>: push {r3, lr} + 0x10000236 <+2>: bl 0x1000156c + 0x1000023a <+6>: ldr r0, [pc, #8] @ (0x10000244 ) + 0x1000023c <+8>: bl 0x100015fc <__wrap_puts> + 0x10000240 <+12>: b.n 0x1000023a + 0x10000242 <+14>: nop + 0x10000244 <+16>: adds r4, r1, r7 + 0x10000246 <+18>: asrs r0, r0, #32 +End of assembler dump. +(gdb) x/x 0x100001dc +0x100001dc : 0x10000235 +``` + +`x/x $r1` reports `0x10000235
` because Thumb function pointers have +bit 0 set. The actual instruction starts at `0x10000234`, as `disas $r1` +shows. The preceding `ldr r1, [pc, #80]` reads the literal-pool word at +`0x100001dc`; `x/x 0x100001dc` confirms that word is `0x10000235`. +This proves that the **second** indirect call enters `main()`. + +The first call performs runtime initialization. When `main()` returns, the +third call enters the SDK exit path; the following `bkpt` catches the +unexpected case where that exit path returns. + +### Step 13: Set a Breakpoint at Main + +> **REVIEW:** We've used `b main` and `b *ADDRESS` many times in Weeks 1-2. This is the same technique! + +Let's verify we understand the boot process by setting a breakpoint at main: + +**Type this command:** + +```gdb +(gdb) b main +``` + +**You should see:** + +``` +Breakpoint 1 at 0x10000234: file C:/Users/assem.KEVINTHOMAS/OneDrive/Documents/Embedded-Hacking/0x0001_hello-world/0x0001_hello-world.c, line 5. +Note: automatically using hardware breakpoints for read-only addresses. +``` + +**Now continue:** + +```gdb +(gdb) c +``` + +**You should see:** + +``` +Continuing. + +Thread 1 "rp2350.cm0" hit Breakpoint 1, main () + at C:/Users/assem.KEVINTHOMAS/OneDrive/Documents/Embedded-Hacking/0x0001_hello-world/0x0001_hello-world.c:5 +5 stdio_init_all(); +(gdb) +``` + + We've traced the entire boot process from power-on to `main()`! + +--- + +## Part 14: Understanding the Binary Info Header + +### Step 14: Examine the Binary Info Header + +Between the default ISRs and the reset handler, there's a special data structure called the **binary info header**. Let's look at it: + +**Type this command:** + +```gdb +(gdb) x/5x 0x10000138 +``` + +**You should see:** + +``` +0x10000138 <__binary_info_header_end>: 0xffffded3 0x10210142 0x000001ff 0x00001bb0 +0x10000148 <__binary_info_header_end+16>: 0xab123579 +``` + +### Decoding the Binary Info Header + +| Address | Value | Meaning | +| ------------ | ------------ | ----------------------------------------- | +| `0x10000138` | `0xffffded3` | Start marker (PICOBIN_BLOCK_MARKER_START) | +| `0x1000013c` | `0x10212142` | Image type descriptor | +| `0x10000140` | `0x000001ff` | Item header/size field | +| `0x10000144` | `0x00001bb0` | Link to next block or data | +| `0x10000148` | `0xab123579` | End marker (PICOBIN_BLOCK_MARKER_END) | + +**Why does GDB show this as instructions?** + +GDB doesn't know this is data, not code! It tries to disassemble it as Thumb instructions, which results in nonsense. This is why you'll see things like: + +```gdb +(gdb) x/i 0x10000138 +``` + +``` + 0x10000138 <__binary_info_header_end>: udf #211 @ 0xd3 +``` + +That's not real code - it's the magic number `0xffffded3` being misinterpreted! + +--- + +## Part 15: Static Analysis with Ghidra - Examining the Boot Sequence + +> **REVIEW:** In Week 1, we set up a Ghidra project and analyzed our hello-world binary. Now we'll use Ghidra to understand the boot sequence from a static analysis perspective! + +### Why Use Ghidra for Boot Analysis? + +While GDB is excellent for dynamic analysis (watching code execute), Ghidra excels at: + +- **Seeing the big picture** - Understanding code flow without running it +- **Cross-references** - Finding all places that call a function +- **Decompilation** - Seeing C-like code even for assembly routines +- **Annotation** - Adding notes and renaming functions for clarity + +### Step 15: Open Your Project in Ghidra + +> **REVIEW:** If you haven't created the project yet, refer back to Week 1 Part 5 for setup instructions. + +1. Launch Ghidra and open your `0x0001_hello-world` project +2. Double-click on the `.elf` file to open it in the CodeBrowser +3. If prompted to auto-analyze, click **Yes** + +### Step 16: Navigate to the Vector Table + +1. In the **Navigation** menu, select **Go To...** +2. Type `0x10000000` and press Enter +3. You should see the vector table data + +**What you'll see in the Listing view:** + +``` + // + // .text + // SHT_PROGBITS [0x10000000 - 0x100019cb] + // ram:10000000-ram:100019cb + // + assume spsr = 0x0 (Default) + __vectors XREF[4]: Entry Point (*) , + __flash_binary_start runtime_init_install_ram_vector_ + __VECTOR_TABLE _elfProgramHeaders::00000028 (*) , + __logical_binary_start _elfSectionHeaders::00000034 (*) + 10000000 00 undefine 00h + 10000001 20 ?? 20h + 10000002 08 ?? 08h + 10000003 20 ?? 20h + 10000004 5d ?? 5Dh ] ? -> 1000015d + 10000005 01 ?? 01h + 10000006 00 ?? 00h + 10000007 10 ?? 10h + 10000008 1b ?? 1Bh ? -> 1000011b + 10000009 01 ?? 01h + 1000000a 00 ?? 00h + 1000000b 10 ?? 10h + 1000000c 1d ?? 1Dh ? -> 1000011d + 1000000d 01 ?? 01h + 1000000e 00 ?? 00h + 1000000f 10 ?? 10h +... +``` + +> Tip: **Notice:** Ghidra shows the vector table data as individual bytes by default. You can see it has labeled the start as `__vectors`, `__flash_binary_start`, `__VECTOR_TABLE`, and `__logical_binary_start`. The arrows (like `? -> 1000015d`) show that Ghidra recognizes these bytes as pointers to code addresses! To see the data formatted as 32-bit addresses instead of bytes, you can right-click and retype the data. + +### Step 17: Navigate to the Reset Handler + +1. In the Symbol Tree panel (left side), expand **Functions** +2. Find and click on `_reset_handler` (or search for it) +3. Alternatively, double-click on `_reset_handler` in the vector table listing + +**What you'll see in the Decompile view (right panel):** + +Ghidra will show you a decompiled version of the reset handler. While it won't be perfect C code (since this is hand-written assembly), it helps visualize the flow: + +```c +void _reset_handler(void) + +{ + bool bVar1; + undefined4 uVar2; + int iVar3; + undefined4 *puVar4; + int *piVar5; + int *piVar6; + int *piVar7; + + if (_DAT_d0000000 != 0) { + _DAT_e000ed08 = 0; + bVar1 = (bool)isCurrentModePrivileged(); + if (bVar1) { + setMainStackPointer(_gpio_set_function_masked64); + } + /* WARNING: Could not recover jumptable at 0x1000015a. Too many branches */ + /* WARNING: Treating indirect jump as call */ + (*pcRam00000004)(8,_gpio_set_function_masked64); + return; + } + piVar5 = &data_cpy_table; + uVar2 = 0; + while( true ) { + iVar3 = *piVar5; + piVar6 = piVar5 + 1; + piVar7 = piVar5 + 2; + piVar5 = piVar5 + 3; + if (iVar3 == 0) break; + uVar2 = data_cpy(uVar2,iVar3,*piVar6,*piVar7); + } + for (puVar4 = (undefined4 *)&__TMC_END__; puVar4 != (undefined4 *)&end; puVar4 = puVar4 + 1) { + *puVar4 = 0; + } + runtime_init(); + iVar3 = main(); + /* WARNING: Subroutine does not return */ + exit(iVar3); +} +``` + +### Step 18: Trace the Path to Main + +Use the same evidence chain as GDB, but statically in the Listing view: + +1. In the Symbol Tree, find the `main` function at `0x10000234`. +2. Right-click `main` and select **References -> Show References to main**. +3. Double-click the reference at `0x1000018c` to jump to the second `blx r1`. +4. Select the instruction immediately above it: `ldr r1,[DAT_100001dc]` at + `0x1000018a`. +5. Double-click `DAT_100001dc`, or press **G** and enter `0x100001dc`. + +**You should see:** + +| Location | Type | Label | +| ------------------------- | ---- | ------------------ | +| `1000018c` | CALL | `blx r1` (to main) | + +At `0x100001dc`, Ghidra shows the literal-pool value `0x10000235`. That is +the Thumb function pointer loaded into `r1` immediately before the call. +Clear bit 0 to obtain the actual first instruction address: + +```text +0x10000235 (Thumb function pointer; bit 0 is set) +0x10000234 (main's first instruction; bit 0 cleared) +``` + +This proves the second indirect call at `0x1000018c` reaches `main`. + +### Step 19: Examine Platform Entry + +In Ghidra, look at `platform_entry`: + +**Listing View:** +``` + platform_entry + crt0.S:512 (2) + 10000186 14 49 ldr r1,[DAT_100001d8 ] = 1000137Dh + crt0.S:513 (2) + 10000188 88 47 blx r1=>runtime_init void runtime_init(void) + crt0.S:514 (2) + 1000018a 14 49 ldr r1,[DAT_100001dc ] = 10000235h + crt0.S:515 (2) + 1000018c 88 47 blx r1=>main int main(void) + crt0.S:516 (2) + 1000018e 14 49 ldr r1,[DAT_100001e0 ] = 10001375h + crt0.S:517 (2) + 10000190 88 47 blx r1=>exit void exit(int status) + LAB_10000192 XREF[1]: 10000194 (j) + crt0.S:521 (2) + 10000192 00 be bkpt 0x0 + crt0.S:522 (2) + 10000194 fd e7 b LAB_10000192 +``` + +The `DAT_100001dc = 10000235h` annotation is the static proof. The preceding +`ldr` loads that Thumb function pointer into `r1`; the following `blx r1` at +`0x1000018c` calls it. Ghidra clears the Thumb bit and labels the target +`main` at `0x10000234`. + +> **Key Insight:** The reset vector identifies the reset handler, the reset +> handler reaches `platform_entry`, and this literal-pool entry proves that +> `platform_entry`'s second indirect call reaches `main`. + +### Step 20: Create a Boot Sequence Graph + +Ghidra can visualize the call flow: + +1. With `_reset_handler` selected, go to **Window -> Function Call Graph** +2. This shows a visual graph of all function calls from the reset handler +3. You will see `_reset_handler` at the top with arrows going down to its four direct callees: `data_cpy`, `runtime_init`, `main`, and `exit` + +### Comparing GDB and Ghidra for Boot Analysis + +| Aspect | GDB (Dynamic) | Ghidra (Static) | +| ------ | ------------- | --------------- | +| **Sees runtime values** | Yes - register contents, memory | No - must infer from code | +| **Needs hardware** | Yes - Pico 2 must be connected | No - works offline | +| **Shows code flow** | Step-by-step execution | Full graph visualization | +| **Best for** | Watching what happens | Understanding structure | +| **Thumb bit handling** | Shows with +1 (0x1000015d) | Shows actual addr (0x1000015c) | + +### Ghidra Tips for Boot Analysis + +1. **Rename functions** - Right-click and rename unclear labels for future reference +2. **Add comments** - Press `;` to add inline comments explaining code +3. **Set data types** - Help Ghidra understand structures like the vector table +4. **Use bookmarks** - Mark important locations with **Ctrl+D** + +--- + +## Part 16: Summary and Review + +### The Complete Boot Sequence + +``` ++-----------------------------------------------------------------+ +| 1. POWER ON | +| Cortex-M33 begins at 0x00000000 (bootrom) | ++-----------------------------------------------------------------+ +| 2. BOOTROM | +| - Initializes hardware | +| - Configures flash XIP (no separate boot2 on RP2350) | +| - Finds IMAGE_DEF within first 4 kB of flash image | ++-----------------------------------------------------------------+ +| 3. VECTOR TABLE (0x10000000) | +| - Reads SP from offset 0x00 -> 0x20082000 | +| - Reads Reset Handler from offset 0x04 -> 0x1000015d | ++-----------------------------------------------------------------+ +| 4. RESET HANDLER (0x1000015c) | +| - Checks CPUID (Core 0 continues, Core 1 waits) | +| - Copies .data from flash to RAM | +| - Zeros .bss section | ++-----------------------------------------------------------------+ +| 5. PLATFORM ENTRY (0x10000186) | +| - Calls runtime_init() | +| - Calls main() | +| - Calls exit() when main returns | ++-----------------------------------------------------------------+ +| 6. YOUR CODE RUNS! | +| main() at 0x10000234 | ++-----------------------------------------------------------------+ +``` + +### Key Addresses to Remember + +| Address | What's There | +| ------------ | ---------------------------------------- | +| `0x00000000` | Bootrom (32KB, read-only) | +| `0x10000000` | Vector table / XIP flash start | +| `0x1000015c` | Reset handler (`_reset_handler`) | +| `0x10000234` | Your `main()` function | +| `0x20000000` | Start of RAM | +| `0x20082000` | Initial stack pointer (top of SCRATCH_Y) | +| `0xd0000000` | SIO base (CPUID register) | + +### Weeks 1-2 Concepts We Applied + +| Previous Concept | How We Used It This Week | +| ---------------- | ------------------------ | +| Memory Layout (Flash/RAM) | Understood why data must be copied from flash to RAM | +| GDB `x` command | Examined vector table, reset handler, and boot code | +| Breakpoints (`b`) | Set breakpoints to trace the boot sequence | +| Thumb Mode Addresses | Recognized LSB=1 means Thumb code in vector table | +| Stack Pointer | Saw how SP is initialized from the vector table | +| Ghidra Analysis | Used decompiler to understand boot flow | + +### GDB Commands Reference + +| Command | What It Does | New/Review | +| ---------------- | --------------------------------- | ---------- | +| `x/Nx ADDRESS` | Examine N hex values at ADDRESS | Review | +| `x/Ni ADDRESS` | Examine N instructions at ADDRESS | Review | +| `b main` | Set breakpoint at main function | Review | +| `b *ADDRESS` | Set breakpoint at exact address | Review | +| `si` | Step one instruction | Review | +| `c` | Continue execution | Review | +| `info registers` | Show all register values | Review | +| `monitor reset halt` | Reset and halt the target | Review | + +### Key Concepts + +| Concept | Definition | +| ---------------- | ----------------------------------------------------- | +| **Bootrom** | 32KB factory-programmed ROM that initializes the chip | +| **Vector Table** | List of addresses for SP and exception handlers | +| **XIP** | Execute In Place - running code directly from flash | +| **Thumb Mode** | ARM's compact instruction set (LSB=1 in addresses) | +| **BSS** | Section for uninitialized globals (must be zeroed) | +| **crt0.S** | C Runtime startup assembly file | +| **Reset Handler**| First function called after power-on/reset | +| **CPUID** | Register identifying which CPU core is executing | + +### Ghidra Actions We Used + +| Action | How to Access | Purpose | +| ------ | ------------- | ------- | +| Go To Address | Navigation -> Go To... | Jump to specific memory address | +| Show References | Right-click -> References -> Show References to | Find all callers of a function | +| Function Call Graph | Window -> Function Call Graph | Visualize call flow | +| Add Comment | Press `;` | Document your analysis | +| Rename Symbol | Right-click -> Rename | Give meaningful names to functions | + +--- + +--- + +## Key Takeaways + +### Building on Weeks 1-2 + +1. **GDB skills compound** - The `x`, `b`, `si`, and `disas` commands you learned in Weeks 1-2 are essential for understanding the boot process. Each week adds new applications for the same core skills. + +2. **Memory layout is fundamental** - Understanding flash vs RAM from Week 2 explains why startup code must copy data and zero BSS. + +3. **Ghidra complements GDB** - Dynamic analysis (GDB) shows what happens at runtime; static analysis (Ghidra) reveals the overall structure. Use both together! + +### New Concepts This Week + +4. **The boot process is deterministic** - Every RP2350 boots the same way, and understanding this helps you debug startup problems. + +5. **The bootrom can't be changed** - It's burned into silicon. Security features depend on this immutability. + +6. **The vector table is critical** - It tells the CPU where to start and how to handle errors. + +7. **Thumb mode uses the LSB** - Address `0x1000015d` means "run Thumb code at `0x1000015c`". + +8. **Startup code does essential work** - Copying data, zeroing BSS, and initializing the runtime all happen before `main()`. + +9. **Only Core 0 runs startup** - Core 1 waits in the bootrom until explicitly started. + +--- + +## Security Implications + +### How Boot Sequence Knowledge Applies to Security + +Understanding the boot process is critical for both attackers and defenders. Knowledge of how the RP2350 boots reveals potential attack vectors and defense strategies. + +#### Attack Scenarios + +| Scenario | Attack | Boot Process Knowledge Required | +| -------- | ------ | ------------------------------- | +| **Firmware Replacement** | Replace the entire flash image with malicious firmware | Understanding IMAGE_DEF structure and how bootrom validates firmware | +| **Vector Table Hijacking** | Modify the reset handler address to point to malicious code | Knowing the vector table location at `0x10000000` | +| **Bootrom Exploitation** | Find bugs in the immutable bootrom to bypass security | Understanding bootrom behavior and sequence | +| **Debug Port Attack** | Use SWD/JTAG to dump firmware or inject code | Knowledge of how to halt and examine the boot process | +| **Startup Code Modification** | Change how data is copied or BSS is cleared | Understanding crt0 and runtime_init sequences | + +#### Real-World Applications + +**Industrial Control Systems:** + +- An attacker with physical access could replace firmware to hide malicious behavior +- Understanding the boot sequence helps identify the earliest point where security checks can be added + +**IoT Devices:** + +- Compromised boot code could establish backdoors before the main application runs +- Secure boot implementations verify the vector table and reset handler integrity + +**Medical Devices:** + +- Boot-time attacks could modify critical safety parameters before device operation +- Understanding initialization helps implement tamper detection + +### Defense Strategies + +#### 1. Secure Boot Implementation + +``` ++-----------------------------------------------------+ +| SECURE BOOT FLOW | ++-----------------------------------------------------+ +| Bootrom (immutable) | +| ↓ | +| Verify IMAGE_DEF signature | +| ↓ | +| Verify application image signature | +| ↓ | +| If all valid: Jump to reset handler | +| If any invalid: Refuse to boot | ++-----------------------------------------------------+ +``` + +**Implementation:** Use cryptographic signatures to verify each boot stage before execution. + +#### 2. Debug Port Protection + +- **Production devices:** Permanently disable SWD/JTAG in final products +- **Debug authentication:** Require cryptographic challenge-response before allowing debug access +- **Fuses:** Blow hardware fuses to disable debug ports permanently + +#### 3. Flash Protection + +- **Read protection:** Enable flash read protection to prevent dumping firmware +- **Write protection:** Make critical boot sectors write-protected after initial programming +- **Encrypted storage:** Store firmware encrypted in flash + +#### 4. Memory Protection Unit (MPU) + +Configure the Cortex-M33's MPU to: + +- Mark code regions as execute-only (no reading code as data) +- Separate privileged and unprivileged memory regions +- Prevent execution from RAM regions (defend against code injection) + +#### 5. Boot-Time Integrity Checks + +```c +// Early in reset handler or runtime_init +void verify_boot_integrity(void) { + // Check vector table hasn't been modified + uint32_t vector_table_checksum = calculate_checksum(0x10000000, VECTOR_TABLE_SIZE); + if (vector_table_checksum != EXPECTED_CHECKSUM) { + // Vector table tampered - refuse to boot + secure_halt(); + } + + // Check critical data structures + // Verify stack pointer is in valid range + // etc. +} +``` + +#### 6. Anti-Tampering Hardware + +- **Tamper detection:** Sensors that detect case opening or voltage glitching +- **Response actions:** Erase sensitive keys, refuse to boot, or alert monitoring systems +- **Secure elements:** Store cryptographic keys in separate tamper-resistant chips + +### Lessons for Defenders + +1. **The bootrom is your trust anchor** - Its immutability makes it the foundation of security. RP2350's secure boot features leverage this. + +2. **Early is critical** - Security checks in the reset handler or runtime_init run before any application code, making them harder to bypass. + +3. **Defense in depth** - Multiple layers (hardware fuses, encrypted storage, secure boot, MPU) make attacks much harder. + +4. **Physical access = game over** - If an attacker can connect a debug probe, they can potentially compromise the device. Physical security matters! + +5. **Know your boot sequence** - Understanding exactly what runs when helps you identify where to add security checks and what assets need protection. + +### Security Research Value + +For security researchers and penetration testers, boot sequence analysis helps: + +- **Find vulnerabilities:** Many security bugs exist in startup code that runs before normal security checks +- **Develop exploits:** Understanding memory layout and initialization is essential for exploit development +- **Assess attack surface:** Knowing what's accessible at boot time reveals potential attack vectors +- **Build better defenses:** You can't defend what you don't understand + +> **"To know your enemy, you must become your enemy."** - Sun Tzu + +Understanding how an attacker would analyze and exploit the boot sequence is essential for building robust defenses. + +--- + +## Glossary + +### New Terms This Week + +| Term | Definition | +| ----------------- | ----------------------------------------------------------------------- | +| **Bootrom** | Factory-programmed ROM containing first-stage bootloader | +| **BSS** | Block Started by Symbol - section for uninitialized global variables | +| **CPUID** | Register that identifies which CPU core is executing | +| **crt0** | C Runtime Zero - the startup code that runs before main | +| **IMAGE_DEF** | Structure that marks valid firmware for the bootrom | +| **Linker Script** | File that defines memory layout for the compiled program | +| **Reset Handler** | First function called after reset/power-on | +| **Thumb Mode** | Compact instruction encoding used by Cortex-M | +| **Vector Table** | Array of addresses for stack pointer and exception handlers | +| **VTOR** | Vector Table Offset Register - tells CPU where to find the vector table | +| **XIP** | Execute In Place - running code directly from flash memory | + +### Review Terms from Weeks 1-2 + +| Term | Definition | How We Used It | +| ---- | ---------- | -------------- | +| **Breakpoint** | Marker that pauses program execution | Set at reset handler and main | +| **Register** | Fast storage inside the processor | Watched SP, LR, PC during boot | +| **Stack Pointer** | Register pointing to top of stack | Saw initial value in vector table | +| **Flash Memory** | Read-only storage for code | Contains vector table and boot code | +| **SRAM** | Read-write memory for data | Where stack and variables live | + +--- + +## Additional Resources + +### RP2350 Datasheet + +For more details on the boot process, see Chapter 5 of the RP2350 Datasheet: +https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf + +### Pico SDK Source Code + +The startup code lives in: + +- `crt0.S` - Main startup assembly (vector table at `.section .vectors`, reset handler, data copy, BSS clear, platform_entry) +- `memmap_default.ld` - Default linker script (section ordering: `.vectors` -> `.binary_info_header` -> `.embedded_block` -> `.reset`) +- `embedded_start_block.inc.S` - IMAGE_DEF block (replaces RP2040's `boot2_generic_03h.S`) + +> **Note:** The RP2040 used a `boot2_generic_03h.S` second-stage bootloader occupying the first 256 bytes of flash. The RP2350 eliminated this; the bootrom handles flash XIP setup directly. The SDK still includes a `boot2` mechanism for compatibility, but it is **not** placed at flash address 0 - it is embedded in the data copy table and executed from the stack during startup. + +### Bootrom Source + +The bootrom source is available at: +https://github.com/raspberrypi/pico-bootrom-rp2350 + +--- + +## Part 17: Proving the Boot Sequence with objdump + +Everything we have learned about the boot sequence can be proven directly from the compiled ELF binary using `arm-none-eabi-objdump`. The bootrom is not in your ELF (it is mask ROM burned into the chip at `0x00000000`), but everything your firmware provides - the vector table, the IMAGE_DEF, and the reset handler - lives in your ELF starting at `0x10000000`. + +### Step 1: List All Sections + +```bash +arm-none-eabi-objdump -h build/0x0001_hello-world.elf +``` + +Expected output (key sections): + +``` +Idx Name Size VMA LMA + 0 .text 000019cc 10000000 10000000 + 3 .binary_info 0000002c 10001b20 10001b20 + 4 .ram_vector_table 00000110 20000000 20000000 + 6 .data 0000019c 20000110 10001b4c +``` + +> Tip: The Pico SDK merges `.vectors`, `.embedded_block`, and `.reset` all into `.text` at `0x10000000`. They are not separate named ELF sections - they are sub-regions inside `.text`. + +### Step 2: Dump the First 0x150 Bytes of Flash - One Command, Zero Skips + +```bash +arm-none-eabi-objdump -s --start-address=0x10000000 --stop-address=0x10000150 build/0x0001_hello-world.elf +``` + +Raw output from that command: + +``` + 10000000 00200820 5d010010 1b010010 1d010010 . . ]........... + 10000010 11010010 11010010 11010010 11010010 ................ + 10000020 11010010 11010010 11010010 11010010 ................ + 10000030 11010010 11010010 11010010 11010010 ................ + 10000040 11010010 11010010 11010010 11010010 ................ + 10000050 11010010 11010010 11010010 11010010 ................ + 10000060 11010010 11010010 11010010 11010010 ................ + 10000070 11010010 11010010 11010010 11010010 ................ + 10000080 11010010 11010010 11010010 11010010 ................ + 10000090 11010010 11010010 11010010 11010010 ................ + 100000a0 11010010 11010010 11010010 11010010 ................ + 100000b0 11010010 11010010 11010010 11010010 ................ + 100000c0 11010010 11010010 11010010 11010010 ................ + 100000d0 11010010 11010010 11010010 11010010 ................ + 100000e0 11010010 11010010 11010010 11010010 ................ + 100000f0 11010010 11010010 11010010 11010010 ................ + 10000100 11010010 11010010 11010010 11010010 ................ + 10000110 eff30580 103800be 00be00be 00be00be .....8.......... + 10000120 00be00be f2eb8871 201b0010 4c1b0010 .......q ...L... + 10000130 a0010010 90a31ae7 d3deffff 42012110 ............B.!. + 10000140 ff010000 b01b0000 793512ab ........y5.. +``` + +Every address annotated, no skips: + +#### 0x10000000 - Vector Table, Mandatory Entries + +| Address | Raw Bytes (LE) | Decoded | What it is | +|---------|----------------|---------|------------| +| `0x10000000` | `00 20 08 20` | `0x20082000` | **Initial SP** - top of SCRATCH_Y RAM. Bootrom loads MSP from here before doing anything else. | +| `0x10000004` | `5d 01 00 10` | `0x1000015d` | **Reset_Handler** address with Thumb bit set. Strip bit 0 -> real address `0x1000015c`. Bootrom jumps here. | +| `0x10000008` | `1b 01 00 10` | `0x1000011b` | **NMI** handler address (Thumb, -> `0x1000011a`). | +| `0x1000000c` | `1d 01 00 10` | `0x1000011d` | **HardFault** handler address (Thumb, -> `0x1000011c`). | + +#### 0x10000010-0x1000010f - Vector Table, IRQ Slots (all 52 external IRQs) + +``` + 10000010 11010010 11010010 ...(repeats through 0x1000010f)... +``` + +Every 4-byte word here is `11 01 00 10` = pointer `0x10000111`. +That is the **default IRQ handler** address with Thumb bit set (-> `0x10000110`). +The RP2350 Cortex-M33 has 16 system vectors (offsets 0x00-0x3f) plus up to 52 +external IRQ vectors (offsets 0x40-0xff = addresses `0x10000040`-`0x1000010f`). +Every IRQ the application does not register gets this default handler pointer. +This block is 240 bytes (`0x10000010` to `0x1000010f`) of nothing but that one +repeated pointer. + +#### 0x10000110-0x10000127 - Default IRQ Handler Code + +``` + 10000110 eff30580 103800be 00be00be 00be00be + 10000120 00be00be f2eb8871 +``` + +| Address | Bytes | ARM Thumb-2 Instruction | What it does | +|---------|-------|-------------------------|--------------| +| `0x10000110` | `ef f3 05 80` | `MRS r0, IPSR` | Read the Interrupt Program Status Register into r0. The low 9 bits = the active vector number. | +| `0x10000114` | `10 38` | `SUBS r0, #16` | Vector 16 = IRQ0, so subtract 16 to convert vector number -> IRQ index. | +| `0x10000116` | `00 be` | `BKPT #0` | Software breakpoint. If a debugger is attached, it stops here and you can inspect r0 to see which IRQ fired. If no debugger is attached, the CPU enters a fault loop and the chip hangs. | +| `0x10000118`-`0x10000127` | `00 be` *12 | `BKPT #0` repeating | Alignment padding to the next 4-byte boundary. | + +This is the **entire** default IRQ handler. It is intentionally minimal: if your +code triggers an IRQ you did not register, it crashes visibly instead of silently. + +#### 0x10000128-0x1000013b - Binary Info Pointer Table + +``` + 10000120 201b0010 4c1b0010 + 10000130 a0010010 90a31ae7 +``` + +| Address | Bytes (LE) | Decoded Value | What it is | +|---------|------------|---------------|------------| +| `0x10000128` | `20 1b 00 10` | `0x10001b20` | Pointer to **start** of `.binary_info` data section in flash. | +| `0x1000012c` | `4c 1b 00 10` | `0x10001b4c` | Pointer to **end** of `.binary_info` data section in flash. | +| `0x10000130` | `a0 01 00 10` | `0x100001a0` | Pointer to `binary_info_callback` function. | +| `0x10000134` | `90 a3 1a e7` | (magic marker) | `BINARY_INFO_MARKER_END` - marks the end of this pointer table. | + +`picotool` reads this table to extract the program name, version string, URL, +and GPIO pin map from any compiled binary without running it. + +#### 0x10000138-0x1000014c - IMAGE_DEF Block (this build) + +``` + 10000130 a0010010 90a31ae7 d3deffff 42012110 + 10000140 ff010000 b01b0000 793512ab 4ff00000 +``` + +| Address | Bytes | What it is | +|---------|-------|------------| +| `0x10000138` | `d3 de ff ff` | `PICOBIN_BLOCK_MARKER_START` - the bootrom scans flash for this exact 4-byte sequence to locate the IMAGE_DEF. | +| `0x1000013c` | `42 01 21 10` | IMAGE_DEF content (image type, flags, version). | +| `0x10000140` | `ff 01 00 00` | IMAGE_DEF content (continuation). | +| `0x10000144` | `b0 1b 00 00` | IMAGE_DEF content (continuation). | +| `0x10000148` | `79 35 12 ab` | `PICOBIN_BLOCK_MARKER_END` - bootrom stops scanning here. | + +The IMAGE_DEF sits at `0x10000138`-`0x1000014b` in this build, +well within the 4 KB scan window the bootrom uses (Datasheet 5.9.5, p. 429). + +#### Full Flash Map: 0x10000000-0x1000015c + +``` + 0x10000000-0x1000000f Vector Table: mandatory entries (SP, Reset, NMI, HardFault) + 0x10000010-0x1000010f Vector Table: 52 external IRQ slots -> all point to default handler + 0x10000110-0x10000127 Default IRQ handler code (MRS / SUBS / BKPT) + 0x10000128-0x10000137 Binary info pointer table (start / end / callback / magic end) + 0x10000138-0x1000014b IMAGE_DEF block (d3 de ff ff ... 79 35 12 ab) + 0x10000150-0x1000015b (padding / alignment) + 0x1000015c Reset_Handler (_reset_handler in crt0.S) ← bootrom jumps here +``` + +### Step 4: Confirmed Boot Sequence (proven from ELF) + +``` ++-----------------------------------------------------------------+ +| PROVEN BOOT SEQUENCE (0x0001_hello-world) | ++-----------------------------------------------------------------+ +| 1. Bootrom reads 0x10000000 | +| -> SP = 0x20082000 (offset +0x00 of vector table) | +| -> RST = 0x1000015d (offset +0x04, Thumb -> 0x1000015c) | ++-----------------------------------------------------------------+ +| 2. Bootrom scans first 4 kB for IMAGE_DEF | +| -> Found at 0x10000138 (this build) | +| -> Start marker: d3 de ff ff | +| -> End marker: 79 35 12 ab | ++-----------------------------------------------------------------+ +| 3. Bootrom jumps to reset handler at 0x1000015c | +| -> _reset_handler (crt0.S) runs | +| -> Checks CPUID - Core 1 sent back to bootrom | +| -> Core 0: .data copied, .bss zeroed, platform_entry called | ++-----------------------------------------------------------------+ +| 4. platform_entry calls runtime_init -> main -> exit | ++-----------------------------------------------------------------+ +``` + +> **Datasheet References:** +> +> - 5.1.5.1 (p. 357): Block markers `0xffffded3` (start) and `0xab123579` (end) +> - 5.9.5 (p. 429): IMAGE_DEF must appear within first 4 kB of flash image +> - 5.9.5.1 (p. 429): Bootrom enters via reset handler at vector table offset +4 + +--- + +**Remember:** Understanding the boot process is fundamental to embedded systems work. Whether you're debugging a system that won't start, reverse engineering firmware, or building secure boot chains, this knowledge is essential! + +Happy exploring! + + + diff --git a/WEEK03/WEEK03.pdf b/WEEK03/WEEK03.pdf new file mode 100644 index 0000000..d933dc6 Binary files /dev/null and b/WEEK03/WEEK03.pdf differ diff --git a/WEEK03/img/fig1.png b/WEEK03/img/fig1.png new file mode 100644 index 0000000..2675b54 Binary files /dev/null and b/WEEK03/img/fig1.png differ diff --git a/WEEK03/img/fig2.png b/WEEK03/img/fig2.png new file mode 100644 index 0000000..ea88c10 Binary files /dev/null and b/WEEK03/img/fig2.png differ diff --git a/WEEK03/img/fig3.png b/WEEK03/img/fig3.png new file mode 100644 index 0000000..d8508da Binary files /dev/null and b/WEEK03/img/fig3.png differ diff --git a/WEEK03/img/fig4.png b/WEEK03/img/fig4.png new file mode 100644 index 0000000..844d58f Binary files /dev/null and b/WEEK03/img/fig4.png differ diff --git a/WEEK03/img/fig5.png b/WEEK03/img/fig5.png new file mode 100644 index 0000000..842d5ee Binary files /dev/null and b/WEEK03/img/fig5.png differ diff --git a/WEEK03/slides/WEEK03-IMG00.svg b/WEEK03/slides/WEEK03-IMG00.svg new file mode 100644 index 0000000..d40fbfd --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 03 + + +Embedded System Analysis: +Understanding the RP2350 Architecture +w/ Comprehensive Firmware Analysis + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK03/slides/WEEK03-IMG01.svg b/WEEK03/slides/WEEK03-IMG01.svg new file mode 100644 index 0000000..0b8b8bd --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG01.svg @@ -0,0 +1,70 @@ + + + + + +RP2350 Boot Sequence +Power-On to main() — 5 Steps + + + +STEP 1 +Power On +Cortex-M33 wakes, execution at 0x00000000 (Bootrom) + + +▼ + + + +STEP 2 +Bootrom Executes +32KB on-chip ROM — finds IMAGE_DEF at 0x10000000 + + +▼ + + + +STEP 3 +Flash XIP Setup (bootrom-managed) +Bootrom configures flash interface & XIP (no boot2 on RP2350) + + +▼ + + + +STEP 4 +Vector Table & Reset Handler +Reads SP from offset 0x00 -> 0x20082000 +Reads Reset Handler from 0x04 -> 0x1000015d + + +▼ + + + +STEP 5 +C Runtime Startup (crt0.S) +Copy .data from flash -> RAM +Zero .bss section +Call runtime_init() -> main() + + + +Key Insight +Your main() is the LAST thing to run. +All 5 steps must complete first! + \ No newline at end of file diff --git a/WEEK03/slides/WEEK03-IMG02.svg b/WEEK03/slides/WEEK03-IMG02.svg new file mode 100644 index 0000000..b7c0025 --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG02.svg @@ -0,0 +1,84 @@ + + + + + +The Bootrom +32KB Factory-Programmed ROM — Where It All Begins + + + +Bootrom Properties + + +Size +32 KB + + +Location +0x00000000 + + +Modifiable? +NO — mask ROM + + +Purpose +Boot the chip + +Burned into silicon at factory +Like BIOS in your computer + + + +What It Does + + +1. +Initialize hardware + + +2. +Check boot sources + + +3. +Validate IMAGE_DEF + + +4. +Configure flash + + +5. +Jump to your code + + + +IMAGE_DEF — Magic Markers +Bootrom looks for these to validate firmware + + +Start Marker +0xFFFFDED3 +"I'm a valid Pico binary!" + + +End Marker +0xAB123579 +"End of header block" + +Bootrom reads flash at 0x10000000, +finds these markers, then boots. + \ No newline at end of file diff --git a/WEEK03/slides/WEEK03-IMG03.svg b/WEEK03/slides/WEEK03-IMG03.svg new file mode 100644 index 0000000..4db555b --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG03.svg @@ -0,0 +1,74 @@ + + + + + +XIP — Execute In Place +Run Code Directly from Flash — No Copy Needed + + + +Book Analogy + + +Without XIP +Photocopy every page, read copy + + +With XIP +Read directly from the book! + + + +Why Use XIP? + + +Saves RAM +Code stays in flash + + +Faster Boot +No bulk copy needed + + +Simpler +Less memory mgmt + + + +XIP Flash Region at 0x10000000 + + + +Vector Table +SP at offset 0x00 | Reset Handler at offset 0x04 | Exception handlers... + + + +Your Code +_reset_handler | main() | other functions + + + +Read-Only Data +Strings like "hello, world" | constant values + + +0x10000000 +0x100001xx +0x10001xxx + +CPU fetches instructions directly +from flash via XIP cache. + \ No newline at end of file diff --git a/WEEK03/slides/WEEK03-IMG04.svg b/WEEK03/slides/WEEK03-IMG04.svg new file mode 100644 index 0000000..22034ba --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG04.svg @@ -0,0 +1,80 @@ + + + + + +The Vector Table +CPU's Instruction Manual at 0x10000000 + + + +Vector Table Layout + + + +Offset +Address +Value +Meaning + + + +0x00 +0x10000000 +0x20082000 +Initial SP + + + +0x04 +0x10000004 +0x1000015D +Reset Handler + + + +0x08 +0x10000008 +0x1000011B +NMI Handler + + + +0x0C +0x1000000C +0x1000011D +HardFault Handler + + + +GDB: +x/4x 0x10000000 + + + +On Power-On +1. CPU reads SP from 0x00 +2. Sets SP = 0x20082000 +3. Reads Reset from 0x04 +4. Jumps to 0x1000015C + + + +Default Handlers +NMI, HardFault, SVCall, +PendSV, SysTick all use: + +bkpt 0x0000 +<- stops debugger + \ No newline at end of file diff --git a/WEEK03/slides/WEEK03-IMG05.svg b/WEEK03/slides/WEEK03-IMG05.svg new file mode 100644 index 0000000..c3d03a0 --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG05.svg @@ -0,0 +1,70 @@ + + + + + +Thumb Mode Addressing +Why Addresses End in Odd Numbers + + + +The LSB Rule +ARM Cortex-M uses the Least Significant +Bit (LSB) to indicate instruction mode: + + + +LSB = 1 (odd) +Thumb mode + + + +LSB = 0 (even) +ARM mode + + + +Reset Handler Example + +Vector table stores: +0x1000015D + +Actual code address: +0x1000015C + +The +1 means: +"Use Thumb mode" + + + +GDB Shows + +0x1000015D +with Thumb bit + + +Vector table raw value + + + +Ghidra Shows + +0x1000015C +actual address + + +Real instruction location + +Both are correct — just displayed differently! + \ No newline at end of file diff --git a/WEEK03/slides/WEEK03-IMG06.svg b/WEEK03/slides/WEEK03-IMG06.svg new file mode 100644 index 0000000..f7d8b17 --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG06.svg @@ -0,0 +1,69 @@ + + + + + +Linker Script Memory Map +memmap_default.ld — Where Everything Lives + + + +Memory Regions + + + +Flash (XIP) +0x10000000 +varies +Your code (read-only) + + + +RAM +0x20000000 +512 KB +Main RAM (r/w) + + + +SCRATCH_X +0x20080000 +4 KB +Core 0 scratch (HW: SRAM8) + + + +SCRATCH_Y +0x20081000 +4 KB +Core 0 stack! (HW: SRAM9) + + + +Stack Pointer Calculation + +__StackTop = ORIGIN(SCRATCH_Y) + + LENGTH(SCRATCH_Y) + + +ORIGIN +0x20081000 + ++ LENGTH +0x1000 +(4 KB) + += __StackTop = 0x20082000 +<- matches vector table! + \ No newline at end of file diff --git a/WEEK03/slides/WEEK03-IMG07.svg b/WEEK03/slides/WEEK03-IMG07.svg new file mode 100644 index 0000000..262aac0 --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG07.svg @@ -0,0 +1,87 @@ + + + + + +Reset Handler — 4 Phases +_reset_handler at 0x1000015C + + + +Phase 1: Core Check +0x1000015C — 0x10000168 + +mov r0, #0xD0000000 +Read CPUID -> Core 0 continues + + + +Phase 2: Data Copy +0x1000016A — 0x10000176 + +ldmia r4!, {r1,r2,r3} +Copy .data from flash -> RAM + + + +Phase 3: BSS Clear +0x10000178 — 0x10000184 + +stmia r1!, {r0} +r0 = 0 +Zero all uninitialized globals + + + +Phase 4: Platform Entry +0x10000186+ + +blx r1 +-> main() +runtime_init -> main -> exit + + + +Execution Flow + + + +Core Check +CPUID == 0? + +-> + + +Data Copy +flash -> RAM + +-> + + +BSS Clear +zero globals + +-> + + +Platform Entry +-> main()! + + + +Why check cores? +RP2350 has 2 cores. +Only Core 0 runs startup. +Core 1 returns to bootrom and waits. + \ No newline at end of file diff --git a/WEEK03/slides/WEEK03-IMG08.svg b/WEEK03/slides/WEEK03-IMG08.svg new file mode 100644 index 0000000..b85e7ce --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG08.svg @@ -0,0 +1,93 @@ + + + + + +Data Copy & BSS Clear +Initializing RAM Before main() Can Run + + + +Phase 2: Data Copy +Copy initialized variables flash -> RAM + +C code: + +int counter = 42; + +Value 42 stored in flash +but variables live in RAM! + + + +Flash + +-> + + +RAM + + +data_cpy_table has entries: + +src: 0x10001B4C (flash) +dst: 0x20000110 (RAM) + + + +Phase 3: BSS Clear +Zero uninitialized global variables + +C code: + +int my_counter; + +C standard requires +this to start at zero. + + + +r1 = BSS start +r2 = BSS end +r0 = 0 + +Loop: store 0, advance r1 +Until r1 == r2 -> done! + + + +Key Assembly Instructions + + + +ldmia r4!, {r1,r2,r3} + +Load source, dest, end from table + + +bl data_cpy + +Copy word-by-word until done + + + +movs r0, #0 + +Load zero into r0 + + +stmia r1!, {r0} + +Store zero, advance pointer + \ No newline at end of file diff --git a/WEEK03/slides/WEEK03-IMG09.svg b/WEEK03/slides/WEEK03-IMG09.svg new file mode 100644 index 0000000..8f0373c --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG09.svg @@ -0,0 +1,82 @@ + + + + + +Platform Entry -> main() +The Final Step — 3 Function Calls at 0x10000186 + + + +platform_entry Assembly + + +0x10000186 +ldr r1, [DAT] +-> load runtime_init addr + + +0x10000188 +blx r1 +-> call runtime_init() + + +0x1000018C +blx r1 +-> call main() + + +0x10000190 +blx r1 +-> call exit() + + + +Call Sequence + + +runtime_init() +SDK setup + +-> + + +main() +YOUR CODE + +-> + + +exit() +cleanup + + + +runtime_init() +Initializes SDK systems: +• Clock configuration +• GPIO setup +• C++ constructor calls +• Peripheral initialization + + +After main() Returns +exit() handles cleanup. +Then: + +bkpt 0x0000 +<- infinite halt + +Should never be reached! + \ No newline at end of file diff --git a/WEEK03/slides/WEEK03-IMG10.svg b/WEEK03/slides/WEEK03-IMG10.svg new file mode 100644 index 0000000..73b2c7c --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG10.svg @@ -0,0 +1,92 @@ + + + + + +Secure Boot & Attack Vectors +Why Boot Sequence Knowledge Matters for Security + + + +Attack Scenarios + + +Firmware Replacement +Replace flash with malicious code + + +Vector Table Hijack +Modify reset handler address + + +Debug Port Attack +SWD/JTAG to dump or inject code + + +Startup Code Modification +Change crt0 data copy / BSS init + +Physical access = game over + + + +Defense Strategies + + +1. Secure Boot + + +2. Debug Port Lock + + +3. Flash Read Protect + + +4. MPU Configuration + + +5. Integrity Checks + +Defense in depth! + + + +Secure Boot Chain + + +Bootrom +immutable + +-> + + +Verify Sig +IMAGE_DEF + +-> + + +Verify App +signature + +-> + + +Boot! +or refuse + +Each stage cryptographically verifies +the next before handing off control. +Bootrom = trust anchor (can't be changed) + \ No newline at end of file diff --git a/WEEK04/WEEK04-BN.md b/WEEK04/WEEK04-BN.md new file mode 100644 index 0000000..e90e210 --- /dev/null +++ b/WEEK04/WEEK04-BN.md @@ -0,0 +1,1820 @@ +# Week 4-BN: Binary Ninja Personal — Resolve, Hack, and Patch the RP2350 (Raw `.bin`) + +*** + +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +- Build the two lesson projects with `Release` and get both an `.elf` and a raw `.bin` +- Dump the **ELF symbol map** with `arm-none-eabi-nm` and use it as ground truth +- Load the raw `.bin` into Binary Ninja at `0x10000000` +- **Break at `main`** on live silicon, even though `main` can move between programs +- **Hack a running target live** by editing a register in Binary Ninja's Registers widget +- **Resolve the functions in the Binary Ninja GUI** using the ELF symbol map +- **Patch** the bytes that control the behavior, export the image, and flash it + +--- + +## How This Guide Works + +The build produces two files for each project: + +| File | What it is | How we use it | +| ---- | ---------- | ------------- | +| `.elf` | The linked image with a full symbol table | Ground truth for every function address and name | +| `.bin` | The raw flash image, no headers, no symbols | The image we load into Binary Ninja and reverse | + +The `.bin` is built **from** the `.elf`, so the ELF tells you exactly what is at every address. We use the ELF symbol map to resolve functions in Binary Ninja, and we reverse-engineer the raw `.bin` the way a real extracted firmware image is reversed. + +> **Build `Release`, not `Debug`.** Every address in this guide matches the Week 4 lesson, and the Week 4 lesson is a `Release` build. `Release` optimizes the code the same way the original lesson was built: it folds `age = 42` away in Project 1 and inlines `blink_and_print` into `main` in Project 2. If you build `Debug`, the SDK function addresses move and Project 2 keeps a separate `blink_and_print`, so nothing lines up. Always build `Release` for this lesson. + +The order is **dynamic first, static second**, twice — once per project: + +1. Break on the live target and prove what the code does. +2. Hack it live in the debugger and watch the behavior change. +3. Resolve the functions in Binary Ninja using the ELF symbol map. +4. Patch the bytes, export, convert, and flash. + +| Project | Prints | Also does | The hack | +| ------- | ------ | --------- | -------- | +| `0x0005_intro-to-variables` | `age: 43` | loops on `printf` | change `43` to `70` | +| `0x0008_uninitialized-variables` | `age: 0` | blinks the red LED on GPIO 16 | change `0` to `66`, move the LED to GPIO 17 | + +> **Addresses come from your build.** Every address here is from the `Release` build produced in Step 3. Confirm against your own `.elf` with the command in Step 4. + +--- + +## Part 1: Build, Flash, and Get the Symbol Map + +### Step 1: Install the toolchain + +**Windows x64** + +- Install the **Raspberry Pi Pico** extension in VS Code. It installs the ARM GNU toolchain, CMake, Ninja, and the Pico SDK. +- Install **Binary Ninja Personal** and complete its license activation. +- Install **PuTTY** for the serial monitor. + +**macOS Apple Silicon** + +```bash +brew install cmake ninja +``` + +- Install **Binary Ninja Personal** and complete its license activation. +- Install the **Arm GNU Toolchain**, or let the VS Code Pico extension manage it. + +**Linux x64** + +```bash +sudo apt install cmake ninja-build gcc-arm-none-eabi libnewlib-arm-none-eabi git python3 openocd minicom +``` + +- Install **Binary Ninja Personal** and complete its license activation. + +### Step 2: Verify your tools are the right architecture (do not skip this) + +On **macOS Apple Silicon**, the most common failure is an Intel `x86_64` tool on your `PATH`: + +``` +zsh: bad CPU type in executable: cmake +``` + +You may have **two Homebrews**: the arm64 one at `/opt/homebrew` and the Intel one at `/usr/local`. If `/usr/local/bin` wins, every `brew` tool is x86_64. Check: + +```bash +file "$(which cmake)" +file "$(which ninja)" +file "$(which arm-none-eabi-gdb)" +file "$(which arm-none-eabi-nm)" +file "$(which openocd)" +file "$(which telnet)" +``` + +All must report `arm64`. If any is `x86_64`, put the Apple Silicon prefix first for the session and check again: + +```bash +export PATH="/opt/homebrew/bin:$PATH" +hash -r +file "$(which cmake)" +``` + +To make it permanent, add that `export` to `~/.zshrc`. Do not use Rosetta as a fix; OpenOCD and GDB are exactly the kind of programs where a translation layer produces failures that look like debugger bugs. + +**`telnet` is special — and optional.** The GDB MI workflow does not need it; it is only used by the command-port fallback. macOS no longer ships `telnet`, and the Homebrew build is often the Intel one, so `telnet 127.0.0.1 4444` fails with `bad CPU type in executable`. Your `brew` command itself may also be the Intel one: if `brew install telnet` fails with `.../portable-ruby/.../ruby: Bad CPU type in executable`, you are running the Intel Homebrew. Call the Apple Silicon Homebrew explicitly: + +```bash +/opt/homebrew/bin/brew install telnet +``` + +If you would rather not install anything, macOS ships an arm64 `nc`, which can connect to the same OpenOCD port: + +```bash +nc 127.0.0.1 4444 +``` + +**Windows x64** and **Linux x64** do not have this problem. Skip to Step 3. + +### Step 3: Build the two projects with `Release` + +Run this once inside `0x0005_intro-to-variables/` and once inside `0x0008_uninitialized-variables/`: + +```bash +cmake -B build -G Ninja -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 -DCMAKE_BUILD_TYPE=Release +cmake --build build +``` + +**Point Binary Ninja at this repository (once).** Every console snippet below reads the repo root from `~/.embedded-hacking-repo`, so Binary Ninja never needs a database open and nothing is hardcoded. From the repo root, run once: + +**macOS / Linux:** + +```bash +pwd > ~/.embedded-hacking-repo +``` + +**Windows (PowerShell):** + +```powershell +(Get-Location).Path | Set-Content "$env:USERPROFILE\.embedded-hacking-repo" +``` + +**Then build from the Binary Ninja console**, so the whole build -> patch -> flash loop stays inside Binary Ninja. The console inherits a minimal `PATH` — on macOS just `/usr/bin:/bin:/usr/sbin:/sbin` — so it does not see Homebrew; add your package manager's `bin` first, then run plain `cmake`. + +**macOS Apple Silicon:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +os.environ["PATH"] = "/opt/homebrew/bin:" + os.environ["PATH"] # the console's PATH omits Homebrew +for name in ("0x0005_intro-to-variables", "0x0008_uninitialized-variables"): + proj = os.path.join(root, name) + subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) + subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +**Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +for name in ("0x0005_intro-to-variables", "0x0008_uninitialized-variables"): + proj = os.path.join(root, name) + subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) + subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +for name in ("0x0005_intro-to-variables", "0x0008_uninitialized-variables"): + proj = os.path.join(root, name) + subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) + subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +Each build directory now contains the pair we need: + +- `0x0005_intro-to-variables/build/0x0005_intro-to-variables.elf` and `.bin` — `.bin` is **15292** bytes +- `0x0008_uninitialized-variables/build/0x0008_uninitialized-variables.elf` and `.bin` — `.bin` is **15668** bytes + +If the ARM toolchain is not on your `PATH`, add `-DPICO_TOOLCHAIN_PATH=...`: + +| OS | Typical toolchain path | +| -- | ---------------------- | +| Windows x64 | `C:/Program Files/Arm GNU Toolchain arm-none-eabi/14.2 rel1/bin` | +| macOS Apple Silicon | `/Applications/ArmGNUToolchain/14.2.rel1/arm-none-eabi/bin` | +| Linux x64 | `/usr` | + +### Step 4: Dump the ELF symbol map + +This is the ground truth for the whole lesson. Run `arm-none-eabi-nm` on each ELF and keep the output in a terminal or a text file: + +**macOS Apple Silicon / Linux x64:** + +```bash +arm-none-eabi-nm -n --defined-only build/0x0005_intro-to-variables.elf | grep -E ' [Tt] ' +arm-none-eabi-nm -n --defined-only build/0x0008_uninitialized-variables.elf | grep -E ' [Tt] ' +``` + +**Windows x64:** + +```powershell +arm-none-eabi-nm -n --defined-only build\0x0005_intro-to-variables.elf | Select-String ' [Tt] ' +``` + +Each line is `address type name`. The `T`/`t` type is a function. Here are the functions this lesson uses. + +**Project 1 — `0x0005_intro-to-variables`:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | reset entry | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | calls `runtime_init`, `main`, `exit` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | copies `.data` from flash to SRAM | +| `0x100001e4` | `_init` | `void _init(void)` | runs `.init_array` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | C runtime boilerplate | +| `0x10000234` | `main` | `int main(void)` | the lesson function | +| `0x10000248` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | SDK GPIO helper | +| `0x10002cfc` | `exit` | `void exit(int)` | C runtime exit | +| `0x10002d04` | `runtime_init` | `void runtime_init(void)` | SDK runtime init | +| `0x10002f54` | `stdio_init_all` | `bool stdio_init_all(void)` | SDK serial init | +| `0x100030e4` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | the `printf` wrapper | + +These signatures come from the ELF's DWARF debug info, so they are exact. You apply them in Binary Ninja in Step 16 (`G` -> address, then `Y` -> Change Type). + +**Project 2 — `0x0008_uninitialized-variables`:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | reset entry | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | calls `runtime_init`, `main`, `exit` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | copies `.data` from flash to SRAM | +| `0x100001e4` | `_init` | `void _init(void)` | runs `.init_array` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | C runtime boilerplate | +| `0x10000234` | `main` | `int main(void)` | the lesson function (`blink_and_print` inlined) | +| `0x10000278` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | SDK GPIO helper | +| `0x100002b4` | `gpio_init` | `void gpio_init(uint)` | SDK GPIO init | +| `0x10000d10` | `sleep_ms` | `void sleep_ms(uint32_t)` | SDK delay | +| `0x10002e74` | `exit` | `void exit(int)` | C runtime exit | +| `0x10002e7c` | `runtime_init` | `void runtime_init(void)` | SDK runtime init | +| `0x100030cc` | `stdio_init_all` | `bool stdio_init_all(void)` | SDK serial init | +| `0x1000325c` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | the `printf` wrapper | + +> **`main` is `0x10000234` in both projects.** In Project 2 the `static void blink_and_print` helper is inlined into `main` by the `Release` optimizer, so it does not appear as a separate symbol. That is why both projects put `main` at the same address. In a `Debug` build it stays separate and `main` moves — another reason to build `Release`. + +### Step 5: Flash Project 1 and confirm `age: 43` + +A `.bin` has no headers, so OpenOCD must be told the base address `0x10000000`. From the repository root: + +**macOS Apple Silicon / Linux x64:** + +```bash +./flash.sh 0x0005_intro-to-variables/build/0x0005_intro-to-variables.bin +``` + +**Windows x64 (PowerShell):** + +```powershell +.\flash.ps1 -Bin 0x0005_intro-to-variables\build\0x0005_intro-to-variables.bin +``` + +**Or flash from the Binary Ninja console** (with a database open, so the repo root is taken from it — otherwise use the terminal form above): + +**macOS Apple Silicon / Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x0005_intro-to-variables", "build", "0x0005_intro-to-variables.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("flashing in the background; log:", log) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x0005_intro-to-variables", "build", "0x0005_intro-to-variables.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # free the probe first +subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "flash.ps1"), "-Bin", bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT) +print("flashing in the background; log:", log) +``` + +Wait for `wrote 15292 bytes ...` and `** Verified OK **`. Open a serial monitor at **115200** baud: + +- **Windows x64:** PuTTY -> Connection type **Serial**, the Pico's COM port, speed `115200`. +- **macOS Apple Silicon:** `screen /dev/tty.usbmodem* 115200` (quit with `Ctrl-A` then `K`). +- **Linux x64:** `minicom -D /dev/ttyACM0 -b 115200`. + +``` +age: 43 +age: 43 +age: 43 +... +``` + +### Step 6: Flash Project 2 and confirm `age: 0` + red LED + +```bash +# macOS / Linux +./flash.sh 0x0008_uninitialized-variables/build/0x0008_uninitialized-variables.bin +``` +```powershell +# Windows +.\flash.ps1 -Bin 0x0008_uninitialized-variables\build\0x0008_uninitialized-variables.bin +``` + +**Or flash from the Binary Ninja console** (same form as Step 5, pointing at the Project 2 `.bin`): + +**macOS / Linux:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x0008_uninitialized-variables", "build", "0x0008_uninitialized-variables.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) +subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("flashing in the background; log:", log) +``` + +**Windows:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x0008_uninitialized-variables", "build", "0x0008_uninitialized-variables.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) +subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "flash.ps1"), "-Bin", bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT) +print("flashing in the background; log:", log) +``` + +Wait for `wrote 15668 bytes ...`. The serial monitor shows: + +``` +age: 0 +age: 0 +age: 0 +... +``` + +and the **red LED on GPIO 16** blinks once per second. + +--- + +## Part 2: Load the Raw `.bin` into Binary Ninja + +Start from a fresh Binary Ninja state. If you already have a `.bndb` for this lesson, **close it and start over**; a stale database keeps old names and patches. + +### Step 7: Bring the raw `.bin` into Binary Ninja + +A raw `.bin` has no headers, so Binary Ninja cannot know where it belongs or what architecture it is. You must supply both. If you just double-click the `.bin`, Binary Ninja may load it at address `0x0` with a guessed architecture, and every address in this lesson will be wrong. + +1. Choose `File -> Open with Options...` (do **not** use plain `File -> Open`). +2. Select `0x0005_intro-to-variables/build/0x0005_intro-to-variables.bin`. +3. In the loader options, set: + - **Architecture:** `thumb2` (the ARMv7-M / ARMv8-M Thumb-2 architecture, which covers the Cortex-M33) + - **Platform:** `thumb2` + - **Base Address:** `0x10000000` (the XIP flash base) +4. Click **Open**. + +Binary Ninja analyzes the image and opens the linear view. + +**Verify the load before going further.** Press `G`, type `0x10000000`, and read the first two words: + +``` +0x10000000 0x20082000 initial stack pointer +0x10000004 0x1000015d reset vector (bit 0 = Thumb) +``` + +If you instead see data at `0x00000000`, or a vector word without bit 0 set, close the tab and repeat with `Open with Options`. The Cortex-M33 only executes Thumb-2, so `thumb2` is the only correct architecture. + +> **Console equivalent:** +> ```python +> load("0x0005_intro-to-variables/build/0x0005_intro-to-variables.bin", +> options={"loader.imageBase": 0x10000000, "loader.platform": "thumb2"}) +> ``` + +### Step 8: Save it as a Binary Ninja database (`.bndb`) + +Binary Ninja never writes back into the `.bin`. Your names, comments, types, and patches live in a separate **`.bndb`** database. Save one now, before you make any changes: + +1. Choose `File -> Save As...`. +2. Save it next to the image as `0x0005_intro-to-variables.bndb`. +3. From now on, save with `File -> Save` (`Cmd+S` on macOS, `Ctrl+S` on Windows/Linux) whenever you rename or patch. + +The two files have different roles: + +| File | Role | +| ---- | ---- | +| `0x0005_intro-to-variables.bin` | the raw firmware image; Binary Ninja never modifies it | +| `0x0005_intro-to-variables.bndb` | your analysis database: names, types, comments, and patches | + +When you come back later, **open the `.bndb`**, not the `.bin`; that restores all your work. If a database gets messy, delete the `.bndb` and re-import the `.bin` from Step 7 — the firmware is never at risk. You export the patched image out of this view later, in Step 19. + +### Step 9: The views you will use + +- **Linear view:** the disassembly listing. You navigate, read, and patch here. +- **Graph view:** the control-flow graph of the current function. +- **Decompiler (HLIL):** the pseudo-C decompilation. +- **Hex view:** raw bytes, used for patching. +- **Function list:** the sidebar list of every detected function. + +Navigation: `G` go to address, `N` rename, `Y` set type or signature, `;` add a comment. Breakpoints are set from the OpenOCD command port, not from the GUI — see Step 13. + +> **macOS function keys:** the top-row `F` keys are usually mapped to system functions. Every step here uses menu paths that work without them. + +--- + +## Part 3: Dynamic — Break at `main` and Hack Live (Project 1) + +### Step 10: Start OpenOCD as a live debug server + +Make sure no other OpenOCD is running; a forgotten server holds port `3333`. + +**macOS / Linux:** + +```bash +ps aux | grep -i openocd +``` + +**Windows (PowerShell):** + +```powershell +Get-Process | Where-Object { $_.ProcessName -like '*openocd*' } +``` + +Stop any leftover server gracefully: + +**macOS / Linux:** + +```bash +pkill -TERM -f openocd +``` + +**Windows (PowerShell):** + +```powershell +Get-Process openocd -ErrorAction SilentlyContinue | Stop-Process +``` + +Start the server **parked at `main`**: + +**macOS Apple Silicon / Linux x64:** + +```bash +BP_ADDR=0x10000234 ./debug-server.sh +``` + +**Windows x64 (PowerShell):** + +```powershell +$env:BP_ADDR="0x10000234"; .\debug-server.ps1 +``` + +**Or start it from the Binary Ninja console**, freeing the probe first and launching the server in the background so the console returns immediately: + +**macOS Apple Silicon / Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # stop any running server first +log = os.path.join(root, "openocd.log") +p = subprocess.Popen([os.path.join(root, "debug-server.sh")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("OpenOCD started (pid", p.pid, "); log:", log) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # stop any running server first +log = os.path.join(root, "openocd.log") +p = subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "debug-server.ps1")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT) +print("OpenOCD started (pid", p.pid, "); log:", log) +``` + +`Popen` returns in a few milliseconds; the server keeps running in the background. Check `openocd.log` for `Listening on port 3333`, then connect in Step 11. + +Wait for: + +``` +Info : [rp2350.dap.core0] Examination succeed +Startup breakpoint at 0x10000234 (2-byte hardware execute, one-shot). +Info : starting gdb server for rp2350.dap.core0 on 3333 +Info : Listening on port 3333 for gdb connections +``` + +> **`BP_ADDR` parks the core at `main` before any client connects.** The script arms a 2-byte hardware breakpoint and then does the startup `reset run`, so the core runs from the vector table and stops at your address with no debugger attached yet. When Binary Ninja connects a moment later, the first thing it reads is already the truth: `Stopped at 0x10000234`. This is the whole reason the lab works cleanly — you never have to drive a reset from outside the GUI. +> +> Use the address you actually want to stop at: +> +> | What you want to stop at | Project 1 `0x0005` | Project 2 `0x0008` | Command | +> | --- | --- | --- | --- | +> | `main` (once per reset) | `0x10000234` | `0x10000234` | `BP_ADDR=0x10000234 ./debug-server.sh` | +> | The **loop** — the `printf` call, hit every iteration | `0x1000023e` | `0x1000024e` | `BP_ADDR=0x1000023e ./debug-server.sh` | +> +> ```bash +> BP_ADDR=0x10000234 ./debug-server.sh # park at main +> BP_ADDR=0x1000023e ./debug-server.sh # park in the loop instead +> ``` +> +> ```powershell +> $env:BP_ADDR="0x10000234"; .\debug-server.ps1 # park at main +> $env:BP_ADDR="0x1000023e"; .\debug-server.ps1 # park in the loop +> ``` +> +> **Note the loop address is not the same in both projects.** Project 2 does more setup before the loop, so its `bl __wrap_printf` sits at `0x1000024e`, not `0x1000023e`. Both were verified against the Release `.elf` with `arm-none-eabi-objdump` and confirmed live on hardware. +> +> **This startup stop is single-use.** OpenOCD flushes breakpoints when a client connects, so this one is gone once Binary Ninja attaches — fine for `main`, which only runs once per reset. Every breakpoint after that is set from the Binary Ninja GUI (Step 13) and is repeatable. To stop at `main` again, restart the server with `BP_ADDR` and reconnect. + +> **Exactly one core.** The line must say `core0` and must **not** mention `core1`. Core1 is never started by this firmware; exposing it makes Binary Ninja read core1's reset-state registers, which are not real addresses, and OpenOCD floods the log with `Failed to read memory at 0xf0000000`. The scripts already use `USE_CORE=0`; do not change it. + +> **Windows driver note:** the Debug Probe must use the **WinUSB** driver. If OpenOCD reports `unable to open CMSIS-DAP device`, install it with [Zadig](https://zadig.akeo.ie/) (select `Debug Probe (CMSIS-DAP)` -> WinUSB). + +### Step 11: Connect Binary Ninja to the GDB server + +1. Make sure the image is open and analyzed (Part 2) and the server from Step 10 is running (parked at `main`). +2. Choose `Debugger -> Connect to Remote Process`. +3. In the **adapter** dropdown, select **GDB MI**. +4. In the **connect** settings group, set **IP Address** to `127.0.0.1` and **Port** to `3333`. +5. Set **Full GDB Executable Path** to the `arm-none-eabi-gdb` from the **Arm GNU Toolchain 14.2.rel1**. It ships for all three hosts, and the Raspberry Pi Pico VS Code extension installs that same 14.2.rel1 toolchain (including `arm-none-eabi-gdb`) on all of them: + + | OS | `arm-none-eabi-gdb` path | + | -- | ------------------------ | + | macOS Apple Silicon | `/Applications/ArmGNUToolchain/14.2.rel1/arm-none-eabi/bin/arm-none-eabi-gdb` (or the Pico extension's `~/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb`) | + | Windows x64 | `%USERPROFILE%\.pico-sdk\toolchain\14_2_Rel1\bin\arm-none-eabi-gdb.exe` (Pico extension), or `C:\Program Files (x86)\Arm GNU Toolchain arm-none-eabi\14.2 rel1\bin\arm-none-eabi-gdb.exe` | + | Linux x64 | `~/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb` (Pico extension), or the `bin/` directory of the extracted `arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi` tarball | +6. Click **Accept**. + +> **Use the GDB MI adapter.** It launches a real `arm-none-eabi-gdb --interpreter=mi2` and lets Binary Ninja drive it, so breakpoints and stepping go through real GDB — which sends the correct 2-byte breakpoint length and handles step-over itself. Verified working end to end: connect, GUI breakpoints (**Add Hardware Breakpoint...**, hardware execute), **Step Into** / **Step Over**, and register edits. Stops are reported as `Breakpoint` (not `SingleStep`). +> +> **Do NOT have any breakpoints set in Binary Ninja before you connect.** With the GDB MI adapter, attaching while Binary Ninja already has a breakpoint **hangs the session**. Start the server parked with `BP_ADDR` (Step 10), connect, and only add hardware breakpoints *after* the connection is up. This is a Binary Ninja bug; it is the single most common GDB MI failure. +> +> **The GDB executable path matters.** Use the **14.2.rel1** build on every OS (Windows, macOS, Linux). The 13.3.rel1 build — which is what `/opt/homebrew/bin/arm-none-eabi-gdb` symlinks to — did **not** connect in testing. +> +> **This step is temporary.** Vector35 plans to ship a GDB binary with the GDB MI adapter ([Vector35/debugger#929](https://github.com/Vector35/debugger/issues/929), milestone *Langara*). Once that lands, Binary Ninja provides GDB itself and you will not need to set **Full GDB Executable Path** at all. +> +> **Do not pick Corellium.** Binary Ninja's adapter dropdown also lists **Corellium**, which is for Corellium's virtual devices and expects an API token, not a local OpenOCD server. It is not the adapter for this lab. The dropdown is a combo box, so an accidental arrow-key press can land on it — always read the label back and confirm it says **GDB MI** before clicking **Accept**. + +> **The adapter and port are not saved in the `.bndb`.** Every time you relaunch Binary Ninja you must re-select **GDB MI**, re-enter port `3333`, and re-set the GDB path. + +> **Watch for an off-screen error dialog.** When a connection fails, Binary Ninja pops a `Binary Ninja critical alert` window that can be positioned mostly outside the main window (seen at `2430,331` with the main window at `2560,30`), which makes it look like nothing happened. If the connect seems to do nothing, check your other display. + +The target keeps running. Open the **Registers** tab (bug icon) and confirm you see live values. `pc` inside `0x10003xxx` and `sp` just below `0x20082000` are healthy. + +> **If `pc` is `0x00000088`, `0x000000ec`, or `sp` is `0xf0000000`, the session is bad.** Restart the server, then restart Binary Ninja (a server restart while attached leaves Binary Ninja in a stale session), and connect again. + +### Step 12: Find `main` without relying on its address + +`main` can move between programs, so we do not guess it. We follow the one fixed path to it. Press `G` and go to `0x10000000`: + +``` +0x10000000 0x20082000 initial stack pointer (top of SRAM) +0x10000004 0x1000015d reset vector +``` + +Bit 0 of a vector is the Thumb bit, so `0x1000015d` means "start at `0x1000015c`". That is `_reset_handler`. Follow the reset path to `0x10000186`, `platform_entry`: + +```asm +10000186 : +10000186: 4914 ldr r1, [pc, #80] ; @ 0x100001d8 +10000188: 4788 blx r1 ; runtime_init +1000018a: 4914 ldr r1, [pc, #80] ; @ 0x100001dc +1000018c: 4788 blx r1 ; main <-- the fixed anchor +1000018e: 4914 ldr r1, [pc, #80] ; @ 0x100001e0 +10000190: 4788 blx r1 ; exit +10000192: be00 bkpt 0x0000 +``` + +**The middle `blx` at `0x1000018c` is the call to `main`.** `platform_entry` is byte-identical in both projects, so `0x1000018c` catches `main` no matter where the linker placed it. The literal pool at `0x100001dc` holds `main | 1`, and clearing bit 0 gives `0x10000234`. + +### Step 13: Set a hardware breakpoint in the GUI + +With the **GDB MI** adapter, Binary Ninja sets breakpoints through real GDB, which sends the correct 2-byte length, so you set them **in the UI**. There is no command port here. + +> **Why older drafts used the command port.** Binary Ninja's **GDB RSP** adapter is its own minimal RSP client and sends a **1-byte** breakpoint (`Z0,,1`); the Cortex-M33 FPB comparators need 2 bytes, so OpenOCD rejected it with `only breakpoints of two bytes length supported`. The old workaround was to arm breakpoints by hand over telnet. **The GDB MI adapter does not have this problem** — it drives real `arm-none-eabi-gdb`, which sends the right length. So everything below is done in the GUI. The command port still exists as a fallback (see the end of this step), but you do not need it. + +#### Where you can stop + +| You want to stop at | Address | How | Repeatable? | +| --- | --- | --- | --- | +| **`main`** | `0x10000234` | The server starts parked there with `BP_ADDR=0x10000234` (Step 10), so Binary Ninja is already stopped at `main` when it connects. | No — `main` runs once per reset. | +| **The loop** (`printf` call) | Project 1 `0x1000023e`, Project 2 `0x1000024e` | Set a hardware breakpoint in the GUI, then click **Resume**. | Yes — fires on every iteration. | + +#### Set the loop breakpoint in the GUI + +1. Press `G`, type the loop address (`0x1000023e` for Project 1, `0x1000024e` for Project 2), and press Enter. +2. Set a **hardware execution** breakpoint at that address, either way: + - `Debugger -> Add Hardware Breakpoint...` — a **hardware execute** (`HE`) breakpoint. **Use this one.** + - click the line and press `F2` (`Debugger -> Toggle Breakpoint`) — a **software** breakpoint. It will **not** work here: the code is in read-only flash, so GDB cannot install it and the core just keeps running. +3. Click **Resume**. The core is already running the loop, so the breakpoint fires on the next iteration. Binary Ninja stops with the PC at the loop address and reports it as a **Breakpoint** — verified: `Stopped (Breakpoint) at 0x1000023e`. + +> **No breakpoints before you connect.** With GDB MI, a breakpoint set before the connection hangs the session (Step 11). Start parked with `BP_ADDR`, connect, *then* add breakpoints. + +#### Stepping + +With the target halted at the breakpoint, **Step Into** (`F7`) and **Step Over** (`F8`) run through real GDB and move the PC. Verified: `0x1000023e -> 0x100030e4 -> 0x100030e6 -> ...`. + +> **Step Over on the raw `.bin` steps *into* calls.** The raw image has no symbol for `__wrap_printf`, so **Step Over** at the `printf` call behaves like **Step Into**. When the lab needs to execute the call and then stop, it moves the breakpoint to the return site and clicks **Resume** instead (Step 14 shows this). + +> **Never use Binary Ninja's Restart button.** On RP2350 it resets and halts inside the boot ROM (`pc=0x88`, `sp=0xf0000000`). To reset cleanly, restart the server with `BP_ADDR` and reconnect. + +> **If you ever need the command port.** It is still there — `nc 127.0.0.1 4444`, and `bp 2 hw` still arms a breakpoint, `rbp ` / `rbp all` still remove them. It is the fallback if you switch back to the **GDB RSP** adapter, whose 1-byte breakpoints the GUI cannot set. With GDB MI you do not need it for this lab. + +### Step 14: HACK IT LIVE — change the printed value + +`main` loads the constant `0x2b` (43) into `r1` and calls `printf` on every iteration. We break on that call in the GUI and change it live. + +1. Press `G`, go to `0x1000023e` (the `bl __wrap_printf`). +2. Set a **hardware execute** breakpoint there: `Debugger -> Add Hardware Breakpoint...`. (Do not use `F2` — that is a software breakpoint and will not work on read-only flash.) +3. Click **Resume** in Binary Ninja. The target is already running the loop, so the breakpoint fires on the next iteration. Binary Ninja stops with the program counter at `0x1000023e` and `r1 = 0x2b`. +4. Open the **Registers** widget (bug icon -> **Registers**). +5. Find `r1`. Its value is `0x2b`. +6. **Set `r1` to `0x46`.** From Binary Ninja's Python console (`Plugins -> Python Console`): + ```python + dbg.set_reg_value("r1", 0x46) + ``` + `dbg.set_reg_value(name, value)` writes one register (returns `True` on success). You can also right-click `r1` in the **Registers** widget, press `E` (edit), type `46`, and press Enter. The widget may not repaint the value, but the write reaches the target — you confirm it by the printed output in the next steps. +7. **Move the breakpoint past the call.** You want `printf` to run once and then stop, so move the breakpoint from `0x1000023e` to the instruction *after* the call, `0x10000242` (the `b.n` that closes the loop): remove the breakpoint at `0x1000023e` and set a hardware breakpoint at `0x10000242`. Two reasons not to just click **Step Over** here: a breakpoint left on the current PC re-traps the step, and Binary Ninja's **Step Over** steps *into* `__wrap_printf` on this raw `.bin` because the image carries no symbol for the call. Moving the breakpoint to the return site is deterministic. +8. Click **Resume** in Binary Ninja. The core executes `bl __wrap_printf` with `r1 = 0x46`, so this iteration prints `age: 70`, then stops at `0x10000242`. +9. Look at your serial monitor — the `screen` session on the Pico's USB serial port — and at the **Target** tab in Binary Ninja: + + ``` + age: 70 + ``` + +You changed a running program's output without touching the binary. + +### Step 14b: HACK THE STRING LIVE — change `age:` to `foo:` + +The text `"age: %d\r\n"` lives in flash (`.rodata`) at `0x100034a0`, and flash is **read-only at runtime** — a debugger write there does not stick (verified: writing `0x66` to `0x100034a0` read back unchanged). So you cannot overwrite the text in place. Instead you redirect the pointer: at the `printf` call, `r0` holds the string address, so you point `r0` at a replacement string you place in RAM. + +1. Arm the breakpoint at the `printf` call and hit it, exactly as in Step 14 steps 1-3. At the stop, `r0 = 0x100034a0` and `r1 = 0x2b`. +2. Put the replacement string into free RAM at `0x20080000` from Binary Ninja's **Python console** (`Plugins -> Python Console`) — no command port needed: + ```python + dbg.write_memory(0x20080000, b"foo: %d\r\n\x00") + ``` + `dbg.write_memory(address, bytes)` is Binary Ninja's debugger memory-write API; it returns `True` on success. That writes the bytes `66 6f 6f 3a 20 25 64 0d 0a 00` = `"foo: %d\r\n\0"`. +3. Point `r0` at that string: + ```python + dbg.set_reg_value("r0", 0x20080000) + ``` + (Or right-click `r0` in the **Registers** widget, press `E`, type `0x20080000`, and press Enter.) +4. Move the breakpoint past the call in the GUI (remove it at `0x1000023e`, set one at `0x10000242`) and click **Resume**. The core runs `printf` with `r0` pointing at your RAM string and `r1 = 0x2b`, so this iteration prints: + ``` + foo: 43 + ``` + then stops at `0x10000242`. + +Like the value hack, this is **one iteration only**: the loop reloads `r0` (and `r1`) from flash on every pass, so the next line is `age: 43` again. The permanent version is the static patch in Step 19b. + +### Step 15: Why the hack reverts (and why we patch next) + +Press **Resume**. The loop branches back to `0x1000023a`, which reloads `movs r1, #43`, so the next line is `age: 43`. The live edit changed one iteration only. There is no variable in memory to change; the value is baked into the instruction. To make `age: 70` permanent we must patch the instruction. That is the static pass. + +Press **Pause** to stop the output flood. + +### Step 15b: Kill the debugger and OpenOCD + +The live hack is done. Do this **before** the static pass. + +1. In the **Debugger** sidebar, click the **X** (**Kill**) (or **`Debugger -> Kill`**) to disconnect Binary Ninja. +2. **Kill does not stop the OpenOCD process** — `debug-server.sh` started it separately, and it keeps running and holding the probe. Stop it from the Binary Ninja console: + + **macOS / Linux:** + + ```python + import subprocess + subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # stop the debug server, free the probe + ``` + + **Windows:** + + ```python + import subprocess + subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # stop the debug server, free the probe + ``` + +3. Confirm nothing is left: `pgrep -fl openocd` (macOS/Linux) prints nothing. + +From a terminal it is the same: `pkill -TERM -f openocd`, or `Get-Process openocd | Stop-Process` on Windows. + +--- + +## Part 4: Static — Resolve the Functions in Binary Ninja and Patch (Project 1) + +### Step 16: Resolve the functions in the Binary Ninja GUI + +We now name the functions in Binary Ninja using the ELF symbol map from Step 4. Binary Ninja loaded the raw `.bin` with **no symbols**, so every function shows as `sub_` — resolving means giving each one its real name and signature. + +Three keys do all the work: + +| Key | Binary Ninja action | Use it for | +| --- | --- | --- | +| `G` | Go to address | Jump to a function's address | +| `Y` | **Change Type** | Set the function's signature. The dialog shows the full prototype, so this sets the name *and* the type in one step. | +| `N` | Rename | Rename only, when you just want the name and not the type | + +For each function below: `G` to its address, then **`Y` (Change Type)** and type the prototype from the table. + +#### How to resolve a function in Binary Ninja (`Y`) + +`Y` is the **Change Type** key, and it is what actually resolves the function — it turns `void sub_10002f54()` into `bool stdio_init_all(void)`. The Change Type dialog shows the full declaration (name and type), so typing the prototype sets both: + +1. `G` to the function's address. The cursor lands on the function. +2. Press **`Y`**. In the Change Type dialog, type the prototype from the table exactly — for example `bool stdio_init_all(void)` — and press Enter. + +The decompiler header then shows the real prototype, and calls to the function read cleanly instead of `sub_()`. `N` is only for renaming without touching the type; `Y` alone sets both the name and the type. + +If `Y` seems to do nothing, confirm the cursor is on the function, or right-click it and pick **Change Type...**. Binary Ninja parses what you type and silently keeps the old type if it does not parse, so glance at the header after each `Y`. + +#### Worked example: `main` + +1. Press `G`, type `0x10000234`, press Enter. The view jumps there; the cursor lands on `sub_10000234`. +2. Press **`Y`** (Change Type), type `int main(void)`, press Enter. That sets the name to `main` and the type to `int(void)`. + +> **Binary Ninja shows `int32_t` where Ghidra shows `int`.** After you set `int main(void)`, the decompiler header may read `int32_t main(void)`. That is the same type — on this platform `int` is 32 bits and Binary Ninja's parser normalises it to `int32_t`. Do not fight it; it is not an error. + +#### Worked example: `stdio_init_all` + +1. `G` -> `0x10002f54`. +2. `Y` -> `bool stdio_init_all(void)`. + +It returns **`bool`**, not `void` — the ELF says `_Bool stdio_init_all(void)`. Our `main` ignores the return value, so the decompiler still reads cleanly. + +#### Worked example: `uart_init` + +1. `G` -> `0x10000e10`. +2. `Y` -> `uint uart_init(uart_inst_t *uart, uint baudrate)`. + +#### Worked example: `__wrap_printf` + +1. `G` -> `0x100030e4`. +2. `Y` -> `int __wrap_printf(const char *fmt, ...)`. Keep the `...` — `printf` is variadic. + +> **`printf` in our source is `__wrap_printf` in the binary.** The SDK links our `printf` calls to its `__wrap_printf` wrapper. + +The rest of the chain is the same two keystrokes per function (`G`, then `Y`). This is **our code plus the library functions it actually calls** — not the whole SDK. `main` only calls `stdio_init_all` and `printf`, so we follow that chain down: `stdio_init_all` pulls in the stdio/UART setup, and `printf` lands in the SDK's `__wrap_printf`. + +The call chain for this project: + +``` +main +├── stdio_init_all +│ ├── stdio_uart_init ── gpio_set_function, uart_init +│ ├── stdio_set_driver_enabled +│ ├── stdio_out_chars_crlf +│ ├── stdio_put_string ── strlen +│ └── time_us_64 +└── __wrap_printf ── __wrap_vprintf +``` + +**Project 1 — resolve every function in that chain:** + +| Address | Rename to (`N`) | Signature (`Y`) | +| ------- | --------------- | --------------- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | +| `0x100001e4` | `_init` | `void _init(void)` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | +| **`0x10000234`** | **`main`** | **`int main(void)`** | +| `0x10002cfc` | `exit` | `void exit(int)` | +| `0x10002d04` | `runtime_init` | `void runtime_init(void)` | +| `0x10002f54` | `stdio_init_all` | `bool stdio_init_all(void)` | +| `0x100032a0` | `stdio_uart_init` | `void stdio_uart_init(void)` | +| `0x10002f2c` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | +| `0x10002d30` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | +| `0x10002e40` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | +| `0x100030e4` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | +| `0x10003020` | `__wrap_vprintf` | `int __wrap_vprintf(const char*, va_list)` | +| `0x10000248` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | +| `0x10000e10` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | +| `0x10000da0` | `time_us_64` | `uint64_t time_us_64(void)` | +| `0x100033e0` | `strlen` | `size_t strlen(const char*)` | + +> **A `void` return type may not stick — here is the fix.** Binary Ninja treats `void` as low-confidence, and its analysis can override it with an inferred type — most often `int32_t` on this 32-bit target. It is most visible on `_reset_handler` (a hand-written assembly entry that never returns normally), but it can happen to **any** function whose return type Binary Ninja thinks it can infer. +> +> Setting the full signature with `Y` reproduces the unwanted `int32_t`, and `fn.return_type = ...` fails too. What works is the **return-value** setter: +> +> ```python +> from binaryninja import ReturnValue, Type +> fn = bv.get_function_at(0x1000015c) +> if fn is not None: +> fn.return_value = ReturnValue(Type.void()) +> ``` +> +> That holds `_reset_handler` at `void` even after reanalysis (verified live). Setting the signature and the return type both failing while `return_value` succeeds looks like a bug or inconsistency in this build (BN 6.0.10601). If it still will not stick, leave it — it does not affect the rest of the lesson. + +> **`__wrap_printf` is the real symbol.** `printf` in our source compiles to the SDK's `__wrap_printf` (which forwards to `__wrap_vprintf`). Rename it `printf` if you prefer the lesson's shorthand, but `__wrap_printf` is what the ELF says. +> +> **`stdio_init_all` returns `bool`, not `void`** — `_Bool stdio_init_all(void)` in the ELF. The `main` source ignores the return value, so the decompiler still reads fine. + +> **Shortcut — resolves name *and* type for every function.** Instead of doing `N` + `Y` by hand, paste this into Binary Ninja's Python console (`Plugins -> Python Console`). It sets each function's name and signature programmatically: +> +> ```python +> from binaryninja import Symbol, SymbolType +> # The raw .bin has no headers, so these SDK types don't exist. set_user_type() +> # re-parses each signature as C, so an undefined name raises +> # "SyntaxError: unknown type name '...'". Define them first. +> sdk = bv.parse_types_from_string(""" +> typedef unsigned int uint; +> typedef char* va_list; +> struct stdio_driver; +> typedef struct stdio_driver stdio_driver_t; +> struct uart_inst; +> typedef struct uart_inst uart_inst_t; +> enum gpio_function { +> GPIO_FUNC_XIP = 0, GPIO_FUNC_SPI = 1, GPIO_FUNC_UART = 2, GPIO_FUNC_I2C = 3, +> GPIO_FUNC_PWM = 4, GPIO_FUNC_SIO = 5, GPIO_FUNC_PIO0 = 6, GPIO_FUNC_PIO1 = 7, +> GPIO_FUNC_GPCK = 8, GPIO_FUNC_USB = 9, GPIO_FUNC_NULL = 0x1f, +> }; +> typedef enum gpio_function gpio_function_t; +> """) +> for name, ty in sdk.types.items(): +> bv.define_user_type(name, ty) +> +> # address: (name, signature); None means "leave the type alone" +> funcs = { +> 0x1000015c: ("_reset_handler", "void _reset_handler(void)"), +> 0x10000186: ("platform_entry", "void platform_entry(void)"), +> 0x1000019a: ("data_cpy", "void data_cpy(void*, void*, void*)"), +> 0x100001e4: ("_init", "void _init(void)"), +> 0x10000210: ("frame_dummy", "void frame_dummy(void)"), +> 0x10000234: ("main", "int main(void)"), +> 0x10002cfc: ("exit", "void exit(int)"), +> 0x10002d04: ("runtime_init", "void runtime_init(void)"), +> 0x10002f54: ("stdio_init_all", "bool stdio_init_all(void)"), +> 0x100032a0: ("stdio_uart_init", "void stdio_uart_init(void)"), +> 0x10002f2c: ("stdio_set_driver_enabled", "void stdio_set_driver_enabled(stdio_driver_t*, bool)"), +> 0x10002d30: ("stdio_out_chars_crlf", "void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)"), +> 0x10002e40: ("stdio_put_string", "int stdio_put_string(const char*, int, bool, bool)"), +> 0x100030e4: ("__wrap_printf", "int __wrap_printf(const char*, ...)"), +> 0x10003020: ("__wrap_vprintf", "int __wrap_vprintf(const char*, va_list)"), +> 0x10000248: ("gpio_set_function", "void gpio_set_function(uint, gpio_function_t)"), +> 0x10000e10: ("uart_init", "uint uart_init(uart_inst_t*, uint)"), +> 0x10000da0: ("time_us_64", "uint64_t time_us_64(void)"), +> 0x100033e0: ("strlen", "size_t strlen(const char*)"), +> } +> for addr, (name, sig) in funcs.items(): +> bv.define_user_symbol(Symbol(SymbolType.FunctionSymbol, addr, name)) +> if sig: +> f = bv.get_function_at(addr) +> if f is not None: +> f.set_user_type(sig) +> ``` +> +> SDK type names (`stdio_driver_t`, `gpio_function_t`, `uart_inst_t`, plus `uint` and `va_list`) are **not** in the raw `.bin`. `set_user_type` re-parses each signature as C, so an undefined name raises `SyntaxError: unknown type name '...'` and stops the loop — it is not harmless. The `sdk` block above defines them first (an opaque `struct`/`enum`/`typedef` is enough to parse). If you add a function that uses another SDK type, add a definition for it to that block too. + +### Step 17: Read `main` in the decompiler + +Open the **Decompiler** view on `main`. It reads: + +```c +int32_t main(void) +{ + stdio_init_all(); + do + { + printf("age: %d\r\n", 0x2b); + } while (true); +} +``` + +The `0x2b` is the value we edited live. Now make it permanent. + +### Step 18: Patch `0x2b` to `0x46` in the GUI + +Go to `0x1000023a`: + +```asm +1000023a: 212b movs r1, #43 ; 0x2b +``` + +The halfword is `0x212b`, stored little-endian as `2b 21`. The immediate is the low byte, so the byte at the instruction's own address is `0x2b`. Change it to `0x46` (70). + +**Option A — Hex view:** + +1. Switch to the **Hex** view (`View -> Hex`). +2. Toggle the lock off so editing is enabled. +3. Go to `0x1000023a` and change the byte `2B` to `46`. +4. Return to the linear view, right-click the function -> `Reanalyze`. + +**Option B — Python console:** + +```python +bv.write(0x1000023a, b"\x46") +print(hex(bv.read(0x1000023a, 1)[0])) # -> 0x46 +``` + +After reanalysis the instruction reads `movs r1, #70`. + +### Step 18b: Patch the string `age:` to `foo:` in the GUI + +The format string `"age: %d\r\n"` starts at `0x100034a0`. Its first three bytes are `61 67 65` (`age`). Change them to `66 6f 6f` (`foo`), leaving the `: %d\r\n` tail untouched, so the line prints `foo: 70`. + +**Option A — Hex view:** + +1. Switch to the **Hex** view (`View -> Hex`). +2. Go to `0x100034a0` and change the three bytes `61 67 65` to `66 6f 6f`. +3. Return to the linear view and reanalyze. + +**Option B — Python console:** + +```python +bv.write(0x100034a0, b"foo") +print(bv.read(0x100034a0, 10)) # -> b'foo: %d\r\n\x00' +``` + +Keep the replacement exactly three bytes. If you use a shorter string you must pad it, or `%d` shifts and `printf` reads the wrong argument. A longer string would overwrite the `: %d` tail. + +### Step 19: Export the patched `.bin` + +```python +import os +seg = next(s for s in bv.segments if s.data_length) # the loadable image segment +data = bv.read(seg.start, seg.data_length) # base + size come from the view itself +out = os.path.join(os.path.join(root, "0x0005_intro-to-variables", "build"), "0x0005_intro-to-variables-h.bin") +open(out, "wb").write(data) +print(len(data), out) # -> 15292 /.../build/0x0005_intro-to-variables-h.bin +``` + +Where the two numbers come from — nothing is hardcoded: + +- **`seg.start`** is the image base Binary Ninja loaded the `.bin` at (`0x10000000`), the same value you pass to `uf2conv --base`. +- **`seg.data_length`** is the segment's size in the file (`0x3bbc` = 15292). Exactly one segment carries data (the image); every peripheral and synthetic segment has `data_length == 0`, so `next(...)` picks the image. +- Reading `seg.start` for `seg.data_length` bytes therefore grabs exactly the image. + +Two gotchas this avoids: + +- **No relative path.** Binary Ninja's Python console runs with a read-only working directory (inside the app bundle), so `open("0x0005_intro-to-variables-h.bin", "wb")` fails with `OSError: [Errno 30] Read-only file system`. `root` (from `~/.embedded-hacking-repo`, Step 3) is the repo, so the file is written into the project's `build/` — no machine-specific path and no database needed. +- **Read the image, not the whole view.** `bv.read(bv.start, bv.length)` spans the entire mapped range (`0x10000000`..`0xe008000c`), which is not the image. The segment's `data_length` is the image size. + +A different size means you exported a partial view. + +### Step 20: Convert to UF2 + +Run from the project directory: + +**macOS Apple Silicon / Linux x64:** + +```bash +python3 ../uf2conv.py 0x0005_intro-to-variables-h.bin \ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +**Windows x64:** + +```cmd +python ..\uf2conv.py 0x0005_intro-to-variables-h.bin ^ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +> **Or convert from the Binary Ninja console** — it is a normal Python interpreter, so you never have to leave the app. `chdir` to a writable directory first (the default one is read-only), then run the script: +> +> ```python +> import os, sys, runpy +> os.chdir(os.path.join(root, "0x0005_intro-to-variables", "build")) # the project build dir (writable) +> sys.argv = ["uf2conv.py", "0x0005_intro-to-variables-h.bin", +> "--base", "0x10000000", "--family", "0xe48bff59", "--output", "hacked.uf2"] +> runpy.run_path("../../uf2conv.py", run_name="__main__") # path to your uf2conv.py +> ``` +> +> This writes `hacked.uf2` next to the `.bin`, ready to drag onto the Pico. + +### Step 21: Flash and verify `age: 70` + +Hold **BOOTSEL**, plug in the Pico 2, and drag `hacked.uf2` onto the **`RP2350`** drive. Open the serial monitor: + +``` +age: 70 +age: 70 +age: 70 +... +``` + +**43 became 70, permanently, with one byte changed and no source code.** + +> **Faster: flash over the Debug Probe (no BOOTSEL).** The repo's `flash.sh` writes the raw `.bin` straight into XIP flash over SWD (`program 0x10000000 verify reset exit`), so you never touch BOOTSEL or a UF2. Run it from a terminal (`./flash.sh `), or from the Binary Ninja console **without freezing it** — use `subprocess.Popen`, which returns immediately, and send OpenOCD's output to a log file. (`subprocess.run` blocks the console until the flash finishes; do not use it here.) +> +> ```python +> import os, subprocess +> root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +> bin_path = os.path.join(os.path.join(root, "0x0005_intro-to-variables", "build"), "0x0005_intro-to-variables-h.bin") +> log = os.path.join(os.path.join(root, "0x0005_intro-to-variables", "build"), "flash.log") +> subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +> p = subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], +> stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +> print("flashing in the background; log:", log) +> ``` +> +> The `pkill` frees the probe first; on Windows use `subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"])`. +> +> The console is free the moment this returns. Check it with `print(p.poll())` (`None` = still running, `0` = done) or read `flash.log` — success ends with `** Verified OK **`. (Verified: `Popen` returns in ~1 ms; the flash itself takes ~2 s.) +> +> The same non-blocking form without the script: +> +> ```python +> import os, subprocess +> ocd = os.path.expanduser("~/.pico-sdk/openocd/0.12.0+dev") +> bin_path = os.path.join(os.path.join(root, "0x0005_intro-to-variables", "build"), "0x0005_intro-to-variables-h.bin") +> log = os.path.join(os.path.join(root, "0x0005_intro-to-variables", "build"), "flash.log") +> subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +> p = subprocess.Popen([f"{ocd}/openocd", "-s", f"{ocd}/scripts", +> "-f", "interface/cmsis-dap.cfg", "-f", "target/rp2350.cfg", +> "-c", "adapter speed 5000", +> "-c", f"program {bin_path} 0x10000000 verify reset exit"], +> stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +> print("flashing in the background; log:", log) +> ``` +> +> **The Debug Probe is single-owner.** If Binary Ninja is still attached (the `debug-server.sh` OpenOCD is running), the flash cannot grab the probe. Detach in Binary Ninja and stop that OpenOCD first: +> +> ```bash +> # macOS / Linux +> pkill -TERM -f openocd +> ``` +> ```powershell +> # Windows +> Get-Process openocd -ErrorAction SilentlyContinue | Stop-Process +> ``` +> +> Success looks like `Programming Finished` -> `Verified OK` -> `Resetting Target`. On Windows use `flash.ps1` (`.\flash.ps1 -Bin `) the same way. + +--- + +## Part 5: Dynamic — Break at `main` and Hack Live (Project 2) + +### Step 22: Reflash Project 2 and reload Binary Ninja + +Part 4 left the Pico running the patched Project 1 image. Put the original Project 2 back and start a fresh session. + +1. Stop any running debug server so the flash script can use the probe: + + ```bash + # macOS / Linux + pkill -TERM -f openocd + ``` + ```powershell + # Windows + Get-Process openocd -ErrorAction SilentlyContinue | Stop-Process + ``` + +2. Flash the original Project 2 image: + + ```bash + # macOS / Linux + ./flash.sh 0x0008_uninitialized-variables/build/0x0008_uninitialized-variables.bin + ``` + ```powershell + # Windows + .\flash.ps1 -Bin 0x0008_uninitialized-variables\build\0x0008_uninitialized-variables.bin + ``` + + **Or do steps 1–2 from the Binary Ninja console** (the active view is still Project 1, so take the repo root from it and point at the Project 2 `.bin`): + + **macOS / Linux:** + + ```python + import os, subprocess + root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) + bin_path = os.path.join(root, "0x0008_uninitialized-variables", "build", "0x0008_uninitialized-variables.bin") + log = os.path.join(os.path.dirname(bin_path), "flash.log") + subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first + subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) + print("flashing Project 2 in the background; log:", log) + ``` + + **Windows:** + + ```python + import os, subprocess + root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) + bin_path = os.path.join(root, "0x0008_uninitialized-variables", "build", "0x0008_uninitialized-variables.bin") + log = os.path.join(os.path.dirname(bin_path), "flash.log") + subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # free the probe first + subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "flash.ps1"), "-Bin", bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT) + print("flashing Project 2 in the background; log:", log) + ``` + +3. Start the debug server again (Step 10) and wait for `Listening on port 3333`. +4. Load Project 2 and save its database — see Step 22b. +5. Connect Binary Ninja again (Step 11): adapter **GDB MI**, IP `127.0.0.1`, port `3333`. + +Confirm the Pico prints `age: 0` and blinks the red LED. + +### Step 22b: Load Project 2 into Binary Ninja and save the database + +Exactly like Steps 7–8, but for Project 2. **Use `File -> Open with Options...`** (not plain `File -> Open`), select `0x0008_uninitialized-variables/build/0x0008_uninitialized-variables.bin`, and set: + +- **Architecture:** `thumb2` +- **Platform:** `thumb2` +- **Base Address:** `0x10000000` + +Click **Open**. Then press `G`, type `0x10000000`, and confirm the first two words: + +``` +0x10000000 0x20082000 initial stack pointer +0x10000004 0x1000015d reset vector (bit 0 = Thumb) +``` + +If you see data at `0x00000000`, close the tab and redo it with `Open with Options`. + +Save it with `File -> Save As...` as `0x0008_uninitialized-variables.bndb` (next to the `.bin`). From now on open the `.bndb`, not the `.bin`; save with `Cmd+S` / `Ctrl+S` after every rename or patch. + +> **Console equivalent:** +> ```python +> load("0x0008_uninitialized-variables/build/0x0008_uninitialized-variables.bin", +> options={"loader.imageBase": 0x10000000, "loader.platform": "thumb2"}) +> ``` + +Then resolve the functions for Project 2 the same way as Project 1 — Step 26. + +### Step 23: Break at `main` + +`main` is at `0x10000234` in this project too. The GUI sets breakpoints fine (Step 13); the only caution is not to drive `reset run` from the port while Binary Ninja is attached (it desyncs Binary Ninja's view). Use `BP_ADDR`, which arms `main` before Binary Ninja connects: + +1. Stop the server (Ctrl-C), then start it parked at `main`: + ```bash + # macOS / Linux + BP_ADDR=0x10000234 ./debug-server.sh + ``` + ```powershell + # Windows + $env:BP_ADDR="0x10000234"; .\debug-server.ps1 + ``` + + **Or restart it from the Binary Ninja console** — kill any running server, then start it parked at `main`: + + **macOS / Linux:** + + ```python + import os, subprocess + root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() + log = os.path.join(root, "openocd.log") + subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # kill any running server first + subprocess.Popen([os.path.join(root, "debug-server.sh")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) + print("OpenOCD restarted parked at main; log:", log) + ``` + + **Windows:** + + ```python + import os, subprocess + root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() + log = os.path.join(root, "openocd.log") + subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # kill any running server first + subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "debug-server.ps1")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT) + print("OpenOCD restarted parked at main; log:", log) + ``` +2. Connect Binary Ninja (Step 11): adapter **GDB MI**, IP `127.0.0.1`, port `3333`. + +The target is already halted at `main` when Binary Ninja connects, and the sidebar reads `Stopped at 0x10000234`. + +> If you instead want to reach `main` on an already-connected session, you must Detach, send `reset run` from the port, then reconnect. Arming `main` and resetting while attached leaves the sidebar showing a stale address. + +`main` sets up GPIO 16 and then loops: print `age`, turn the LED on, sleep, turn it off, sleep. The whole loop is one function because `blink_and_print` was inlined: + +```asm +10000234
: +10000234: b538 push {r3, r4, r5, lr} +10000236: f002 ff49 bl 0x100030cc ; stdio_init_all +1000023a: 2010 movs r0, #16 ; LED_PIN +1000023c: f000 f83a bl 0x100002b4 ; gpio_init +10000240: f04f 0501 mov.w r5, #1 +10000244: 2310 movs r3, #16 ; LED_PIN +10000246: ec45 3044 mcrr 0, 4, r3, r5, cr4 ; gpio_set_dir(16, OUT) +1000024a: 2100 movs r1, #0 ; age +1000024c: 4809 ldr r0, [pc, #36] ; @ 0x10000274 -> "age: %d\r\n" +1000024e: f003 f805 bl 0x1000325c ; __wrap_printf +10000252: 2410 movs r4, #16 ; LED_PIN +10000254: ec45 4040 mcrr 0, 4, r4, r5, cr0 ; gpio_put(16, 1) +10000258: f44f 70fa mov.w r0, #500 +1000025c: f000 fd58 bl 0x10000d10 ; sleep_ms +10000260: f04f 0300 mov.w r3, #0 +10000264: ec43 4040 mcrr 0, 4, r4, r3, cr0 ; gpio_put(16, 0) +10000268: f44f 70fa mov.w r0, #500 +1000026c: f000 fd50 bl 0x10000d10 ; sleep_ms +10000270: e7eb b.n 0x1000024a +10000272: bf00 nop +10000274: 10003618 .word 0x10003618 +``` + +Look at the **Registers** widget at `0x1000024e`: `r1` is `0`, which is why the Pico prints `age: 0`. + +### Step 24: Inspect the GPIO registers live + +The GPIO hardware is memory-mapped. Go to each address and watch it change as you step: + +| Address | Block | Role | +| ------- | ----- | ---- | +| `0x40028000` | `IO_BANK0` | pin function select and status | +| `0x40038000` | `PADS_BANK0` | pad configuration | +| `0xd0000000` | `SIO` | single-cycle GPIO block driven by `mcrr` | + +Step Over through `0x10000254` (`mcrr 0, 4, r4, r5, cr0`) and watch the SIO output register change: this is `gpio_put(16, 1)` turning the red LED on at the hardware level. + +### Step 25: HACK IT LIVE — change the printed value + +1. Press `G`, go to `0x1000024e` (the `bl __wrap_printf`). +2. Set a **hardware execute** breakpoint at `0x1000024e` in the GUI (`Debugger -> Add Hardware Breakpoint...`; not `F2`). Note `0x1000024e` — Project 2's loop sits at a different address than Project 1's. +3. Click **Resume** in Binary Ninja. The target is already looping, so the breakpoint fires on the next pass. Binary Ninja stops with `r1 = 0`. +4. Set `r1` to `0x42` (66): + ```python + dbg.set_reg_value("r1", 0x42) + ``` + (Or right-click `r1` in the **Registers** widget, press `E`, type `42`, and press Enter.) +5. **Move the breakpoint past the call.** `0x10000252` is the instruction right after the `bl __wrap_printf`. Remove the breakpoint at `0x1000024e` and set a hardware breakpoint at `0x10000252`, then click **Resume**. The core runs `printf` with `r1 = 0x42` and stops at `0x10000252`. (Not **Step Over** — it steps into the call on this symbol-less `.bin`, and a breakpoint left on the current PC re-traps the step; Step 13 explains both.) +6. Look at your serial monitor and the **Target** tab: + + ``` + age: 66 + ``` + +Press **Resume** and the next iteration prints `age: 0` again, because the loop reloads `movs r1, #0` each pass. The live hack is temporary; the static patch makes it permanent. + +### Step 25b: HACK THE STRING LIVE — change `age:` to `foo:` + +Same idea as Project 1, different addresses. Here the format string is at `0x10003618` and the `printf` call is at `0x1000024e`. + +1. Hit the breakpoint at `0x1000024e` as in Step 25. At the stop, `r0 = 0x10003618` and `r1 = 0`. +2. Write the replacement string to free RAM at `0x20080000` from the **Python console** (`dbg.write_memory` — no command port needed): + ```python + dbg.write_memory(0x20080000, b"foo: %d\r\n\x00") + ``` + Bytes `66 6f 6f 3a 20 25 64 0d 0a 00` = `"foo: %d\r\n\0"`. +3. Point `r0` at that string: + ```python + dbg.set_reg_value("r0", 0x20080000) + ``` + (Or right-click `r0` in the **Registers** widget, press `E`, type `0x20080000`, and press Enter.) +4. Move the breakpoint past the call in the GUI (remove it at `0x1000024e`, set one at `0x10000252`) and click **Resume**. This iteration prints: + ``` + foo: 0 + ``` + then stops at `0x10000252`. One iteration only — the loop reloads `r0` each pass. The permanent version is the static patch in Step 28b. + +### Step 25c: Kill the debugger and OpenOCD + +Same as Step 15b: click the **X** (**Kill**) in the **Debugger** sidebar (or **`Debugger -> Kill`**), then stop OpenOCD from the Binary Ninja console: + +**macOS / Linux:** + +```python +import subprocess +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # stop the debug server, free the probe +``` + +**Windows:** + +```python +import subprocess +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # stop the debug server, free the probe +``` + +--- + +## Part 6: Static — Resolve the Functions and Patch (Project 2) + +### Step 26: Resolve the functions in the Binary Ninja GUI + +Same two keys as Step 16 — `G` to the address, then `Y` (Change Type) to set the prototype — using the Project 2 ELF symbol map from Step 4. + +The mechanics are identical to Step 16, so here are the worked examples for the functions that are specific to this project. + +#### `main` + +1. `G` -> `0x10000234`. +2. `Y` -> `int main(void)` (Binary Ninja shows `int32_t main(void)` — the same 32-bit `int`). + +#### `gpio_init` + +1. `G` -> `0x100002b4`. +2. `Y` -> `void gpio_init(uint gpio)`. + +#### `sleep_ms` + +1. `G` -> `0x10000d10`. +2. `Y` -> `void sleep_ms(uint32_t ms)`. + +#### `stdio_init_all` and `__wrap_printf` + +Same as Project 1, different addresses: `stdio_init_all` at `0x100030cc` (`bool stdio_init_all(void)`), and `__wrap_printf` at `0x1000325c` (`int __wrap_printf(const char *fmt, ...)`). + +Then work down the table the same way. + +Same idea as Project 1: **our code plus what it calls**, not the whole SDK. The call chain here is one function longer because `main` also drives the GPIO and sleeps: + +``` +main +├── stdio_init_all +│ ├── stdio_uart_init ── gpio_set_function, uart_init +│ ├── stdio_set_driver_enabled +│ ├── stdio_out_chars_crlf +│ ├── stdio_put_string ── strlen +│ └── time_us_64 +├── gpio_init +├── __wrap_printf ── __wrap_vprintf +└── sleep_ms +``` + +Two things in this project have **no symbol of their own**, because the compiler inlined them into `main`: + +- `blink_and_print` — the `static` helper in our own source is inlined, so there is no `blink_and_print` address to rename. You see its body directly inside `main`. +- `gpio_set_dir` and `gpio_put` — these are `static inline` in the SDK headers, so they compile to the `mcrr`/SIO writes you see in `main` rather than to calls. + +**Project 2 — resolve every function in that chain:** + +| Address | Rename to (`N`) | Signature (`Y`) | +| ------- | --------------- | --------------- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | +| `0x100001e4` | `_init` | `void _init(void)` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | +| **`0x10000234`** | **`main`** | **`int main(void)`** | +| `0x100002b4` | `gpio_init` | `void gpio_init(uint)` | +| `0x10000278` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | +| `0x10000d10` | `sleep_ms` | `void sleep_ms(uint32_t)` | +| `0x10002e74` | `exit` | `void exit(int)` | +| `0x10002e7c` | `runtime_init` | `void runtime_init(void)` | +| `0x100030cc` | `stdio_init_all` | `bool stdio_init_all(void)` | +| `0x10003418` | `stdio_uart_init` | `void stdio_uart_init(void)` | +| `0x100030a4` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | +| `0x10002ea8` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | +| `0x10002fb8` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | +| `0x1000325c` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | +| `0x10003198` | `__wrap_vprintf` | `int __wrap_vprintf(const char*, va_list)` | +| `0x10000f88` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | +| `0x10000ef4` | `time_us_64` | `uint64_t time_us_64(void)` | +| `0x10003558` | `strlen` | `size_t strlen(const char*)` | + +Python console shortcut (resolves name **and** type): + +```python +from binaryninja import Symbol, SymbolType +# The raw .bin has no headers, so these SDK types don't exist. set_user_type() +# re-parses each signature as C, so an undefined name raises +# "SyntaxError: unknown type name '...'". Define them first. +sdk = bv.parse_types_from_string(""" +typedef unsigned int uint; +typedef char* va_list; +struct stdio_driver; +typedef struct stdio_driver stdio_driver_t; +struct uart_inst; +typedef struct uart_inst uart_inst_t; +enum gpio_function { + GPIO_FUNC_XIP = 0, GPIO_FUNC_SPI = 1, GPIO_FUNC_UART = 2, GPIO_FUNC_I2C = 3, + GPIO_FUNC_PWM = 4, GPIO_FUNC_SIO = 5, GPIO_FUNC_PIO0 = 6, GPIO_FUNC_PIO1 = 7, + GPIO_FUNC_GPCK = 8, GPIO_FUNC_USB = 9, GPIO_FUNC_NULL = 0x1f, +}; +typedef enum gpio_function gpio_function_t; +""") +for name, ty in sdk.types.items(): + bv.define_user_type(name, ty) + +# address: (name, signature); None means "leave the type alone" +funcs = { + 0x1000015c: ("_reset_handler", "void _reset_handler(void)"), + 0x10000186: ("platform_entry", "void platform_entry(void)"), + 0x1000019a: ("data_cpy", "void data_cpy(void*, void*, void*)"), + 0x100001e4: ("_init", "void _init(void)"), + 0x10000210: ("frame_dummy", "void frame_dummy(void)"), + 0x10000234: ("main", "int main(void)"), + 0x100002b4: ("gpio_init", "void gpio_init(uint)"), + 0x10000278: ("gpio_set_function", "void gpio_set_function(uint, gpio_function_t)"), + 0x10000d10: ("sleep_ms", "void sleep_ms(uint32_t)"), + 0x10002e74: ("exit", "void exit(int)"), + 0x10002e7c: ("runtime_init", "void runtime_init(void)"), + 0x100030cc: ("stdio_init_all", "bool stdio_init_all(void)"), + 0x10003418: ("stdio_uart_init", "void stdio_uart_init(void)"), + 0x100030a4: ("stdio_set_driver_enabled", "void stdio_set_driver_enabled(stdio_driver_t*, bool)"), + 0x10002ea8: ("stdio_out_chars_crlf", "void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)"), + 0x10002fb8: ("stdio_put_string", "int stdio_put_string(const char*, int, bool, bool)"), + 0x1000325c: ("__wrap_printf", "int __wrap_printf(const char*, ...)"), + 0x10003198: ("__wrap_vprintf", "int __wrap_vprintf(const char*, va_list)"), + 0x10000f88: ("uart_init", "uint uart_init(uart_inst_t*, uint)"), + 0x10000ef4: ("time_us_64", "uint64_t time_us_64(void)"), + 0x10003558: ("strlen", "size_t strlen(const char*)"), +} +for addr, (name, sig) in funcs.items(): + bv.define_user_symbol(Symbol(SymbolType.FunctionSymbol, addr, name)) + if sig: + f = bv.get_function_at(addr) + if f is not None: + f.set_user_type(sig) +``` + +The decompiler now shows `main` initializing GPIO 16 and looping. We make two changes: + +- **Move the LED from GPIO 16 to GPIO 17** by patching three `0x10` immediates. +- **Change the printed value from 0 to 66** by patching one `0x00` immediate. + +### Step 27: Patch 1 — move the LED from GPIO 16 to GPIO 17 + +GPIO 16 is the red LED; GPIO 17 is the green LED. The pin number appears in three instructions. Change the low byte of each from `10` to `11`: + +| Address | Instruction | Bytes before | Bytes after | Role | +| ------- | ----------- | ------------ | ----------- | ---- | +| `0x1000023a` | `movs r0, #16` | `10 20` | `11 20` | pin passed to `gpio_init` | +| `0x10000244` | `movs r3, #16` | `10 23` | `11 23` | pin used by `gpio_set_dir` | +| `0x10000252` | `movs r4, #16` | `10 24` | `11 24` | pin used by `gpio_put` in the blink loop | + +In the **Hex** view (lock off), change each byte and reanalyze. Or in the Python console: + +```python +for addr in (0x1000023a, 0x10000244, 0x10000252): + bv.write(addr, b"\x11") +``` + +> **All three are required.** If you patch only the `gpio_set_dir` site, pin 17's output driver is enabled but `gpio_put` still drives pin 16, whose driver was never enabled. Nothing lights up. This is the most common mistake in this lesson. + +### Step 28: Patch 2 — change the printed value from 0 to 66 + +`main` loads `age = 0` with `movs r1, #0` at `0x1000024a`. Change the immediate byte from `00` to `42` (`0x42` = 66): + +```python +bv.write(0x1000024a, b"\x42") +``` + +Verify all four patches: + +```python +for addr in (0x1000023a, 0x10000244, 0x10000252, 0x1000024a): + print(hex(addr), hex(bv.read(addr, 1)[0])) +# -> 0x1000023a 0x11 +# -> 0x10000244 0x11 +# -> 0x10000252 0x11 +# -> 0x1000024a 0x42 +``` + +### Step 28b: Patch the string `age:` to `foo:` + +The format string starts at `0x10003618`; change its first three bytes `61 67 65` (`age`) to `66 6f 6f` (`foo`): + +```python +bv.write(0x10003618, b"foo") +print(bv.read(0x10003618, 10)) # -> b'foo: %d\r\n\x00' +``` + +Exactly three bytes, same rule as Project 1: a shorter string must be padded, a longer one overwrites the `: %d` tail. + +### Step 29: Export, convert, and flash + +```python +import os +seg = next(s for s in bv.segments if s.data_length) # the loadable image segment +data = bv.read(seg.start, seg.data_length) # base + size from the view itself +out = os.path.join(os.path.join(root, "0x0008_uninitialized-variables", "build"), "0x0008_uninitialized-variables-h.bin") +open(out, "wb").write(data) +print(len(data), out) # -> 15668 /.../build/0x0008_uninitialized-variables-h.bin +``` + +Same as Project 1: `seg.start` is the load base and `seg.data_length` is the image size (here `0x3d34` = 15668) — both read from the view, and no relative path (the console's CWD is read-only). + +**macOS Apple Silicon / Linux x64:** + +```bash +python3 ../uf2conv.py 0x0008_uninitialized-variables-h.bin \ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +**Windows x64:** + +```cmd +python ..\uf2conv.py 0x0008_uninitialized-variables-h.bin ^ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +Or run the conversion from the Binary Ninja console, exactly as in Step 20 (`os.chdir` to the build dir, then `runpy.run_path("../../uf2conv.py", run_name="__main__")` with `sys.argv` set to the arguments above). + +Hold **BOOTSEL**, plug in the Pico 2, drag `hacked.uf2` onto the **`RP2350`** drive. Or flash the `.bin` over the Debug Probe with SWD — no BOOTSEL — from the console, exactly as in Step 21 (stop any running OpenOCD first, and use `Popen`, not `run`, so the console is not blocked): + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +bin_path = os.path.join(os.path.join(root, "0x0008_uninitialized-variables", "build"), "0x0008_uninitialized-variables-h.bin") +log = os.path.join(os.path.join(root, "0x0008_uninitialized-variables", "build"), "flash.log") +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +p = subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("flashing in the background; log:", log) +``` + +### Step 30: Verify + +Open the serial monitor: + +``` +age: 66 +age: 66 +age: 66 +... +``` + +The **green LED on GPIO 17** now blinks instead of the red one. + +**We changed the printed value and moved the LED, with four bytes and no source code.** + +--- + +## Cheatsheet + +### Binary Ninja GUI actions + +| Action | How | +| ------ | --- | +| Go to address | `G` | +| Rename function/symbol | `N` | +| Set type or signature | `Y` | +| Add comment | `;` | +| Open Hex view | `View -> Hex` | +| Enable hex editing | Toggle the lock in the status bar | +| Reanalyze after a patch | Right-click function -> `Reanalyze` | +| Edit a register live | `dbg.set_reg_value("r1", 0x46)` in the Python console (or right-click the register, press `E`, type hex, Enter) | +| Set a breakpoint | `Debugger -> Add Hardware Breakpoint...` (hardware execute). Do **not** use `F2` — software breakpoints cannot be written to read-only flash. | +| Move a breakpoint | Remove it and set it at the new address in the GUI (command-port fallback: `rbp ` then `bp 2 hw`) | +| Confirm what is armed | The **Breakpoints** widget lists it (command-port fallback: `mdw 0xE0002000 8`, each armed breakpoint shows as ``) | +| Apply the ELF symbol map | Paste the Python snippet from Step 16 / 26 into the Python Console | + +### OpenOCD server and reset + +The server runs with `gdb_breakpoint_override hard` so that flash-writes are never attempted. Breakpoints in this lab are set in the Binary Ninja GUI through the **GDB MI** adapter (Step 13). The command-port rows below are the fallback if you use the **GDB RSP** adapter instead. + +| Action | Command | +| ------ | ------- | +| Connect to the OpenOCD prompt (fallback) | `nc 127.0.0.1 4444` (or `telnet 127.0.0.1 4444`) | +| Reset and run (command port) | `reset run` | +| Check core state (command port) | `targets` | +| Set a breakpoint in the GUI | `Debugger -> Add Hardware Breakpoint...` (hardware execute; `F2` software breakpoints do not work on flash) | +| (fallback) Add a breakpoint without the GUI | `bp 2 hw` | +| Remove one breakpoint | `rbp ` — **address only, no length, no `hw`** | +| Remove every breakpoint | `rbp all` | +| Start the server parked at `main` | macOS/Linux: `BP_ADDR=0x10000234 ./debug-server.sh` — Windows: `$env:BP_ADDR="0x10000234"; .\debug-server.ps1` (one-shot) | +| Start the server parked in the loop | macOS/Linux: `BP_ADDR=0x1000023e ./debug-server.sh` — Windows: `$env:BP_ADDR="0x1000023e"; .\debug-server.ps1` (`0x1000024e` for Project 2) | +| Break on the loop in a running target | set a hardware breakpoint in the GUI at the loop address, then **Resume** — repeatable | +| Make Binary Ninja stepping work | `rp2350.dap.core0 configure -rtos none` (already in the scripts) | +| Step without re-trapping | move the breakpoint off the current PC first, then **Step Into**/**Step Over** | +| Reset without desyncing Binary Ninja | **Detach**, `reset run` on the port, reconnect — never `reset run` while attached | + +### Every address and byte we changed + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x0005` | `0x1000023a` | `2b` | `46` | prints `age: 70` | +| `0x0008` | `0x1000023a` | `10` | `11` | `gpio_init` configures GPIO 17 | +| `0x0008` | `0x10000244` | `10` | `11` | `gpio_set_dir` enables GPIO 17 | +| `0x0008` | `0x10000252` | `10` | `11` | `gpio_put` drives GPIO 17 | +| `0x0008` | `0x1000024a` | `00` | `42` | prints `age: 66` | +| `0x0005` | `0x100034a0` | `61 67 65` | `66 6f 6f` | string prints `foo:` instead of `age:` | +| `0x0008` | `0x10003618` | `61 67 65` | `66 6f 6f` | string prints `foo:` instead of `age:` | + +### Raw image facts + +| Item | Value | +| ---- | ----- | +| Build type | `Release` | +| Load base address | `0x10000000` | +| Project 1 size | `15292` bytes | +| Project 2 size | `15668` bytes | +| Fixed `main` anchor (both projects) | `0x1000018c` (reset handler middle `blx`) | +| `main` (both projects) | `0x10000234` | +| `printf` call, Project 1 | `0x1000023e` | +| `printf` call, Project 2 | `0x1000024e` | +| RP2350 UF2 family ID | `0xe48bff59` | + +--- + +## Troubleshooting + +### Binary Ninja hangs or crashes when you connect (macOS 27) + +Three different causes have been seen on this setup; check them in this order. + +- **A breakpoint set before connecting.** With the **GDB MI** adapter, if the binary view already has a breakpoint, the session hangs. Start parked with `BP_ADDR`, connect, then add breakpoints (see the next entry). +- **The wrong GDB executable.** Point **Full GDB Executable Path** at the **14.2.rel1** toolchain (Step 11). The 13.3.rel1 build (what `/opt/homebrew/bin/arm-none-eabi-gdb` symlinks to) did not connect in testing. +- **The LLDB adapter.** A crash report with `libdebuggercore.dylib -> std::terminate() -> abort()` and `liblldb` in the stack is the **LLDB** adapter, not GDB MI. Avoid LLDB on this setup. + +**Use GDB MI**, with the 14.2.rel1 path above. If it still fails, fall back to plain `arm-none-eabi-gdb` against the same server — the addresses and register values are identical to the GUI steps. + +If Binary Ninja hangs, force-quit it; the connect dialog has no working Cancel. The static steps (resolve, patch, export, flash) never touch the debugger and always work. + +### The GUI refuses to set a breakpoint (GDB RSP adapter only) + +If you are on the **GDB RSP** adapter, the GUI cannot set breakpoints on this target. That adapter is Binary Ninja's own minimal RSP client and sends a **1-byte** breakpoint (`Z0,,1`); the Cortex-M33 comparators need 2 bytes, so OpenOCD answers `only breakpoints of two bytes length supported`. It affects every address, both `Toggle Breakpoint` and `Add Hardware Breakpoint`, and the dialog's **Size** field is disabled. `gdb_breakpoint_override` makes no difference. + +**Fix: use the GDB MI adapter** (Step 11). It drives real GDB, which sends the correct length, so GUI breakpoints just work. If you must stay on GDB RSP, arm breakpoints from the command port after connecting (`bp 2 hw`) — but the lab uses GDB MI and does not need that. + +### GDB MI hangs when you connect (a breakpoint already existed) + +With the **GDB MI** adapter, if Binary Ninja already has a breakpoint set when you connect, the session **hangs**. This is a Binary Ninja bug. The working order is: + +1. Start the server parked, e.g. `BP_ADDR=0x10000234 ./debug-server.sh` (Windows: `$env:BP_ADDR="0x10000234"; .\debug-server.ps1`). +2. Connect with the **GDB MI** adapter. +3. Only *then* set hardware breakpoints in the UI. + +Never have a breakpoint in the binary view before the GDB MI connection. If it hangs, quit Binary Ninja, restart the server with `BP_ADDR`, and connect again before adding any breakpoints. + +### Step Into / Step Over does nothing (PC never moves) + +Two causes have been seen on this target. + +1. **A breakpoint on the current PC re-traps the step.** OpenOCD's step-over-breakpoint logic fails with `Duplicate Breakpoint address` and the PC stays put. Fix: move the breakpoint off the current PC (in the GUI), then step. +2. **The `hwthread` RTOS (GDB RSP adapter only).** With the **GDB RSP** adapter, OpenOCD can log `fake step thread 0` and reply without stepping, because the RP2350 config's `-rtos hwthread` makes the current thread id 1 while Binary Ninja sends thread id 0. Fix: `rp2350.dap.core0 configure -rtos none` (the launcher scripts already pass this). **GDB MI does not hit this.** + +To tell them apart, turn on OpenOCD logging (`log_output /tmp/ocd.log`, then `debug_level 3` on the command port) and look for `fake step` versus `Duplicate Breakpoint`. + +### `zsh: bad CPU type in executable: cmake` + +An Intel `x86_64` tool is on your `PATH` on Apple Silicon. Run Step 2: `export PATH="/opt/homebrew/bin:$PATH"`, then `hash -r`. Add it to `~/.zshrc` to make it permanent. + +### My addresses do not match this guide + +You probably built `Debug`. This lesson is a `Release` build. Re-run Step 3 with `-DCMAKE_BUILD_TYPE=Release`. A `Debug` build moves the SDK functions and keeps `blink_and_print` separate, so Project 2's `main` is not at `0x10000234`. + +### A breakpoint never fires + +First, confirm you actually set one, and that it is a **hardware** breakpoint. With the **GDB MI** adapter, `Debugger -> Add Hardware Breakpoint...` (hardware execute) should land in the **Breakpoints** widget. If nothing lands, or the core keeps running, you probably used `F2` (`Toggle Breakpoint`) — that is a software breakpoint and cannot be written to read-only flash, so it never installs. Also check you are on **GDB MI**, not **GDB RSP** (the GDB RSP adapter cannot set breakpoints on this target at all). + +Then check the order and the state: + +- **Arm it only after Binary Ninja is connected.** OpenOCD flushes every breakpoint when a client attaches (`breakpoint_remove_all_internal` -> `Delete all breakpoints`), so anything armed earlier is gone. This also applies to `BP_ADDR` on the startup command line, and to `hbreak` followed by `detach` in GDB. +- **Verify it is armed:** `mdw 0xE0002000 8`. You should see your address with the low bit set (`0x10000234` -> `0x10000235`). All zeros means nothing is armed — re-read this first, because it distinguishes "not armed" from "armed but never reached". +- **Is the core running?** `poll` on the command port should not report a halt. If it is stopped, click **Resume**. +- **Does the address get reached again?** `main` runs once per reset, so use `BP_ADDR` at startup (Step 10) rather than `reset run` while attached. Loop addresses such as `0x1000023e` fire on the next pass with no reset — arm them and click **Resume** in Binary Ninja. +- **With GDB MI the stop is reported as `Breakpoint`** and appears in the **Breakpoints** widget, because GDB really did set it. (On the old **GDB RSP** workaround the stop showed as `SingleStep` with an empty widget, because the breakpoint was armed behind Binary Ninja's back.) + +### I edit `r1` (or another register) and it reverts + +`main` reloads the value at the top of every loop iteration — `movs r1, #43` at `0x1000023a` runs right before the `printf` at `0x1000023e`. So `r1` is only `0x46` for the instant between your edit and the next pass; then it is `0x2b` again. The edit sticks only if the core is **genuinely stopped** at the breakpoint and stays stopped. + +If it keeps reverting, the core is running, which almost always means the breakpoint is not installed — usually because it is a **software** breakpoint (`F2`) that cannot be written to read-only flash. Use `Debugger -> Add Hardware Breakpoint...` (hardware execute). Confirm with `mdw 0xE0002000 4` on the command port: a hardware breakpoint shows as `0x1000023f`; all zeros means nothing is armed. + +> **The Registers widget is a snapshot, not a live view.** Binary Ninja reads the registers at each stop and shows that snapshot; it does not poll the target, and there is no "refresh registers" command (only "Force Update Memory Cache", which is for memory). So a value changed outside Binary Ninja will not appear until the next stop. + +### The serial capture is garbage on macOS + +Reading `/dev/cu.usbmodem*` with a bare `read()` returns garbage. Set **raw termios at 115200** first: clear canonical/echo flags, set `CLOCAL|CREAD`, and `B115200` on input and output. `screen /dev/cu.usbmodem* 115200` does all of this for you; a script must call `tcsetattr` itself. Once set, the capture reads clean `age: 43` lines. + +### It worked for a second, then stopped (Binary Ninja's view desyncs) + +This is the most common failure, and it has one main cause: **driving the core from the OpenOCD command port while Binary Ninja is connected.** + +- If you send `reset run` from the port while attached, the core resets, runs, and halts at your breakpoint — but Binary Ninja never receives the stop event. Its sidebar keeps showing the *previous* location, so **Step** and **Resume** act on a stale PC and appear to do nothing. Verified: target at `0x1000023e` while the sidebar still read `Stopped at 0x10003020`. +- If the OpenOCD process dies (or you restart it) while attached, Binary Ninja keeps believing it is connected: the sidebar stays, but the menu shows **Pause** enabled and **Resume**/**Step** disabled because Binary Ninja last saw the target *running*. + +Recovery: **Detach, then reconnect.** If Detach does nothing (the connection is already dead), restart Binary Ninja — its menu still shows a session that no longer exists. + +Prevention: +- Stop at `main` with `BP_ADDR` on a fresh server start, not with `reset run` while attached. +- For loop addresses, set the breakpoint in the GUI and click **Resume**. Let Binary Ninja be the thing that starts the core. +- If you must reset, **Detach first**, `reset run`, then reconnect. +- Never leave a breakpoint on the PC you are about to step or resume from (see the stepping section above). + +> **If the stop is at `0x1000320c` rather than your breakpoint,** you stopped inside `stdio_uart_out_flush`, not at `main`. See the next section. + +### The target "blows past" `main` and stops at `0x1000320c` instead + +`0x1000320c` is inside `stdio_uart_out_flush`: + +```asm +1000320c: 6993 ldr r3, [r2, #24] +1000320e: 071b lsls r3, r3, #28 +10003210: d4fc bmi.n 0x1000320c +``` + +That is the UART transmit-FIFO drain loop inside `printf`, so the core is running `main`'s loop and simply spends nearly all its time there. The breakpoint at `main` did not fire because `main`'s entry (`0x10000234`) runs exactly **once per reset**. If you arm the breakpoint after the reset, or set it while the target is already running and just resume, the core is already past `0x10000234` and will never re-execute it. Either arm the breakpoint **before** resetting, or break inside the loop at `0x1000023e`, which fires every iteration. + +**`0x1000320c` is not a function.** It is one instruction inside `stdio_uart_out_flush`, which starts at `0x10003208`: + +```asm +10003208 : +10003208: 4b02 ldr r3, [pc, #8] ; @ 0x10003214 +1000320a: 681a ldr r2, [r3] +1000320c: 6993 ldr r3, [r2, #24] ; the core sits here while the UART drains +1000320e: 071b lsls r3, r3, #28 +10003210: d4fc bmi.n 0x1000320c +10003212: 4770 bx lr +10003214: 20000850 .word 0x20000850 +``` + +If Binary Ninja has created a function at `0x1000320c` (for example because the debugger stopped at that PC), the decompiler shows garbage: registers named `entry_r4`/`entry_r5`, and stores to invented constants like `0x3a` and `0xfffffff6`. Delete that bogus function (right-click it -> `Delete Function`, or put the cursor on it and press `U` to undefine) and reanalyze. The real function is `stdio_uart_out_flush` at `0x10003208`. + +### The console floods with `Failed to read memory at 0xf0000000` + +Core1 is exposed. The scripts must run with `USE_CORE=0`. Stop the server, confirm only `core0` is reported, restart, then restart Binary Ninja. + +### `Connect to Remote Process` is greyed out and Pause does nothing + +Binary Ninja is in a stale session, usually because the debug server restarted while attached. Quit and reopen Binary Ninja (or the `.bndb`) and connect again. + +### The decompiler still shows the old value after patching + +Right-click the function and choose `Reanalyze`. + +### Project 2's LED does not light at all after patching + +You patched only some of the three GPIO 16 sites. All three of `0x1000023a`, `0x10000244`, and `0x10000252` must change. + +--- + +## Fallback: do the dynamic steps with GDB (macOS 27) + +If Binary Ninja's debugger crashes on attach on macOS 27 (see Troubleshooting), you can still do the live hack with the ARM GDB from the toolchain, against the same OpenOCD server. The addresses and register values are identical to the GUI steps. + +Start the debug server (Step 10), then in a new terminal: + +``` +arm-none-eabi-gdb +``` + +At the `(gdb)` prompt: + +``` +set architecture armv8-m.main +target extended-remote :3333 +hbreak *0x1000023e +continue +``` + +Do **not** run `monitor reset run` before `hbreak`. `0x1000023e` is inside `main`'s loop, so the breakpoint fires on the next iteration with no reset. If you reset first, the core runs `main` and you will not catch it. + +GDB stops at the `printf` call. Confirm the value, change it, and let it run: + +``` +info registers pc r1 # pc = 0x1000023e, r1 = 0x2b +set $r1 = 0x46 +stepi +continue +``` + +The serial monitor prints `age: 70` for the iteration you changed — the same temporary live hack as editing `r1` in the Binary Ninja Registers widget. When you are done, press `Ctrl-C`, then `detach` and `quit`. + +**If you specifically want to stop at `main` (`0x10000234`),** remember its entry runs only once per reset, so the breakpoint must be armed *before* the reset: + +``` +monitor reset halt +hbreak *0x10000234 +continue +``` + +If you instead set it while the target is running and just `continue`, you will "blow past" `main` and catch the core inside `printf` — in this build at `0x1000320c`, the `stdio_uart_out_flush` UART-drain loop. + +Project 2 is the same with the other call site and value: + +``` +hbreak *0x1000024e +continue +info registers pc r1 # pc = 0x1000024e, r1 = 0 +set $r1 = 0x42 +stepi +``` + +`hbreak` sets a hardware breakpoint, which is required for read-only flash. It works from plain GDB because GDB sends the 2-byte length the Cortex-M33 comparators need. Binary Ninja's **GDB MI** adapter goes through the same GDB, so its GUI breakpoints work too; the old **GDB RSP** adapter was the one that sent a 1-byte length and could not set breakpoints here. + +## Glossary + +| Term | Definition | +| ---- | ---------- | +| **`.bss`** | Section for uninitialized global variables; zeroed by startup code | +| **`.data`** | Section for initialized global variables; copied from flash to SRAM at boot | +| **`.elf`** | Linked image with the symbol table; the ground truth for addresses and names | +| **`.rodata`** | Read-only section for constants and string literals; stays in flash | +| **GPIO** | General Purpose Input/Output — controllable pins on the microcontroller | +| **Hardware breakpoint** | A breakpoint serviced by the CPU comparators, required for read-only flash | +| **Inlining** | The optimizer replacing a function call with the function body; why `blink_and_print` disappears in `Release` | +| **Literal pool** | A block of 32-bit constants that Thumb-2 code reaches with PC-relative `ldr` | +| **MMIO** | Memory-mapped I/O — hardware registers accessed as memory addresses | +| **SIO** | Single-cycle I/O — the fast GPIO block in the RP2350, at `0xd0000000` | +| **Thumb bit** | Bit 0 of a Cortex-M function pointer; selects Thumb instruction mode | +| **UF2** | USB Flashing Format — the file format the Pico 2 bootloader accepts | +| **Vector table** | The first words of flash: initial stack pointer and exception vectors | + +--- + +**Remember:** the ELF tells you what every address is, and the `.bin` is what you actually patch. Prove the behavior dynamically, resolve the names from the ELF, then patch the bytes and flash. diff --git a/WEEK04/WEEK04-BN.pdf b/WEEK04/WEEK04-BN.pdf new file mode 100644 index 0000000..357e90a Binary files /dev/null and b/WEEK04/WEEK04-BN.pdf differ diff --git a/WEEK04/WEEK04-SLIDES.pdf b/WEEK04/WEEK04-SLIDES.pdf new file mode 100644 index 0000000..4716ca1 Binary files /dev/null and b/WEEK04/WEEK04-SLIDES.pdf differ diff --git a/WEEK04/WEEK04.md b/WEEK04/WEEK04.md new file mode 100644 index 0000000..bdc437e --- /dev/null +++ b/WEEK04/WEEK04.md @@ -0,0 +1,945 @@ +# Week 4: Variables in Embedded Systems: Debugging and Hacking Variables w/ GPIO Output Basics + +*** +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: + +- Understand what variables are and how they're stored in memory +- Know the difference between initialized, uninitialized, and constant variables +- Use Ghidra to analyze binaries without debug symbols +- Patch binary files to change program behavior permanently +- Control GPIO pins to blink LEDs on the Pico 2 +- Convert patched binaries to UF2 format for flashing +- Understand the `.data`, `.bss`, and `.rodata` memory sections + +--- + +## Part 1: Understanding Variables + +### What is a Variable? + +A **variable** is like a labeled box where you can store information. Imagine you have a row of boxes numbered 0 to 9. Each box can hold one item. In programming: + +- The **boxes** are memory locations (addresses in SRAM) +- The **items** are the values you store +- The **labels** are the variable names you choose + +``` ++-----------------------------------------------------------------+ +| Memory (SRAM) - Like a row of numbered boxes | +| | +| Box 0 Box 1 Box 2 Box 3 Box 4 ... | +| +----+ +----+ +----+ +----+ +----+ | +| | 42 | | 17 | | 0 | |255 | | 99 | | +| +----+ +----+ +----+ +----+ +----+ | +| age score count max temp | +| | ++-----------------------------------------------------------------+ +``` + +### Declaration vs Definition + +When working with variables, there are two important concepts: + +| Concept | What It Does | Example | +| ------------------ | ------------------------------------ | -------------------------- | +| **Declaration** | Tells the compiler the name and type | `uint8_t age;` | +| **Definition** | Allocates memory for the variable | (happens with declaration) | +| **Initialization** | Assigns an initial value | `uint8_t age = 42;` | + +**Important Rule:** You must declare a variable BEFORE you use it! + +### Understanding Data Types + +The **data type** tells the compiler how much memory to allocate: + +| Type | Size | Range | Description | +| ---------- | ------- | ------------------------------- | ----------------------- | +| `uint8_t` | 1 byte | 0 to 255 | Unsigned 8-bit integer | +| `int8_t` | 1 byte | -128 to 127 | Signed 8-bit integer | +| `uint16_t` | 2 bytes | 0 to 65,535 | Unsigned 16-bit integer | +| `int16_t` | 2 bytes | -32,768 to 32,767 | Signed 16-bit integer | +| `uint32_t` | 4 bytes | 0 to 4,294,967,295 | Unsigned 32-bit integer | +| `int32_t` | 4 bytes | -2,147,483,648 to 2,147,483,647 | Signed 32-bit integer | + +### Anatomy of a Variable Declaration + +Let's break down this line of code: + +```c +uint8_t age = 42; +``` + +| Part | Meaning | +| --------- | ----------------------------------------------------- | +| `uint8_t` | Data type - unsigned 8-bit integer (1 byte) | +| `age` | Variable name - how we refer to this storage location | +| `=` | Assignment operator - puts a value into the variable | +| `42` | The initial value | +| `;` | Semicolon - tells compiler the statement is complete | + +--- + +## Part 2: Memory Sections - Where Variables Live + +### The Three Main Sections + +When your program is compiled, variables go to different places depending on how they're declared: + +``` ++-----------------------------------------------------------------+ +| .data Section (Flash -> copied to RAM at startup) | +| Contains: Initialized global/static variables | +| Example: int counter = 42; | ++-----------------------------------------------------------------+ +| .bss Section (RAM - zeroed at startup) | +| Contains: Uninitialized global/static variables | +| Example: int counter; (will be 0) | ++-----------------------------------------------------------------+ +| .rodata Section (Flash - read only) | +| Contains: Constants, string literals | +| Example: const int MAX = 100; | +| Example: "hello, world" | ++-----------------------------------------------------------------+ +``` + +### What Happens to Uninitialized Variables? + +In older C compilers, uninitialized variables could contain "garbage" - random leftover data. But modern compilers (including the Pico SDK) are smarter: + +1. Uninitialized global variables go into the `.bss` section +2. The `.bss` section is **NOT stored in the binary** (saves space!) +3. At boot, the startup code uses `memset` to **zero out** all of `.bss` +4. So uninitialized variables are always `0`! + +This is why in our code: +```c +uint8_t age; // This will be 0, not garbage! +``` + +--- + +## Part 3: Understanding GPIO (General Purpose Input/Output) + +### What is GPIO? + +**GPIO** stands for **General Purpose Input/Output**. These are pins on the microcontroller that you can control with software. Think of them as tiny switches you can turn on and off. + +``` ++-----------------------------------------------------------------+ +| Raspberry Pi Pico 2 | +| | +| GPIO 16 -------► Red LED | +| GPIO 17 -------► Green LED | +| GPIO 18 -------► Blue LED | +| ... | +| GPIO 25 -------► Onboard LED | ++-----------------------------------------------------------------+ +``` + +### GPIO Functions in the Pico SDK + +The Pico SDK provides simple functions to control GPIO pins: + +| Function | Purpose | +| ------------------------------ | ------------------------------- | +| `gpio_init(pin)` | Initialize a GPIO pin for use | +| `gpio_set_dir(pin, direction)` | Set pin as INPUT or OUTPUT | +| `gpio_put(pin, value)` | Set pin HIGH (1) or LOW (0) | +| `sleep_ms(ms)` | Wait for specified milliseconds | + +### What Happens Behind the Scenes? + +Each high-level function calls lower-level code. Let's trace `gpio_init()`: + +``` +gpio_init(LED_PIN) + ↓ +gpio_set_dir(LED_PIN, GPIO_IN) // Initially set as input + ↓ +gpio_put(LED_PIN, 0) // Set output value to 0 + ↓ +gpio_set_function(LED_PIN, GPIO_FUNC_SIO) // Connect to SIO block +``` + +The SIO (Single-cycle I/O) block is a special hardware unit in the RP2350 that provides fast GPIO control! + +--- + +## Part 4: Setting Up Your Environment + +### Prerequisites + +Before we start, make sure you have: + +1. A Raspberry Pi Pico 2 board +2. Ghidra installed (for static analysis) +3. Python installed (for UF2 conversion) +4. The sample projects: + - `0x0005_intro-to-variables` + - `0x0008_uninitialized-variables` +5. A serial monitor (PuTTY, minicom, or screen) + +### Project Structure + +``` +Embedded-Hacking/ ++-- 0x0005_intro-to-variables/ +| +-- build/ +| | +-- 0x0005_intro-to-variables.uf2 +| | +-- 0x0005_intro-to-variables.bin +| +-- 0x0005_intro-to-variables.c ++-- 0x0008_uninitialized-variables/ +| +-- build/ +| | +-- 0x0008_uninitialized-variables.uf2 +| | +-- 0x0008_uninitialized-variables.bin +| +-- 0x0008_uninitialized-variables.c ++-- uf2conv.py +``` + +--- + +## Part 5: Hands-On Tutorial - Analyzing Variables in Ghidra + +### Step 1: Review the Source Code + +First, let's look at the code we'll be analyzing: + +**File: `0x0005_intro-to-variables.c`** + +```c +#include +#include "pico/stdlib.h" + +int main(void) { + uint8_t age = 42; + + age = 43; + + stdio_init_all(); + + while (true) + printf("age: %d\r\n", age); +} +``` + +**What this code does:** + +1. Declares a variable `age` and initializes it to `42` +2. Changes `age` to `43` +3. Initializes the serial output +4. Prints `age` forever in a loop + +### Step 2: Flash the Binary to Your Pico 2 + +1. Hold the BOOTSEL button on your Pico 2 +2. Plug in the USB cable (while holding BOOTSEL) +3. Release BOOTSEL - a drive called "RPI-RP2" appears +4. Drag and drop `0x0005_intro-to-variables.uf2` onto the drive +5. The Pico will reboot and start running! + +### Step 3: Verify It's Working + +Open your serial monitor (PuTTY, minicom, or screen) and you should see: + +``` +age: 43 +age: 43 +age: 43 +... +``` + +The program is printing `43` because that's what we assigned after the initial `42`. + +--- + +## Part 6: Setting Up Ghidra for Binary Analysis + +### Step 4: Start Ghidra + +**Open a terminal and type:** + +```cmd +ghidraRun +``` + +Ghidra will open. Now we need to create a new project. + +### Step 5: Create a New Project + +1. Click **File** -> **New Project** +2. Select **Non-Shared Project** +3. Click **Next** +4. Enter Project Name: `0x0005_intro-to-variables` +5. Click **Finish** + +### Step 6: Import the Binary + +1. Open your file explorer +2. Navigate to the `Embedded-Hacking` folder +3. Find `0x0005_intro-to-variables.bin` +4. Select Cortex M Little Endian 32 +5. Select Options and set up the .text and offset 10000000 +6. **Drag and drop** the `.bin` file into Ghidra's project window + +### Step 7: Configure the Binary Format + +A dialog appears. The file is identified as a "BIN" (raw binary without debug symbols). + +**Click the three dots (...) next to "Language" and:** + +1. Search for "Cortex" +2. Select **ARM Cortex 32 little endian default** +3. Click **OK** + +**Click the "Options..." button and:** + +1. Change **Block Name** to `.text` +2. Change **Base Address** to `10000000` (the XIP address!) +3. Click **OK** + +### Step 8: Open and Analyze + +1. Double-click on the file in the project window +2. A dialog asks "Analyze now?" - Click **Yes** +3. Use default analysis options and click **Analyze** + +Wait for analysis to complete (watch the progress bar in the bottom right). + +--- + +## Part 7: Navigating and Resolving Functions + +### Step 9: Find the Functions + +Look at the **Symbol Tree** panel on the left. Expand **Functions**. + +You'll see function names like: + +- `FUN_1000019a` +- `FUN_10000210` +- `FUN_10000234` + +These are auto-generated names because we imported a raw binary without symbols! + +### Step 10: Resolve Known Functions + +From our previous chapters, we know what some of these functions are: + +| Ghidra Name | Actual Name | How We Know | +| -------------- | ------------- | -------------------------- | +| `FUN_1000019a` | `data_cpy` | From Week 3 boot analysis | +| `FUN_10000210` | `frame_dummy` | From Week 3 boot analysis | +| `FUN_10000234` | `main` | This is where our code is! | + +### Step 11: Update Main's Signature + +For `main`, let's also fix the return type: + +1. Right-click on `main` in the Decompile window +2. Select **Edit Function Signature** +3. Change to: `int main(void)` +4. Click **OK** + +--- + +## Part 8: Analyzing the Main Function + +### Step 12: Examine Main in Ghidra + +Click on `main` (or `FUN_10000234`). Look at the **Decompile** window: + +You'll see something like: + +```c +void FUN_10000234(void) + +{ + FUN_10002f54(); + do { + FUN_100030e4(DAT_10000244,0x2b); + } while( true ); +} +``` + +### Step 13: Resolve stdio_init_all + +1. Click on `FUN_10002f54` +2. Right-click -> **Edit Function Signature** +3. Change to: `bool stdio_init_all(void)` +4. Click **OK** + +### Step 14: Resolve printf + +1. Click on `FUN_100030e4` +2. Right-click -> **Edit Function Signature** +3. Change the name to `void printf (undefined4 param_1, ...)` +4. Check the **Varargs** checkbox (printf takes variable arguments!) +5. Click **OK** + +### Step 15: Understand the Optimization + +Look at the updated decompiled code. This will look different if you resolved your functions however do you notice something interesting? + +```c +int main(void) + +{ + stdio_init_all(); + do { + printf(DAT_10000244,0x2b); + } while( true ); +} +``` + +**Where's `uint8_t age = 42`?** It's gone! + +The compiler **optimized it out**! Here's what happened: + +1. Original code: `age = 42`, then `age = 43` +2. Compiler sees: "The `42` is never used, only `43` matters" +3. Compiler removes the unused `42` and just uses `43` directly + +**What is `0x2b`?** Let's check: + +- `0x2b` in hexadecimal = `43` in decimal + +The compiler replaced our variable with the constant value! + +--- + +## Part 9: Patching the Binary - Changing the Value + +### Step 16: Find the Value to Patch + +Look at the **Listing** window (assembly view). Find the instruction that loads `0x2b`: + +```assembly +1000023a 2b 21 movs r1,#0x2b +``` + +This instruction loads the value `0x2b` (43) into register `r1` before calling `printf`. + +### Step 17: Patch the Instruction + +We're going to change `0x2b` (43) to `0x46` (70)! + +To patch instructions cleanly in Ghidra without assembler context conflicts, use the **Bytes Window** workflow: + +1. Ensure the Bytes window is open (**Window** -> **Bytes: 0x0005_intro-to-variables.bin**). +2. In the Bytes window toolbar, click the **pencil icon** (**Toggle Edit Mode**) to enable editing. +3. In the **Listing** window, click on address `1000023a` (`movs r1,#0x2b`) and press **`C`** (**Clear Code Bytes**). +4. In the **Bytes** window at offset `1000023a`, click on byte `2B` and change it to **`46`**. +5. In the **Listing** window, click back on address `1000023a` and press **`D`** (**Disassemble**). + +*(Alternatively, you can right-click the instruction at `1000023a` in the Listing, select **Patch Instruction**, replace immediate `0x2b` with `0x46`, and press Enter)*. + +The instruction now reads: +```assembly +1000023a 46 21 movs r1,#0x46 +``` + +### Step 18: Export the Patched Binary + +1. Click **File** -> **Export Program** +2. Set **Format** to **Raw Bytes** +3. Navigate to your build directory +4. Name the file `0x0005_intro-to-variables-h.bin` +5. Click **OK** + +--- + +## Part 10: Converting and Flashing the Hacked Binary + +### Step 19: Convert to UF2 Format + +The Pico 2 expects UF2 files, not raw BIN files. We need to convert it! + +**Open a terminal and navigate to your project directory:** + +```cmd +cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0005_intro-to-variables +``` + +**Run the conversion command:** + +```cmd +python ..\uf2conv.py build\0x0005_intro-to-variables-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +**What this command means:** + +- `uf2conv.py` = the conversion script +- `--base 0x10000000` = the XIP base address +- `--family 0xe48bff59` = the RP2350 family ID +- `--output build\hacked.uf2` = the output filename + +### Step 20: Flash the Hacked Binary + +1. Hold BOOTSEL and plug in your Pico 2 +2. Drag and drop `hacked.uf2` onto the RPI-RP2 drive +3. Open your serial monitor + +**You should see:** + +``` +age: 70 +age: 70 +age: 70 +... +``` + + **BOOM! We hacked it!** The value changed from 43 to 70! + +--- + +## Part 11: Uninitialized Variables and GPIO + +Now let's work with a more complex example that includes GPIO control. + +### Step 21: Review the Uninitialized Variables Code + +**File: `0x0008_uninitialized-variables.c`** + +```c +#include +#include "pico/stdlib.h" + +#define LED_PIN 16 + +int main(void) { + uint8_t age; // Uninitialized! + + stdio_init_all(); + + gpio_init(LED_PIN); + gpio_set_dir(LED_PIN, GPIO_OUT); + + while (true) { + printf("age: %d\r\n", age); + + gpio_put(LED_PIN, 1); + sleep_ms(500); + + gpio_put(LED_PIN, 0); + sleep_ms(500); + } +} +``` + +**What this code does:** + +1. Declares `age` without initializing it (will be 0 due to BSS zeroing) +2. Initializes GPIO 16 as an output +3. In a loop: prints age, blinks the LED + +### Step 22: Flash and Verify + +1. Flash `0x0008_uninitialized-variables.uf2` to your Pico 2 +2. Open your serial monitor + +**You should see:** + +``` +age: 0 +age: 0 +age: 0 +... +``` + +And the **red LED on GPIO 16 should be blinking**! + +The value is `0` because uninitialized variables in the `.bss` section are zeroed at startup. + +--- + +## Part 12: Analyzing GPIO Code in Ghidra + +### Step 23: Set Up Ghidra for the New Binary + +1. Create a new project: `0x0008_uninitialized-variables` +2. Import `0x0008_uninitialized-variables.bin` +3. Set Language to **ARM Cortex 32 little endian** +4. Set Base Address to `.text` and `10000000` +5. Auto-analyze + +### Step 24: Resolve the Functions + +Find and rename these functions: + +| Ghidra Name | Actual Name | +| -------------- | ---------------- | +| `FUN_10000234` | `main` | +| `FUN_100030cc` | `stdio_init_all` | +| `FUN_100002b4` | `gpio_init` | +| `FUN_1000325c` | `printf` | + +For `gpio_init`, set the signature to: +```c +void gpio_init(uint gpio) +``` + +### Step 25: Examine the Main Function + +The decompiled main should look something like: + +```c +void FUN_10000234(void) + +{ + undefined4 extraout_r1; + undefined4 extraout_r2; + undefined4 in_cr0; + undefined4 in_cr4; + + FUN_100030cc(); + FUN_100002b4(0x10); + coprocessor_moveto2(0,4,0x10,1,in_cr4); + do { + FUN_1000325c(DAT_10000274,0); + coprocessor_moveto2(0,4,0x10,1,in_cr0); + FUN_10000d10(500); + coprocessor_moveto2(0,4,0x10,0,in_cr0); + FUN_10000d10(500,extraout_r1,extraout_r2,0); + } while( true ); +} +``` + +--- + +## Part 13: Hacking GPIO - Changing the LED Pin + +### Step 26: Find the GPIO Pin Value + +Look in the assembly for instructions that use `0x10` (which is 16 in decimal - our LED pin): + +```assembly +1000023a 10 20 movs r0,#0x10 +``` + +This is where `gpio_init(LED_PIN)` is called with GPIO 16. + +### Step 27: Patch GPIO 16 to GPIO 17 + +We'll change the red LED (GPIO 16) to the green LED (GPIO 17)! + +1. In the **Listing** window, click address `1000023a` (`movs r0,#0x10`) and press **`C`** (**Clear Code Bytes**). +2. In the **Bytes** window (with the pencil icon enabled), locate offset `1000023a`, click byte `10`, and change it to **`11`**. +3. In the **Listing** window, click back on address `1000023a` and press **`D`** (**Disassemble**). + +*(Alternatively, right-click `movs r0,#0x10` -> **Patch Instruction** -> change `0x10` to `0x11` and press Enter)*. + +### Step 28: Find All GPIO 16 References + +There are more places that use GPIO 16. Look for: + +```assembly +10000244 10 23 movs r3,#0x10 +``` + +This is used in `gpio_set_dir`. In Listing press **`C`** at `10000244`, change byte `10` to **`11`** in the Bytes window, and press **`D`** in Listing. + +```assembly +10000252 10 24 movs r4,#0x10 +``` + +This is inside the loop for `gpio_put`. In Listing press **`C`** at `10000252`, change byte `10` to **`11`** in the Bytes window, and press **`D`** in Listing. +Verify the patched bytes: + +- `10000244`: `10 23` -> `11 23` +- `10000252`: `10 24` -> `11 24` + +### Step 29: Bonus - Change the Printed Value + +Let's also change the printed value from `0` to `0x42` (66 in decimal): + +```assembly +1000024a 00 21 movs r1,#0x0 +``` + +1. In Listing, click `1000024a` and press **`C`** (**Clear Code Bytes**). +2. In the Bytes window, change byte `00` to **`42`**. +3. In Listing, click back on `1000024a` and press **`D`** (**Disassemble**). +4. Verify the instruction bytes change from `00 21` to `42 21`. + +--- + +## Part 14: Export and Test the Hacked GPIO + +### Step 30: Export the Patched Binary + +1. Click **File** -> **Export Program** +2. Format: **Raw Bytes** +3. Filename: `0x0008_uninitialized-variables-h.bin` +4. Click **OK** + +### Step 31: Convert to UF2 + +```cmd +cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0008_uninitialized-variables +python ..\uf2conv.py build\0x0008_uninitialized-variables-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +### Step 32: Flash and Verify + +1. Flash `hacked.uf2` to your Pico 2 +2. Check your serial monitor + +**You should see:** + +``` +age: 66 +age: 66 +age: 66 +... +``` + +And now the **GREEN LED on GPIO 17** should be blinking instead of the red one! + + **We successfully:** + +1. Changed the printed value from 0 to 66 +2. Changed which LED blinks from red (GPIO 16) to green (GPIO 17) + +--- + +## Part 15: Deep Dive - GPIO at the Assembly Level + +### Understanding the GPIO Coprocessor + +The RP2350 has a special **GPIO coprocessor** that provides fast, single-cycle GPIO control. This is different from the RP2040! + +The coprocessor is accessed using special ARM instructions: + +```assembly +mcrr p0, #4, r4, r5, c0 ; GPIO output control +mcrr p0, #4, r4, r5, c4 ; GPIO direction control +``` + +**What this means:** + +- `mcrr` = Move to Coprocessor from two ARM Registers +- `p0` = Coprocessor 0 (the GPIO coprocessor) +- `r4` = Contains the GPIO pin number +- `r5` = Contains the value (0 or 1) +- `c0` = Output value register +- `c4` = Output enable register + +### The Full GPIO Initialization Sequence + +When you call `gpio_init(16)`, here's what actually happens: + +``` +Step 1: Configure pad (address 0x40038044) ++-----------------------------------------------------------------+ +| - Clear OD bit (output disable) | +| - Set IE bit (input enable) | +| - Clear ISO bit (isolation) | ++-----------------------------------------------------------------+ + +Step 2: Set function (address 0x40028084) ++-----------------------------------------------------------------+ +| - Set FUNCSEL to 5 (SIO - Software I/O) | ++-----------------------------------------------------------------+ + +Step 3: Enable output (via coprocessor) ++-----------------------------------------------------------------+ +| - mcrr p0, #4, r4, r5, c4 (where r4=16, r5=1) | ++-----------------------------------------------------------------+ +``` + +### Raw Assembly LED Blink + +Here's what a completely hand-written assembly LED blink looks like: + +```assembly +; Initialize GPIO 16 as output +movs r4, #0x10 ; GPIO 16 +movs r5, #0x01 ; Enable +mcrr p0, #4, r4, r5, c4 ; Set as output + +; Configure pad registers +ldr r3, =0x40038044 ; Pad control for GPIO 16 +ldr r2, [r3] ; Load current config +bic r2, r2, #0x80 ; Clear OD (output disable) +orr r2, r2, #0x40 ; Set IE (input enable) +str r2, [r3] ; Store config + +; Set GPIO function to SIO +ldr r3, =0x40028084 ; IO bank control for GPIO 16 +movs r2, #5 ; FUNCSEL = SIO +str r2, [r3] ; Set function + +; Main loop +loop: + ; LED ON + movs r4, #0x10 ; GPIO 16 + movs r5, #0x01 ; High + mcrr p0, #4, r4, r5, c0 + + ; Delay + ldr r2, =0x17D7840 ; ~25 million iterations +delay1: + subs r2, r2, #1 + bne delay1 + + ; LED OFF + movs r4, #0x10 ; GPIO 16 + movs r5, #0x00 ; Low + mcrr p0, #4, r4, r5, c0 + + ; Delay + ldr r2, =0x17D7840 +delay2: + subs r2, r2, #1 + bne delay2 + + b loop ; Repeat forever +``` + +--- + +## Part 16: Summary and Review + +### What We Accomplished + +1. **Learned about variables** - How they're declared, initialized, and stored +2. **Understood memory sections** - `.data`, `.bss`, and `.rodata` +3. **Analyzed binaries in Ghidra** - Without debug symbols! +4. **Patched binaries** - Changed values directly in the binary +5. **Controlled GPIO** - Made LEDs blink +6. **Changed program behavior** - Different LED, different value + +### The Binary Patching Workflow + +``` ++-----------------------------------------------------------------+ +| 1. Import .bin file into Ghidra | +| - Set language to ARM Cortex | +| - Set base address to 0x10000000 | ++-----------------------------------------------------------------+ +| 2. Analyze and resolve functions | +| - Rename functions to meaningful names | +| - Fix function signatures | ++-----------------------------------------------------------------+ +| 3. Find the values/instructions to patch | +| - Look in the assembly listing | +| - Bytes window (pencil, C, edit byte, D) or Patch Inst. | ++-----------------------------------------------------------------+ +| 4. Export the patched binary | +| - File -> Export Program | +| - Format: Raw Bytes | ++-----------------------------------------------------------------+ +| 5. Convert to UF2 | +| - python uf2conv.py file.bin --base 0x10000000 | +| --family 0xe48bff59 --output hacked.uf2 | ++-----------------------------------------------------------------+ +| 6. Flash and verify | +| - Hold BOOTSEL, plug in, drag UF2 | +| - Check serial output and LED behavior | ++-----------------------------------------------------------------+ +``` + +### Key Memory Sections + +| Section | Location | Contains | Writable? | +| --------- | -------- | ------------------------------ | --------- | +| `.text` | Flash | Code | No | +| `.rodata` | Flash | Constants, strings | No | +| `.data` | RAM | Initialized globals | Yes | +| `.bss` | RAM | Uninitialized globals (zeroed) | Yes | + +### Important Ghidra Commands + +| Action | How To Do It | +| ----------------- | ------------------------------------- | +| Rename function | Right-click -> Edit Function Signature | +| Patch instruction | Bytes window (pencil, C, edit, D) or Patch Instruction | +| Export binary | File -> Export Program -> Raw Bytes | +| Go to address | Press 'G' and enter address | + +--- + +--- + +## Key Takeaways + +1. **Variables are just memory locations** - The compiler assigns them addresses in SRAM. + +2. **Compilers optimize aggressively** - Unused code and values may be removed entirely. + +3. **Uninitialized doesn't mean random** - Modern compilers zero out the `.bss` section. + +4. **Ghidra works without symbols** - You can analyze any binary, even stripped ones. + +5. **Binary patching is powerful** - You can change behavior without source code. + +6. **UF2 conversion is required** - The Pico 2 needs UF2 format, not raw binaries. + +7. **GPIO is just memory-mapped I/O** - Writing to specific addresses controls hardware. + +--- + +## Glossary + +| Term | Definition | +| ------------------ | --------------------------------------------------------------------- | +| **BSS** | Block Started by Symbol - section for uninitialized global variables | +| **Declaration** | Telling the compiler a variable's name and type | +| **Definition** | Allocating memory for a variable | +| **GPIO** | General Purpose Input/Output - controllable pins on a microcontroller | +| **Initialization** | Assigning an initial value to a variable | +| **Linker** | Tool that combines compiled code and assigns memory addresses | +| **Optimization** | Compiler removing or simplifying code for efficiency | +| **Patching** | Modifying bytes directly in a binary file | +| **rodata** | Read-only data section for constants and string literals | +| **SIO** | Single-cycle I/O - fast GPIO control block in RP2350 | +| **UF2** | USB Flashing Format - file format for Pico 2 firmware | +| **Variable** | A named storage location in memory | + +--- + +## Additional Resources + +### GPIO Coprocessor Reference + +The RP2350 GPIO coprocessor instructions: + +| Instruction | Description | +| -------------------------- | ---------------------------- | +| `mcrr p0, #4, Rt, Rt2, c0` | Set/clear GPIO output | +| `mcrr p0, #4, Rt, Rt2, c4` | Set/clear GPIO output enable | + +### RP2350 Memory Map Quick Reference + +| Address | Description | +| ------------ | ------------------------ | +| `0x10000000` | XIP Flash (code) | +| `0x20000000` | SRAM (data) | +| `0x40028000` | IO_BANK0 (GPIO control) | +| `0x40038000` | PADS_BANK0 (pad control) | +| `0xd0000000` | SIO (single-cycle I/O) | + +--- + +**Remember:** Every binary you encounter in the real world can be analyzed and understood using these same techniques. Practice makes perfect! + +Happy hacking! + + diff --git a/WEEK04/WEEK04.pdf b/WEEK04/WEEK04.pdf new file mode 100644 index 0000000..165b329 Binary files /dev/null and b/WEEK04/WEEK04.pdf differ diff --git a/WEEK04/WEEK04a.md b/WEEK04/WEEK04a.md new file mode 100644 index 0000000..4c697fe --- /dev/null +++ b/WEEK04/WEEK04a.md @@ -0,0 +1,1063 @@ +# Week 4a: Hardware-Aware Reverse Engineering with CMSIS-SVD: Live GDB and Ghidra Analysis of Stripped Binaries + +*** +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: + +- Understand the fundamental differences between an ELF binary and a raw `.bin` firmware dump. +- Flash and debug `CTF-01.bin` using OpenOCD and a hardware Debug Probe with explicit base addressing ($0\text{x}10000000$). +- Operate GDB without an executable or symbol table, using hardware breakpoints (`hb`) and address-based disassembly (`x/i`). +- Navigate the ARM Cortex-M Vector Table at $0\text{x}10000000$ to find the initial Stack Pointer ($0\text{x}20082000$) and Reset Vector ($0\text{x}1000015B \rightarrow 0\text{x}1000015A$). +- Follow boot execution into `main()` at $0\text{x}100001E0$ without debug symbols. +- Identify the Memory-Mapped I/O (MMIO) peripheral blind spot in stripped binaries (UART0, IO_BANK0, PADS_BANK0, RESETS, SIO). +- Install and configure CMSIS-SVD (`rp2350.svd`) and `PyCortexMDebug` inside GDB for live dynamic peripheral inspection. +- Inspect UART0 baud rate and FIFO status registers, and verify GPIO 0/1 pin multiplexing live over SWD. +- Install and run `SVD-Loader-Ghidra` on `CTF-01.bin` to map peripheral memory blocks and auto-generate C struct definitions. +- Transform raw, obscure pointer decompilation into readable, vendor-grade peripheral struct accesses. +- Understand why arithmetic immediate instructions like `add.w r0, r0, #0x40000000` appear in the assembly listing while resolving cleanly in the decompiler. +- Execute a unified hardware-aware reverse engineering workflow combining static analysis in Ghidra and dynamic analysis in GDB to solve the CTF mission. + +--- + +## Part 1: The Raw Binary Dilemma (.elf vs .bin) + +### ELF Files: The Friendly Development Container + +In previous lessons, when debugging your firmware, you launched GDB pointing to an **Executable and Linkable Format (ELF)** file: + +```cmd +arm-none-eabi-gdb build\CTF-01.elf +``` + +An ELF file is not just machine code. It is a rich, structured container that contains: +1. **ELF Header**: Specifies the target architecture, endianness, and exact entry point address. +2. **Section Headers**: Defines memory segments such as `.text`, `.data`, `.rodata`, and `.bss`. +3. **Symbol Table**: Maps human-readable names (`main`, `grid_deviation`, `evaluate_grid`) to exact virtual memory addresses. +4. **DWARF Debug Information**: Links individual assembly instructions back to original C source file line numbers, variable types, and stack frame layouts. + +When GDB loads an ELF file, it knows the name of every function, the layout of every struct, and the location of `main`. + +### Raw .bin Files: The Harsh Reality of Firmware Extraction + +In real-world hardware reverse engineering, red teaming, and firmware extraction (such as reading an external SPI flash chip or intercepting an over-the-air firmware update), you will almost never have access to an ELF file. + +Instead, you are handed a **raw binary file (`.bin`)**: + +``` ++-----------------------------------------------------------------+ +| Comparison: ELF Container vs. Raw .bin Firmware | +| | +| CTF-01.elf (Development Build) | +| +-----------------------------------------------------------+ | +| | ELF Header (Entry Point: 0x1000015b) | | +| | Symbol Table (main -> 0x100001e0, evaluate_grid -> ...) | | +| | DWARF Debug Data (C Source Line Mapping) | | +| | Section Table (.text, .rodata, .data, .bss) | | +| | Machine Code Payload (15,920 bytes) | | +| +-----------------------------------------------------------+ | +| | +| CTF-01.bin (Field Recovery Image) | +| +-----------------------------------------------------------+ | +| | [Flat Machine Code Bytes Only - No Headers, No Symbols] | | +| | 00 20 08 20 5b 01 00 10 1b 01 00 10 1d 01 00 10 ... | | +| +-----------------------------------------------------------+ | ++-----------------------------------------------------------------+ +``` + +A `.bin` file is a byte-for-byte memory dump of flash memory. It contains: +- **No ELF header** +- **No section names** +- **No symbol names** +- **No debug information** +- **No base address metadata** (the file itself does not record where in memory it belongs) + +If you attempt to launch GDB with `0x0001b_ctf/CTF-01.bin` directly: + +```powershell +arm-none-eabi-gdb 0x0001b_ctf\CTF-01.bin +``` + +GDB immediately halts with an error: + +```text +"0x0001b_ctf/CTF-01.bin": not in executable format: file format not recognized +``` + +### The Hardware MMIO Blind Spot + +Stripped binaries introduce a second, even larger obstacle: **hardware peripherals**. + +Microcontrollers interact with the outside world using **Memory-Mapped Input/Output (MMIO)**. Peripherals like UART, GPIO, Clocks, and Resets do not have special CPU instructions. Instead, they are mapped to specific, fixed physical addresses in the microcontroller's memory map: + +| Subsystem | RP2350 Physical Address Base | Function | +| :--- | :--- | :--- | +| `RESETS` | `0x40020000` | Subsystem reset controller (releases UART and GPIO from reset) | +| `IO_BANK0` | `0x40028000` | GPIO pin function select (`FUNCSEL`) and overrides | +| `PADS_BANK0` | `0x40038000` | Electrical pad controls (drive strength, pulls, Schmitt) | +| `UART0` | `0x40070000` | Serial UART interface (115200 8N1 telemetry console) | +| `SIO` | `0xd0000000` | Single-cycle I/O (fast CPU core check, GPIO controls) | + +When analyzing a raw binary, neither GDB nor Ghidra understands what `0x40070018` or `0x40028004` mean. To standard tools, they are just arbitrary hexadecimal numbers. Reverse engineers are left manually cross-referencing thousands of pages of microcontroller datasheets. + +In this tutorial, you will master the two industry-standard tools that eliminate this blind spot: +1. **PyCortexMDebug** for live, dynamic hardware awareness in **GDB**. +2. **SVD-Loader-Ghidra** for automatic struct mapping and clear decompilation in **Ghidra**. + +--- + +## Part 2: The Target Application: `0x0001b_ctf` (Operation Black Start) + +### Mission Briefing + +Our primary target is the emergency firmware build from **`0x0001b_ctf`** (**CTF-01: Operation Black Start**). + +In this scenario, a critical cyberattack severed the primary SCADA network coordinating regional power grid interconnections. An emergency firmware build was deployed to the **GRID-7** fleet of relay controllers to manage an automated black-start restoration. However, the rushed build contains two critical defects: +1. **Miscalibrated Safety Threshold**: The frozen grid frequency deviation latched at $0.87\text{ Hz}$ is evaluated against a corrupt threshold of $95$ ($0.95\text{ Hz}$) instead of the hard engineering safety limit of $60$ ($0.60\text{ Hz}$), causing the relay to report a false-safe `STABLE` status. +2. **Hardcoded False Status Line**: The operator console line falsely asserts `SIGNAL: NORMAL` regardless of actual channel health. + +The source code was overwritten during the crisis build process. The only surviving artifact is the compiled raw binary: **`CTF-01.bin`**. + +### The Source Code Behind the Binary + +Let's review the firmware architecture from `0x0001b_ctf/src/` to understand what machine code the compiler generated: + +#### `main.c` + +```c +#include "console.h" +#include "grid.h" +#include "pico/stdlib.h" + +int main(void) +{ + stdio_init_all(); + retain_dispatch_frame(); + evaluate_grid(); + print_boot_banner(); + while (true) { + print_status(); + sleep_ms(1000); + } + return 0; +} +``` + +#### `grid.c` + +```c +#include "grid.h" +#include + +// Frozen grid frequency deviation reading latched when comms were severed. +volatile uint32_t grid_deviation = 87; + +// Operator-facing classification of the frozen reading (drives GRID STATUS). +volatile uint32_t operator_state = 0; + +// Automated dispatch authorization decision (drives DISPATCH PATH). +volatile uint32_t dispatch_state = 0; + +// Quarantined black-start authorization frame, retained in flash, never sent. +static volatile const char dispatch_frame[] = + "WORLDGRID:BLACKSTART:GRID-7:WATER-3"; + +void retain_dispatch_frame(void) +{ + volatile char frame_marker = dispatch_frame[0]; + (void)frame_marker; +} + +void evaluate_grid(void) +{ + operator_state = (grid_deviation < SAFE_THRESHOLD) ? 1 : 0; + dispatch_state = (grid_deviation < SAFE_THRESHOLD) ? 1 : 0; +} +``` + +#### `console.c` + +```c +#include "console.h" +#include "grid.h" +#include + +void print_boot_banner(void) +{ + printf("GLOBAL EMBEDDED RESPONSE NETWORK\r\n"); + printf("BLACK START WINDOW: 27 MINUTES\r\n"); + printf("UART0 115200 8N1 | AUTHORIZED LAB CONSOLE\r\n"); + printf("SIGNAL: NORMAL\r\n"); + printf("RESPONSE> "); +} + +void print_status(void) +{ + printf("GRID STATUS: %s\r\n", operator_state ? "STABLE" : "CRITICAL"); + printf("DISPATCH PATH: %s\r\n", dispatch_state ? "AUTHORIZED" : "HELD"); + printf("LAST FRAME: QUARANTINED\r\n"); + printf("RESPONSE> "); +} +``` + +### Hardware Actions Performed by This Code + +1. **`stdio_init_all()`**: + - Releases the UART0, IO_BANK0, and PADS_BANK0 peripherals from reset via `RESETS` (`0x40020000`). + - Configures `GPIO0` (TX) and `GPIO1` (RX) pin multiplexing to function select `2` (UART0) in `IO_BANK0` (`0x40028000`). + - Configures pad electrical properties in `PADS_BANK0` (`0x40038000`). + - Programs baud rate divisors (`UARTIBRD`, `UARTFBRD`) and line controls (`UARTLCR_H`) in `UART0` (`0x40070000`) for 115200 baud, 8 data bits, no parity, 1 stop bit (8N1). +2. **`retain_dispatch_frame()`**: + - Anchors the secret authorization token string `"WORLDGRID:BLACKSTART:GRID-7:WATER-3"` at physical flash address $0\text{x}100037A0$. +3. **`evaluate_grid()`**: + - Compares the global `grid_deviation` variable ($87$) against `SAFE_THRESHOLD` (compiled as `cmp r3, #94`). +4. **`print_boot_banner()` & `print_status()`**: + - Streams formatted strings through the UART0 Transmit FIFO buffer (`UART0_UARTDR`). + +In your workspace, this challenge is located in `0x0001b_ctf/`: +- `0x0001b_ctf/CTF-01.bin` (Raw 15,920-byte stripped firmware) +- `0x0001b_ctf/CTF-01.uf2` (Packaged UF2 image) + +For the remainder of this lesson, we assume you **only have `0x0001b_ctf/CTF-01.bin`**. + +--- + +## Part 3: Flashing and Connecting OpenOCD with Raw Binaries + +### Flashing a Raw Binary via OpenOCD + +When flashing an `.elf` file, OpenOCD reads target memory addresses directly from the ELF program headers. However, because a `.bin` file contains no header information, you **must explicitly specify the physical base address** ($0\text{x}10000000$ for RP2350 XIP flash). + +Open a PowerShell terminal in your repository root: + +```powershell +& "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\openocd.exe" ` + -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" ` + -f interface/cmsis-dap.cfg ` + -f target/rp2350.cfg ` + -c "adapter speed 5000; program 0x0001b_ctf/CTF-01.bin 0x10000000 verify reset exit" +``` + +Notice the crucial parameter: `0x10000000`. This instructs OpenOCD to write the binary bytes starting at the exact beginning of external flash memory. + +```text +** Programming Started ** +[rp2350.dap.core0] target halted due to debug-request +wrote 16384 bytes from file 0x0001b_ctf/CTF-01.bin in 0.412s +** Programming Finished ** +** Verify Started ** +verified 15920 bytes in 0.082s +** Verified OK ** +** Resetting Target ** +shutdown command invoked +``` + +### Starting OpenOCD as a Live Debug Server + +To debug the running firmware interactively, launch OpenOCD without the `exit` command. Keep this terminal open: + +```powershell +& "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\openocd.exe" ` + -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" ` + -f interface/cmsis-dap.cfg ` + -f target/rp2350.cfg ` + -c "adapter speed 5000; init" +``` + +OpenOCD initializes the SWD hardware connection and listens for GDB connections on TCP port `3333`. + +```text +Info : Hardware thread awareness created +Info : Listening on port 3333 for gdb connections +``` + +--- + +## Part 4: Dynamic Analysis in GDB Without an ELF + +Because we are analyzing a raw binary, we do not pass a file name to GDB on startup. + +Open a second terminal window: + +```powershell +arm-none-eabi-gdb +``` + +### Step 1: Set Target Architecture and Connect + +Inside the GDB prompt, tell GDB what processor architecture to expect, connect over TCP to OpenOCD, and immediately halt the processor: + +```gdb +set architecture armv8-m.main +target extended-remote :3333 +monitor reset halt +``` + +```text +The target architecture is assumed to be armv8-m.main +Remote debugging using :3333 +target halted due to debug-request, current mode: Thread +xPSR: 0x01000000 pc: 0x1000015a msp: 0x20082000 +``` + +### Step 2: Decode the Hardware Vector Table + +How did the CPU know to halt at `0x1000015a`? + +On ARM Cortex-M processors, the first entries of flash memory ($0\text{x}10000000$) define the **Vector Table**: +- **Word 0 ($0\text{x}10000000$)**: Initial Main Stack Pointer (`msp`). +- **Word 1 ($0\text{x}10000004$)**: Reset Vector (address of the first instruction to run upon reboot). + +Let's inspect the first four 32-bit words of flash using GDB's examine command (`x/4wx`): + +```gdb +x/4wx 0x10000000 +``` + +```text +0x10000000: 0x20082000 0x1000015b 0x1000011b 0x1000011d +``` + +Let's analyze what these words reveal: + +``` ++-----------------------------------------------------------------+ +| RP2350 Cortex-M33 Vector Table Decoding | +| | +| Address Value Meaning | +| 0x10000000: 0x20082000 Initial MSP (Top of 512KB SRAM) | +| 0x10000004: 0x1000015b Reset Vector (Thumb Execution Bit 0) | +| Execution begins at: 0x1000015a | ++-----------------------------------------------------------------+ +``` + +> [!NOTE] +> The Reset Vector value is `0x1000015b`. In ARM architecture, bit 0 indicates Thumb instruction mode ($0\text{x}1000015A + 1$). The processor automatically clears bit 0 and begins executing Thumb instructions at address `0x1000015a`. + +### Step 3: Why Symbolic Commands Fail + +If you try to use symbolic commands, GDB cannot help you: + +```gdb +break main +disassemble main +``` + +```text +No symbol table is loaded. Use the "file" command. +``` + +There are no symbols! To reverse engineer this firmware dynamically, we must navigate using **instruction addresses**. + +### Step 4: Disassemble by Address Range + +Disassemble 12 instructions starting at the Reset Handler address ($0\text{x}1000015A$): + +```gdb +x/12i 0x1000015a +``` + +```text +=> 0x1000015a: mov.w r0, #3489660928 ; 0xd0000000 (SIO base) + 0x1000015e: ldr r0, [r0, #0] ; SIO_CPUID (reads core id) + 0x10000160: cbz r0, 0x10000166 ; if Core 0, branch to init + 0x10000162: movs r0, #0 + 0x10000164: b.n 0x1000014e ; Core 1 sleeps + 0x10000166: add r4, pc, #52 ; loads init table pointer + 0x10000168: ldmia r4!, {r1, r2, r3} + 0x1000016a: cmp r1, #0 + 0x1000016c: beq.n 0x10000174 + 0x1000016e: bl 0x10000196 ; copy data section to SRAM + 0x10000172: b.n 0x10000168 + 0x10000174: ldr r1, [pc, #84] ; loads BSS bounds +``` + +Notice instruction `0x1000015a`: it immediately reads SIO register `0xd0000000` (`CPUID`) to check whether execution is occurring on Core 0 or Core 1! + +Now look further down the boot sequence at address `0x10000186`: + +```gdb +x/6i 0x10000184 +``` + +```text + 0x10000184: blx r1 + 0x10000186: ldr r1, [pc, #80] ; loads address from 0x100001d8 + 0x10000188: blx r1 ; calls main()! + 0x1000018a: ldr r1, [pc, #80] + 0x1000018c: blx r1 + 0x1000018e: bkpt 0x0000 +``` + +Inspect the pointer stored at `0x100001d8`: + +```gdb +x/wx 0x100001d8 +``` + +```text +0x100001d8: 0x100001e1 +``` + +Value `0x100001e1` is Thumb address $0\text{x}100001E0 + 1$. This reveals that **`main()` is located at address `0x100001e0`**! + +### Step 5: Setting Hardware Breakpoints on Flash Memory + +In RAM, GDB can set software breakpoints by temporarily replacing instructions with a breakpoint opcode (`bkpt`). However, external XIP flash ($0\text{x}10000000 - 0\text{x}1FFFFFFF$) is **read-only**. GDB cannot write to flash memory while the processor is running. + +Therefore, you must use **hardware breakpoints** (`hb`): + +```gdb +hb *0x100001e0 +continue +``` + +```text +Hardware assisted breakpoint 1 at 0x100001e0 +Continuing. + +Breakpoint 1, 0x100001e0 in ?? () +``` + +We are now stopped at the entry point of `main()` inside a completely stripped binary! + +Disassemble the instructions in `main()` from `0x100001e0` to `0x10000216`: + +```gdb +disassemble 0x100001e0, 0x10000216 +``` + +```text +Dump of assembler code from 0x100001e0 to 0x10000216: +=> 0x100001e0: push {r7, lr} + 0x100001e2: sub sp, #8 + 0x100001e4: bl 0x1000308c ; stdio_init_all() + 0x100001e8: ldr r3, [pc, #124] ; [0x10000268] -> 0x100037a0 (dispatch_frame) + 0x100001ea: ldr r2, [pc, #128] ; [0x1000026c] -> 0x200005d8 (grid_deviation) + 0x100001ec: ldrb r3, [r3, #0] ; retain_dispatch_frame(): reads dispatch_frame[0] ('W') + 0x100001ee: ldr r6, [pc, #128] ; [0x10000270] -> 0x20000844 (operator_state) + 0x100001f0: strb.w r3, [sp, #7] ; store frame_marker + 0x100001f4: ldrb.w r3, [sp, #7] ; reload frame_marker (volatile) + 0x100001f8: ldr r3, [r2, #0] ; evaluate_grid(): read grid_deviation (87) + 0x100001fa: ldr r5, [pc, #120] ; [0x10000274] -> 0x20000834 (dispatch_state) + 0x100001fc: cmp r3, #94 ; compare site A: if grid_deviation <= 94 + 0x100001fe: ite hi + 0x10000200: movhi r3, #0 + 0x10000202: movls r3, #1 + 0x10000204: str r3, [r6, #0] ; operator_state = 1 (STABLE) + 0x10000206: ldr r3, [r2, #0] ; read grid_deviation again + 0x10000208: ldr r0, [pc, #108] ; string pointer + 0x1000020a: cmp r3, #94 ; compare site B: if grid_deviation <= 94 + 0x1000020c: ite hi + 0x1000020e: movhi r3, #0 + 0x10000210: movls r3, #1 + 0x10000212: str r3, [r5, #0] ; dispatch_state = 1 (AUTHORIZED) + 0x10000214: bl 0x1000311c ; puts("GLOBAL EMBEDDED RESPONSE NETWORK") +End of assembler dump. +``` + +Notice what reverse engineering revealed right before our eyes: +- `0x100001e4`: `bl 0x1000308c` initializes UART0 serial communications. +- `0x100001e8`: Loads pointer `0x100037a0`. Inspecting `x/s 0x100037a0` reveals the secret token `"WORLDGRID:BLACKSTART:GRID-7:WATER-3"`! +- `0x100001fc` & `0x1000020a`: Both threshold checks compare `r3` against `#94` (`0x5e`). Because `grid_deviation` is $87$, $87 \le 94$, so both `operator_state` and `dispatch_state` are incorrectly set to $1$! + +### Step 6: The MMIO Blindness in Action + +Now step past `0x100001e4` (`stdio_init_all`) using `nexti` or `stepi`. + +In standard GDB, if you want to inspect what happened to UART0, you are forced to type raw hex addresses: + +```gdb +x/wx 0x40070018 +``` + +```text +0x40070018: 0x00000090 +``` + +What does `0x90` mean? +- Is the Transmit FIFO empty? +- Is the Receive FIFO full? +- Is the UART transmitter currently busy? +- What baud rate divisor was written to `0x40070024`? + +Standard GDB has no way to tell you. Let's fix that right now. + +--- + +## Part 5: Installing and Configuring CMSIS-SVD in GDB (PyCortexMDebug) + +### What is CMSIS-SVD? + +**CMSIS-SVD (Common Microcontroller Software Interface Standard - System View Description)** is an open XML specification developed by ARM. Silicon vendors publish an `.svd` file for every chip they manufacture. + +An SVD file contains a complete, machine-readable description of: +- Every peripheral on the chip +- Its physical base address +- Every register and offset +- Every bitfield, bitmask, access permission, and human-readable description + +``` ++-----------------------------------------------------------------+ +| CMSIS-SVD Hierarchy Tree | +| | +| Device: RP2350 | +| +-- Peripheral: UART0 (Base: 0x40070000) | +| | +-- Register: UARTDR (Offset: 0x000) | +| | +-- Register: UARTFR (Offset: 0x018) | +| | +-- Register: UARTIBRD (Offset: 0x024) | +| | +-- Register: UARTLCR_H (Offset: 0x02c) | +| +-- Peripheral: IO_BANK0 (Base: 0x40028000) | +| | +-- Register: GPIO0_CTRL (Offset: 0x004) -> UART0 TX | +| | +-- Register: GPIO1_CTRL (Offset: 0x00c) -> UART0 RX | +| +-- Peripheral: PADS_BANK0 (Base: 0x40038000) | +| | +-- Register: GPIO0 (Offset: 0x004) | +| +-- Peripheral: SIO (Base: 0xd0000000) | +| +-- Register: CPUID (Offset: 0x000) | ++-----------------------------------------------------------------+ +``` + +### Step 1: Obtain `rp2350.svd` + +A pre-downloaded copy of `rp2350.svd` is included directly in your course repository under `WEEK04/rp2350.svd`. You can also download the latest version directly from the official open-source `cmsis-svd-data` repository. + +Open a PowerShell terminal and create a dedicated `svd` directory in your user profile: + +```powershell +New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.svd" + +# Option A: Copy from local course folder +Copy-Item "WEEK04\rp2350.svd" "$env:USERPROFILE\.svd\rp2350.svd" + +# Option B: Download directly from GitHub +Invoke-WebRequest ` + -Uri "https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/RaspberryPi/rp2350.svd" ` + -OutFile "$env:USERPROFILE\.svd\rp2350.svd" +``` + +Verify that the file is in place: + +```powershell +Get-Item "$env:USERPROFILE\.svd\rp2350.svd" +``` + +### Step 2: Install `PyCortexMDebug` + +`PyCortexMDebug` is an open-source Python extension for GDB created by Bill Nahill. It parses SVD XML files and exposes high-level peripheral inspection commands inside your GDB session. + +Clone the repository into your user profile: + +```powershell +cd "$env:USERPROFILE" +git clone https://github.com/bnahill/PyCortexMDebug.git +``` + +### Step 3: Load PyCortexMDebug in GDB + +In your active GDB session, load the Python script and parse `rp2350.svd`: + +```gdb +source ~/PyCortexMDebug/scripts/gdb.py +svd_load ~/.svd/rp2350.svd +``` + +```text +Loading SVD file /Users/username/.svd/rp2350.svd... +Loaded 52 peripherals +``` + +Just like that, GDB now understands every single register, offset, and bitfield on the RP2350 microcontroller! + +> [!TIP] +> You can automate this process so SVD support is always active. Add the following lines to your `~/.gdbinit` file: +> ```gdb +> source ~/PyCortexMDebug/scripts/gdb.py +> svd_load ~/.svd/rp2350.svd +> ``` + +--- + +## Part 6: Live Dynamic Peripheral Inspection & Manipulation in GDB + +Now that GDB is hardware-aware, let's explore what we can do on our stripped `CTF-01.bin` firmware without touching source code. + +### Command 1: List All On-Chip Peripherals + +Type `svd` with no arguments to list all 52 peripherals on the RP2350: + +```gdb +svd +``` + +```text +Available Peripherals: + ACCESSCTRL ADC BUSCTRL CLOCKS + DMA GLITCH_DETECTOR I2C0 I2C1 + IO_BANK0 IO_QSPI OTP PADS_BANK0 + PADS_QSPI PIX_RP2040 PIO0 PIO1 + PIO2 PLL_SYS PLL_USB POWMAN + PWM QMI RESETS ROSC + SHA256 SIO SPI0 SPI1 + SYSINFO SYSCFG TBMAN TICKS + TIMER0 TIMER1 TRNG UART0 + UART1 USB VREG_AND_CHIP_RESET WATCHDOG + XIP_AUX XIP_CTRL XIP_QMI XOSC +``` + +### Command 2: Inspect a Peripheral Live (`svd UART0`) + +Inspect the **UART0** peripheral. GDB automatically reads the physical hardware registers across SWD and displays their live state: + +```gdb +svd UART0 +``` + +```text +UART0 @ 0x40070000: + UARTDR : 0x00000000 + UARTRSR : 0x00000000 + UARTFR : 0x00000090 + UARTILPR : 0x00000000 + UARTIBRD : 0x00000043 + UARTFBRD : 0x00000035 + UARTLCR_H : 0x00000070 + UARTCR : 0x00000301 + UARTIFLS : 0x00000012 + UARTIMSC : 0x00000000 + UARTRIS : 0x00000000 + UARTMIS : 0x00000000 + UARTICR : 0x00000000 + UARTDMACR : 0x00000003 +``` + +Look at what this dump proves: +- `UARTIBRD` ($0\text{x}43 = 67$) and `UARTFBRD` ($0\text{x}35 = 53$): These are the exact integer and fractional divisors for $115200\text{ baud}$ with a $125\text{ MHz}$ reference clock! +- `UARTLCR_H = 0x00000070`: Bits 6:5 are `11` ($8\text{ data bits}$) and bit 4 is `1` (FIFOs enabled). +- `UARTCR = 0x00000301`: Bit 0 is `UARTEN` ($1$), Bit 8 is `TXE` (Transmit Enable), Bit 9 is `RXE` (Receive Enable). + +### Command 3: Decode Bitfields (`svd /r UART0 UARTFR`) + +Remember the mysterious `0x00000090` we saw when reading `0x40070018`? Let's use the `/r` (raw/register decode) flag to decode it: + +```gdb +svd /r UART0 UARTFR +``` + +```text +UART0.UARTFR @ 0x40070018: 0x00000090 + [7] TXFE : 1 (Transmit FIFO empty) + [6] RXFF : 0 (Receive FIFO full) + [5] TXFF : 0 (Transmit FIFO full) + [4] RXFE : 1 (Receive FIFO empty) + [3] BUSY : 0 (UART busy transmitting) + [2] DCD : 0 (Data carrier detect) + [1] DSR : 0 (Data set ready) + [0] CTS : 0 (Clear to send) +``` + +In a single command, GDB completely decoded the raw hardware byte into human-readable FIFO and serial transmission states! + +Now inspect the pin multiplexer for `GPIO0`: + +```gdb +svd /r IO_BANK0 GPIO0_CTRL +``` + +```text +IO_BANK0.GPIO0_CTRL @ 0x40028004: 0x00000002 + [4:0] FUNCSEL : 2 (Function 2: UART0 TX) +``` + +This verifies that `GPIO0` has been successfully multiplexed to function as the hardware `UART0 TX` pin. + +### Command 4: Live Hardware Manipulation via SWD (`svd /w`) + +You can also **write** to registers by name using `svd /w`. + +While the processor is halted in GDB, look at your physical Pico 2 hardware board. Let's toggle the LED or write to a register directly: + +```gdb +svd /w SIO GPIO_OUT_SET 0x00010000 +``` + +You have full, symbolic, hardware-level control over a running target directly through GDB without having an ELF file or compiling a single line of code. + +--- + +## Part 7: Transitioning to Static Analysis in Ghidra + +Dynamic analysis in GDB lets us verify hardware state while stepping through execution. However, to understand the overall architecture, algorithms, and logic of a stripped firmware binary, we must perform **static analysis in Ghidra**. + +### Step 1: Import the Stripped Binary into Ghidra + +1. Launch Ghidra: `ghidraRun` +2. Create a new project named `CTF-01` +3. Drag and drop `0x0001b_ctf/CTF-01.bin` into the Active Project window. +4. In the Import dialog: + - **Language**: Click `...`, search for `Cortex`, and select **ARM:LE:32:Cortex (default)**. + - Click **Options...**: + - Change **Block Name** to `.text` + - Change **Base Address** to `10000000` (XIP Flash base) + - Click **OK**, then click **OK** to complete the import. +5. Double-click the file to open the CodeBrowser. +6. When prompted to analyze, click **Yes**, accept default analyzers, and click **Analyze**. + +### Step 2: The Decompiler Before SVD (The Pointer Maze) + +Navigate to `main` at address `0x100001E0` in the Ghidra decompiler. + +Before adding SVD support, the decompiler output looks like this: + +```c +void FUN_100001e0(void) +{ + byte extraout_DL; + + FUN_1000308c(); + FUN_1000311c(DAT_10000278); + FUN_1000311c(DAT_1000027c); + FUN_1000311c(DAT_10000280); + FUN_1000311c(DAT_10000284); + FUN_10003218(DAT_10000288); + do { + FUN_10003218(DAT_10000294,extraout_DL); + FUN_10003218(DAT_100002a0,extraout_DL); + FUN_1000311c(DAT_100002a4); + FUN_10003218(DAT_10000288); + FUN_10000cc4(1000); + } while( true ); +} +``` + +Now navigate to subroutine `FUN_100002a8` (`gpio_set_function`): + +```c +void FUN_100002a8(uint param_1, uint param_2) +{ + *(uint *)(0x40038000 + param_1 * 4 + 4) = + *(uint *)(0x40038000 + param_1 * 4 + 4) & 0xffffff7f | 0x40; + *(uint *)(0x40028000 + param_1 * 8 + 4) = param_2; + return; +} +``` + +Look at that decompilation: +- `*(uint *)(0x40038000 + param_1 * 4 + 4)` +- `*(uint *)(0x40028000 + param_1 * 8 + 4) = param_2` + +Ghidra doesn't know that `0x40038000` is `PADS_BANK0`, nor that `0x40028000` is `IO_BANK0`. In fact, if you double-click `0x40038000`, Ghidra warns that the address is **unmapped** because the `.bin` import only created a memory block for flash ($0\text{x}10000000$). + +--- + +## Part 8: Installing and Running SVD-Loader in Ghidra + +### Step 1: Install SVD-Loader-Ghidra + +`SVD-Loader-Ghidra` is an open-source Ghidra script created by Leveldown Security. It parses CMSIS-SVD files and automatically reconstructs microcontroller memory maps and data structures inside Ghidra. + +Open a PowerShell terminal and clone the repository: + +```powershell +cd "$env:USERPROFILE" +git clone https://github.com/leveldown-security/SVD-Loader-Ghidra.git +``` + +> [!IMPORTANT] +> **Ghidra 11/12+ Runtime Compatibility Fix (`#@runtime Jython`):** +> Modern Ghidra versions default to `PyGhidra` (CPython 3) for `.py` scripts. If Ghidra was not launched via `pyghidraRun`, running `SVD-Loader.py` will fail with: +> `Unable to load script: SVD-Loader.py - detail: Ghidra was not started with PyGhidra. Python is not available` +> +> `SVD-Loader` was developed for Ghidra's built-in **Jython** interpreter. To instruct Ghidra to use the built-in Jython engine, ensure `#@runtime Jython` is present at the top of `SVD-Loader.py`: +> ```python +> # Load specified SVD and generate peripheral memory maps & structures. +> #@runtime Jython +> #@author Thomas Roth , Ryan Pavlik +> ``` +> *(You can add this line using any text editor, or in Ghidra by right-clicking `SVD-Loader.py` in the Script Manager and selecting **Edit with basic editor**).* + +### Step 2: Add the Script to Ghidra Script Manager + +1. In Ghidra's CodeBrowser, open the **Script Manager**: + - Go to menu **Window** -> **Script Manager** (or click the **Script Manager** toolbar icon). + +> [!NOTE] +> **Script Manager Toolbar Icons Explained:** +> - **New Script (White Paper Icon):** To create a new script from scratch directly in Ghidra, you click the **piece of white paper** ("Create New Script") icon on the toolbar. Ghidra then prompts you to choose the script type: **`PyGhidra`** (Python 3 in Ghidra 11+), **`Java`**, or **`Jython`** (Python 2.7). +> - **Manage Script Directories (Folder with List Icon):** Because `SVD-Loader` is an existing multi-file package that relies on the bundled `cmsis_svd` parser library, we do not need to create a blank script. Instead, we register its cloned directory. + +2. In the top-right toolbar of the Script Manager window, click the **Manage Script Directories** icon (looks like a small folder with a list). +3. In the "Ghidra Script Directories / Bundle Manager" window that appears, click the **Display file chooser to add bundles to list** icon (the green `+` / folder icon on the top right). +4. Browse to and select your cloned directory: + `C:\Users\\SVD-Loader-Ghidra` +5. Click **OK** / **Select**, then close the Script Directories window. + +*(Alternatively, you can copy both `SVD-Loader.py` and the `cmsis_svd` folder directly into your default `~/ghidra_scripts` or `C:\Users\\ghidra_scripts` directory, which Ghidra discovers automatically).* + +### Step 3: Run SVD-Loader + +1. In the Script Manager search filter box, type: `SVD` +2. Locate **`SVD-Loader.py`** in the list. +3. Check the checkbox in the **In Tool** column next to `SVD-Loader.py`. This binds `SVD-Loader` directly to your CodeBrowser toolbar and menu for convenient access! +4. Select `SVD-Loader.py` and click the green **Run Script** button in the top right (or double-click the script entry). +5. A file picker dialog opens: + - Navigate to: `C:\Users\\.svd\rp2350.svd` + - Click **Open**. + +``` ++-----------------------------------------------------------------+ +| What SVD-Loader Does Automatically in Ghidra | +| | +| 1. Memory Blocks: Creates mapped, volatile memory blocks for | +| UART0, IO_BANK0, PADS_BANK0, SIO, CLOCKS, and RESETS. | +| 2. Symbol Labels: Creates global symbol labels at the exact | +| address of every register (e.g., UART0_UARTFR). | +| 3. C Structs: Generates full peripheral data structures in | +| the Data Type Manager (e.g., struct UART0_Type). | ++-----------------------------------------------------------------+ +``` + +Check the Ghidra Console window at the bottom of the screen. You will see: + +```text +Loaded SVD: rp2350.svd +Created peripheral block: SIO at 0xd0000000 (size: 0x1000) +Created peripheral block: PADS_BANK0 at 0x40038000 (size: 0x1000) +Created peripheral block: IO_BANK0 at 0x40028000 (size: 0x1000) +Created peripheral block: UART0 at 0x40070000 (size: 0x1000) +... +Successfully imported all peripherals! +``` + +### Step 4: Re-Run Auto-Analysis to Propagate References + +When you initially imported `CTF-01.bin`, Ghidra performed auto-analysis against only the initial Flash block ($0\text{x}10000000$). Now that `SVD-Loader.py` has created all 52 on-chip peripheral memory blocks, re-run analysis so Ghidra evaluates references against the newly created regions: + +1. Click menu **Analysis** -> **Auto Analyze 'CTF-01.bin'...** (or press keyboard shortcut **`A`**). +2. Ensure **Reference**, **Subroutine References**, and **Constant Reference Analyzer** are enabled. +3. Click **Analyze**. + +--- + +## Part 9: Decompiler Transformation: Before and After + +Now that the SVD structures are loaded, let's examine subroutine `FUN_100002a8` (`gpio_set_function`) again. + +### Side-by-Side Decompilation Comparison + +``` ++-----------------------------------------------------------------+ +| Decompilation of gpio_set_function() | +| | +| BEFORE SVD-Loader: | +| void FUN_100002a8(uint param_1, uint param_2) | +| { | +| *(uint *)(0x40038000 + param_1 * 4 + 4) = | +| *(uint *)(0x40038000 + param_1 * 4 + 4) & 0xffffff7f | +| | 0x40; | +| *(uint *)(0x40028000 + param_1 * 8 + 4) = param_2; | +| return; | +| } | +| | +| AFTER SVD-Loader: | +| void gpio_set_function(uint gpio, uint fn) | +| { | +| PADS_BANK0->GPIO[gpio] = | +| (PADS_BANK0->GPIO[gpio] & ~PADS_BANK0_OD) | +| | PADS_BANK0_IE; | +| IO_BANK0->GPIO[gpio].CTRL = fn; /* 2 = UART0 */ | +| return; | +| } | ++-----------------------------------------------------------------+ +``` + +Look at the difference: +1. **`*(uint *)(0x40038000 + param_1 * 4 + 4)`** is recognized as indexing into `PADS_BANK0` electrical pad controls. +2. **`& 0xffffff7f | 0x40`** is clearly revealed as clearing the `OD` (Output Disable) bit and setting the `IE` (Input Enable) bit. +3. **`*(uint *)(0x40028000 + param_1 * 8 + 4) = param_2`** immediately resolves in Ghidra's decompiler to: + ```c + (&Peripherals::IO_BANK0.GPIO0_CTRL)[param_1 * 2] = param_2; + ``` + **Why `[param_1 * 2]`?** In RP2350's `IO_BANK0`, each GPIO pin has two 32-bit registers (8 bytes total): `GPIOx_STATUS` (offset $+0$) and `GPIOx_CTRL` (offset $+4$). Because `GPIO0_CTRL` is a pointer to a 4-byte `uint32_t`, indexing by `[param_1 * 2]` steps forward by $2 \times 4\text{ bytes} = 8\text{ bytes}$ per pin, landing directly on each pin's `CTRL` register to assign `param_2` ($2$ for `UART0`)! + +> [!NOTE] +> **Understanding Assembly Listing vs. Decompiler Resolution:** +> You may notice that in the raw disassembly Listing view, line `100002be` still appears as: +> ```assembly +> 100002be 00 f1 80 40 add.w r0, r0, #0x40000000 +> ``` +> Why does `#0x40000000` not resolve to a peripheral label here? +> - **Arithmetic Immediates vs. Memory Operands:** `add.w` is an ALU integer addition, not a load or store instruction. In assembly listings, immediate scalar constants remain literal numbers. +> - **Intermediate Math vs. Target Address:** $0\text{x}40000000$ is the APB/AHB bridge base. The actual peripheral register address ($0\text{x}40028004$ for `IO_BANK0_GPIO0_CTRL`) is calculated dynamically at runtime by adding the pin index offset ($gpio \times 8$), bridge base ($0\text{x}40000000$), peripheral offset ($0\text{x}28000$), and register offset ($+4$). +> - **Where Resolution Appears:** Ghidra resolves this in the **Decompiler window** via data-flow analysis, and in the Listing window as **XREF** annotations on the subsequent `str`/`ldr` instructions that dereference the calculated pointer. If you want `#0x40000000` to show a name in the Listing, right-click the number and select **Set Equate...** (press **`E`**) to label it `PERIPHERALS_BASE`. + +### Exploring Structs in the Data Type Manager + +In Ghidra's **Data Type Manager** panel (bottom-left): +1. Expand the tree node for `CTF-01.bin`. +2. Expand the `rp2350.svd` category. +3. Locate **`UART0_Type`**: + - Double-click `UART0_Type` to open Ghidra's Structure Editor. + - You can see every field, its byte offset, and its data type: + - `0x000`: `UARTDR` (`uint32_t`) + - `0x018`: `UARTFR` (`uint32_t`) + - `0x024`: `UARTIBRD` (`uint32_t`) + - `0x028`: `UARTFBRD` (`uint32_t`) + - `0x02c`: `UARTLCR_H` (`uint32_t`) + - `0x030`: `UARTCR` (`uint32_t`) + +You can apply these struct types to any pointer in Ghidra by right-clicking a variable in the decompiler and selecting **Retype Variable** -> `UART0_Type *`. + +--- + +## Part 10: The Complete Hardware-Aware Reverse Engineering Workflow + +By combining SVD in both GDB and Ghidra, you achieve a seamless reverse engineering loop: + +``` ++-----------------------------------------------------------------+ +| The Hardware-Aware Reverse Engineering Loop | +| | +| +-----------------------------------------------------------+ | +| | 1. GHIDRA (Static Analysis + rp2350.svd) | | +| | - Identifies functions, call graph, and MMIO | | +| | - Pinpoints exact register addresses to watch | | +| +-----------------------------------------------------------+ | +| | | +| v | +| +-----------------------------------------------------------+ | +| | 2. GDB + OpenOCD (Dynamic Analysis + rp2350.svd) | | +| | - Sets hardware breakpoint (hb *0x100001e0) | | +| | - Steps through instructions with si / ni | | +| | - Inspects peripheral bitfields live (svd /r) | | +| +-----------------------------------------------------------+ | +| | | +| v | +| +-----------------------------------------------------------+ | +| | 3. LIVE HARDWARE INTERACTION | | +| | - Validates UART console telemetry (115200 8N1) | | +| | - Proves binary patches on physical silicon | | +| +-----------------------------------------------------------+ | ++-----------------------------------------------------------------+ +``` + +### Applying the Loop to Solve CTF-01 + +1. **Locate Defects via Ghidra Static Analysis**: + - In `main()` ($0\text{x}100001E0$), identify Compare Site A at $0\text{x}100001FC$ (`cmp r3, #94`) and Compare Site B at $0\text{x}1000020A$ (`cmp r3, #94`). + - Notice that while $0.87\text{ Hz}$ is dangerously high, it passes because $87 \le 94$. + - To enforce the $0.60\text{ Hz}$ safety limit, $x < 60$ is equivalent to $x \le 59$. The comparison immediate must be patched from `0x5E` ($94$) to `0x3B` ($59$). +2. **Locate the Quarantined Flag in Flash**: + - Trace the pointer loaded at address $0\text{x}100001E8$ to address $0\text{x}100037A0$. + - Inspecting that memory reveals the token: `"WORLDGRID:BLACKSTART:GRID-7:WATER-3"`. +3. **Patch and Export in Ghidra**: + - Use the **Bytes Window** workflow to prevent ARM Thumb IT-block context conflicts: + 1. Open the Bytes window (**Window** -> **Bytes: CTF-01.bin**). + 2. In the Bytes window toolbar, click the **pencil icon** (**Toggle Edit Mode**). + 3. In the Listing window, click `0x100001FC` and press **`C`** (**Clear Code Bytes**). + 4. In the Bytes window at offset `100001fc`, click on byte `5E` and change it to **`3B`**. + 5. In the Listing window, click back on `0x100001FC` and press **`D`** (**Disassemble**). + 6. Repeat at `0x1000020A`: click `0x1000020A` in the Listing, press **`C`**, change `5E` to **`3B`** in the Bytes window, click back in the Listing, and press **`D`**. + - Export the patched binary as `CTF-01_fixed.bin` via **File** -> **Export Program** -> **Format**: **Raw Bytes**. +4. **Verify on Live Hardware via OpenOCD & GDB**: + - Flash the patched binary to the Pico 2. + - Attach your USB-UART adapter to GPIO 0 (TX) and GPIO 1 (RX) at 115200 baud. + - Observe the corrected, truthful telemetry: + ```text + GRID STATUS: CRITICAL + DISPATCH PATH: HELD + LAST FRAME: QUARANTINED + RESPONSE> + ``` + +--- + +## Part 11: Summary, Cheatsheets & Review + +### GDB Raw Binary Debugging Cheatsheet + +| Task | Command | Description | +| :--- | :--- | :--- | +| Set Architecture | `set architecture armv8-m.main` | Configures GDB for ARM Cortex-M33 cores. | +| Connect to OpenOCD | `target extended-remote :3333` | Connects to OpenOCD debug server. | +| Reset & Halt | `monitor reset halt` | Sends reset signal and halts CPU at vector table. | +| Read Vector Table | `x/4wx 0x10000000` | Displays Initial Stack Pointer and Reset Vector. | +| Disassemble at PC | `x/10i $pc` | Disassembles 10 instructions at current Program Counter. | +| Disassemble Range | `disassemble 0x100001e0, 0x10000216` | Disassembles instructions between two hex addresses. | +| Hardware Breakpoint| `hb *0x100001e0` | Sets hardware breakpoint on read-only flash memory. | +| Read Memory Word | `x/wx 0x40070018` | Reads one 32-bit hexadecimal word from memory. | +| Write Memory Word | `set *0xd0000014 = 0x10000` | Writes 32-bit value directly to memory address. | + +### PyCortexMDebug SVD Cheatsheet + +| Task | Command | Description | +| :--- | :--- | :--- | +| Load Plugin | `source ~/PyCortexMDebug/scripts/gdb.py` | Imports PyCortexMDebug into GDB Python engine. | +| Load SVD File | `svd_load ~/.svd/rp2350.svd` | Parses chip peripheral XML definition. | +| List Peripherals | `svd` | Lists all on-chip hardware peripheral blocks. | +| Dump Peripheral | `svd UART0` | Reads and displays all registers in a peripheral. | +| Decode Bitfields | `svd /r UART0 UARTFR` | Decodes individual bitfields and named flags. | +| Write Register | `svd /w SIO GPIO_OUT_SET 0x10000` | Writes to peripheral register by symbolic name. | + +### RP2350 Peripheral Memory Map Quick Reference + +| Peripheral | Base Address | Size | Primary Purpose | +| :--- | :--- | :--- | :--- | +| `XIP_FLASH` | `0x10000000` | Up to 16MB | External QSPI Flash execution memory | +| `SRAM` | `0x20000000` | 512KB | On-chip data memory (stack, heap, `.data`, `.bss`) | +| `RESETS` | `0x40020000` | 4KB | Subsystem reset controller | +| `IO_BANK0` | `0x40028000` | 4KB | GPIO pin function multiplexing (`FUNCSEL`) | +| `PADS_BANK0`| `0x40038000` | 4KB | Electrical drive strength, pulls, and enables | +| `UART0` | `0x40070000` | 4KB | Serial communication interface 0 (115200 8N1) | +| `UART1` | `0x40078000` | 4KB | Serial communication interface 1 | +| `SIO` | `0xd0000000` | 4KB | Single-cycle I/O fast GPIO controls | + +--- + +## Key Takeaways + +1. **A raw `.bin` is not an ELF**: It has no headers, no symbols, and no entry point metadata. You must supply the base address ($0\text{x}10000000$) when flashing and debug using explicit addresses. +2. **Flash requires hardware breakpoints**: External flash is read-only during execution. Always use `hb *address` instead of software breakpoints (`b`). +3. **The Vector Table tells all**: Even without symbols, `x/4wx 0x10000000` gives you the initial stack pointer and the reset vector within seconds. +4. **SVD bridges the hardware gap**: CMSIS-SVD turns raw hexadecimal registers into named peripherals, registers, and bitfields across both GDB and Ghidra. +5. **GDB handles live dynamic manipulation**: `PyCortexMDebug` lets you inspect peripheral registers live, decode bitfields with `svd /r`, and toggle pins directly over SWD using `svd /w`. +6. **Ghidra handles static comprehension**: `SVD-Loader-Ghidra` creates mapped memory blocks and C structs, converting raw pointer arithmetic into readable code. +7. **Arithmetic instructions remain arithmetic**: Instructions like `add.w r0, r0, #0x40000000` calculate base addresses across multiple steps; resolution appears in the decompiler and at the load/store instructions that dereference the address. + +--- + +## Glossary + +| Term | Definition | +| :--- | :--- | +| **CMSIS-SVD** | Cortex Microcontroller Software Interface Standard - System View Description; an XML format describing microcontroller hardware peripherals. | +| **DWARF** | Standardized debugging data format embedded in ELF binaries that maps machine code to source code lines and symbols. | +| **Hardware Breakpoint (`hb`)** | A breakpoint implemented using dedicated CPU comparator registers, required for debugging code in read-only flash memory. | +| **MMIO** | Memory-Mapped Input/Output; a hardware architecture where peripheral registers are mapped into the CPU's regular memory address space. | +| **MSP** | Main Stack Pointer; the primary ARM Cortex-M stack pointer register. | +| **OpenOCD** | Open On-Chip Debugger; a software bridge that connects GDB to physical hardware via a Debug Probe. | +| **PyCortexMDebug** | A GDB Python extension that parses SVD files to provide live peripheral inspection and register manipulation. | +| **Reset Vector** | The address stored at offset $0\text{x}00000004$ in the vector table that points to the first instruction executed upon CPU reset. | +| **SIO** | Single-Cycle I/O; a dedicated RP2350 hardware block providing zero-wait-state GPIO manipulation. | +| **SVD-Loader** | A Ghidra script that imports SVD files to create memory blocks, symbol labels, and C structs for decompilation. | +| **SWD** | Serial Wire Debug; a two-wire physical debug protocol (SWCLK, SWDIO) used to debug ARM microcontrollers. | +| **UART** | Universal Asynchronous Receiver-Transmitter; a physical hardware communication protocol used for serial data transfer. | +| **XIP** | eXecute In Place; running code directly from external flash memory without copying it to RAM first. | + +*** + +Happy Hacking! diff --git a/WEEK04/WEEK04a.pdf b/WEEK04/WEEK04a.pdf new file mode 100644 index 0000000..169b4a9 Binary files /dev/null and b/WEEK04/WEEK04a.pdf differ diff --git a/WEEK04/rp2350.svd b/WEEK04/rp2350.svd new file mode 100644 index 0000000..aa2c362 --- /dev/null +++ b/WEEK04/rp2350.svd @@ -0,0 +1,105849 @@ + + + + Raspberry Pi + RP2350 + RP + 0.1 + + Dual Cortex-M33 or Hazard3 processors at 150MHz + 520kB on-chip SRAM, in 10 independent banks + Extended low-power sleep states with optional SRAM retention: as low as 10uA DVDD + 8kB of one-time-programmable storage (OTP) + Up to 16MB of external QSPI flash/PSRAM via dedicated QSPI bus + Additional 16MB flash/PSRAM accessible via optional second chip-select + On-chip switched-mode power supply to generate core voltage + Low-quiescent-current LDO mode can be enabled for sleep states + 2x on-chip PLLs for internal or external clock generation + GPIOs are 5V-tolerant (powered), and 3.3V-failsafe (unpowered) + Security features: + Optional boot signing, enforced by on-chip mask ROM, with key fingerprint in OTP + Protected OTP storage for optional boot decryption key + Global bus filtering based on Arm or RISC-V security/privilege levels + Peripherals, GPIOs and DMA channels individually assignable to security domains + Hardware mitigations for fault injection attacks + Hardware SHA-256 accelerator + Peripherals: + 2x UARTs + 2x SPI controllers + 2x I2C controllers + 24x PWM channels + USB 1.1 controller and PHY, with host and device support + 12x PIO state machines + 1x HSTX peripheral + + 32 + 32 + 0xffffffff + 0x00000000 + read-write + + Copyright (c) 2024 Raspberry Pi Ltd. + + SPDX-License-Identifier: BSD-3-Clause + + + CM33 + r1p0 + little + true + true + 8 + 4 + 1 + 1 + false + 52 + + 8 + + + RESETS + 0x40020000 + + 0 + 12 + registers + + + + RESET + 0x00000000 + 0x1fffffff + + + USBCTRL + [28:28] + read-write + + + UART1 + [27:27] + read-write + + + UART0 + [26:26] + read-write + + + TRNG + [25:25] + read-write + + + TIMER1 + [24:24] + read-write + + + TIMER0 + [23:23] + read-write + + + TBMAN + [22:22] + read-write + + + SYSINFO + [21:21] + read-write + + + SYSCFG + [20:20] + read-write + + + SPI1 + [19:19] + read-write + + + SPI0 + [18:18] + read-write + + + SHA256 + [17:17] + read-write + + + PWM + [16:16] + read-write + + + PLL_USB + [15:15] + read-write + + + PLL_SYS + [14:14] + read-write + + + PIO2 + [13:13] + read-write + + + PIO1 + [12:12] + read-write + + + PIO0 + [11:11] + read-write + + + PADS_QSPI + [10:10] + read-write + + + PADS_BANK0 + [9:9] + read-write + + + JTAG + [8:8] + read-write + + + IO_QSPI + [7:7] + read-write + + + IO_BANK0 + [6:6] + read-write + + + I2C1 + [5:5] + read-write + + + I2C0 + [4:4] + read-write + + + HSTX + [3:3] + read-write + + + DMA + [2:2] + read-write + + + BUSCTRL + [1:1] + read-write + + + ADC + [0:0] + read-write + + + + + WDSEL + 0x00000004 + 0x00000000 + + + USBCTRL + [28:28] + read-write + + + UART1 + [27:27] + read-write + + + UART0 + [26:26] + read-write + + + TRNG + [25:25] + read-write + + + TIMER1 + [24:24] + read-write + + + TIMER0 + [23:23] + read-write + + + TBMAN + [22:22] + read-write + + + SYSINFO + [21:21] + read-write + + + SYSCFG + [20:20] + read-write + + + SPI1 + [19:19] + read-write + + + SPI0 + [18:18] + read-write + + + SHA256 + [17:17] + read-write + + + PWM + [16:16] + read-write + + + PLL_USB + [15:15] + read-write + + + PLL_SYS + [14:14] + read-write + + + PIO2 + [13:13] + read-write + + + PIO1 + [12:12] + read-write + + + PIO0 + [11:11] + read-write + + + PADS_QSPI + [10:10] + read-write + + + PADS_BANK0 + [9:9] + read-write + + + JTAG + [8:8] + read-write + + + IO_QSPI + [7:7] + read-write + + + IO_BANK0 + [6:6] + read-write + + + I2C1 + [5:5] + read-write + + + I2C0 + [4:4] + read-write + + + HSTX + [3:3] + read-write + + + DMA + [2:2] + read-write + + + BUSCTRL + [1:1] + read-write + + + ADC + [0:0] + read-write + + + + + RESET_DONE + 0x00000008 + 0x00000000 + + + USBCTRL + [28:28] + read-only + + + UART1 + [27:27] + read-only + + + UART0 + [26:26] + read-only + + + TRNG + [25:25] + read-only + + + TIMER1 + [24:24] + read-only + + + TIMER0 + [23:23] + read-only + + + TBMAN + [22:22] + read-only + + + SYSINFO + [21:21] + read-only + + + SYSCFG + [20:20] + read-only + + + SPI1 + [19:19] + read-only + + + SPI0 + [18:18] + read-only + + + SHA256 + [17:17] + read-only + + + PWM + [16:16] + read-only + + + PLL_USB + [15:15] + read-only + + + PLL_SYS + [14:14] + read-only + + + PIO2 + [13:13] + read-only + + + PIO1 + [12:12] + read-only + + + PIO0 + [11:11] + read-only + + + PADS_QSPI + [10:10] + read-only + + + PADS_BANK0 + [9:9] + read-only + + + JTAG + [8:8] + read-only + + + IO_QSPI + [7:7] + read-only + + + IO_BANK0 + [6:6] + read-only + + + I2C1 + [5:5] + read-only + + + I2C0 + [4:4] + read-only + + + HSTX + [3:3] + read-only + + + DMA + [2:2] + read-only + + + BUSCTRL + [1:1] + read-only + + + ADC + [0:0] + read-only + + + + + + + PSM + 0x40018000 + + 0 + 16 + registers + + + + FRCE_ON + 0x00000000 + Force block out of reset (i.e. power it on) + 0x00000000 + + + PROC1 + [24:24] + read-write + + + PROC0 + [23:23] + read-write + + + ACCESSCTRL + [22:22] + read-write + + + SIO + [21:21] + read-write + + + XIP + [20:20] + read-write + + + SRAM9 + [19:19] + read-write + + + SRAM8 + [18:18] + read-write + + + SRAM7 + [17:17] + read-write + + + SRAM6 + [16:16] + read-write + + + SRAM5 + [15:15] + read-write + + + SRAM4 + [14:14] + read-write + + + SRAM3 + [13:13] + read-write + + + SRAM2 + [12:12] + read-write + + + SRAM1 + [11:11] + read-write + + + SRAM0 + [10:10] + read-write + + + BOOTRAM + [9:9] + read-write + + + ROM + [8:8] + read-write + + + BUSFABRIC + [7:7] + read-write + + + PSM_READY + [6:6] + read-write + + + CLOCKS + [5:5] + read-write + + + RESETS + [4:4] + read-write + + + XOSC + [3:3] + read-write + + + ROSC + [2:2] + read-write + + + OTP + [1:1] + read-write + + + PROC_COLD + [0:0] + read-write + + + + + FRCE_OFF + 0x00000004 + Force into reset (i.e. power it off) + 0x00000000 + + + PROC1 + [24:24] + read-write + + + PROC0 + [23:23] + read-write + + + ACCESSCTRL + [22:22] + read-write + + + SIO + [21:21] + read-write + + + XIP + [20:20] + read-write + + + SRAM9 + [19:19] + read-write + + + SRAM8 + [18:18] + read-write + + + SRAM7 + [17:17] + read-write + + + SRAM6 + [16:16] + read-write + + + SRAM5 + [15:15] + read-write + + + SRAM4 + [14:14] + read-write + + + SRAM3 + [13:13] + read-write + + + SRAM2 + [12:12] + read-write + + + SRAM1 + [11:11] + read-write + + + SRAM0 + [10:10] + read-write + + + BOOTRAM + [9:9] + read-write + + + ROM + [8:8] + read-write + + + BUSFABRIC + [7:7] + read-write + + + PSM_READY + [6:6] + read-write + + + CLOCKS + [5:5] + read-write + + + RESETS + [4:4] + read-write + + + XOSC + [3:3] + read-write + + + ROSC + [2:2] + read-write + + + OTP + [1:1] + read-write + + + PROC_COLD + [0:0] + read-write + + + + + WDSEL + 0x00000008 + Set to 1 if the watchdog should reset this + 0x00000000 + + + PROC1 + [24:24] + read-write + + + PROC0 + [23:23] + read-write + + + ACCESSCTRL + [22:22] + read-write + + + SIO + [21:21] + read-write + + + XIP + [20:20] + read-write + + + SRAM9 + [19:19] + read-write + + + SRAM8 + [18:18] + read-write + + + SRAM7 + [17:17] + read-write + + + SRAM6 + [16:16] + read-write + + + SRAM5 + [15:15] + read-write + + + SRAM4 + [14:14] + read-write + + + SRAM3 + [13:13] + read-write + + + SRAM2 + [12:12] + read-write + + + SRAM1 + [11:11] + read-write + + + SRAM0 + [10:10] + read-write + + + BOOTRAM + [9:9] + read-write + + + ROM + [8:8] + read-write + + + BUSFABRIC + [7:7] + read-write + + + PSM_READY + [6:6] + read-write + + + CLOCKS + [5:5] + read-write + + + RESETS + [4:4] + read-write + + + XOSC + [3:3] + read-write + + + ROSC + [2:2] + read-write + + + OTP + [1:1] + read-write + + + PROC_COLD + [0:0] + read-write + + + + + DONE + 0x0000000c + Is the subsystem ready? + 0x00000000 + + + PROC1 + [24:24] + read-only + + + PROC0 + [23:23] + read-only + + + ACCESSCTRL + [22:22] + read-only + + + SIO + [21:21] + read-only + + + XIP + [20:20] + read-only + + + SRAM9 + [19:19] + read-only + + + SRAM8 + [18:18] + read-only + + + SRAM7 + [17:17] + read-only + + + SRAM6 + [16:16] + read-only + + + SRAM5 + [15:15] + read-only + + + SRAM4 + [14:14] + read-only + + + SRAM3 + [13:13] + read-only + + + SRAM2 + [12:12] + read-only + + + SRAM1 + [11:11] + read-only + + + SRAM0 + [10:10] + read-only + + + BOOTRAM + [9:9] + read-only + + + ROM + [8:8] + read-only + + + BUSFABRIC + [7:7] + read-only + + + PSM_READY + [6:6] + read-only + + + CLOCKS + [5:5] + read-only + + + RESETS + [4:4] + read-only + + + XOSC + [3:3] + read-only + + + ROSC + [2:2] + read-only + + + OTP + [1:1] + read-only + + + PROC_COLD + [0:0] + read-only + + + + + + + CLOCKS + 0x40010000 + + 0 + 212 + registers + + + CLOCKS_IRQ + 30 + + + + CLK_GPOUT0_CTRL + 0x00000000 + Clock control, can be changed on-the-fly (except for auxsrc) + 0x00000000 + + + ENABLED + clock generator is enabled + [28:28] + read-only + + + NUDGE + An edge on this signal shifts the phase of the output by 1 cycle of the input clock + This can be done at any time + [20:20] + read-write + + + PHASE + This delays the enable signal by up to 3 cycles of the input clock + This must be set before the clock is enabled to have any effect + [17:16] + read-write + + + DC50 + Enables duty cycle correction for odd divisors, can be changed on-the-fly + [12:12] + read-write + + + ENABLE + Starts and stops the clock generator cleanly + [11:11] + read-write + + + KILL + Asynchronously kills the clock generator, enable must be set low before deasserting kill + [10:10] + read-write + + + AUXSRC + Selects the auxiliary clock source, will glitch when switching + [8:5] + read-write + + + clksrc_pll_sys + 0 + + + clksrc_gpin0 + 1 + + + clksrc_gpin1 + 2 + + + clksrc_pll_usb + 3 + + + clksrc_pll_usb_primary_ref_opcg + 4 + + + rosc_clksrc + 5 + + + xosc_clksrc + 6 + + + lposc_clksrc + 7 + + + clk_sys + 8 + + + clk_usb + 9 + + + clk_adc + 10 + + + clk_ref + 11 + + + clk_peri + 12 + + + clk_hstx + 13 + + + otp_clk2fc + 14 + + + + + + + CLK_GPOUT0_DIV + 0x00000004 + 0x00010000 + + + INT + Integer part of clock divisor, 0 -> max+1, can be changed on-the-fly + [31:16] + read-write + + + FRAC + Fractional component of the divisor, can be changed on-the-fly + [15:0] + read-write + + + + + CLK_GPOUT0_SELECTED + 0x00000008 + Indicates which src is currently selected (one-hot) + 0x00000001 + + + CLK_GPOUT0_SELECTED + This slice does not have a glitchless mux (only the AUX_SRC field is present, not SRC) so this register is hardwired to 0x1. + [0:0] + read-only + + + + + CLK_GPOUT1_CTRL + 0x0000000c + Clock control, can be changed on-the-fly (except for auxsrc) + 0x00000000 + + + ENABLED + clock generator is enabled + [28:28] + read-only + + + NUDGE + An edge on this signal shifts the phase of the output by 1 cycle of the input clock + This can be done at any time + [20:20] + read-write + + + PHASE + This delays the enable signal by up to 3 cycles of the input clock + This must be set before the clock is enabled to have any effect + [17:16] + read-write + + + DC50 + Enables duty cycle correction for odd divisors, can be changed on-the-fly + [12:12] + read-write + + + ENABLE + Starts and stops the clock generator cleanly + [11:11] + read-write + + + KILL + Asynchronously kills the clock generator, enable must be set low before deasserting kill + [10:10] + read-write + + + AUXSRC + Selects the auxiliary clock source, will glitch when switching + [8:5] + read-write + + + clksrc_pll_sys + 0 + + + clksrc_gpin0 + 1 + + + clksrc_gpin1 + 2 + + + clksrc_pll_usb + 3 + + + clksrc_pll_usb_primary_ref_opcg + 4 + + + rosc_clksrc + 5 + + + xosc_clksrc + 6 + + + lposc_clksrc + 7 + + + clk_sys + 8 + + + clk_usb + 9 + + + clk_adc + 10 + + + clk_ref + 11 + + + clk_peri + 12 + + + clk_hstx + 13 + + + otp_clk2fc + 14 + + + + + + + CLK_GPOUT1_DIV + 0x00000010 + 0x00010000 + + + INT + Integer part of clock divisor, 0 -> max+1, can be changed on-the-fly + [31:16] + read-write + + + FRAC + Fractional component of the divisor, can be changed on-the-fly + [15:0] + read-write + + + + + CLK_GPOUT1_SELECTED + 0x00000014 + Indicates which src is currently selected (one-hot) + 0x00000001 + + + CLK_GPOUT1_SELECTED + This slice does not have a glitchless mux (only the AUX_SRC field is present, not SRC) so this register is hardwired to 0x1. + [0:0] + read-only + + + + + CLK_GPOUT2_CTRL + 0x00000018 + Clock control, can be changed on-the-fly (except for auxsrc) + 0x00000000 + + + ENABLED + clock generator is enabled + [28:28] + read-only + + + NUDGE + An edge on this signal shifts the phase of the output by 1 cycle of the input clock + This can be done at any time + [20:20] + read-write + + + PHASE + This delays the enable signal by up to 3 cycles of the input clock + This must be set before the clock is enabled to have any effect + [17:16] + read-write + + + DC50 + Enables duty cycle correction for odd divisors, can be changed on-the-fly + [12:12] + read-write + + + ENABLE + Starts and stops the clock generator cleanly + [11:11] + read-write + + + KILL + Asynchronously kills the clock generator, enable must be set low before deasserting kill + [10:10] + read-write + + + AUXSRC + Selects the auxiliary clock source, will glitch when switching + [8:5] + read-write + + + clksrc_pll_sys + 0 + + + clksrc_gpin0 + 1 + + + clksrc_gpin1 + 2 + + + clksrc_pll_usb + 3 + + + clksrc_pll_usb_primary_ref_opcg + 4 + + + rosc_clksrc_ph + 5 + + + xosc_clksrc + 6 + + + lposc_clksrc + 7 + + + clk_sys + 8 + + + clk_usb + 9 + + + clk_adc + 10 + + + clk_ref + 11 + + + clk_peri + 12 + + + clk_hstx + 13 + + + otp_clk2fc + 14 + + + + + + + CLK_GPOUT2_DIV + 0x0000001c + 0x00010000 + + + INT + Integer part of clock divisor, 0 -> max+1, can be changed on-the-fly + [31:16] + read-write + + + FRAC + Fractional component of the divisor, can be changed on-the-fly + [15:0] + read-write + + + + + CLK_GPOUT2_SELECTED + 0x00000020 + Indicates which src is currently selected (one-hot) + 0x00000001 + + + CLK_GPOUT2_SELECTED + This slice does not have a glitchless mux (only the AUX_SRC field is present, not SRC) so this register is hardwired to 0x1. + [0:0] + read-only + + + + + CLK_GPOUT3_CTRL + 0x00000024 + Clock control, can be changed on-the-fly (except for auxsrc) + 0x00000000 + + + ENABLED + clock generator is enabled + [28:28] + read-only + + + NUDGE + An edge on this signal shifts the phase of the output by 1 cycle of the input clock + This can be done at any time + [20:20] + read-write + + + PHASE + This delays the enable signal by up to 3 cycles of the input clock + This must be set before the clock is enabled to have any effect + [17:16] + read-write + + + DC50 + Enables duty cycle correction for odd divisors, can be changed on-the-fly + [12:12] + read-write + + + ENABLE + Starts and stops the clock generator cleanly + [11:11] + read-write + + + KILL + Asynchronously kills the clock generator, enable must be set low before deasserting kill + [10:10] + read-write + + + AUXSRC + Selects the auxiliary clock source, will glitch when switching + [8:5] + read-write + + + clksrc_pll_sys + 0 + + + clksrc_gpin0 + 1 + + + clksrc_gpin1 + 2 + + + clksrc_pll_usb + 3 + + + clksrc_pll_usb_primary_ref_opcg + 4 + + + rosc_clksrc_ph + 5 + + + xosc_clksrc + 6 + + + lposc_clksrc + 7 + + + clk_sys + 8 + + + clk_usb + 9 + + + clk_adc + 10 + + + clk_ref + 11 + + + clk_peri + 12 + + + clk_hstx + 13 + + + otp_clk2fc + 14 + + + + + + + CLK_GPOUT3_DIV + 0x00000028 + 0x00010000 + + + INT + Integer part of clock divisor, 0 -> max+1, can be changed on-the-fly + [31:16] + read-write + + + FRAC + Fractional component of the divisor, can be changed on-the-fly + [15:0] + read-write + + + + + CLK_GPOUT3_SELECTED + 0x0000002c + Indicates which src is currently selected (one-hot) + 0x00000001 + + + CLK_GPOUT3_SELECTED + This slice does not have a glitchless mux (only the AUX_SRC field is present, not SRC) so this register is hardwired to 0x1. + [0:0] + read-only + + + + + CLK_REF_CTRL + 0x00000030 + Clock control, can be changed on-the-fly (except for auxsrc) + 0x00000000 + + + AUXSRC + Selects the auxiliary clock source, will glitch when switching + [6:5] + read-write + + + clksrc_pll_usb + 0 + + + clksrc_gpin0 + 1 + + + clksrc_gpin1 + 2 + + + clksrc_pll_usb_primary_ref_opcg + 3 + + + + + SRC + Selects the clock source glitchlessly, can be changed on-the-fly + [1:0] + read-write + + + rosc_clksrc_ph + 0 + + + clksrc_clk_ref_aux + 1 + + + xosc_clksrc + 2 + + + lposc_clksrc + 3 + + + + + + + CLK_REF_DIV + 0x00000034 + 0x00010000 + + + INT + Integer part of clock divisor, 0 -> max+1, can be changed on-the-fly + [23:16] + read-write + + + + + CLK_REF_SELECTED + 0x00000038 + Indicates which src is currently selected (one-hot) + 0x00000001 + + + CLK_REF_SELECTED + The glitchless multiplexer does not switch instantaneously (to avoid glitches), so software should poll this register to wait for the switch to complete. This register contains one decoded bit for each of the clock sources enumerated in the CTRL SRC field. At most one of these bits will be set at any time, indicating that clock is currently present at the output of the glitchless mux. Whilst switching is in progress, this register may briefly show all-0s. + [3:0] + read-only + + + + + CLK_SYS_CTRL + 0x0000003c + Clock control, can be changed on-the-fly (except for auxsrc) + 0x00000000 + + + AUXSRC + Selects the auxiliary clock source, will glitch when switching + [7:5] + read-write + + + clksrc_pll_sys + 0 + + + clksrc_pll_usb + 1 + + + rosc_clksrc + 2 + + + xosc_clksrc + 3 + + + clksrc_gpin0 + 4 + + + clksrc_gpin1 + 5 + + + + + SRC + Selects the clock source glitchlessly, can be changed on-the-fly + [0:0] + read-write + + + clk_ref + 0 + + + clksrc_clk_sys_aux + 1 + + + + + + + CLK_SYS_DIV + 0x00000040 + 0x00010000 + + + INT + Integer part of clock divisor, 0 -> max+1, can be changed on-the-fly + [31:16] + read-write + + + FRAC + Fractional component of the divisor, can be changed on-the-fly + [15:0] + read-write + + + + + CLK_SYS_SELECTED + 0x00000044 + Indicates which src is currently selected (one-hot) + 0x00000001 + + + CLK_SYS_SELECTED + The glitchless multiplexer does not switch instantaneously (to avoid glitches), so software should poll this register to wait for the switch to complete. This register contains one decoded bit for each of the clock sources enumerated in the CTRL SRC field. At most one of these bits will be set at any time, indicating that clock is currently present at the output of the glitchless mux. Whilst switching is in progress, this register may briefly show all-0s. + [1:0] + read-only + + + + + CLK_PERI_CTRL + 0x00000048 + Clock control, can be changed on-the-fly (except for auxsrc) + 0x00000000 + + + ENABLED + clock generator is enabled + [28:28] + read-only + + + ENABLE + Starts and stops the clock generator cleanly + [11:11] + read-write + + + KILL + Asynchronously kills the clock generator, enable must be set low before deasserting kill + [10:10] + read-write + + + AUXSRC + Selects the auxiliary clock source, will glitch when switching + [7:5] + read-write + + + clk_sys + 0 + + + clksrc_pll_sys + 1 + + + clksrc_pll_usb + 2 + + + rosc_clksrc_ph + 3 + + + xosc_clksrc + 4 + + + clksrc_gpin0 + 5 + + + clksrc_gpin1 + 6 + + + + + + + CLK_PERI_DIV + 0x0000004c + 0x00010000 + + + INT + Integer part of clock divisor, 0 -> max+1, can be changed on-the-fly + [17:16] + read-write + + + + + CLK_PERI_SELECTED + 0x00000050 + Indicates which src is currently selected (one-hot) + 0x00000001 + + + CLK_PERI_SELECTED + This slice does not have a glitchless mux (only the AUX_SRC field is present, not SRC) so this register is hardwired to 0x1. + [0:0] + read-only + + + + + CLK_HSTX_CTRL + 0x00000054 + Clock control, can be changed on-the-fly (except for auxsrc) + 0x00000000 + + + ENABLED + clock generator is enabled + [28:28] + read-only + + + NUDGE + An edge on this signal shifts the phase of the output by 1 cycle of the input clock + This can be done at any time + [20:20] + read-write + + + PHASE + This delays the enable signal by up to 3 cycles of the input clock + This must be set before the clock is enabled to have any effect + [17:16] + read-write + + + ENABLE + Starts and stops the clock generator cleanly + [11:11] + read-write + + + KILL + Asynchronously kills the clock generator, enable must be set low before deasserting kill + [10:10] + read-write + + + AUXSRC + Selects the auxiliary clock source, will glitch when switching + [7:5] + read-write + + + clk_sys + 0 + + + clksrc_pll_sys + 1 + + + clksrc_pll_usb + 2 + + + clksrc_gpin0 + 3 + + + clksrc_gpin1 + 4 + + + + + + + CLK_HSTX_DIV + 0x00000058 + 0x00010000 + + + INT + Integer part of clock divisor, 0 -> max+1, can be changed on-the-fly + [17:16] + read-write + + + + + CLK_HSTX_SELECTED + 0x0000005c + Indicates which src is currently selected (one-hot) + 0x00000001 + + + CLK_HSTX_SELECTED + This slice does not have a glitchless mux (only the AUX_SRC field is present, not SRC) so this register is hardwired to 0x1. + [0:0] + read-only + + + + + CLK_USB_CTRL + 0x00000060 + Clock control, can be changed on-the-fly (except for auxsrc) + 0x00000000 + + + ENABLED + clock generator is enabled + [28:28] + read-only + + + NUDGE + An edge on this signal shifts the phase of the output by 1 cycle of the input clock + This can be done at any time + [20:20] + read-write + + + PHASE + This delays the enable signal by up to 3 cycles of the input clock + This must be set before the clock is enabled to have any effect + [17:16] + read-write + + + ENABLE + Starts and stops the clock generator cleanly + [11:11] + read-write + + + KILL + Asynchronously kills the clock generator, enable must be set low before deasserting kill + [10:10] + read-write + + + AUXSRC + Selects the auxiliary clock source, will glitch when switching + [7:5] + read-write + + + clksrc_pll_usb + 0 + + + clksrc_pll_sys + 1 + + + rosc_clksrc_ph + 2 + + + xosc_clksrc + 3 + + + clksrc_gpin0 + 4 + + + clksrc_gpin1 + 5 + + + + + + + CLK_USB_DIV + 0x00000064 + 0x00010000 + + + INT + Integer part of clock divisor, 0 -> max+1, can be changed on-the-fly + [19:16] + read-write + + + + + CLK_USB_SELECTED + 0x00000068 + Indicates which src is currently selected (one-hot) + 0x00000001 + + + CLK_USB_SELECTED + This slice does not have a glitchless mux (only the AUX_SRC field is present, not SRC) so this register is hardwired to 0x1. + [0:0] + read-only + + + + + CLK_ADC_CTRL + 0x0000006c + Clock control, can be changed on-the-fly (except for auxsrc) + 0x00000000 + + + ENABLED + clock generator is enabled + [28:28] + read-only + + + NUDGE + An edge on this signal shifts the phase of the output by 1 cycle of the input clock + This can be done at any time + [20:20] + read-write + + + PHASE + This delays the enable signal by up to 3 cycles of the input clock + This must be set before the clock is enabled to have any effect + [17:16] + read-write + + + ENABLE + Starts and stops the clock generator cleanly + [11:11] + read-write + + + KILL + Asynchronously kills the clock generator, enable must be set low before deasserting kill + [10:10] + read-write + + + AUXSRC + Selects the auxiliary clock source, will glitch when switching + [7:5] + read-write + + + clksrc_pll_usb + 0 + + + clksrc_pll_sys + 1 + + + rosc_clksrc_ph + 2 + + + xosc_clksrc + 3 + + + clksrc_gpin0 + 4 + + + clksrc_gpin1 + 5 + + + + + + + CLK_ADC_DIV + 0x00000070 + 0x00010000 + + + INT + Integer part of clock divisor, 0 -> max+1, can be changed on-the-fly + [19:16] + read-write + + + + + CLK_ADC_SELECTED + 0x00000074 + Indicates which src is currently selected (one-hot) + 0x00000001 + + + CLK_ADC_SELECTED + This slice does not have a glitchless mux (only the AUX_SRC field is present, not SRC) so this register is hardwired to 0x1. + [0:0] + read-only + + + + + DFTCLK_XOSC_CTRL + 0x00000078 + 0x00000000 + + + SRC + [1:0] + read-write + + + NULL + 0 + + + clksrc_pll_usb_primary + 1 + + + clksrc_gpin0 + 2 + + + + + + + DFTCLK_ROSC_CTRL + 0x0000007c + 0x00000000 + + + SRC + [1:0] + read-write + + + NULL + 0 + + + clksrc_pll_sys_primary_rosc + 1 + + + clksrc_gpin1 + 2 + + + + + + + DFTCLK_LPOSC_CTRL + 0x00000080 + 0x00000000 + + + SRC + [1:0] + read-write + + + NULL + 0 + + + clksrc_pll_usb_primary_lposc + 1 + + + clksrc_gpin1 + 2 + + + + + + + CLK_SYS_RESUS_CTRL + 0x00000084 + 0x000000ff + + + CLEAR + For clearing the resus after the fault that triggered it has been corrected + [16:16] + read-write + + + FRCE + Force a resus, for test purposes only + [12:12] + read-write + + + ENABLE + Enable resus + [8:8] + read-write + + + TIMEOUT + This is expressed as a number of clk_ref cycles + and must be >= 2x clk_ref_freq/min_clk_tst_freq + [7:0] + read-write + + + + + CLK_SYS_RESUS_STATUS + 0x00000088 + 0x00000000 + + + RESUSSED + Clock has been resuscitated, correct the error then send ctrl_clear=1 + [0:0] + read-only + + + + + FC0_REF_KHZ + 0x0000008c + Reference clock frequency in kHz + 0x00000000 + + + FC0_REF_KHZ + [19:0] + read-write + + + + + FC0_MIN_KHZ + 0x00000090 + Minimum pass frequency in kHz. This is optional. Set to 0 if you are not using the pass/fail flags + 0x00000000 + + + FC0_MIN_KHZ + [24:0] + read-write + + + + + FC0_MAX_KHZ + 0x00000094 + Maximum pass frequency in kHz. This is optional. Set to 0x1ffffff if you are not using the pass/fail flags + 0x01ffffff + + + FC0_MAX_KHZ + [24:0] + read-write + + + + + FC0_DELAY + 0x00000098 + Delays the start of frequency counting to allow the mux to settle + Delay is measured in multiples of the reference clock period + 0x00000001 + + + FC0_DELAY + [2:0] + read-write + + + + + FC0_INTERVAL + 0x0000009c + The test interval is 0.98us * 2**interval, but let's call it 1us * 2**interval + The default gives a test interval of 250us + 0x00000008 + + + FC0_INTERVAL + [3:0] + read-write + + + + + FC0_SRC + 0x000000a0 + Clock sent to frequency counter, set to 0 when not required + Writing to this register initiates the frequency count + 0x00000000 + + + FC0_SRC + [7:0] + read-write + + + NULL + 0 + + + pll_sys_clksrc_primary + 1 + + + pll_usb_clksrc_primary + 2 + + + rosc_clksrc + 3 + + + rosc_clksrc_ph + 4 + + + xosc_clksrc + 5 + + + clksrc_gpin0 + 6 + + + clksrc_gpin1 + 7 + + + clk_ref + 8 + + + clk_sys + 9 + + + clk_peri + 10 + + + clk_usb + 11 + + + clk_adc + 12 + + + clk_hstx + 13 + + + lposc_clksrc + 14 + + + otp_clk2fc + 15 + + + pll_usb_clksrc_primary_dft + 16 + + + + + + + FC0_STATUS + 0x000000a4 + Frequency counter status + 0x00000000 + + + DIED + Test clock stopped during test + [28:28] + read-only + + + FAST + Test clock faster than expected, only valid when status_done=1 + [24:24] + read-only + + + SLOW + Test clock slower than expected, only valid when status_done=1 + [20:20] + read-only + + + FAIL + Test failed + [16:16] + read-only + + + WAITING + Waiting for test clock to start + [12:12] + read-only + + + RUNNING + Test running + [8:8] + read-only + + + DONE + Test complete + [4:4] + read-only + + + PASS + Test passed + [0:0] + read-only + + + + + FC0_RESULT + 0x000000a8 + Result of frequency measurement, only valid when status_done=1 + 0x00000000 + + + KHZ + [29:5] + read-only + + + FRAC + [4:0] + read-only + + + + + WAKE_EN0 + 0x000000ac + enable clock in wake mode + 0xffffffff + + + CLK_SYS_SIO + [31:31] + read-write + + + CLK_SYS_SHA256 + [30:30] + read-write + + + CLK_SYS_PSM + [29:29] + read-write + + + CLK_SYS_ROSC + [28:28] + read-write + + + CLK_SYS_ROM + [27:27] + read-write + + + CLK_SYS_RESETS + [26:26] + read-write + + + CLK_SYS_PWM + [25:25] + read-write + + + CLK_SYS_POWMAN + [24:24] + read-write + + + CLK_REF_POWMAN + [23:23] + read-write + + + CLK_SYS_PLL_USB + [22:22] + read-write + + + CLK_SYS_PLL_SYS + [21:21] + read-write + + + CLK_SYS_PIO2 + [20:20] + read-write + + + CLK_SYS_PIO1 + [19:19] + read-write + + + CLK_SYS_PIO0 + [18:18] + read-write + + + CLK_SYS_PADS + [17:17] + read-write + + + CLK_SYS_OTP + [16:16] + read-write + + + CLK_REF_OTP + [15:15] + read-write + + + CLK_SYS_JTAG + [14:14] + read-write + + + CLK_SYS_IO + [13:13] + read-write + + + CLK_SYS_I2C1 + [12:12] + read-write + + + CLK_SYS_I2C0 + [11:11] + read-write + + + CLK_SYS_HSTX + [10:10] + read-write + + + CLK_HSTX + [9:9] + read-write + + + CLK_SYS_GLITCH_DETECTOR + [8:8] + read-write + + + CLK_SYS_DMA + [7:7] + read-write + + + CLK_SYS_BUSFABRIC + [6:6] + read-write + + + CLK_SYS_BUSCTRL + [5:5] + read-write + + + CLK_SYS_BOOTRAM + [4:4] + read-write + + + CLK_SYS_ADC + [3:3] + read-write + + + CLK_ADC + [2:2] + read-write + + + CLK_SYS_ACCESSCTRL + [1:1] + read-write + + + CLK_SYS_CLOCKS + [0:0] + read-write + + + + + WAKE_EN1 + 0x000000b0 + enable clock in wake mode + 0x7fffffff + + + CLK_SYS_XOSC + [30:30] + read-write + + + CLK_SYS_XIP + [29:29] + read-write + + + CLK_SYS_WATCHDOG + [28:28] + read-write + + + CLK_USB + [27:27] + read-write + + + CLK_SYS_USBCTRL + [26:26] + read-write + + + CLK_SYS_UART1 + [25:25] + read-write + + + CLK_PERI_UART1 + [24:24] + read-write + + + CLK_SYS_UART0 + [23:23] + read-write + + + CLK_PERI_UART0 + [22:22] + read-write + + + CLK_SYS_TRNG + [21:21] + read-write + + + CLK_SYS_TIMER1 + [20:20] + read-write + + + CLK_SYS_TIMER0 + [19:19] + read-write + + + CLK_SYS_TICKS + [18:18] + read-write + + + CLK_REF_TICKS + [17:17] + read-write + + + CLK_SYS_TBMAN + [16:16] + read-write + + + CLK_SYS_SYSINFO + [15:15] + read-write + + + CLK_SYS_SYSCFG + [14:14] + read-write + + + CLK_SYS_SRAM9 + [13:13] + read-write + + + CLK_SYS_SRAM8 + [12:12] + read-write + + + CLK_SYS_SRAM7 + [11:11] + read-write + + + CLK_SYS_SRAM6 + [10:10] + read-write + + + CLK_SYS_SRAM5 + [9:9] + read-write + + + CLK_SYS_SRAM4 + [8:8] + read-write + + + CLK_SYS_SRAM3 + [7:7] + read-write + + + CLK_SYS_SRAM2 + [6:6] + read-write + + + CLK_SYS_SRAM1 + [5:5] + read-write + + + CLK_SYS_SRAM0 + [4:4] + read-write + + + CLK_SYS_SPI1 + [3:3] + read-write + + + CLK_PERI_SPI1 + [2:2] + read-write + + + CLK_SYS_SPI0 + [1:1] + read-write + + + CLK_PERI_SPI0 + [0:0] + read-write + + + + + SLEEP_EN0 + 0x000000b4 + enable clock in sleep mode + 0xffffffff + + + CLK_SYS_SIO + [31:31] + read-write + + + CLK_SYS_SHA256 + [30:30] + read-write + + + CLK_SYS_PSM + [29:29] + read-write + + + CLK_SYS_ROSC + [28:28] + read-write + + + CLK_SYS_ROM + [27:27] + read-write + + + CLK_SYS_RESETS + [26:26] + read-write + + + CLK_SYS_PWM + [25:25] + read-write + + + CLK_SYS_POWMAN + [24:24] + read-write + + + CLK_REF_POWMAN + [23:23] + read-write + + + CLK_SYS_PLL_USB + [22:22] + read-write + + + CLK_SYS_PLL_SYS + [21:21] + read-write + + + CLK_SYS_PIO2 + [20:20] + read-write + + + CLK_SYS_PIO1 + [19:19] + read-write + + + CLK_SYS_PIO0 + [18:18] + read-write + + + CLK_SYS_PADS + [17:17] + read-write + + + CLK_SYS_OTP + [16:16] + read-write + + + CLK_REF_OTP + [15:15] + read-write + + + CLK_SYS_JTAG + [14:14] + read-write + + + CLK_SYS_IO + [13:13] + read-write + + + CLK_SYS_I2C1 + [12:12] + read-write + + + CLK_SYS_I2C0 + [11:11] + read-write + + + CLK_SYS_HSTX + [10:10] + read-write + + + CLK_HSTX + [9:9] + read-write + + + CLK_SYS_GLITCH_DETECTOR + [8:8] + read-write + + + CLK_SYS_DMA + [7:7] + read-write + + + CLK_SYS_BUSFABRIC + [6:6] + read-write + + + CLK_SYS_BUSCTRL + [5:5] + read-write + + + CLK_SYS_BOOTRAM + [4:4] + read-write + + + CLK_SYS_ADC + [3:3] + read-write + + + CLK_ADC + [2:2] + read-write + + + CLK_SYS_ACCESSCTRL + [1:1] + read-write + + + CLK_SYS_CLOCKS + [0:0] + read-write + + + + + SLEEP_EN1 + 0x000000b8 + enable clock in sleep mode + 0x7fffffff + + + CLK_SYS_XOSC + [30:30] + read-write + + + CLK_SYS_XIP + [29:29] + read-write + + + CLK_SYS_WATCHDOG + [28:28] + read-write + + + CLK_USB + [27:27] + read-write + + + CLK_SYS_USBCTRL + [26:26] + read-write + + + CLK_SYS_UART1 + [25:25] + read-write + + + CLK_PERI_UART1 + [24:24] + read-write + + + CLK_SYS_UART0 + [23:23] + read-write + + + CLK_PERI_UART0 + [22:22] + read-write + + + CLK_SYS_TRNG + [21:21] + read-write + + + CLK_SYS_TIMER1 + [20:20] + read-write + + + CLK_SYS_TIMER0 + [19:19] + read-write + + + CLK_SYS_TICKS + [18:18] + read-write + + + CLK_REF_TICKS + [17:17] + read-write + + + CLK_SYS_TBMAN + [16:16] + read-write + + + CLK_SYS_SYSINFO + [15:15] + read-write + + + CLK_SYS_SYSCFG + [14:14] + read-write + + + CLK_SYS_SRAM9 + [13:13] + read-write + + + CLK_SYS_SRAM8 + [12:12] + read-write + + + CLK_SYS_SRAM7 + [11:11] + read-write + + + CLK_SYS_SRAM6 + [10:10] + read-write + + + CLK_SYS_SRAM5 + [9:9] + read-write + + + CLK_SYS_SRAM4 + [8:8] + read-write + + + CLK_SYS_SRAM3 + [7:7] + read-write + + + CLK_SYS_SRAM2 + [6:6] + read-write + + + CLK_SYS_SRAM1 + [5:5] + read-write + + + CLK_SYS_SRAM0 + [4:4] + read-write + + + CLK_SYS_SPI1 + [3:3] + read-write + + + CLK_PERI_SPI1 + [2:2] + read-write + + + CLK_SYS_SPI0 + [1:1] + read-write + + + CLK_PERI_SPI0 + [0:0] + read-write + + + + + ENABLED0 + 0x000000bc + indicates the state of the clock enable + 0x00000000 + + + CLK_SYS_SIO + [31:31] + read-only + + + CLK_SYS_SHA256 + [30:30] + read-only + + + CLK_SYS_PSM + [29:29] + read-only + + + CLK_SYS_ROSC + [28:28] + read-only + + + CLK_SYS_ROM + [27:27] + read-only + + + CLK_SYS_RESETS + [26:26] + read-only + + + CLK_SYS_PWM + [25:25] + read-only + + + CLK_SYS_POWMAN + [24:24] + read-only + + + CLK_REF_POWMAN + [23:23] + read-only + + + CLK_SYS_PLL_USB + [22:22] + read-only + + + CLK_SYS_PLL_SYS + [21:21] + read-only + + + CLK_SYS_PIO2 + [20:20] + read-only + + + CLK_SYS_PIO1 + [19:19] + read-only + + + CLK_SYS_PIO0 + [18:18] + read-only + + + CLK_SYS_PADS + [17:17] + read-only + + + CLK_SYS_OTP + [16:16] + read-only + + + CLK_REF_OTP + [15:15] + read-only + + + CLK_SYS_JTAG + [14:14] + read-only + + + CLK_SYS_IO + [13:13] + read-only + + + CLK_SYS_I2C1 + [12:12] + read-only + + + CLK_SYS_I2C0 + [11:11] + read-only + + + CLK_SYS_HSTX + [10:10] + read-only + + + CLK_HSTX + [9:9] + read-only + + + CLK_SYS_GLITCH_DETECTOR + [8:8] + read-only + + + CLK_SYS_DMA + [7:7] + read-only + + + CLK_SYS_BUSFABRIC + [6:6] + read-only + + + CLK_SYS_BUSCTRL + [5:5] + read-only + + + CLK_SYS_BOOTRAM + [4:4] + read-only + + + CLK_SYS_ADC + [3:3] + read-only + + + CLK_ADC + [2:2] + read-only + + + CLK_SYS_ACCESSCTRL + [1:1] + read-only + + + CLK_SYS_CLOCKS + [0:0] + read-only + + + + + ENABLED1 + 0x000000c0 + indicates the state of the clock enable + 0x00000000 + + + CLK_SYS_XOSC + [30:30] + read-only + + + CLK_SYS_XIP + [29:29] + read-only + + + CLK_SYS_WATCHDOG + [28:28] + read-only + + + CLK_USB + [27:27] + read-only + + + CLK_SYS_USBCTRL + [26:26] + read-only + + + CLK_SYS_UART1 + [25:25] + read-only + + + CLK_PERI_UART1 + [24:24] + read-only + + + CLK_SYS_UART0 + [23:23] + read-only + + + CLK_PERI_UART0 + [22:22] + read-only + + + CLK_SYS_TRNG + [21:21] + read-only + + + CLK_SYS_TIMER1 + [20:20] + read-only + + + CLK_SYS_TIMER0 + [19:19] + read-only + + + CLK_SYS_TICKS + [18:18] + read-only + + + CLK_REF_TICKS + [17:17] + read-only + + + CLK_SYS_TBMAN + [16:16] + read-only + + + CLK_SYS_SYSINFO + [15:15] + read-only + + + CLK_SYS_SYSCFG + [14:14] + read-only + + + CLK_SYS_SRAM9 + [13:13] + read-only + + + CLK_SYS_SRAM8 + [12:12] + read-only + + + CLK_SYS_SRAM7 + [11:11] + read-only + + + CLK_SYS_SRAM6 + [10:10] + read-only + + + CLK_SYS_SRAM5 + [9:9] + read-only + + + CLK_SYS_SRAM4 + [8:8] + read-only + + + CLK_SYS_SRAM3 + [7:7] + read-only + + + CLK_SYS_SRAM2 + [6:6] + read-only + + + CLK_SYS_SRAM1 + [5:5] + read-only + + + CLK_SYS_SRAM0 + [4:4] + read-only + + + CLK_SYS_SPI1 + [3:3] + read-only + + + CLK_PERI_SPI1 + [2:2] + read-only + + + CLK_SYS_SPI0 + [1:1] + read-only + + + CLK_PERI_SPI0 + [0:0] + read-only + + + + + INTR + 0x000000c4 + Raw Interrupts + 0x00000000 + + + CLK_SYS_RESUS + [0:0] + read-only + + + + + INTE + 0x000000c8 + Interrupt Enable + 0x00000000 + + + CLK_SYS_RESUS + [0:0] + read-write + + + + + INTF + 0x000000cc + Interrupt Force + 0x00000000 + + + CLK_SYS_RESUS + [0:0] + read-write + + + + + INTS + 0x000000d0 + Interrupt status after masking & forcing + 0x00000000 + + + CLK_SYS_RESUS + [0:0] + read-only + + + + + + + TICKS + 0x40108000 + + 0 + 72 + registers + + + + PROC0_CTRL + 0x00000000 + Controls the tick generator + 0x00000000 + + + RUNNING + Is the tick generator running? + [1:1] + read-only + + + ENABLE + start / stop tick generation + [0:0] + read-write + + + + + PROC0_CYCLES + 0x00000004 + 0x00000000 + + + PROC0_CYCLES + Total number of clk_tick cycles before the next tick. + [8:0] + read-write + + + + + PROC0_COUNT + 0x00000008 + 0x00000000 + + + PROC0_COUNT + Count down timer: the remaining number clk_tick cycles before the next tick is generated. + [8:0] + read-only + + + + + PROC1_CTRL + 0x0000000c + Controls the tick generator + 0x00000000 + + + RUNNING + Is the tick generator running? + [1:1] + read-only + + + ENABLE + start / stop tick generation + [0:0] + read-write + + + + + PROC1_CYCLES + 0x00000010 + 0x00000000 + + + PROC1_CYCLES + Total number of clk_tick cycles before the next tick. + [8:0] + read-write + + + + + PROC1_COUNT + 0x00000014 + 0x00000000 + + + PROC1_COUNT + Count down timer: the remaining number clk_tick cycles before the next tick is generated. + [8:0] + read-only + + + + + TIMER0_CTRL + 0x00000018 + Controls the tick generator + 0x00000000 + + + RUNNING + Is the tick generator running? + [1:1] + read-only + + + ENABLE + start / stop tick generation + [0:0] + read-write + + + + + TIMER0_CYCLES + 0x0000001c + 0x00000000 + + + TIMER0_CYCLES + Total number of clk_tick cycles before the next tick. + [8:0] + read-write + + + + + TIMER0_COUNT + 0x00000020 + 0x00000000 + + + TIMER0_COUNT + Count down timer: the remaining number clk_tick cycles before the next tick is generated. + [8:0] + read-only + + + + + TIMER1_CTRL + 0x00000024 + Controls the tick generator + 0x00000000 + + + RUNNING + Is the tick generator running? + [1:1] + read-only + + + ENABLE + start / stop tick generation + [0:0] + read-write + + + + + TIMER1_CYCLES + 0x00000028 + 0x00000000 + + + TIMER1_CYCLES + Total number of clk_tick cycles before the next tick. + [8:0] + read-write + + + + + TIMER1_COUNT + 0x0000002c + 0x00000000 + + + TIMER1_COUNT + Count down timer: the remaining number clk_tick cycles before the next tick is generated. + [8:0] + read-only + + + + + WATCHDOG_CTRL + 0x00000030 + Controls the tick generator + 0x00000000 + + + RUNNING + Is the tick generator running? + [1:1] + read-only + + + ENABLE + start / stop tick generation + [0:0] + read-write + + + + + WATCHDOG_CYCLES + 0x00000034 + 0x00000000 + + + WATCHDOG_CYCLES + Total number of clk_tick cycles before the next tick. + [8:0] + read-write + + + + + WATCHDOG_COUNT + 0x00000038 + 0x00000000 + + + WATCHDOG_COUNT + Count down timer: the remaining number clk_tick cycles before the next tick is generated. + [8:0] + read-only + + + + + RISCV_CTRL + 0x0000003c + Controls the tick generator + 0x00000000 + + + RUNNING + Is the tick generator running? + [1:1] + read-only + + + ENABLE + start / stop tick generation + [0:0] + read-write + + + + + RISCV_CYCLES + 0x00000040 + 0x00000000 + + + RISCV_CYCLES + Total number of clk_tick cycles before the next tick. + [8:0] + read-write + + + + + RISCV_COUNT + 0x00000044 + 0x00000000 + + + RISCV_COUNT + Count down timer: the remaining number clk_tick cycles before the next tick is generated. + [8:0] + read-only + + + + + + + PADS_BANK0 + 0x40038000 + + 0 + 204 + registers + + + + VOLTAGE_SELECT + 0x00000000 + Voltage select. Per bank control + 0x00000000 + + + VOLTAGE_SELECT + [0:0] + read-write + + + 3v3 + 0 + Set voltage to 3.3V (DVDD >= 2V5) + + + 1v8 + 1 + Set voltage to 1.8V (DVDD <= 1V8) + + + + + + + GPIO0 + 0x00000004 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO1 + 0x00000008 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO2 + 0x0000000c + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO3 + 0x00000010 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO4 + 0x00000014 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO5 + 0x00000018 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO6 + 0x0000001c + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO7 + 0x00000020 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO8 + 0x00000024 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO9 + 0x00000028 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO10 + 0x0000002c + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO11 + 0x00000030 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO12 + 0x00000034 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO13 + 0x00000038 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO14 + 0x0000003c + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO15 + 0x00000040 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO16 + 0x00000044 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO17 + 0x00000048 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO18 + 0x0000004c + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO19 + 0x00000050 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO20 + 0x00000054 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO21 + 0x00000058 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO22 + 0x0000005c + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO23 + 0x00000060 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO24 + 0x00000064 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO25 + 0x00000068 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO26 + 0x0000006c + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO27 + 0x00000070 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO28 + 0x00000074 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO29 + 0x00000078 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO30 + 0x0000007c + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO31 + 0x00000080 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO32 + 0x00000084 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO33 + 0x00000088 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO34 + 0x0000008c + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO35 + 0x00000090 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO36 + 0x00000094 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO37 + 0x00000098 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO38 + 0x0000009c + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO39 + 0x000000a0 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO40 + 0x000000a4 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO41 + 0x000000a8 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO42 + 0x000000ac + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO43 + 0x000000b0 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO44 + 0x000000b4 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO45 + 0x000000b8 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO46 + 0x000000bc + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO47 + 0x000000c0 + 0x00000116 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + SWCLK + 0x000000c4 + 0x0000005a + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + SWD + 0x000000c8 + 0x0000005a + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + + + PADS_QSPI + 0x40040000 + + 0 + 28 + registers + + + + VOLTAGE_SELECT + 0x00000000 + Voltage select. Per bank control + 0x00000000 + + + VOLTAGE_SELECT + [0:0] + read-write + + + 3v3 + 0 + Set voltage to 3.3V (DVDD >= 2V5) + + + 1v8 + 1 + Set voltage to 1.8V (DVDD <= 1V8) + + + + + + + GPIO_QSPI_SCLK + 0x00000004 + 0x00000156 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO_QSPI_SD0 + 0x00000008 + 0x00000156 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO_QSPI_SD1 + 0x0000000c + 0x00000156 + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO_QSPI_SD2 + 0x00000010 + 0x0000015a + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO_QSPI_SD3 + 0x00000014 + 0x0000015a + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + GPIO_QSPI_SS + 0x00000018 + 0x0000015a + + + ISO + Pad isolation control. Remove this once the pad is configured by software. + [8:8] + read-write + + + OD + Output disable. Has priority over output enable from peripherals + [7:7] + read-write + + + IE + Input enable + [6:6] + read-write + + + DRIVE + Drive strength. + [5:4] + read-write + + + 2mA + 0 + + + 4mA + 1 + + + 8mA + 2 + + + 12mA + 3 + + + + + PUE + Pull up enable + [3:3] + read-write + + + PDE + Pull down enable + [2:2] + read-write + + + SCHMITT + Enable schmitt trigger + [1:1] + read-write + + + SLEWFAST + Slew rate control. 1 = Fast, 0 = Slow + [0:0] + read-write + + + + + + + IO_QSPI + 0x40030000 + + 0 + 576 + registers + + + IO_IRQ_QSPI + 23 + + + IO_IRQ_QSPI_NS + 24 + + + + USBPHY_DP_STATUS + 0x00000000 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + USBPHY_DP_CTRL + 0x00000004 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + uart1_tx + 2 + + + i2c0_sda + 3 + + + siob_proc_56 + 5 + + + null + 31 + + + + + + + USBPHY_DM_STATUS + 0x00000008 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + USBPHY_DM_CTRL + 0x0000000c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + uart1_rx + 2 + + + i2c0_scl + 3 + + + siob_proc_57 + 5 + + + null + 31 + + + + + + + GPIO_QSPI_SCLK_STATUS + 0x00000010 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO_QSPI_SCLK_CTRL + 0x00000014 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + xip_sclk + 0 + + + uart1_cts + 2 + + + i2c1_sda + 3 + + + siob_proc_58 + 5 + + + uart1_tx + 11 + + + null + 31 + + + + + + + GPIO_QSPI_SS_STATUS + 0x00000018 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO_QSPI_SS_CTRL + 0x0000001c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + xip_ss_n_0 + 0 + + + uart1_rts + 2 + + + i2c1_scl + 3 + + + siob_proc_59 + 5 + + + uart1_rx + 11 + + + null + 31 + + + + + + + GPIO_QSPI_SD0_STATUS + 0x00000020 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO_QSPI_SD0_CTRL + 0x00000024 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + xip_sd0 + 0 + + + uart0_tx + 2 + + + i2c0_sda + 3 + + + siob_proc_60 + 5 + + + null + 31 + + + + + + + GPIO_QSPI_SD1_STATUS + 0x00000028 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO_QSPI_SD1_CTRL + 0x0000002c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + xip_sd1 + 0 + + + uart0_rx + 2 + + + i2c0_scl + 3 + + + siob_proc_61 + 5 + + + null + 31 + + + + + + + GPIO_QSPI_SD2_STATUS + 0x00000030 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO_QSPI_SD2_CTRL + 0x00000034 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + xip_sd2 + 0 + + + uart0_cts + 2 + + + i2c1_sda + 3 + + + siob_proc_62 + 5 + + + uart0_tx + 11 + + + null + 31 + + + + + + + GPIO_QSPI_SD3_STATUS + 0x00000038 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO_QSPI_SD3_CTRL + 0x0000003c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + xip_sd3 + 0 + + + uart0_rts + 2 + + + i2c1_scl + 3 + + + siob_proc_63 + 5 + + + uart0_rx + 11 + + + null + 31 + + + + + + + IRQSUMMARY_PROC0_SECURE + 0x00000200 + 0x00000000 + + + GPIO_QSPI_SD3 + [7:7] + read-only + + + GPIO_QSPI_SD2 + [6:6] + read-only + + + GPIO_QSPI_SD1 + [5:5] + read-only + + + GPIO_QSPI_SD0 + [4:4] + read-only + + + GPIO_QSPI_SS + [3:3] + read-only + + + GPIO_QSPI_SCLK + [2:2] + read-only + + + USBPHY_DM + [1:1] + read-only + + + USBPHY_DP + [0:0] + read-only + + + + + IRQSUMMARY_PROC0_NONSECURE + 0x00000204 + 0x00000000 + + + GPIO_QSPI_SD3 + [7:7] + read-only + + + GPIO_QSPI_SD2 + [6:6] + read-only + + + GPIO_QSPI_SD1 + [5:5] + read-only + + + GPIO_QSPI_SD0 + [4:4] + read-only + + + GPIO_QSPI_SS + [3:3] + read-only + + + GPIO_QSPI_SCLK + [2:2] + read-only + + + USBPHY_DM + [1:1] + read-only + + + USBPHY_DP + [0:0] + read-only + + + + + IRQSUMMARY_PROC1_SECURE + 0x00000208 + 0x00000000 + + + GPIO_QSPI_SD3 + [7:7] + read-only + + + GPIO_QSPI_SD2 + [6:6] + read-only + + + GPIO_QSPI_SD1 + [5:5] + read-only + + + GPIO_QSPI_SD0 + [4:4] + read-only + + + GPIO_QSPI_SS + [3:3] + read-only + + + GPIO_QSPI_SCLK + [2:2] + read-only + + + USBPHY_DM + [1:1] + read-only + + + USBPHY_DP + [0:0] + read-only + + + + + IRQSUMMARY_PROC1_NONSECURE + 0x0000020c + 0x00000000 + + + GPIO_QSPI_SD3 + [7:7] + read-only + + + GPIO_QSPI_SD2 + [6:6] + read-only + + + GPIO_QSPI_SD1 + [5:5] + read-only + + + GPIO_QSPI_SD0 + [4:4] + read-only + + + GPIO_QSPI_SS + [3:3] + read-only + + + GPIO_QSPI_SCLK + [2:2] + read-only + + + USBPHY_DM + [1:1] + read-only + + + USBPHY_DP + [0:0] + read-only + + + + + IRQSUMMARY_DORMANT_WAKE_SECURE + 0x00000210 + 0x00000000 + + + GPIO_QSPI_SD3 + [7:7] + read-only + + + GPIO_QSPI_SD2 + [6:6] + read-only + + + GPIO_QSPI_SD1 + [5:5] + read-only + + + GPIO_QSPI_SD0 + [4:4] + read-only + + + GPIO_QSPI_SS + [3:3] + read-only + + + GPIO_QSPI_SCLK + [2:2] + read-only + + + USBPHY_DM + [1:1] + read-only + + + USBPHY_DP + [0:0] + read-only + + + + + IRQSUMMARY_DORMANT_WAKE_NONSECURE + 0x00000214 + 0x00000000 + + + GPIO_QSPI_SD3 + [7:7] + read-only + + + GPIO_QSPI_SD2 + [6:6] + read-only + + + GPIO_QSPI_SD1 + [5:5] + read-only + + + GPIO_QSPI_SD0 + [4:4] + read-only + + + GPIO_QSPI_SS + [3:3] + read-only + + + GPIO_QSPI_SCLK + [2:2] + read-only + + + USBPHY_DM + [1:1] + read-only + + + USBPHY_DP + [0:0] + read-only + + + + + INTR + 0x00000218 + Raw Interrupts + 0x00000000 + + + GPIO_QSPI_SD3_EDGE_HIGH + [31:31] + read-write + oneToClear + + + GPIO_QSPI_SD3_EDGE_LOW + [30:30] + read-write + oneToClear + + + GPIO_QSPI_SD3_LEVEL_HIGH + [29:29] + read-only + + + GPIO_QSPI_SD3_LEVEL_LOW + [28:28] + read-only + + + GPIO_QSPI_SD2_EDGE_HIGH + [27:27] + read-write + oneToClear + + + GPIO_QSPI_SD2_EDGE_LOW + [26:26] + read-write + oneToClear + + + GPIO_QSPI_SD2_LEVEL_HIGH + [25:25] + read-only + + + GPIO_QSPI_SD2_LEVEL_LOW + [24:24] + read-only + + + GPIO_QSPI_SD1_EDGE_HIGH + [23:23] + read-write + oneToClear + + + GPIO_QSPI_SD1_EDGE_LOW + [22:22] + read-write + oneToClear + + + GPIO_QSPI_SD1_LEVEL_HIGH + [21:21] + read-only + + + GPIO_QSPI_SD1_LEVEL_LOW + [20:20] + read-only + + + GPIO_QSPI_SD0_EDGE_HIGH + [19:19] + read-write + oneToClear + + + GPIO_QSPI_SD0_EDGE_LOW + [18:18] + read-write + oneToClear + + + GPIO_QSPI_SD0_LEVEL_HIGH + [17:17] + read-only + + + GPIO_QSPI_SD0_LEVEL_LOW + [16:16] + read-only + + + GPIO_QSPI_SS_EDGE_HIGH + [15:15] + read-write + oneToClear + + + GPIO_QSPI_SS_EDGE_LOW + [14:14] + read-write + oneToClear + + + GPIO_QSPI_SS_LEVEL_HIGH + [13:13] + read-only + + + GPIO_QSPI_SS_LEVEL_LOW + [12:12] + read-only + + + GPIO_QSPI_SCLK_EDGE_HIGH + [11:11] + read-write + oneToClear + + + GPIO_QSPI_SCLK_EDGE_LOW + [10:10] + read-write + oneToClear + + + GPIO_QSPI_SCLK_LEVEL_HIGH + [9:9] + read-only + + + GPIO_QSPI_SCLK_LEVEL_LOW + [8:8] + read-only + + + USBPHY_DM_EDGE_HIGH + [7:7] + read-write + oneToClear + + + USBPHY_DM_EDGE_LOW + [6:6] + read-write + oneToClear + + + USBPHY_DM_LEVEL_HIGH + [5:5] + read-only + + + USBPHY_DM_LEVEL_LOW + [4:4] + read-only + + + USBPHY_DP_EDGE_HIGH + [3:3] + read-write + oneToClear + + + USBPHY_DP_EDGE_LOW + [2:2] + read-write + oneToClear + + + USBPHY_DP_LEVEL_HIGH + [1:1] + read-only + + + USBPHY_DP_LEVEL_LOW + [0:0] + read-only + + + + + PROC0_INTE + 0x0000021c + Interrupt Enable for proc0 + 0x00000000 + + + GPIO_QSPI_SD3_EDGE_HIGH + [31:31] + read-write + + + GPIO_QSPI_SD3_EDGE_LOW + [30:30] + read-write + + + GPIO_QSPI_SD3_LEVEL_HIGH + [29:29] + read-write + + + GPIO_QSPI_SD3_LEVEL_LOW + [28:28] + read-write + + + GPIO_QSPI_SD2_EDGE_HIGH + [27:27] + read-write + + + GPIO_QSPI_SD2_EDGE_LOW + [26:26] + read-write + + + GPIO_QSPI_SD2_LEVEL_HIGH + [25:25] + read-write + + + GPIO_QSPI_SD2_LEVEL_LOW + [24:24] + read-write + + + GPIO_QSPI_SD1_EDGE_HIGH + [23:23] + read-write + + + GPIO_QSPI_SD1_EDGE_LOW + [22:22] + read-write + + + GPIO_QSPI_SD1_LEVEL_HIGH + [21:21] + read-write + + + GPIO_QSPI_SD1_LEVEL_LOW + [20:20] + read-write + + + GPIO_QSPI_SD0_EDGE_HIGH + [19:19] + read-write + + + GPIO_QSPI_SD0_EDGE_LOW + [18:18] + read-write + + + GPIO_QSPI_SD0_LEVEL_HIGH + [17:17] + read-write + + + GPIO_QSPI_SD0_LEVEL_LOW + [16:16] + read-write + + + GPIO_QSPI_SS_EDGE_HIGH + [15:15] + read-write + + + GPIO_QSPI_SS_EDGE_LOW + [14:14] + read-write + + + GPIO_QSPI_SS_LEVEL_HIGH + [13:13] + read-write + + + GPIO_QSPI_SS_LEVEL_LOW + [12:12] + read-write + + + GPIO_QSPI_SCLK_EDGE_HIGH + [11:11] + read-write + + + GPIO_QSPI_SCLK_EDGE_LOW + [10:10] + read-write + + + GPIO_QSPI_SCLK_LEVEL_HIGH + [9:9] + read-write + + + GPIO_QSPI_SCLK_LEVEL_LOW + [8:8] + read-write + + + USBPHY_DM_EDGE_HIGH + [7:7] + read-write + + + USBPHY_DM_EDGE_LOW + [6:6] + read-write + + + USBPHY_DM_LEVEL_HIGH + [5:5] + read-write + + + USBPHY_DM_LEVEL_LOW + [4:4] + read-write + + + USBPHY_DP_EDGE_HIGH + [3:3] + read-write + + + USBPHY_DP_EDGE_LOW + [2:2] + read-write + + + USBPHY_DP_LEVEL_HIGH + [1:1] + read-write + + + USBPHY_DP_LEVEL_LOW + [0:0] + read-write + + + + + PROC0_INTF + 0x00000220 + Interrupt Force for proc0 + 0x00000000 + + + GPIO_QSPI_SD3_EDGE_HIGH + [31:31] + read-write + + + GPIO_QSPI_SD3_EDGE_LOW + [30:30] + read-write + + + GPIO_QSPI_SD3_LEVEL_HIGH + [29:29] + read-write + + + GPIO_QSPI_SD3_LEVEL_LOW + [28:28] + read-write + + + GPIO_QSPI_SD2_EDGE_HIGH + [27:27] + read-write + + + GPIO_QSPI_SD2_EDGE_LOW + [26:26] + read-write + + + GPIO_QSPI_SD2_LEVEL_HIGH + [25:25] + read-write + + + GPIO_QSPI_SD2_LEVEL_LOW + [24:24] + read-write + + + GPIO_QSPI_SD1_EDGE_HIGH + [23:23] + read-write + + + GPIO_QSPI_SD1_EDGE_LOW + [22:22] + read-write + + + GPIO_QSPI_SD1_LEVEL_HIGH + [21:21] + read-write + + + GPIO_QSPI_SD1_LEVEL_LOW + [20:20] + read-write + + + GPIO_QSPI_SD0_EDGE_HIGH + [19:19] + read-write + + + GPIO_QSPI_SD0_EDGE_LOW + [18:18] + read-write + + + GPIO_QSPI_SD0_LEVEL_HIGH + [17:17] + read-write + + + GPIO_QSPI_SD0_LEVEL_LOW + [16:16] + read-write + + + GPIO_QSPI_SS_EDGE_HIGH + [15:15] + read-write + + + GPIO_QSPI_SS_EDGE_LOW + [14:14] + read-write + + + GPIO_QSPI_SS_LEVEL_HIGH + [13:13] + read-write + + + GPIO_QSPI_SS_LEVEL_LOW + [12:12] + read-write + + + GPIO_QSPI_SCLK_EDGE_HIGH + [11:11] + read-write + + + GPIO_QSPI_SCLK_EDGE_LOW + [10:10] + read-write + + + GPIO_QSPI_SCLK_LEVEL_HIGH + [9:9] + read-write + + + GPIO_QSPI_SCLK_LEVEL_LOW + [8:8] + read-write + + + USBPHY_DM_EDGE_HIGH + [7:7] + read-write + + + USBPHY_DM_EDGE_LOW + [6:6] + read-write + + + USBPHY_DM_LEVEL_HIGH + [5:5] + read-write + + + USBPHY_DM_LEVEL_LOW + [4:4] + read-write + + + USBPHY_DP_EDGE_HIGH + [3:3] + read-write + + + USBPHY_DP_EDGE_LOW + [2:2] + read-write + + + USBPHY_DP_LEVEL_HIGH + [1:1] + read-write + + + USBPHY_DP_LEVEL_LOW + [0:0] + read-write + + + + + PROC0_INTS + 0x00000224 + Interrupt status after masking & forcing for proc0 + 0x00000000 + + + GPIO_QSPI_SD3_EDGE_HIGH + [31:31] + read-only + + + GPIO_QSPI_SD3_EDGE_LOW + [30:30] + read-only + + + GPIO_QSPI_SD3_LEVEL_HIGH + [29:29] + read-only + + + GPIO_QSPI_SD3_LEVEL_LOW + [28:28] + read-only + + + GPIO_QSPI_SD2_EDGE_HIGH + [27:27] + read-only + + + GPIO_QSPI_SD2_EDGE_LOW + [26:26] + read-only + + + GPIO_QSPI_SD2_LEVEL_HIGH + [25:25] + read-only + + + GPIO_QSPI_SD2_LEVEL_LOW + [24:24] + read-only + + + GPIO_QSPI_SD1_EDGE_HIGH + [23:23] + read-only + + + GPIO_QSPI_SD1_EDGE_LOW + [22:22] + read-only + + + GPIO_QSPI_SD1_LEVEL_HIGH + [21:21] + read-only + + + GPIO_QSPI_SD1_LEVEL_LOW + [20:20] + read-only + + + GPIO_QSPI_SD0_EDGE_HIGH + [19:19] + read-only + + + GPIO_QSPI_SD0_EDGE_LOW + [18:18] + read-only + + + GPIO_QSPI_SD0_LEVEL_HIGH + [17:17] + read-only + + + GPIO_QSPI_SD0_LEVEL_LOW + [16:16] + read-only + + + GPIO_QSPI_SS_EDGE_HIGH + [15:15] + read-only + + + GPIO_QSPI_SS_EDGE_LOW + [14:14] + read-only + + + GPIO_QSPI_SS_LEVEL_HIGH + [13:13] + read-only + + + GPIO_QSPI_SS_LEVEL_LOW + [12:12] + read-only + + + GPIO_QSPI_SCLK_EDGE_HIGH + [11:11] + read-only + + + GPIO_QSPI_SCLK_EDGE_LOW + [10:10] + read-only + + + GPIO_QSPI_SCLK_LEVEL_HIGH + [9:9] + read-only + + + GPIO_QSPI_SCLK_LEVEL_LOW + [8:8] + read-only + + + USBPHY_DM_EDGE_HIGH + [7:7] + read-only + + + USBPHY_DM_EDGE_LOW + [6:6] + read-only + + + USBPHY_DM_LEVEL_HIGH + [5:5] + read-only + + + USBPHY_DM_LEVEL_LOW + [4:4] + read-only + + + USBPHY_DP_EDGE_HIGH + [3:3] + read-only + + + USBPHY_DP_EDGE_LOW + [2:2] + read-only + + + USBPHY_DP_LEVEL_HIGH + [1:1] + read-only + + + USBPHY_DP_LEVEL_LOW + [0:0] + read-only + + + + + PROC1_INTE + 0x00000228 + Interrupt Enable for proc1 + 0x00000000 + + + GPIO_QSPI_SD3_EDGE_HIGH + [31:31] + read-write + + + GPIO_QSPI_SD3_EDGE_LOW + [30:30] + read-write + + + GPIO_QSPI_SD3_LEVEL_HIGH + [29:29] + read-write + + + GPIO_QSPI_SD3_LEVEL_LOW + [28:28] + read-write + + + GPIO_QSPI_SD2_EDGE_HIGH + [27:27] + read-write + + + GPIO_QSPI_SD2_EDGE_LOW + [26:26] + read-write + + + GPIO_QSPI_SD2_LEVEL_HIGH + [25:25] + read-write + + + GPIO_QSPI_SD2_LEVEL_LOW + [24:24] + read-write + + + GPIO_QSPI_SD1_EDGE_HIGH + [23:23] + read-write + + + GPIO_QSPI_SD1_EDGE_LOW + [22:22] + read-write + + + GPIO_QSPI_SD1_LEVEL_HIGH + [21:21] + read-write + + + GPIO_QSPI_SD1_LEVEL_LOW + [20:20] + read-write + + + GPIO_QSPI_SD0_EDGE_HIGH + [19:19] + read-write + + + GPIO_QSPI_SD0_EDGE_LOW + [18:18] + read-write + + + GPIO_QSPI_SD0_LEVEL_HIGH + [17:17] + read-write + + + GPIO_QSPI_SD0_LEVEL_LOW + [16:16] + read-write + + + GPIO_QSPI_SS_EDGE_HIGH + [15:15] + read-write + + + GPIO_QSPI_SS_EDGE_LOW + [14:14] + read-write + + + GPIO_QSPI_SS_LEVEL_HIGH + [13:13] + read-write + + + GPIO_QSPI_SS_LEVEL_LOW + [12:12] + read-write + + + GPIO_QSPI_SCLK_EDGE_HIGH + [11:11] + read-write + + + GPIO_QSPI_SCLK_EDGE_LOW + [10:10] + read-write + + + GPIO_QSPI_SCLK_LEVEL_HIGH + [9:9] + read-write + + + GPIO_QSPI_SCLK_LEVEL_LOW + [8:8] + read-write + + + USBPHY_DM_EDGE_HIGH + [7:7] + read-write + + + USBPHY_DM_EDGE_LOW + [6:6] + read-write + + + USBPHY_DM_LEVEL_HIGH + [5:5] + read-write + + + USBPHY_DM_LEVEL_LOW + [4:4] + read-write + + + USBPHY_DP_EDGE_HIGH + [3:3] + read-write + + + USBPHY_DP_EDGE_LOW + [2:2] + read-write + + + USBPHY_DP_LEVEL_HIGH + [1:1] + read-write + + + USBPHY_DP_LEVEL_LOW + [0:0] + read-write + + + + + PROC1_INTF + 0x0000022c + Interrupt Force for proc1 + 0x00000000 + + + GPIO_QSPI_SD3_EDGE_HIGH + [31:31] + read-write + + + GPIO_QSPI_SD3_EDGE_LOW + [30:30] + read-write + + + GPIO_QSPI_SD3_LEVEL_HIGH + [29:29] + read-write + + + GPIO_QSPI_SD3_LEVEL_LOW + [28:28] + read-write + + + GPIO_QSPI_SD2_EDGE_HIGH + [27:27] + read-write + + + GPIO_QSPI_SD2_EDGE_LOW + [26:26] + read-write + + + GPIO_QSPI_SD2_LEVEL_HIGH + [25:25] + read-write + + + GPIO_QSPI_SD2_LEVEL_LOW + [24:24] + read-write + + + GPIO_QSPI_SD1_EDGE_HIGH + [23:23] + read-write + + + GPIO_QSPI_SD1_EDGE_LOW + [22:22] + read-write + + + GPIO_QSPI_SD1_LEVEL_HIGH + [21:21] + read-write + + + GPIO_QSPI_SD1_LEVEL_LOW + [20:20] + read-write + + + GPIO_QSPI_SD0_EDGE_HIGH + [19:19] + read-write + + + GPIO_QSPI_SD0_EDGE_LOW + [18:18] + read-write + + + GPIO_QSPI_SD0_LEVEL_HIGH + [17:17] + read-write + + + GPIO_QSPI_SD0_LEVEL_LOW + [16:16] + read-write + + + GPIO_QSPI_SS_EDGE_HIGH + [15:15] + read-write + + + GPIO_QSPI_SS_EDGE_LOW + [14:14] + read-write + + + GPIO_QSPI_SS_LEVEL_HIGH + [13:13] + read-write + + + GPIO_QSPI_SS_LEVEL_LOW + [12:12] + read-write + + + GPIO_QSPI_SCLK_EDGE_HIGH + [11:11] + read-write + + + GPIO_QSPI_SCLK_EDGE_LOW + [10:10] + read-write + + + GPIO_QSPI_SCLK_LEVEL_HIGH + [9:9] + read-write + + + GPIO_QSPI_SCLK_LEVEL_LOW + [8:8] + read-write + + + USBPHY_DM_EDGE_HIGH + [7:7] + read-write + + + USBPHY_DM_EDGE_LOW + [6:6] + read-write + + + USBPHY_DM_LEVEL_HIGH + [5:5] + read-write + + + USBPHY_DM_LEVEL_LOW + [4:4] + read-write + + + USBPHY_DP_EDGE_HIGH + [3:3] + read-write + + + USBPHY_DP_EDGE_LOW + [2:2] + read-write + + + USBPHY_DP_LEVEL_HIGH + [1:1] + read-write + + + USBPHY_DP_LEVEL_LOW + [0:0] + read-write + + + + + PROC1_INTS + 0x00000230 + Interrupt status after masking & forcing for proc1 + 0x00000000 + + + GPIO_QSPI_SD3_EDGE_HIGH + [31:31] + read-only + + + GPIO_QSPI_SD3_EDGE_LOW + [30:30] + read-only + + + GPIO_QSPI_SD3_LEVEL_HIGH + [29:29] + read-only + + + GPIO_QSPI_SD3_LEVEL_LOW + [28:28] + read-only + + + GPIO_QSPI_SD2_EDGE_HIGH + [27:27] + read-only + + + GPIO_QSPI_SD2_EDGE_LOW + [26:26] + read-only + + + GPIO_QSPI_SD2_LEVEL_HIGH + [25:25] + read-only + + + GPIO_QSPI_SD2_LEVEL_LOW + [24:24] + read-only + + + GPIO_QSPI_SD1_EDGE_HIGH + [23:23] + read-only + + + GPIO_QSPI_SD1_EDGE_LOW + [22:22] + read-only + + + GPIO_QSPI_SD1_LEVEL_HIGH + [21:21] + read-only + + + GPIO_QSPI_SD1_LEVEL_LOW + [20:20] + read-only + + + GPIO_QSPI_SD0_EDGE_HIGH + [19:19] + read-only + + + GPIO_QSPI_SD0_EDGE_LOW + [18:18] + read-only + + + GPIO_QSPI_SD0_LEVEL_HIGH + [17:17] + read-only + + + GPIO_QSPI_SD0_LEVEL_LOW + [16:16] + read-only + + + GPIO_QSPI_SS_EDGE_HIGH + [15:15] + read-only + + + GPIO_QSPI_SS_EDGE_LOW + [14:14] + read-only + + + GPIO_QSPI_SS_LEVEL_HIGH + [13:13] + read-only + + + GPIO_QSPI_SS_LEVEL_LOW + [12:12] + read-only + + + GPIO_QSPI_SCLK_EDGE_HIGH + [11:11] + read-only + + + GPIO_QSPI_SCLK_EDGE_LOW + [10:10] + read-only + + + GPIO_QSPI_SCLK_LEVEL_HIGH + [9:9] + read-only + + + GPIO_QSPI_SCLK_LEVEL_LOW + [8:8] + read-only + + + USBPHY_DM_EDGE_HIGH + [7:7] + read-only + + + USBPHY_DM_EDGE_LOW + [6:6] + read-only + + + USBPHY_DM_LEVEL_HIGH + [5:5] + read-only + + + USBPHY_DM_LEVEL_LOW + [4:4] + read-only + + + USBPHY_DP_EDGE_HIGH + [3:3] + read-only + + + USBPHY_DP_EDGE_LOW + [2:2] + read-only + + + USBPHY_DP_LEVEL_HIGH + [1:1] + read-only + + + USBPHY_DP_LEVEL_LOW + [0:0] + read-only + + + + + DORMANT_WAKE_INTE + 0x00000234 + Interrupt Enable for dormant_wake + 0x00000000 + + + GPIO_QSPI_SD3_EDGE_HIGH + [31:31] + read-write + + + GPIO_QSPI_SD3_EDGE_LOW + [30:30] + read-write + + + GPIO_QSPI_SD3_LEVEL_HIGH + [29:29] + read-write + + + GPIO_QSPI_SD3_LEVEL_LOW + [28:28] + read-write + + + GPIO_QSPI_SD2_EDGE_HIGH + [27:27] + read-write + + + GPIO_QSPI_SD2_EDGE_LOW + [26:26] + read-write + + + GPIO_QSPI_SD2_LEVEL_HIGH + [25:25] + read-write + + + GPIO_QSPI_SD2_LEVEL_LOW + [24:24] + read-write + + + GPIO_QSPI_SD1_EDGE_HIGH + [23:23] + read-write + + + GPIO_QSPI_SD1_EDGE_LOW + [22:22] + read-write + + + GPIO_QSPI_SD1_LEVEL_HIGH + [21:21] + read-write + + + GPIO_QSPI_SD1_LEVEL_LOW + [20:20] + read-write + + + GPIO_QSPI_SD0_EDGE_HIGH + [19:19] + read-write + + + GPIO_QSPI_SD0_EDGE_LOW + [18:18] + read-write + + + GPIO_QSPI_SD0_LEVEL_HIGH + [17:17] + read-write + + + GPIO_QSPI_SD0_LEVEL_LOW + [16:16] + read-write + + + GPIO_QSPI_SS_EDGE_HIGH + [15:15] + read-write + + + GPIO_QSPI_SS_EDGE_LOW + [14:14] + read-write + + + GPIO_QSPI_SS_LEVEL_HIGH + [13:13] + read-write + + + GPIO_QSPI_SS_LEVEL_LOW + [12:12] + read-write + + + GPIO_QSPI_SCLK_EDGE_HIGH + [11:11] + read-write + + + GPIO_QSPI_SCLK_EDGE_LOW + [10:10] + read-write + + + GPIO_QSPI_SCLK_LEVEL_HIGH + [9:9] + read-write + + + GPIO_QSPI_SCLK_LEVEL_LOW + [8:8] + read-write + + + USBPHY_DM_EDGE_HIGH + [7:7] + read-write + + + USBPHY_DM_EDGE_LOW + [6:6] + read-write + + + USBPHY_DM_LEVEL_HIGH + [5:5] + read-write + + + USBPHY_DM_LEVEL_LOW + [4:4] + read-write + + + USBPHY_DP_EDGE_HIGH + [3:3] + read-write + + + USBPHY_DP_EDGE_LOW + [2:2] + read-write + + + USBPHY_DP_LEVEL_HIGH + [1:1] + read-write + + + USBPHY_DP_LEVEL_LOW + [0:0] + read-write + + + + + DORMANT_WAKE_INTF + 0x00000238 + Interrupt Force for dormant_wake + 0x00000000 + + + GPIO_QSPI_SD3_EDGE_HIGH + [31:31] + read-write + + + GPIO_QSPI_SD3_EDGE_LOW + [30:30] + read-write + + + GPIO_QSPI_SD3_LEVEL_HIGH + [29:29] + read-write + + + GPIO_QSPI_SD3_LEVEL_LOW + [28:28] + read-write + + + GPIO_QSPI_SD2_EDGE_HIGH + [27:27] + read-write + + + GPIO_QSPI_SD2_EDGE_LOW + [26:26] + read-write + + + GPIO_QSPI_SD2_LEVEL_HIGH + [25:25] + read-write + + + GPIO_QSPI_SD2_LEVEL_LOW + [24:24] + read-write + + + GPIO_QSPI_SD1_EDGE_HIGH + [23:23] + read-write + + + GPIO_QSPI_SD1_EDGE_LOW + [22:22] + read-write + + + GPIO_QSPI_SD1_LEVEL_HIGH + [21:21] + read-write + + + GPIO_QSPI_SD1_LEVEL_LOW + [20:20] + read-write + + + GPIO_QSPI_SD0_EDGE_HIGH + [19:19] + read-write + + + GPIO_QSPI_SD0_EDGE_LOW + [18:18] + read-write + + + GPIO_QSPI_SD0_LEVEL_HIGH + [17:17] + read-write + + + GPIO_QSPI_SD0_LEVEL_LOW + [16:16] + read-write + + + GPIO_QSPI_SS_EDGE_HIGH + [15:15] + read-write + + + GPIO_QSPI_SS_EDGE_LOW + [14:14] + read-write + + + GPIO_QSPI_SS_LEVEL_HIGH + [13:13] + read-write + + + GPIO_QSPI_SS_LEVEL_LOW + [12:12] + read-write + + + GPIO_QSPI_SCLK_EDGE_HIGH + [11:11] + read-write + + + GPIO_QSPI_SCLK_EDGE_LOW + [10:10] + read-write + + + GPIO_QSPI_SCLK_LEVEL_HIGH + [9:9] + read-write + + + GPIO_QSPI_SCLK_LEVEL_LOW + [8:8] + read-write + + + USBPHY_DM_EDGE_HIGH + [7:7] + read-write + + + USBPHY_DM_EDGE_LOW + [6:6] + read-write + + + USBPHY_DM_LEVEL_HIGH + [5:5] + read-write + + + USBPHY_DM_LEVEL_LOW + [4:4] + read-write + + + USBPHY_DP_EDGE_HIGH + [3:3] + read-write + + + USBPHY_DP_EDGE_LOW + [2:2] + read-write + + + USBPHY_DP_LEVEL_HIGH + [1:1] + read-write + + + USBPHY_DP_LEVEL_LOW + [0:0] + read-write + + + + + DORMANT_WAKE_INTS + 0x0000023c + Interrupt status after masking & forcing for dormant_wake + 0x00000000 + + + GPIO_QSPI_SD3_EDGE_HIGH + [31:31] + read-only + + + GPIO_QSPI_SD3_EDGE_LOW + [30:30] + read-only + + + GPIO_QSPI_SD3_LEVEL_HIGH + [29:29] + read-only + + + GPIO_QSPI_SD3_LEVEL_LOW + [28:28] + read-only + + + GPIO_QSPI_SD2_EDGE_HIGH + [27:27] + read-only + + + GPIO_QSPI_SD2_EDGE_LOW + [26:26] + read-only + + + GPIO_QSPI_SD2_LEVEL_HIGH + [25:25] + read-only + + + GPIO_QSPI_SD2_LEVEL_LOW + [24:24] + read-only + + + GPIO_QSPI_SD1_EDGE_HIGH + [23:23] + read-only + + + GPIO_QSPI_SD1_EDGE_LOW + [22:22] + read-only + + + GPIO_QSPI_SD1_LEVEL_HIGH + [21:21] + read-only + + + GPIO_QSPI_SD1_LEVEL_LOW + [20:20] + read-only + + + GPIO_QSPI_SD0_EDGE_HIGH + [19:19] + read-only + + + GPIO_QSPI_SD0_EDGE_LOW + [18:18] + read-only + + + GPIO_QSPI_SD0_LEVEL_HIGH + [17:17] + read-only + + + GPIO_QSPI_SD0_LEVEL_LOW + [16:16] + read-only + + + GPIO_QSPI_SS_EDGE_HIGH + [15:15] + read-only + + + GPIO_QSPI_SS_EDGE_LOW + [14:14] + read-only + + + GPIO_QSPI_SS_LEVEL_HIGH + [13:13] + read-only + + + GPIO_QSPI_SS_LEVEL_LOW + [12:12] + read-only + + + GPIO_QSPI_SCLK_EDGE_HIGH + [11:11] + read-only + + + GPIO_QSPI_SCLK_EDGE_LOW + [10:10] + read-only + + + GPIO_QSPI_SCLK_LEVEL_HIGH + [9:9] + read-only + + + GPIO_QSPI_SCLK_LEVEL_LOW + [8:8] + read-only + + + USBPHY_DM_EDGE_HIGH + [7:7] + read-only + + + USBPHY_DM_EDGE_LOW + [6:6] + read-only + + + USBPHY_DM_LEVEL_HIGH + [5:5] + read-only + + + USBPHY_DM_LEVEL_LOW + [4:4] + read-only + + + USBPHY_DP_EDGE_HIGH + [3:3] + read-only + + + USBPHY_DP_EDGE_LOW + [2:2] + read-only + + + USBPHY_DP_LEVEL_HIGH + [1:1] + read-only + + + USBPHY_DP_LEVEL_LOW + [0:0] + read-only + + + + + + + IO_BANK0 + 0x40028000 + + 0 + 800 + registers + + + IO_IRQ_BANK0 + 21 + + + IO_IRQ_BANK0_NS + 22 + + + + GPIO0_STATUS + 0x00000000 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO0_CTRL + 0x00000004 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + jtag_tck + 0 + + + spi0_rx + 1 + + + uart0_tx + 2 + + + i2c0_sda + 3 + + + pwm_a_0 + 4 + + + siob_proc_0 + 5 + + + pio0_0 + 6 + + + pio1_0 + 7 + + + pio2_0 + 8 + + + xip_ss_n_1 + 9 + + + usb_muxing_overcurr_detect + 10 + + + null + 31 + + + + + + + GPIO1_STATUS + 0x00000008 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO1_CTRL + 0x0000000c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + jtag_tms + 0 + + + spi0_ss_n + 1 + + + uart0_rx + 2 + + + i2c0_scl + 3 + + + pwm_b_0 + 4 + + + siob_proc_1 + 5 + + + pio0_1 + 6 + + + pio1_1 + 7 + + + pio2_1 + 8 + + + coresight_traceclk + 9 + + + usb_muxing_vbus_detect + 10 + + + null + 31 + + + + + + + GPIO2_STATUS + 0x00000010 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO2_CTRL + 0x00000014 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + jtag_tdi + 0 + + + spi0_sclk + 1 + + + uart0_cts + 2 + + + i2c1_sda + 3 + + + pwm_a_1 + 4 + + + siob_proc_2 + 5 + + + pio0_2 + 6 + + + pio1_2 + 7 + + + pio2_2 + 8 + + + coresight_tracedata_0 + 9 + + + usb_muxing_vbus_en + 10 + + + uart0_tx + 11 + + + null + 31 + + + + + + + GPIO3_STATUS + 0x00000018 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO3_CTRL + 0x0000001c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + jtag_tdo + 0 + + + spi0_tx + 1 + + + uart0_rts + 2 + + + i2c1_scl + 3 + + + pwm_b_1 + 4 + + + siob_proc_3 + 5 + + + pio0_3 + 6 + + + pio1_3 + 7 + + + pio2_3 + 8 + + + coresight_tracedata_1 + 9 + + + usb_muxing_overcurr_detect + 10 + + + uart0_rx + 11 + + + null + 31 + + + + + + + GPIO4_STATUS + 0x00000020 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO4_CTRL + 0x00000024 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi0_rx + 1 + + + uart1_tx + 2 + + + i2c0_sda + 3 + + + pwm_a_2 + 4 + + + siob_proc_4 + 5 + + + pio0_4 + 6 + + + pio1_4 + 7 + + + pio2_4 + 8 + + + coresight_tracedata_2 + 9 + + + usb_muxing_vbus_detect + 10 + + + null + 31 + + + + + + + GPIO5_STATUS + 0x00000028 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO5_CTRL + 0x0000002c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi0_ss_n + 1 + + + uart1_rx + 2 + + + i2c0_scl + 3 + + + pwm_b_2 + 4 + + + siob_proc_5 + 5 + + + pio0_5 + 6 + + + pio1_5 + 7 + + + pio2_5 + 8 + + + coresight_tracedata_3 + 9 + + + usb_muxing_vbus_en + 10 + + + null + 31 + + + + + + + GPIO6_STATUS + 0x00000030 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO6_CTRL + 0x00000034 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi0_sclk + 1 + + + uart1_cts + 2 + + + i2c1_sda + 3 + + + pwm_a_3 + 4 + + + siob_proc_6 + 5 + + + pio0_6 + 6 + + + pio1_6 + 7 + + + pio2_6 + 8 + + + usb_muxing_overcurr_detect + 10 + + + uart1_tx + 11 + + + null + 31 + + + + + + + GPIO7_STATUS + 0x00000038 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO7_CTRL + 0x0000003c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi0_tx + 1 + + + uart1_rts + 2 + + + i2c1_scl + 3 + + + pwm_b_3 + 4 + + + siob_proc_7 + 5 + + + pio0_7 + 6 + + + pio1_7 + 7 + + + pio2_7 + 8 + + + usb_muxing_vbus_detect + 10 + + + uart1_rx + 11 + + + null + 31 + + + + + + + GPIO8_STATUS + 0x00000040 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO8_CTRL + 0x00000044 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_rx + 1 + + + uart1_tx + 2 + + + i2c0_sda + 3 + + + pwm_a_4 + 4 + + + siob_proc_8 + 5 + + + pio0_8 + 6 + + + pio1_8 + 7 + + + pio2_8 + 8 + + + xip_ss_n_1 + 9 + + + usb_muxing_vbus_en + 10 + + + null + 31 + + + + + + + GPIO9_STATUS + 0x00000048 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO9_CTRL + 0x0000004c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_ss_n + 1 + + + uart1_rx + 2 + + + i2c0_scl + 3 + + + pwm_b_4 + 4 + + + siob_proc_9 + 5 + + + pio0_9 + 6 + + + pio1_9 + 7 + + + pio2_9 + 8 + + + usb_muxing_overcurr_detect + 10 + + + null + 31 + + + + + + + GPIO10_STATUS + 0x00000050 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO10_CTRL + 0x00000054 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_sclk + 1 + + + uart1_cts + 2 + + + i2c1_sda + 3 + + + pwm_a_5 + 4 + + + siob_proc_10 + 5 + + + pio0_10 + 6 + + + pio1_10 + 7 + + + pio2_10 + 8 + + + usb_muxing_vbus_detect + 10 + + + uart1_tx + 11 + + + null + 31 + + + + + + + GPIO11_STATUS + 0x00000058 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO11_CTRL + 0x0000005c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_tx + 1 + + + uart1_rts + 2 + + + i2c1_scl + 3 + + + pwm_b_5 + 4 + + + siob_proc_11 + 5 + + + pio0_11 + 6 + + + pio1_11 + 7 + + + pio2_11 + 8 + + + usb_muxing_vbus_en + 10 + + + uart1_rx + 11 + + + null + 31 + + + + + + + GPIO12_STATUS + 0x00000060 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO12_CTRL + 0x00000064 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + hstx_0 + 0 + + + spi1_rx + 1 + + + uart0_tx + 2 + + + i2c0_sda + 3 + + + pwm_a_6 + 4 + + + siob_proc_12 + 5 + + + pio0_12 + 6 + + + pio1_12 + 7 + + + pio2_12 + 8 + + + clocks_gpin_0 + 9 + + + usb_muxing_overcurr_detect + 10 + + + null + 31 + + + + + + + GPIO13_STATUS + 0x00000068 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO13_CTRL + 0x0000006c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + hstx_1 + 0 + + + spi1_ss_n + 1 + + + uart0_rx + 2 + + + i2c0_scl + 3 + + + pwm_b_6 + 4 + + + siob_proc_13 + 5 + + + pio0_13 + 6 + + + pio1_13 + 7 + + + pio2_13 + 8 + + + clocks_gpout_0 + 9 + + + usb_muxing_vbus_detect + 10 + + + null + 31 + + + + + + + GPIO14_STATUS + 0x00000070 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO14_CTRL + 0x00000074 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + hstx_2 + 0 + + + spi1_sclk + 1 + + + uart0_cts + 2 + + + i2c1_sda + 3 + + + pwm_a_7 + 4 + + + siob_proc_14 + 5 + + + pio0_14 + 6 + + + pio1_14 + 7 + + + pio2_14 + 8 + + + clocks_gpin_1 + 9 + + + usb_muxing_vbus_en + 10 + + + uart0_tx + 11 + + + null + 31 + + + + + + + GPIO15_STATUS + 0x00000078 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO15_CTRL + 0x0000007c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + hstx_3 + 0 + + + spi1_tx + 1 + + + uart0_rts + 2 + + + i2c1_scl + 3 + + + pwm_b_7 + 4 + + + siob_proc_15 + 5 + + + pio0_15 + 6 + + + pio1_15 + 7 + + + pio2_15 + 8 + + + clocks_gpout_1 + 9 + + + usb_muxing_overcurr_detect + 10 + + + uart0_rx + 11 + + + null + 31 + + + + + + + GPIO16_STATUS + 0x00000080 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO16_CTRL + 0x00000084 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + hstx_4 + 0 + + + spi0_rx + 1 + + + uart0_tx + 2 + + + i2c0_sda + 3 + + + pwm_a_0 + 4 + + + siob_proc_16 + 5 + + + pio0_16 + 6 + + + pio1_16 + 7 + + + pio2_16 + 8 + + + usb_muxing_vbus_detect + 10 + + + null + 31 + + + + + + + GPIO17_STATUS + 0x00000088 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO17_CTRL + 0x0000008c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + hstx_5 + 0 + + + spi0_ss_n + 1 + + + uart0_rx + 2 + + + i2c0_scl + 3 + + + pwm_b_0 + 4 + + + siob_proc_17 + 5 + + + pio0_17 + 6 + + + pio1_17 + 7 + + + pio2_17 + 8 + + + usb_muxing_vbus_en + 10 + + + null + 31 + + + + + + + GPIO18_STATUS + 0x00000090 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO18_CTRL + 0x00000094 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + hstx_6 + 0 + + + spi0_sclk + 1 + + + uart0_cts + 2 + + + i2c1_sda + 3 + + + pwm_a_1 + 4 + + + siob_proc_18 + 5 + + + pio0_18 + 6 + + + pio1_18 + 7 + + + pio2_18 + 8 + + + usb_muxing_overcurr_detect + 10 + + + uart0_tx + 11 + + + null + 31 + + + + + + + GPIO19_STATUS + 0x00000098 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO19_CTRL + 0x0000009c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + hstx_7 + 0 + + + spi0_tx + 1 + + + uart0_rts + 2 + + + i2c1_scl + 3 + + + pwm_b_1 + 4 + + + siob_proc_19 + 5 + + + pio0_19 + 6 + + + pio1_19 + 7 + + + pio2_19 + 8 + + + xip_ss_n_1 + 9 + + + usb_muxing_vbus_detect + 10 + + + uart0_rx + 11 + + + null + 31 + + + + + + + GPIO20_STATUS + 0x000000a0 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO20_CTRL + 0x000000a4 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi0_rx + 1 + + + uart1_tx + 2 + + + i2c0_sda + 3 + + + pwm_a_2 + 4 + + + siob_proc_20 + 5 + + + pio0_20 + 6 + + + pio1_20 + 7 + + + pio2_20 + 8 + + + clocks_gpin_0 + 9 + + + usb_muxing_vbus_en + 10 + + + null + 31 + + + + + + + GPIO21_STATUS + 0x000000a8 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO21_CTRL + 0x000000ac + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi0_ss_n + 1 + + + uart1_rx + 2 + + + i2c0_scl + 3 + + + pwm_b_2 + 4 + + + siob_proc_21 + 5 + + + pio0_21 + 6 + + + pio1_21 + 7 + + + pio2_21 + 8 + + + clocks_gpout_0 + 9 + + + usb_muxing_overcurr_detect + 10 + + + null + 31 + + + + + + + GPIO22_STATUS + 0x000000b0 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO22_CTRL + 0x000000b4 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi0_sclk + 1 + + + uart1_cts + 2 + + + i2c1_sda + 3 + + + pwm_a_3 + 4 + + + siob_proc_22 + 5 + + + pio0_22 + 6 + + + pio1_22 + 7 + + + pio2_22 + 8 + + + clocks_gpin_1 + 9 + + + usb_muxing_vbus_detect + 10 + + + uart1_tx + 11 + + + null + 31 + + + + + + + GPIO23_STATUS + 0x000000b8 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO23_CTRL + 0x000000bc + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi0_tx + 1 + + + uart1_rts + 2 + + + i2c1_scl + 3 + + + pwm_b_3 + 4 + + + siob_proc_23 + 5 + + + pio0_23 + 6 + + + pio1_23 + 7 + + + pio2_23 + 8 + + + clocks_gpout_1 + 9 + + + usb_muxing_vbus_en + 10 + + + uart1_rx + 11 + + + null + 31 + + + + + + + GPIO24_STATUS + 0x000000c0 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO24_CTRL + 0x000000c4 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_rx + 1 + + + uart1_tx + 2 + + + i2c0_sda + 3 + + + pwm_a_4 + 4 + + + siob_proc_24 + 5 + + + pio0_24 + 6 + + + pio1_24 + 7 + + + pio2_24 + 8 + + + clocks_gpout_2 + 9 + + + usb_muxing_overcurr_detect + 10 + + + null + 31 + + + + + + + GPIO25_STATUS + 0x000000c8 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO25_CTRL + 0x000000cc + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_ss_n + 1 + + + uart1_rx + 2 + + + i2c0_scl + 3 + + + pwm_b_4 + 4 + + + siob_proc_25 + 5 + + + pio0_25 + 6 + + + pio1_25 + 7 + + + pio2_25 + 8 + + + clocks_gpout_3 + 9 + + + usb_muxing_vbus_detect + 10 + + + null + 31 + + + + + + + GPIO26_STATUS + 0x000000d0 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO26_CTRL + 0x000000d4 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_sclk + 1 + + + uart1_cts + 2 + + + i2c1_sda + 3 + + + pwm_a_5 + 4 + + + siob_proc_26 + 5 + + + pio0_26 + 6 + + + pio1_26 + 7 + + + pio2_26 + 8 + + + usb_muxing_vbus_en + 10 + + + uart1_tx + 11 + + + null + 31 + + + + + + + GPIO27_STATUS + 0x000000d8 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO27_CTRL + 0x000000dc + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_tx + 1 + + + uart1_rts + 2 + + + i2c1_scl + 3 + + + pwm_b_5 + 4 + + + siob_proc_27 + 5 + + + pio0_27 + 6 + + + pio1_27 + 7 + + + pio2_27 + 8 + + + usb_muxing_overcurr_detect + 10 + + + uart1_rx + 11 + + + null + 31 + + + + + + + GPIO28_STATUS + 0x000000e0 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO28_CTRL + 0x000000e4 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_rx + 1 + + + uart0_tx + 2 + + + i2c0_sda + 3 + + + pwm_a_6 + 4 + + + siob_proc_28 + 5 + + + pio0_28 + 6 + + + pio1_28 + 7 + + + pio2_28 + 8 + + + usb_muxing_vbus_detect + 10 + + + null + 31 + + + + + + + GPIO29_STATUS + 0x000000e8 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO29_CTRL + 0x000000ec + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_ss_n + 1 + + + uart0_rx + 2 + + + i2c0_scl + 3 + + + pwm_b_6 + 4 + + + siob_proc_29 + 5 + + + pio0_29 + 6 + + + pio1_29 + 7 + + + pio2_29 + 8 + + + usb_muxing_vbus_en + 10 + + + null + 31 + + + + + + + GPIO30_STATUS + 0x000000f0 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO30_CTRL + 0x000000f4 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_sclk + 1 + + + uart0_cts + 2 + + + i2c1_sda + 3 + + + pwm_a_7 + 4 + + + siob_proc_30 + 5 + + + pio0_30 + 6 + + + pio1_30 + 7 + + + pio2_30 + 8 + + + usb_muxing_overcurr_detect + 10 + + + uart0_tx + 11 + + + null + 31 + + + + + + + GPIO31_STATUS + 0x000000f8 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO31_CTRL + 0x000000fc + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_tx + 1 + + + uart0_rts + 2 + + + i2c1_scl + 3 + + + pwm_b_7 + 4 + + + siob_proc_31 + 5 + + + pio0_31 + 6 + + + pio1_31 + 7 + + + pio2_31 + 8 + + + usb_muxing_vbus_detect + 10 + + + uart0_rx + 11 + + + null + 31 + + + + + + + GPIO32_STATUS + 0x00000100 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO32_CTRL + 0x00000104 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi0_rx + 1 + + + uart0_tx + 2 + + + i2c0_sda + 3 + + + pwm_a_8 + 4 + + + siob_proc_32 + 5 + + + pio0_32 + 6 + + + pio1_32 + 7 + + + pio2_32 + 8 + + + usb_muxing_vbus_en + 10 + + + null + 31 + + + + + + + GPIO33_STATUS + 0x00000108 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO33_CTRL + 0x0000010c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi0_ss_n + 1 + + + uart0_rx + 2 + + + i2c0_scl + 3 + + + pwm_b_8 + 4 + + + siob_proc_33 + 5 + + + pio0_33 + 6 + + + pio1_33 + 7 + + + pio2_33 + 8 + + + usb_muxing_overcurr_detect + 10 + + + null + 31 + + + + + + + GPIO34_STATUS + 0x00000110 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO34_CTRL + 0x00000114 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi0_sclk + 1 + + + uart0_cts + 2 + + + i2c1_sda + 3 + + + pwm_a_9 + 4 + + + siob_proc_34 + 5 + + + pio0_34 + 6 + + + pio1_34 + 7 + + + pio2_34 + 8 + + + usb_muxing_vbus_detect + 10 + + + uart0_tx + 11 + + + null + 31 + + + + + + + GPIO35_STATUS + 0x00000118 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO35_CTRL + 0x0000011c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi0_tx + 1 + + + uart0_rts + 2 + + + i2c1_scl + 3 + + + pwm_b_9 + 4 + + + siob_proc_35 + 5 + + + pio0_35 + 6 + + + pio1_35 + 7 + + + pio2_35 + 8 + + + usb_muxing_vbus_en + 10 + + + uart0_rx + 11 + + + null + 31 + + + + + + + GPIO36_STATUS + 0x00000120 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO36_CTRL + 0x00000124 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi0_rx + 1 + + + uart1_tx + 2 + + + i2c0_sda + 3 + + + pwm_a_10 + 4 + + + siob_proc_36 + 5 + + + pio0_36 + 6 + + + pio1_36 + 7 + + + pio2_36 + 8 + + + usb_muxing_overcurr_detect + 10 + + + null + 31 + + + + + + + GPIO37_STATUS + 0x00000128 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO37_CTRL + 0x0000012c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi0_ss_n + 1 + + + uart1_rx + 2 + + + i2c0_scl + 3 + + + pwm_b_10 + 4 + + + siob_proc_37 + 5 + + + pio0_37 + 6 + + + pio1_37 + 7 + + + pio2_37 + 8 + + + usb_muxing_vbus_detect + 10 + + + null + 31 + + + + + + + GPIO38_STATUS + 0x00000130 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO38_CTRL + 0x00000134 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi0_sclk + 1 + + + uart1_cts + 2 + + + i2c1_sda + 3 + + + pwm_a_11 + 4 + + + siob_proc_38 + 5 + + + pio0_38 + 6 + + + pio1_38 + 7 + + + pio2_38 + 8 + + + usb_muxing_vbus_en + 10 + + + uart1_tx + 11 + + + null + 31 + + + + + + + GPIO39_STATUS + 0x00000138 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO39_CTRL + 0x0000013c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi0_tx + 1 + + + uart1_rts + 2 + + + i2c1_scl + 3 + + + pwm_b_11 + 4 + + + siob_proc_39 + 5 + + + pio0_39 + 6 + + + pio1_39 + 7 + + + pio2_39 + 8 + + + usb_muxing_overcurr_detect + 10 + + + uart1_rx + 11 + + + null + 31 + + + + + + + GPIO40_STATUS + 0x00000140 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO40_CTRL + 0x00000144 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_rx + 1 + + + uart1_tx + 2 + + + i2c0_sda + 3 + + + pwm_a_8 + 4 + + + siob_proc_40 + 5 + + + pio0_40 + 6 + + + pio1_40 + 7 + + + pio2_40 + 8 + + + usb_muxing_vbus_detect + 10 + + + null + 31 + + + + + + + GPIO41_STATUS + 0x00000148 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO41_CTRL + 0x0000014c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_ss_n + 1 + + + uart1_rx + 2 + + + i2c0_scl + 3 + + + pwm_b_8 + 4 + + + siob_proc_41 + 5 + + + pio0_41 + 6 + + + pio1_41 + 7 + + + pio2_41 + 8 + + + usb_muxing_vbus_en + 10 + + + null + 31 + + + + + + + GPIO42_STATUS + 0x00000150 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO42_CTRL + 0x00000154 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_sclk + 1 + + + uart1_cts + 2 + + + i2c1_sda + 3 + + + pwm_a_9 + 4 + + + siob_proc_42 + 5 + + + pio0_42 + 6 + + + pio1_42 + 7 + + + pio2_42 + 8 + + + usb_muxing_overcurr_detect + 10 + + + uart1_tx + 11 + + + null + 31 + + + + + + + GPIO43_STATUS + 0x00000158 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO43_CTRL + 0x0000015c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_tx + 1 + + + uart1_rts + 2 + + + i2c1_scl + 3 + + + pwm_b_9 + 4 + + + siob_proc_43 + 5 + + + pio0_43 + 6 + + + pio1_43 + 7 + + + pio2_43 + 8 + + + usb_muxing_vbus_detect + 10 + + + uart1_rx + 11 + + + null + 31 + + + + + + + GPIO44_STATUS + 0x00000160 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO44_CTRL + 0x00000164 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_rx + 1 + + + uart0_tx + 2 + + + i2c0_sda + 3 + + + pwm_a_10 + 4 + + + siob_proc_44 + 5 + + + pio0_44 + 6 + + + pio1_44 + 7 + + + pio2_44 + 8 + + + usb_muxing_vbus_en + 10 + + + null + 31 + + + + + + + GPIO45_STATUS + 0x00000168 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO45_CTRL + 0x0000016c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_ss_n + 1 + + + uart0_rx + 2 + + + i2c0_scl + 3 + + + pwm_b_10 + 4 + + + siob_proc_45 + 5 + + + pio0_45 + 6 + + + pio1_45 + 7 + + + pio2_45 + 8 + + + usb_muxing_overcurr_detect + 10 + + + null + 31 + + + + + + + GPIO46_STATUS + 0x00000170 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO46_CTRL + 0x00000174 + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_sclk + 1 + + + uart0_cts + 2 + + + i2c1_sda + 3 + + + pwm_a_11 + 4 + + + siob_proc_46 + 5 + + + pio0_46 + 6 + + + pio1_46 + 7 + + + pio2_46 + 8 + + + usb_muxing_vbus_detect + 10 + + + uart0_tx + 11 + + + null + 31 + + + + + + + GPIO47_STATUS + 0x00000178 + 0x00000000 + + + IRQTOPROC + interrupt to processors, after override is applied + [26:26] + read-only + + + INFROMPAD + input signal from pad, before filtering and override are applied + [17:17] + read-only + + + OETOPAD + output enable to pad after register override is applied + [13:13] + read-only + + + OUTTOPAD + output signal to pad after register override is applied + [9:9] + read-only + + + + + GPIO47_CTRL + 0x0000017c + 0x0000001f + + + IRQOVER + [29:28] + read-write + + + NORMAL + 0 + don't invert the interrupt + + + INVERT + 1 + invert the interrupt + + + LOW + 2 + drive interrupt low + + + HIGH + 3 + drive interrupt high + + + + + INOVER + [17:16] + read-write + + + NORMAL + 0 + don't invert the peri input + + + INVERT + 1 + invert the peri input + + + LOW + 2 + drive peri input low + + + HIGH + 3 + drive peri input high + + + + + OEOVER + [15:14] + read-write + + + NORMAL + 0 + drive output enable from peripheral signal selected by funcsel + + + INVERT + 1 + drive output enable from inverse of peripheral signal selected by funcsel + + + DISABLE + 2 + disable output + + + ENABLE + 3 + enable output + + + + + OUTOVER + [13:12] + read-write + + + NORMAL + 0 + drive output from peripheral signal selected by funcsel + + + INVERT + 1 + drive output from inverse of peripheral signal selected by funcsel + + + LOW + 2 + drive output low + + + HIGH + 3 + drive output high + + + + + FUNCSEL + 0-31 -> selects pin function according to the gpio table + 31 == NULL + [4:0] + read-write + + + spi1_tx + 1 + + + uart0_rts + 2 + + + i2c1_scl + 3 + + + pwm_b_11 + 4 + + + siob_proc_47 + 5 + + + pio0_47 + 6 + + + pio1_47 + 7 + + + pio2_47 + 8 + + + xip_ss_n_1 + 9 + + + usb_muxing_vbus_en + 10 + + + uart0_rx + 11 + + + null + 31 + + + + + + + IRQSUMMARY_PROC0_SECURE0 + 0x00000200 + 0x00000000 + + + GPIO31 + [31:31] + read-only + + + GPIO30 + [30:30] + read-only + + + GPIO29 + [29:29] + read-only + + + GPIO28 + [28:28] + read-only + + + GPIO27 + [27:27] + read-only + + + GPIO26 + [26:26] + read-only + + + GPIO25 + [25:25] + read-only + + + GPIO24 + [24:24] + read-only + + + GPIO23 + [23:23] + read-only + + + GPIO22 + [22:22] + read-only + + + GPIO21 + [21:21] + read-only + + + GPIO20 + [20:20] + read-only + + + GPIO19 + [19:19] + read-only + + + GPIO18 + [18:18] + read-only + + + GPIO17 + [17:17] + read-only + + + GPIO16 + [16:16] + read-only + + + GPIO15 + [15:15] + read-only + + + GPIO14 + [14:14] + read-only + + + GPIO13 + [13:13] + read-only + + + GPIO12 + [12:12] + read-only + + + GPIO11 + [11:11] + read-only + + + GPIO10 + [10:10] + read-only + + + GPIO9 + [9:9] + read-only + + + GPIO8 + [8:8] + read-only + + + GPIO7 + [7:7] + read-only + + + GPIO6 + [6:6] + read-only + + + GPIO5 + [5:5] + read-only + + + GPIO4 + [4:4] + read-only + + + GPIO3 + [3:3] + read-only + + + GPIO2 + [2:2] + read-only + + + GPIO1 + [1:1] + read-only + + + GPIO0 + [0:0] + read-only + + + + + IRQSUMMARY_PROC0_SECURE1 + 0x00000204 + 0x00000000 + + + GPIO47 + [15:15] + read-only + + + GPIO46 + [14:14] + read-only + + + GPIO45 + [13:13] + read-only + + + GPIO44 + [12:12] + read-only + + + GPIO43 + [11:11] + read-only + + + GPIO42 + [10:10] + read-only + + + GPIO41 + [9:9] + read-only + + + GPIO40 + [8:8] + read-only + + + GPIO39 + [7:7] + read-only + + + GPIO38 + [6:6] + read-only + + + GPIO37 + [5:5] + read-only + + + GPIO36 + [4:4] + read-only + + + GPIO35 + [3:3] + read-only + + + GPIO34 + [2:2] + read-only + + + GPIO33 + [1:1] + read-only + + + GPIO32 + [0:0] + read-only + + + + + IRQSUMMARY_PROC0_NONSECURE0 + 0x00000208 + 0x00000000 + + + GPIO31 + [31:31] + read-only + + + GPIO30 + [30:30] + read-only + + + GPIO29 + [29:29] + read-only + + + GPIO28 + [28:28] + read-only + + + GPIO27 + [27:27] + read-only + + + GPIO26 + [26:26] + read-only + + + GPIO25 + [25:25] + read-only + + + GPIO24 + [24:24] + read-only + + + GPIO23 + [23:23] + read-only + + + GPIO22 + [22:22] + read-only + + + GPIO21 + [21:21] + read-only + + + GPIO20 + [20:20] + read-only + + + GPIO19 + [19:19] + read-only + + + GPIO18 + [18:18] + read-only + + + GPIO17 + [17:17] + read-only + + + GPIO16 + [16:16] + read-only + + + GPIO15 + [15:15] + read-only + + + GPIO14 + [14:14] + read-only + + + GPIO13 + [13:13] + read-only + + + GPIO12 + [12:12] + read-only + + + GPIO11 + [11:11] + read-only + + + GPIO10 + [10:10] + read-only + + + GPIO9 + [9:9] + read-only + + + GPIO8 + [8:8] + read-only + + + GPIO7 + [7:7] + read-only + + + GPIO6 + [6:6] + read-only + + + GPIO5 + [5:5] + read-only + + + GPIO4 + [4:4] + read-only + + + GPIO3 + [3:3] + read-only + + + GPIO2 + [2:2] + read-only + + + GPIO1 + [1:1] + read-only + + + GPIO0 + [0:0] + read-only + + + + + IRQSUMMARY_PROC0_NONSECURE1 + 0x0000020c + 0x00000000 + + + GPIO47 + [15:15] + read-only + + + GPIO46 + [14:14] + read-only + + + GPIO45 + [13:13] + read-only + + + GPIO44 + [12:12] + read-only + + + GPIO43 + [11:11] + read-only + + + GPIO42 + [10:10] + read-only + + + GPIO41 + [9:9] + read-only + + + GPIO40 + [8:8] + read-only + + + GPIO39 + [7:7] + read-only + + + GPIO38 + [6:6] + read-only + + + GPIO37 + [5:5] + read-only + + + GPIO36 + [4:4] + read-only + + + GPIO35 + [3:3] + read-only + + + GPIO34 + [2:2] + read-only + + + GPIO33 + [1:1] + read-only + + + GPIO32 + [0:0] + read-only + + + + + IRQSUMMARY_PROC1_SECURE0 + 0x00000210 + 0x00000000 + + + GPIO31 + [31:31] + read-only + + + GPIO30 + [30:30] + read-only + + + GPIO29 + [29:29] + read-only + + + GPIO28 + [28:28] + read-only + + + GPIO27 + [27:27] + read-only + + + GPIO26 + [26:26] + read-only + + + GPIO25 + [25:25] + read-only + + + GPIO24 + [24:24] + read-only + + + GPIO23 + [23:23] + read-only + + + GPIO22 + [22:22] + read-only + + + GPIO21 + [21:21] + read-only + + + GPIO20 + [20:20] + read-only + + + GPIO19 + [19:19] + read-only + + + GPIO18 + [18:18] + read-only + + + GPIO17 + [17:17] + read-only + + + GPIO16 + [16:16] + read-only + + + GPIO15 + [15:15] + read-only + + + GPIO14 + [14:14] + read-only + + + GPIO13 + [13:13] + read-only + + + GPIO12 + [12:12] + read-only + + + GPIO11 + [11:11] + read-only + + + GPIO10 + [10:10] + read-only + + + GPIO9 + [9:9] + read-only + + + GPIO8 + [8:8] + read-only + + + GPIO7 + [7:7] + read-only + + + GPIO6 + [6:6] + read-only + + + GPIO5 + [5:5] + read-only + + + GPIO4 + [4:4] + read-only + + + GPIO3 + [3:3] + read-only + + + GPIO2 + [2:2] + read-only + + + GPIO1 + [1:1] + read-only + + + GPIO0 + [0:0] + read-only + + + + + IRQSUMMARY_PROC1_SECURE1 + 0x00000214 + 0x00000000 + + + GPIO47 + [15:15] + read-only + + + GPIO46 + [14:14] + read-only + + + GPIO45 + [13:13] + read-only + + + GPIO44 + [12:12] + read-only + + + GPIO43 + [11:11] + read-only + + + GPIO42 + [10:10] + read-only + + + GPIO41 + [9:9] + read-only + + + GPIO40 + [8:8] + read-only + + + GPIO39 + [7:7] + read-only + + + GPIO38 + [6:6] + read-only + + + GPIO37 + [5:5] + read-only + + + GPIO36 + [4:4] + read-only + + + GPIO35 + [3:3] + read-only + + + GPIO34 + [2:2] + read-only + + + GPIO33 + [1:1] + read-only + + + GPIO32 + [0:0] + read-only + + + + + IRQSUMMARY_PROC1_NONSECURE0 + 0x00000218 + 0x00000000 + + + GPIO31 + [31:31] + read-only + + + GPIO30 + [30:30] + read-only + + + GPIO29 + [29:29] + read-only + + + GPIO28 + [28:28] + read-only + + + GPIO27 + [27:27] + read-only + + + GPIO26 + [26:26] + read-only + + + GPIO25 + [25:25] + read-only + + + GPIO24 + [24:24] + read-only + + + GPIO23 + [23:23] + read-only + + + GPIO22 + [22:22] + read-only + + + GPIO21 + [21:21] + read-only + + + GPIO20 + [20:20] + read-only + + + GPIO19 + [19:19] + read-only + + + GPIO18 + [18:18] + read-only + + + GPIO17 + [17:17] + read-only + + + GPIO16 + [16:16] + read-only + + + GPIO15 + [15:15] + read-only + + + GPIO14 + [14:14] + read-only + + + GPIO13 + [13:13] + read-only + + + GPIO12 + [12:12] + read-only + + + GPIO11 + [11:11] + read-only + + + GPIO10 + [10:10] + read-only + + + GPIO9 + [9:9] + read-only + + + GPIO8 + [8:8] + read-only + + + GPIO7 + [7:7] + read-only + + + GPIO6 + [6:6] + read-only + + + GPIO5 + [5:5] + read-only + + + GPIO4 + [4:4] + read-only + + + GPIO3 + [3:3] + read-only + + + GPIO2 + [2:2] + read-only + + + GPIO1 + [1:1] + read-only + + + GPIO0 + [0:0] + read-only + + + + + IRQSUMMARY_PROC1_NONSECURE1 + 0x0000021c + 0x00000000 + + + GPIO47 + [15:15] + read-only + + + GPIO46 + [14:14] + read-only + + + GPIO45 + [13:13] + read-only + + + GPIO44 + [12:12] + read-only + + + GPIO43 + [11:11] + read-only + + + GPIO42 + [10:10] + read-only + + + GPIO41 + [9:9] + read-only + + + GPIO40 + [8:8] + read-only + + + GPIO39 + [7:7] + read-only + + + GPIO38 + [6:6] + read-only + + + GPIO37 + [5:5] + read-only + + + GPIO36 + [4:4] + read-only + + + GPIO35 + [3:3] + read-only + + + GPIO34 + [2:2] + read-only + + + GPIO33 + [1:1] + read-only + + + GPIO32 + [0:0] + read-only + + + + + IRQSUMMARY_DORMANT_WAKE_SECURE0 + 0x00000220 + 0x00000000 + + + GPIO31 + [31:31] + read-only + + + GPIO30 + [30:30] + read-only + + + GPIO29 + [29:29] + read-only + + + GPIO28 + [28:28] + read-only + + + GPIO27 + [27:27] + read-only + + + GPIO26 + [26:26] + read-only + + + GPIO25 + [25:25] + read-only + + + GPIO24 + [24:24] + read-only + + + GPIO23 + [23:23] + read-only + + + GPIO22 + [22:22] + read-only + + + GPIO21 + [21:21] + read-only + + + GPIO20 + [20:20] + read-only + + + GPIO19 + [19:19] + read-only + + + GPIO18 + [18:18] + read-only + + + GPIO17 + [17:17] + read-only + + + GPIO16 + [16:16] + read-only + + + GPIO15 + [15:15] + read-only + + + GPIO14 + [14:14] + read-only + + + GPIO13 + [13:13] + read-only + + + GPIO12 + [12:12] + read-only + + + GPIO11 + [11:11] + read-only + + + GPIO10 + [10:10] + read-only + + + GPIO9 + [9:9] + read-only + + + GPIO8 + [8:8] + read-only + + + GPIO7 + [7:7] + read-only + + + GPIO6 + [6:6] + read-only + + + GPIO5 + [5:5] + read-only + + + GPIO4 + [4:4] + read-only + + + GPIO3 + [3:3] + read-only + + + GPIO2 + [2:2] + read-only + + + GPIO1 + [1:1] + read-only + + + GPIO0 + [0:0] + read-only + + + + + IRQSUMMARY_DORMANT_WAKE_SECURE1 + 0x00000224 + 0x00000000 + + + GPIO47 + [15:15] + read-only + + + GPIO46 + [14:14] + read-only + + + GPIO45 + [13:13] + read-only + + + GPIO44 + [12:12] + read-only + + + GPIO43 + [11:11] + read-only + + + GPIO42 + [10:10] + read-only + + + GPIO41 + [9:9] + read-only + + + GPIO40 + [8:8] + read-only + + + GPIO39 + [7:7] + read-only + + + GPIO38 + [6:6] + read-only + + + GPIO37 + [5:5] + read-only + + + GPIO36 + [4:4] + read-only + + + GPIO35 + [3:3] + read-only + + + GPIO34 + [2:2] + read-only + + + GPIO33 + [1:1] + read-only + + + GPIO32 + [0:0] + read-only + + + + + IRQSUMMARY_DORMANT_WAKE_NONSECURE0 + 0x00000228 + 0x00000000 + + + GPIO31 + [31:31] + read-only + + + GPIO30 + [30:30] + read-only + + + GPIO29 + [29:29] + read-only + + + GPIO28 + [28:28] + read-only + + + GPIO27 + [27:27] + read-only + + + GPIO26 + [26:26] + read-only + + + GPIO25 + [25:25] + read-only + + + GPIO24 + [24:24] + read-only + + + GPIO23 + [23:23] + read-only + + + GPIO22 + [22:22] + read-only + + + GPIO21 + [21:21] + read-only + + + GPIO20 + [20:20] + read-only + + + GPIO19 + [19:19] + read-only + + + GPIO18 + [18:18] + read-only + + + GPIO17 + [17:17] + read-only + + + GPIO16 + [16:16] + read-only + + + GPIO15 + [15:15] + read-only + + + GPIO14 + [14:14] + read-only + + + GPIO13 + [13:13] + read-only + + + GPIO12 + [12:12] + read-only + + + GPIO11 + [11:11] + read-only + + + GPIO10 + [10:10] + read-only + + + GPIO9 + [9:9] + read-only + + + GPIO8 + [8:8] + read-only + + + GPIO7 + [7:7] + read-only + + + GPIO6 + [6:6] + read-only + + + GPIO5 + [5:5] + read-only + + + GPIO4 + [4:4] + read-only + + + GPIO3 + [3:3] + read-only + + + GPIO2 + [2:2] + read-only + + + GPIO1 + [1:1] + read-only + + + GPIO0 + [0:0] + read-only + + + + + IRQSUMMARY_DORMANT_WAKE_NONSECURE1 + 0x0000022c + 0x00000000 + + + GPIO47 + [15:15] + read-only + + + GPIO46 + [14:14] + read-only + + + GPIO45 + [13:13] + read-only + + + GPIO44 + [12:12] + read-only + + + GPIO43 + [11:11] + read-only + + + GPIO42 + [10:10] + read-only + + + GPIO41 + [9:9] + read-only + + + GPIO40 + [8:8] + read-only + + + GPIO39 + [7:7] + read-only + + + GPIO38 + [6:6] + read-only + + + GPIO37 + [5:5] + read-only + + + GPIO36 + [4:4] + read-only + + + GPIO35 + [3:3] + read-only + + + GPIO34 + [2:2] + read-only + + + GPIO33 + [1:1] + read-only + + + GPIO32 + [0:0] + read-only + + + + + INTR0 + 0x00000230 + Raw Interrupts + 0x00000000 + + + GPIO7_EDGE_HIGH + [31:31] + read-write + oneToClear + + + GPIO7_EDGE_LOW + [30:30] + read-write + oneToClear + + + GPIO7_LEVEL_HIGH + [29:29] + read-only + + + GPIO7_LEVEL_LOW + [28:28] + read-only + + + GPIO6_EDGE_HIGH + [27:27] + read-write + oneToClear + + + GPIO6_EDGE_LOW + [26:26] + read-write + oneToClear + + + GPIO6_LEVEL_HIGH + [25:25] + read-only + + + GPIO6_LEVEL_LOW + [24:24] + read-only + + + GPIO5_EDGE_HIGH + [23:23] + read-write + oneToClear + + + GPIO5_EDGE_LOW + [22:22] + read-write + oneToClear + + + GPIO5_LEVEL_HIGH + [21:21] + read-only + + + GPIO5_LEVEL_LOW + [20:20] + read-only + + + GPIO4_EDGE_HIGH + [19:19] + read-write + oneToClear + + + GPIO4_EDGE_LOW + [18:18] + read-write + oneToClear + + + GPIO4_LEVEL_HIGH + [17:17] + read-only + + + GPIO4_LEVEL_LOW + [16:16] + read-only + + + GPIO3_EDGE_HIGH + [15:15] + read-write + oneToClear + + + GPIO3_EDGE_LOW + [14:14] + read-write + oneToClear + + + GPIO3_LEVEL_HIGH + [13:13] + read-only + + + GPIO3_LEVEL_LOW + [12:12] + read-only + + + GPIO2_EDGE_HIGH + [11:11] + read-write + oneToClear + + + GPIO2_EDGE_LOW + [10:10] + read-write + oneToClear + + + GPIO2_LEVEL_HIGH + [9:9] + read-only + + + GPIO2_LEVEL_LOW + [8:8] + read-only + + + GPIO1_EDGE_HIGH + [7:7] + read-write + oneToClear + + + GPIO1_EDGE_LOW + [6:6] + read-write + oneToClear + + + GPIO1_LEVEL_HIGH + [5:5] + read-only + + + GPIO1_LEVEL_LOW + [4:4] + read-only + + + GPIO0_EDGE_HIGH + [3:3] + read-write + oneToClear + + + GPIO0_EDGE_LOW + [2:2] + read-write + oneToClear + + + GPIO0_LEVEL_HIGH + [1:1] + read-only + + + GPIO0_LEVEL_LOW + [0:0] + read-only + + + + + INTR1 + 0x00000234 + Raw Interrupts + 0x00000000 + + + GPIO15_EDGE_HIGH + [31:31] + read-write + oneToClear + + + GPIO15_EDGE_LOW + [30:30] + read-write + oneToClear + + + GPIO15_LEVEL_HIGH + [29:29] + read-only + + + GPIO15_LEVEL_LOW + [28:28] + read-only + + + GPIO14_EDGE_HIGH + [27:27] + read-write + oneToClear + + + GPIO14_EDGE_LOW + [26:26] + read-write + oneToClear + + + GPIO14_LEVEL_HIGH + [25:25] + read-only + + + GPIO14_LEVEL_LOW + [24:24] + read-only + + + GPIO13_EDGE_HIGH + [23:23] + read-write + oneToClear + + + GPIO13_EDGE_LOW + [22:22] + read-write + oneToClear + + + GPIO13_LEVEL_HIGH + [21:21] + read-only + + + GPIO13_LEVEL_LOW + [20:20] + read-only + + + GPIO12_EDGE_HIGH + [19:19] + read-write + oneToClear + + + GPIO12_EDGE_LOW + [18:18] + read-write + oneToClear + + + GPIO12_LEVEL_HIGH + [17:17] + read-only + + + GPIO12_LEVEL_LOW + [16:16] + read-only + + + GPIO11_EDGE_HIGH + [15:15] + read-write + oneToClear + + + GPIO11_EDGE_LOW + [14:14] + read-write + oneToClear + + + GPIO11_LEVEL_HIGH + [13:13] + read-only + + + GPIO11_LEVEL_LOW + [12:12] + read-only + + + GPIO10_EDGE_HIGH + [11:11] + read-write + oneToClear + + + GPIO10_EDGE_LOW + [10:10] + read-write + oneToClear + + + GPIO10_LEVEL_HIGH + [9:9] + read-only + + + GPIO10_LEVEL_LOW + [8:8] + read-only + + + GPIO9_EDGE_HIGH + [7:7] + read-write + oneToClear + + + GPIO9_EDGE_LOW + [6:6] + read-write + oneToClear + + + GPIO9_LEVEL_HIGH + [5:5] + read-only + + + GPIO9_LEVEL_LOW + [4:4] + read-only + + + GPIO8_EDGE_HIGH + [3:3] + read-write + oneToClear + + + GPIO8_EDGE_LOW + [2:2] + read-write + oneToClear + + + GPIO8_LEVEL_HIGH + [1:1] + read-only + + + GPIO8_LEVEL_LOW + [0:0] + read-only + + + + + INTR2 + 0x00000238 + Raw Interrupts + 0x00000000 + + + GPIO23_EDGE_HIGH + [31:31] + read-write + oneToClear + + + GPIO23_EDGE_LOW + [30:30] + read-write + oneToClear + + + GPIO23_LEVEL_HIGH + [29:29] + read-only + + + GPIO23_LEVEL_LOW + [28:28] + read-only + + + GPIO22_EDGE_HIGH + [27:27] + read-write + oneToClear + + + GPIO22_EDGE_LOW + [26:26] + read-write + oneToClear + + + GPIO22_LEVEL_HIGH + [25:25] + read-only + + + GPIO22_LEVEL_LOW + [24:24] + read-only + + + GPIO21_EDGE_HIGH + [23:23] + read-write + oneToClear + + + GPIO21_EDGE_LOW + [22:22] + read-write + oneToClear + + + GPIO21_LEVEL_HIGH + [21:21] + read-only + + + GPIO21_LEVEL_LOW + [20:20] + read-only + + + GPIO20_EDGE_HIGH + [19:19] + read-write + oneToClear + + + GPIO20_EDGE_LOW + [18:18] + read-write + oneToClear + + + GPIO20_LEVEL_HIGH + [17:17] + read-only + + + GPIO20_LEVEL_LOW + [16:16] + read-only + + + GPIO19_EDGE_HIGH + [15:15] + read-write + oneToClear + + + GPIO19_EDGE_LOW + [14:14] + read-write + oneToClear + + + GPIO19_LEVEL_HIGH + [13:13] + read-only + + + GPIO19_LEVEL_LOW + [12:12] + read-only + + + GPIO18_EDGE_HIGH + [11:11] + read-write + oneToClear + + + GPIO18_EDGE_LOW + [10:10] + read-write + oneToClear + + + GPIO18_LEVEL_HIGH + [9:9] + read-only + + + GPIO18_LEVEL_LOW + [8:8] + read-only + + + GPIO17_EDGE_HIGH + [7:7] + read-write + oneToClear + + + GPIO17_EDGE_LOW + [6:6] + read-write + oneToClear + + + GPIO17_LEVEL_HIGH + [5:5] + read-only + + + GPIO17_LEVEL_LOW + [4:4] + read-only + + + GPIO16_EDGE_HIGH + [3:3] + read-write + oneToClear + + + GPIO16_EDGE_LOW + [2:2] + read-write + oneToClear + + + GPIO16_LEVEL_HIGH + [1:1] + read-only + + + GPIO16_LEVEL_LOW + [0:0] + read-only + + + + + INTR3 + 0x0000023c + Raw Interrupts + 0x00000000 + + + GPIO31_EDGE_HIGH + [31:31] + read-write + oneToClear + + + GPIO31_EDGE_LOW + [30:30] + read-write + oneToClear + + + GPIO31_LEVEL_HIGH + [29:29] + read-only + + + GPIO31_LEVEL_LOW + [28:28] + read-only + + + GPIO30_EDGE_HIGH + [27:27] + read-write + oneToClear + + + GPIO30_EDGE_LOW + [26:26] + read-write + oneToClear + + + GPIO30_LEVEL_HIGH + [25:25] + read-only + + + GPIO30_LEVEL_LOW + [24:24] + read-only + + + GPIO29_EDGE_HIGH + [23:23] + read-write + oneToClear + + + GPIO29_EDGE_LOW + [22:22] + read-write + oneToClear + + + GPIO29_LEVEL_HIGH + [21:21] + read-only + + + GPIO29_LEVEL_LOW + [20:20] + read-only + + + GPIO28_EDGE_HIGH + [19:19] + read-write + oneToClear + + + GPIO28_EDGE_LOW + [18:18] + read-write + oneToClear + + + GPIO28_LEVEL_HIGH + [17:17] + read-only + + + GPIO28_LEVEL_LOW + [16:16] + read-only + + + GPIO27_EDGE_HIGH + [15:15] + read-write + oneToClear + + + GPIO27_EDGE_LOW + [14:14] + read-write + oneToClear + + + GPIO27_LEVEL_HIGH + [13:13] + read-only + + + GPIO27_LEVEL_LOW + [12:12] + read-only + + + GPIO26_EDGE_HIGH + [11:11] + read-write + oneToClear + + + GPIO26_EDGE_LOW + [10:10] + read-write + oneToClear + + + GPIO26_LEVEL_HIGH + [9:9] + read-only + + + GPIO26_LEVEL_LOW + [8:8] + read-only + + + GPIO25_EDGE_HIGH + [7:7] + read-write + oneToClear + + + GPIO25_EDGE_LOW + [6:6] + read-write + oneToClear + + + GPIO25_LEVEL_HIGH + [5:5] + read-only + + + GPIO25_LEVEL_LOW + [4:4] + read-only + + + GPIO24_EDGE_HIGH + [3:3] + read-write + oneToClear + + + GPIO24_EDGE_LOW + [2:2] + read-write + oneToClear + + + GPIO24_LEVEL_HIGH + [1:1] + read-only + + + GPIO24_LEVEL_LOW + [0:0] + read-only + + + + + INTR4 + 0x00000240 + Raw Interrupts + 0x00000000 + + + GPIO39_EDGE_HIGH + [31:31] + read-write + oneToClear + + + GPIO39_EDGE_LOW + [30:30] + read-write + oneToClear + + + GPIO39_LEVEL_HIGH + [29:29] + read-only + + + GPIO39_LEVEL_LOW + [28:28] + read-only + + + GPIO38_EDGE_HIGH + [27:27] + read-write + oneToClear + + + GPIO38_EDGE_LOW + [26:26] + read-write + oneToClear + + + GPIO38_LEVEL_HIGH + [25:25] + read-only + + + GPIO38_LEVEL_LOW + [24:24] + read-only + + + GPIO37_EDGE_HIGH + [23:23] + read-write + oneToClear + + + GPIO37_EDGE_LOW + [22:22] + read-write + oneToClear + + + GPIO37_LEVEL_HIGH + [21:21] + read-only + + + GPIO37_LEVEL_LOW + [20:20] + read-only + + + GPIO36_EDGE_HIGH + [19:19] + read-write + oneToClear + + + GPIO36_EDGE_LOW + [18:18] + read-write + oneToClear + + + GPIO36_LEVEL_HIGH + [17:17] + read-only + + + GPIO36_LEVEL_LOW + [16:16] + read-only + + + GPIO35_EDGE_HIGH + [15:15] + read-write + oneToClear + + + GPIO35_EDGE_LOW + [14:14] + read-write + oneToClear + + + GPIO35_LEVEL_HIGH + [13:13] + read-only + + + GPIO35_LEVEL_LOW + [12:12] + read-only + + + GPIO34_EDGE_HIGH + [11:11] + read-write + oneToClear + + + GPIO34_EDGE_LOW + [10:10] + read-write + oneToClear + + + GPIO34_LEVEL_HIGH + [9:9] + read-only + + + GPIO34_LEVEL_LOW + [8:8] + read-only + + + GPIO33_EDGE_HIGH + [7:7] + read-write + oneToClear + + + GPIO33_EDGE_LOW + [6:6] + read-write + oneToClear + + + GPIO33_LEVEL_HIGH + [5:5] + read-only + + + GPIO33_LEVEL_LOW + [4:4] + read-only + + + GPIO32_EDGE_HIGH + [3:3] + read-write + oneToClear + + + GPIO32_EDGE_LOW + [2:2] + read-write + oneToClear + + + GPIO32_LEVEL_HIGH + [1:1] + read-only + + + GPIO32_LEVEL_LOW + [0:0] + read-only + + + + + INTR5 + 0x00000244 + Raw Interrupts + 0x00000000 + + + GPIO47_EDGE_HIGH + [31:31] + read-write + oneToClear + + + GPIO47_EDGE_LOW + [30:30] + read-write + oneToClear + + + GPIO47_LEVEL_HIGH + [29:29] + read-only + + + GPIO47_LEVEL_LOW + [28:28] + read-only + + + GPIO46_EDGE_HIGH + [27:27] + read-write + oneToClear + + + GPIO46_EDGE_LOW + [26:26] + read-write + oneToClear + + + GPIO46_LEVEL_HIGH + [25:25] + read-only + + + GPIO46_LEVEL_LOW + [24:24] + read-only + + + GPIO45_EDGE_HIGH + [23:23] + read-write + oneToClear + + + GPIO45_EDGE_LOW + [22:22] + read-write + oneToClear + + + GPIO45_LEVEL_HIGH + [21:21] + read-only + + + GPIO45_LEVEL_LOW + [20:20] + read-only + + + GPIO44_EDGE_HIGH + [19:19] + read-write + oneToClear + + + GPIO44_EDGE_LOW + [18:18] + read-write + oneToClear + + + GPIO44_LEVEL_HIGH + [17:17] + read-only + + + GPIO44_LEVEL_LOW + [16:16] + read-only + + + GPIO43_EDGE_HIGH + [15:15] + read-write + oneToClear + + + GPIO43_EDGE_LOW + [14:14] + read-write + oneToClear + + + GPIO43_LEVEL_HIGH + [13:13] + read-only + + + GPIO43_LEVEL_LOW + [12:12] + read-only + + + GPIO42_EDGE_HIGH + [11:11] + read-write + oneToClear + + + GPIO42_EDGE_LOW + [10:10] + read-write + oneToClear + + + GPIO42_LEVEL_HIGH + [9:9] + read-only + + + GPIO42_LEVEL_LOW + [8:8] + read-only + + + GPIO41_EDGE_HIGH + [7:7] + read-write + oneToClear + + + GPIO41_EDGE_LOW + [6:6] + read-write + oneToClear + + + GPIO41_LEVEL_HIGH + [5:5] + read-only + + + GPIO41_LEVEL_LOW + [4:4] + read-only + + + GPIO40_EDGE_HIGH + [3:3] + read-write + oneToClear + + + GPIO40_EDGE_LOW + [2:2] + read-write + oneToClear + + + GPIO40_LEVEL_HIGH + [1:1] + read-only + + + GPIO40_LEVEL_LOW + [0:0] + read-only + + + + + PROC0_INTE0 + 0x00000248 + Interrupt Enable for proc0 + 0x00000000 + + + GPIO7_EDGE_HIGH + [31:31] + read-write + + + GPIO7_EDGE_LOW + [30:30] + read-write + + + GPIO7_LEVEL_HIGH + [29:29] + read-write + + + GPIO7_LEVEL_LOW + [28:28] + read-write + + + GPIO6_EDGE_HIGH + [27:27] + read-write + + + GPIO6_EDGE_LOW + [26:26] + read-write + + + GPIO6_LEVEL_HIGH + [25:25] + read-write + + + GPIO6_LEVEL_LOW + [24:24] + read-write + + + GPIO5_EDGE_HIGH + [23:23] + read-write + + + GPIO5_EDGE_LOW + [22:22] + read-write + + + GPIO5_LEVEL_HIGH + [21:21] + read-write + + + GPIO5_LEVEL_LOW + [20:20] + read-write + + + GPIO4_EDGE_HIGH + [19:19] + read-write + + + GPIO4_EDGE_LOW + [18:18] + read-write + + + GPIO4_LEVEL_HIGH + [17:17] + read-write + + + GPIO4_LEVEL_LOW + [16:16] + read-write + + + GPIO3_EDGE_HIGH + [15:15] + read-write + + + GPIO3_EDGE_LOW + [14:14] + read-write + + + GPIO3_LEVEL_HIGH + [13:13] + read-write + + + GPIO3_LEVEL_LOW + [12:12] + read-write + + + GPIO2_EDGE_HIGH + [11:11] + read-write + + + GPIO2_EDGE_LOW + [10:10] + read-write + + + GPIO2_LEVEL_HIGH + [9:9] + read-write + + + GPIO2_LEVEL_LOW + [8:8] + read-write + + + GPIO1_EDGE_HIGH + [7:7] + read-write + + + GPIO1_EDGE_LOW + [6:6] + read-write + + + GPIO1_LEVEL_HIGH + [5:5] + read-write + + + GPIO1_LEVEL_LOW + [4:4] + read-write + + + GPIO0_EDGE_HIGH + [3:3] + read-write + + + GPIO0_EDGE_LOW + [2:2] + read-write + + + GPIO0_LEVEL_HIGH + [1:1] + read-write + + + GPIO0_LEVEL_LOW + [0:0] + read-write + + + + + PROC0_INTE1 + 0x0000024c + Interrupt Enable for proc0 + 0x00000000 + + + GPIO15_EDGE_HIGH + [31:31] + read-write + + + GPIO15_EDGE_LOW + [30:30] + read-write + + + GPIO15_LEVEL_HIGH + [29:29] + read-write + + + GPIO15_LEVEL_LOW + [28:28] + read-write + + + GPIO14_EDGE_HIGH + [27:27] + read-write + + + GPIO14_EDGE_LOW + [26:26] + read-write + + + GPIO14_LEVEL_HIGH + [25:25] + read-write + + + GPIO14_LEVEL_LOW + [24:24] + read-write + + + GPIO13_EDGE_HIGH + [23:23] + read-write + + + GPIO13_EDGE_LOW + [22:22] + read-write + + + GPIO13_LEVEL_HIGH + [21:21] + read-write + + + GPIO13_LEVEL_LOW + [20:20] + read-write + + + GPIO12_EDGE_HIGH + [19:19] + read-write + + + GPIO12_EDGE_LOW + [18:18] + read-write + + + GPIO12_LEVEL_HIGH + [17:17] + read-write + + + GPIO12_LEVEL_LOW + [16:16] + read-write + + + GPIO11_EDGE_HIGH + [15:15] + read-write + + + GPIO11_EDGE_LOW + [14:14] + read-write + + + GPIO11_LEVEL_HIGH + [13:13] + read-write + + + GPIO11_LEVEL_LOW + [12:12] + read-write + + + GPIO10_EDGE_HIGH + [11:11] + read-write + + + GPIO10_EDGE_LOW + [10:10] + read-write + + + GPIO10_LEVEL_HIGH + [9:9] + read-write + + + GPIO10_LEVEL_LOW + [8:8] + read-write + + + GPIO9_EDGE_HIGH + [7:7] + read-write + + + GPIO9_EDGE_LOW + [6:6] + read-write + + + GPIO9_LEVEL_HIGH + [5:5] + read-write + + + GPIO9_LEVEL_LOW + [4:4] + read-write + + + GPIO8_EDGE_HIGH + [3:3] + read-write + + + GPIO8_EDGE_LOW + [2:2] + read-write + + + GPIO8_LEVEL_HIGH + [1:1] + read-write + + + GPIO8_LEVEL_LOW + [0:0] + read-write + + + + + PROC0_INTE2 + 0x00000250 + Interrupt Enable for proc0 + 0x00000000 + + + GPIO23_EDGE_HIGH + [31:31] + read-write + + + GPIO23_EDGE_LOW + [30:30] + read-write + + + GPIO23_LEVEL_HIGH + [29:29] + read-write + + + GPIO23_LEVEL_LOW + [28:28] + read-write + + + GPIO22_EDGE_HIGH + [27:27] + read-write + + + GPIO22_EDGE_LOW + [26:26] + read-write + + + GPIO22_LEVEL_HIGH + [25:25] + read-write + + + GPIO22_LEVEL_LOW + [24:24] + read-write + + + GPIO21_EDGE_HIGH + [23:23] + read-write + + + GPIO21_EDGE_LOW + [22:22] + read-write + + + GPIO21_LEVEL_HIGH + [21:21] + read-write + + + GPIO21_LEVEL_LOW + [20:20] + read-write + + + GPIO20_EDGE_HIGH + [19:19] + read-write + + + GPIO20_EDGE_LOW + [18:18] + read-write + + + GPIO20_LEVEL_HIGH + [17:17] + read-write + + + GPIO20_LEVEL_LOW + [16:16] + read-write + + + GPIO19_EDGE_HIGH + [15:15] + read-write + + + GPIO19_EDGE_LOW + [14:14] + read-write + + + GPIO19_LEVEL_HIGH + [13:13] + read-write + + + GPIO19_LEVEL_LOW + [12:12] + read-write + + + GPIO18_EDGE_HIGH + [11:11] + read-write + + + GPIO18_EDGE_LOW + [10:10] + read-write + + + GPIO18_LEVEL_HIGH + [9:9] + read-write + + + GPIO18_LEVEL_LOW + [8:8] + read-write + + + GPIO17_EDGE_HIGH + [7:7] + read-write + + + GPIO17_EDGE_LOW + [6:6] + read-write + + + GPIO17_LEVEL_HIGH + [5:5] + read-write + + + GPIO17_LEVEL_LOW + [4:4] + read-write + + + GPIO16_EDGE_HIGH + [3:3] + read-write + + + GPIO16_EDGE_LOW + [2:2] + read-write + + + GPIO16_LEVEL_HIGH + [1:1] + read-write + + + GPIO16_LEVEL_LOW + [0:0] + read-write + + + + + PROC0_INTE3 + 0x00000254 + Interrupt Enable for proc0 + 0x00000000 + + + GPIO31_EDGE_HIGH + [31:31] + read-write + + + GPIO31_EDGE_LOW + [30:30] + read-write + + + GPIO31_LEVEL_HIGH + [29:29] + read-write + + + GPIO31_LEVEL_LOW + [28:28] + read-write + + + GPIO30_EDGE_HIGH + [27:27] + read-write + + + GPIO30_EDGE_LOW + [26:26] + read-write + + + GPIO30_LEVEL_HIGH + [25:25] + read-write + + + GPIO30_LEVEL_LOW + [24:24] + read-write + + + GPIO29_EDGE_HIGH + [23:23] + read-write + + + GPIO29_EDGE_LOW + [22:22] + read-write + + + GPIO29_LEVEL_HIGH + [21:21] + read-write + + + GPIO29_LEVEL_LOW + [20:20] + read-write + + + GPIO28_EDGE_HIGH + [19:19] + read-write + + + GPIO28_EDGE_LOW + [18:18] + read-write + + + GPIO28_LEVEL_HIGH + [17:17] + read-write + + + GPIO28_LEVEL_LOW + [16:16] + read-write + + + GPIO27_EDGE_HIGH + [15:15] + read-write + + + GPIO27_EDGE_LOW + [14:14] + read-write + + + GPIO27_LEVEL_HIGH + [13:13] + read-write + + + GPIO27_LEVEL_LOW + [12:12] + read-write + + + GPIO26_EDGE_HIGH + [11:11] + read-write + + + GPIO26_EDGE_LOW + [10:10] + read-write + + + GPIO26_LEVEL_HIGH + [9:9] + read-write + + + GPIO26_LEVEL_LOW + [8:8] + read-write + + + GPIO25_EDGE_HIGH + [7:7] + read-write + + + GPIO25_EDGE_LOW + [6:6] + read-write + + + GPIO25_LEVEL_HIGH + [5:5] + read-write + + + GPIO25_LEVEL_LOW + [4:4] + read-write + + + GPIO24_EDGE_HIGH + [3:3] + read-write + + + GPIO24_EDGE_LOW + [2:2] + read-write + + + GPIO24_LEVEL_HIGH + [1:1] + read-write + + + GPIO24_LEVEL_LOW + [0:0] + read-write + + + + + PROC0_INTE4 + 0x00000258 + Interrupt Enable for proc0 + 0x00000000 + + + GPIO39_EDGE_HIGH + [31:31] + read-write + + + GPIO39_EDGE_LOW + [30:30] + read-write + + + GPIO39_LEVEL_HIGH + [29:29] + read-write + + + GPIO39_LEVEL_LOW + [28:28] + read-write + + + GPIO38_EDGE_HIGH + [27:27] + read-write + + + GPIO38_EDGE_LOW + [26:26] + read-write + + + GPIO38_LEVEL_HIGH + [25:25] + read-write + + + GPIO38_LEVEL_LOW + [24:24] + read-write + + + GPIO37_EDGE_HIGH + [23:23] + read-write + + + GPIO37_EDGE_LOW + [22:22] + read-write + + + GPIO37_LEVEL_HIGH + [21:21] + read-write + + + GPIO37_LEVEL_LOW + [20:20] + read-write + + + GPIO36_EDGE_HIGH + [19:19] + read-write + + + GPIO36_EDGE_LOW + [18:18] + read-write + + + GPIO36_LEVEL_HIGH + [17:17] + read-write + + + GPIO36_LEVEL_LOW + [16:16] + read-write + + + GPIO35_EDGE_HIGH + [15:15] + read-write + + + GPIO35_EDGE_LOW + [14:14] + read-write + + + GPIO35_LEVEL_HIGH + [13:13] + read-write + + + GPIO35_LEVEL_LOW + [12:12] + read-write + + + GPIO34_EDGE_HIGH + [11:11] + read-write + + + GPIO34_EDGE_LOW + [10:10] + read-write + + + GPIO34_LEVEL_HIGH + [9:9] + read-write + + + GPIO34_LEVEL_LOW + [8:8] + read-write + + + GPIO33_EDGE_HIGH + [7:7] + read-write + + + GPIO33_EDGE_LOW + [6:6] + read-write + + + GPIO33_LEVEL_HIGH + [5:5] + read-write + + + GPIO33_LEVEL_LOW + [4:4] + read-write + + + GPIO32_EDGE_HIGH + [3:3] + read-write + + + GPIO32_EDGE_LOW + [2:2] + read-write + + + GPIO32_LEVEL_HIGH + [1:1] + read-write + + + GPIO32_LEVEL_LOW + [0:0] + read-write + + + + + PROC0_INTE5 + 0x0000025c + Interrupt Enable for proc0 + 0x00000000 + + + GPIO47_EDGE_HIGH + [31:31] + read-write + + + GPIO47_EDGE_LOW + [30:30] + read-write + + + GPIO47_LEVEL_HIGH + [29:29] + read-write + + + GPIO47_LEVEL_LOW + [28:28] + read-write + + + GPIO46_EDGE_HIGH + [27:27] + read-write + + + GPIO46_EDGE_LOW + [26:26] + read-write + + + GPIO46_LEVEL_HIGH + [25:25] + read-write + + + GPIO46_LEVEL_LOW + [24:24] + read-write + + + GPIO45_EDGE_HIGH + [23:23] + read-write + + + GPIO45_EDGE_LOW + [22:22] + read-write + + + GPIO45_LEVEL_HIGH + [21:21] + read-write + + + GPIO45_LEVEL_LOW + [20:20] + read-write + + + GPIO44_EDGE_HIGH + [19:19] + read-write + + + GPIO44_EDGE_LOW + [18:18] + read-write + + + GPIO44_LEVEL_HIGH + [17:17] + read-write + + + GPIO44_LEVEL_LOW + [16:16] + read-write + + + GPIO43_EDGE_HIGH + [15:15] + read-write + + + GPIO43_EDGE_LOW + [14:14] + read-write + + + GPIO43_LEVEL_HIGH + [13:13] + read-write + + + GPIO43_LEVEL_LOW + [12:12] + read-write + + + GPIO42_EDGE_HIGH + [11:11] + read-write + + + GPIO42_EDGE_LOW + [10:10] + read-write + + + GPIO42_LEVEL_HIGH + [9:9] + read-write + + + GPIO42_LEVEL_LOW + [8:8] + read-write + + + GPIO41_EDGE_HIGH + [7:7] + read-write + + + GPIO41_EDGE_LOW + [6:6] + read-write + + + GPIO41_LEVEL_HIGH + [5:5] + read-write + + + GPIO41_LEVEL_LOW + [4:4] + read-write + + + GPIO40_EDGE_HIGH + [3:3] + read-write + + + GPIO40_EDGE_LOW + [2:2] + read-write + + + GPIO40_LEVEL_HIGH + [1:1] + read-write + + + GPIO40_LEVEL_LOW + [0:0] + read-write + + + + + PROC0_INTF0 + 0x00000260 + Interrupt Force for proc0 + 0x00000000 + + + GPIO7_EDGE_HIGH + [31:31] + read-write + + + GPIO7_EDGE_LOW + [30:30] + read-write + + + GPIO7_LEVEL_HIGH + [29:29] + read-write + + + GPIO7_LEVEL_LOW + [28:28] + read-write + + + GPIO6_EDGE_HIGH + [27:27] + read-write + + + GPIO6_EDGE_LOW + [26:26] + read-write + + + GPIO6_LEVEL_HIGH + [25:25] + read-write + + + GPIO6_LEVEL_LOW + [24:24] + read-write + + + GPIO5_EDGE_HIGH + [23:23] + read-write + + + GPIO5_EDGE_LOW + [22:22] + read-write + + + GPIO5_LEVEL_HIGH + [21:21] + read-write + + + GPIO5_LEVEL_LOW + [20:20] + read-write + + + GPIO4_EDGE_HIGH + [19:19] + read-write + + + GPIO4_EDGE_LOW + [18:18] + read-write + + + GPIO4_LEVEL_HIGH + [17:17] + read-write + + + GPIO4_LEVEL_LOW + [16:16] + read-write + + + GPIO3_EDGE_HIGH + [15:15] + read-write + + + GPIO3_EDGE_LOW + [14:14] + read-write + + + GPIO3_LEVEL_HIGH + [13:13] + read-write + + + GPIO3_LEVEL_LOW + [12:12] + read-write + + + GPIO2_EDGE_HIGH + [11:11] + read-write + + + GPIO2_EDGE_LOW + [10:10] + read-write + + + GPIO2_LEVEL_HIGH + [9:9] + read-write + + + GPIO2_LEVEL_LOW + [8:8] + read-write + + + GPIO1_EDGE_HIGH + [7:7] + read-write + + + GPIO1_EDGE_LOW + [6:6] + read-write + + + GPIO1_LEVEL_HIGH + [5:5] + read-write + + + GPIO1_LEVEL_LOW + [4:4] + read-write + + + GPIO0_EDGE_HIGH + [3:3] + read-write + + + GPIO0_EDGE_LOW + [2:2] + read-write + + + GPIO0_LEVEL_HIGH + [1:1] + read-write + + + GPIO0_LEVEL_LOW + [0:0] + read-write + + + + + PROC0_INTF1 + 0x00000264 + Interrupt Force for proc0 + 0x00000000 + + + GPIO15_EDGE_HIGH + [31:31] + read-write + + + GPIO15_EDGE_LOW + [30:30] + read-write + + + GPIO15_LEVEL_HIGH + [29:29] + read-write + + + GPIO15_LEVEL_LOW + [28:28] + read-write + + + GPIO14_EDGE_HIGH + [27:27] + read-write + + + GPIO14_EDGE_LOW + [26:26] + read-write + + + GPIO14_LEVEL_HIGH + [25:25] + read-write + + + GPIO14_LEVEL_LOW + [24:24] + read-write + + + GPIO13_EDGE_HIGH + [23:23] + read-write + + + GPIO13_EDGE_LOW + [22:22] + read-write + + + GPIO13_LEVEL_HIGH + [21:21] + read-write + + + GPIO13_LEVEL_LOW + [20:20] + read-write + + + GPIO12_EDGE_HIGH + [19:19] + read-write + + + GPIO12_EDGE_LOW + [18:18] + read-write + + + GPIO12_LEVEL_HIGH + [17:17] + read-write + + + GPIO12_LEVEL_LOW + [16:16] + read-write + + + GPIO11_EDGE_HIGH + [15:15] + read-write + + + GPIO11_EDGE_LOW + [14:14] + read-write + + + GPIO11_LEVEL_HIGH + [13:13] + read-write + + + GPIO11_LEVEL_LOW + [12:12] + read-write + + + GPIO10_EDGE_HIGH + [11:11] + read-write + + + GPIO10_EDGE_LOW + [10:10] + read-write + + + GPIO10_LEVEL_HIGH + [9:9] + read-write + + + GPIO10_LEVEL_LOW + [8:8] + read-write + + + GPIO9_EDGE_HIGH + [7:7] + read-write + + + GPIO9_EDGE_LOW + [6:6] + read-write + + + GPIO9_LEVEL_HIGH + [5:5] + read-write + + + GPIO9_LEVEL_LOW + [4:4] + read-write + + + GPIO8_EDGE_HIGH + [3:3] + read-write + + + GPIO8_EDGE_LOW + [2:2] + read-write + + + GPIO8_LEVEL_HIGH + [1:1] + read-write + + + GPIO8_LEVEL_LOW + [0:0] + read-write + + + + + PROC0_INTF2 + 0x00000268 + Interrupt Force for proc0 + 0x00000000 + + + GPIO23_EDGE_HIGH + [31:31] + read-write + + + GPIO23_EDGE_LOW + [30:30] + read-write + + + GPIO23_LEVEL_HIGH + [29:29] + read-write + + + GPIO23_LEVEL_LOW + [28:28] + read-write + + + GPIO22_EDGE_HIGH + [27:27] + read-write + + + GPIO22_EDGE_LOW + [26:26] + read-write + + + GPIO22_LEVEL_HIGH + [25:25] + read-write + + + GPIO22_LEVEL_LOW + [24:24] + read-write + + + GPIO21_EDGE_HIGH + [23:23] + read-write + + + GPIO21_EDGE_LOW + [22:22] + read-write + + + GPIO21_LEVEL_HIGH + [21:21] + read-write + + + GPIO21_LEVEL_LOW + [20:20] + read-write + + + GPIO20_EDGE_HIGH + [19:19] + read-write + + + GPIO20_EDGE_LOW + [18:18] + read-write + + + GPIO20_LEVEL_HIGH + [17:17] + read-write + + + GPIO20_LEVEL_LOW + [16:16] + read-write + + + GPIO19_EDGE_HIGH + [15:15] + read-write + + + GPIO19_EDGE_LOW + [14:14] + read-write + + + GPIO19_LEVEL_HIGH + [13:13] + read-write + + + GPIO19_LEVEL_LOW + [12:12] + read-write + + + GPIO18_EDGE_HIGH + [11:11] + read-write + + + GPIO18_EDGE_LOW + [10:10] + read-write + + + GPIO18_LEVEL_HIGH + [9:9] + read-write + + + GPIO18_LEVEL_LOW + [8:8] + read-write + + + GPIO17_EDGE_HIGH + [7:7] + read-write + + + GPIO17_EDGE_LOW + [6:6] + read-write + + + GPIO17_LEVEL_HIGH + [5:5] + read-write + + + GPIO17_LEVEL_LOW + [4:4] + read-write + + + GPIO16_EDGE_HIGH + [3:3] + read-write + + + GPIO16_EDGE_LOW + [2:2] + read-write + + + GPIO16_LEVEL_HIGH + [1:1] + read-write + + + GPIO16_LEVEL_LOW + [0:0] + read-write + + + + + PROC0_INTF3 + 0x0000026c + Interrupt Force for proc0 + 0x00000000 + + + GPIO31_EDGE_HIGH + [31:31] + read-write + + + GPIO31_EDGE_LOW + [30:30] + read-write + + + GPIO31_LEVEL_HIGH + [29:29] + read-write + + + GPIO31_LEVEL_LOW + [28:28] + read-write + + + GPIO30_EDGE_HIGH + [27:27] + read-write + + + GPIO30_EDGE_LOW + [26:26] + read-write + + + GPIO30_LEVEL_HIGH + [25:25] + read-write + + + GPIO30_LEVEL_LOW + [24:24] + read-write + + + GPIO29_EDGE_HIGH + [23:23] + read-write + + + GPIO29_EDGE_LOW + [22:22] + read-write + + + GPIO29_LEVEL_HIGH + [21:21] + read-write + + + GPIO29_LEVEL_LOW + [20:20] + read-write + + + GPIO28_EDGE_HIGH + [19:19] + read-write + + + GPIO28_EDGE_LOW + [18:18] + read-write + + + GPIO28_LEVEL_HIGH + [17:17] + read-write + + + GPIO28_LEVEL_LOW + [16:16] + read-write + + + GPIO27_EDGE_HIGH + [15:15] + read-write + + + GPIO27_EDGE_LOW + [14:14] + read-write + + + GPIO27_LEVEL_HIGH + [13:13] + read-write + + + GPIO27_LEVEL_LOW + [12:12] + read-write + + + GPIO26_EDGE_HIGH + [11:11] + read-write + + + GPIO26_EDGE_LOW + [10:10] + read-write + + + GPIO26_LEVEL_HIGH + [9:9] + read-write + + + GPIO26_LEVEL_LOW + [8:8] + read-write + + + GPIO25_EDGE_HIGH + [7:7] + read-write + + + GPIO25_EDGE_LOW + [6:6] + read-write + + + GPIO25_LEVEL_HIGH + [5:5] + read-write + + + GPIO25_LEVEL_LOW + [4:4] + read-write + + + GPIO24_EDGE_HIGH + [3:3] + read-write + + + GPIO24_EDGE_LOW + [2:2] + read-write + + + GPIO24_LEVEL_HIGH + [1:1] + read-write + + + GPIO24_LEVEL_LOW + [0:0] + read-write + + + + + PROC0_INTF4 + 0x00000270 + Interrupt Force for proc0 + 0x00000000 + + + GPIO39_EDGE_HIGH + [31:31] + read-write + + + GPIO39_EDGE_LOW + [30:30] + read-write + + + GPIO39_LEVEL_HIGH + [29:29] + read-write + + + GPIO39_LEVEL_LOW + [28:28] + read-write + + + GPIO38_EDGE_HIGH + [27:27] + read-write + + + GPIO38_EDGE_LOW + [26:26] + read-write + + + GPIO38_LEVEL_HIGH + [25:25] + read-write + + + GPIO38_LEVEL_LOW + [24:24] + read-write + + + GPIO37_EDGE_HIGH + [23:23] + read-write + + + GPIO37_EDGE_LOW + [22:22] + read-write + + + GPIO37_LEVEL_HIGH + [21:21] + read-write + + + GPIO37_LEVEL_LOW + [20:20] + read-write + + + GPIO36_EDGE_HIGH + [19:19] + read-write + + + GPIO36_EDGE_LOW + [18:18] + read-write + + + GPIO36_LEVEL_HIGH + [17:17] + read-write + + + GPIO36_LEVEL_LOW + [16:16] + read-write + + + GPIO35_EDGE_HIGH + [15:15] + read-write + + + GPIO35_EDGE_LOW + [14:14] + read-write + + + GPIO35_LEVEL_HIGH + [13:13] + read-write + + + GPIO35_LEVEL_LOW + [12:12] + read-write + + + GPIO34_EDGE_HIGH + [11:11] + read-write + + + GPIO34_EDGE_LOW + [10:10] + read-write + + + GPIO34_LEVEL_HIGH + [9:9] + read-write + + + GPIO34_LEVEL_LOW + [8:8] + read-write + + + GPIO33_EDGE_HIGH + [7:7] + read-write + + + GPIO33_EDGE_LOW + [6:6] + read-write + + + GPIO33_LEVEL_HIGH + [5:5] + read-write + + + GPIO33_LEVEL_LOW + [4:4] + read-write + + + GPIO32_EDGE_HIGH + [3:3] + read-write + + + GPIO32_EDGE_LOW + [2:2] + read-write + + + GPIO32_LEVEL_HIGH + [1:1] + read-write + + + GPIO32_LEVEL_LOW + [0:0] + read-write + + + + + PROC0_INTF5 + 0x00000274 + Interrupt Force for proc0 + 0x00000000 + + + GPIO47_EDGE_HIGH + [31:31] + read-write + + + GPIO47_EDGE_LOW + [30:30] + read-write + + + GPIO47_LEVEL_HIGH + [29:29] + read-write + + + GPIO47_LEVEL_LOW + [28:28] + read-write + + + GPIO46_EDGE_HIGH + [27:27] + read-write + + + GPIO46_EDGE_LOW + [26:26] + read-write + + + GPIO46_LEVEL_HIGH + [25:25] + read-write + + + GPIO46_LEVEL_LOW + [24:24] + read-write + + + GPIO45_EDGE_HIGH + [23:23] + read-write + + + GPIO45_EDGE_LOW + [22:22] + read-write + + + GPIO45_LEVEL_HIGH + [21:21] + read-write + + + GPIO45_LEVEL_LOW + [20:20] + read-write + + + GPIO44_EDGE_HIGH + [19:19] + read-write + + + GPIO44_EDGE_LOW + [18:18] + read-write + + + GPIO44_LEVEL_HIGH + [17:17] + read-write + + + GPIO44_LEVEL_LOW + [16:16] + read-write + + + GPIO43_EDGE_HIGH + [15:15] + read-write + + + GPIO43_EDGE_LOW + [14:14] + read-write + + + GPIO43_LEVEL_HIGH + [13:13] + read-write + + + GPIO43_LEVEL_LOW + [12:12] + read-write + + + GPIO42_EDGE_HIGH + [11:11] + read-write + + + GPIO42_EDGE_LOW + [10:10] + read-write + + + GPIO42_LEVEL_HIGH + [9:9] + read-write + + + GPIO42_LEVEL_LOW + [8:8] + read-write + + + GPIO41_EDGE_HIGH + [7:7] + read-write + + + GPIO41_EDGE_LOW + [6:6] + read-write + + + GPIO41_LEVEL_HIGH + [5:5] + read-write + + + GPIO41_LEVEL_LOW + [4:4] + read-write + + + GPIO40_EDGE_HIGH + [3:3] + read-write + + + GPIO40_EDGE_LOW + [2:2] + read-write + + + GPIO40_LEVEL_HIGH + [1:1] + read-write + + + GPIO40_LEVEL_LOW + [0:0] + read-write + + + + + PROC0_INTS0 + 0x00000278 + Interrupt status after masking & forcing for proc0 + 0x00000000 + + + GPIO7_EDGE_HIGH + [31:31] + read-only + + + GPIO7_EDGE_LOW + [30:30] + read-only + + + GPIO7_LEVEL_HIGH + [29:29] + read-only + + + GPIO7_LEVEL_LOW + [28:28] + read-only + + + GPIO6_EDGE_HIGH + [27:27] + read-only + + + GPIO6_EDGE_LOW + [26:26] + read-only + + + GPIO6_LEVEL_HIGH + [25:25] + read-only + + + GPIO6_LEVEL_LOW + [24:24] + read-only + + + GPIO5_EDGE_HIGH + [23:23] + read-only + + + GPIO5_EDGE_LOW + [22:22] + read-only + + + GPIO5_LEVEL_HIGH + [21:21] + read-only + + + GPIO5_LEVEL_LOW + [20:20] + read-only + + + GPIO4_EDGE_HIGH + [19:19] + read-only + + + GPIO4_EDGE_LOW + [18:18] + read-only + + + GPIO4_LEVEL_HIGH + [17:17] + read-only + + + GPIO4_LEVEL_LOW + [16:16] + read-only + + + GPIO3_EDGE_HIGH + [15:15] + read-only + + + GPIO3_EDGE_LOW + [14:14] + read-only + + + GPIO3_LEVEL_HIGH + [13:13] + read-only + + + GPIO3_LEVEL_LOW + [12:12] + read-only + + + GPIO2_EDGE_HIGH + [11:11] + read-only + + + GPIO2_EDGE_LOW + [10:10] + read-only + + + GPIO2_LEVEL_HIGH + [9:9] + read-only + + + GPIO2_LEVEL_LOW + [8:8] + read-only + + + GPIO1_EDGE_HIGH + [7:7] + read-only + + + GPIO1_EDGE_LOW + [6:6] + read-only + + + GPIO1_LEVEL_HIGH + [5:5] + read-only + + + GPIO1_LEVEL_LOW + [4:4] + read-only + + + GPIO0_EDGE_HIGH + [3:3] + read-only + + + GPIO0_EDGE_LOW + [2:2] + read-only + + + GPIO0_LEVEL_HIGH + [1:1] + read-only + + + GPIO0_LEVEL_LOW + [0:0] + read-only + + + + + PROC0_INTS1 + 0x0000027c + Interrupt status after masking & forcing for proc0 + 0x00000000 + + + GPIO15_EDGE_HIGH + [31:31] + read-only + + + GPIO15_EDGE_LOW + [30:30] + read-only + + + GPIO15_LEVEL_HIGH + [29:29] + read-only + + + GPIO15_LEVEL_LOW + [28:28] + read-only + + + GPIO14_EDGE_HIGH + [27:27] + read-only + + + GPIO14_EDGE_LOW + [26:26] + read-only + + + GPIO14_LEVEL_HIGH + [25:25] + read-only + + + GPIO14_LEVEL_LOW + [24:24] + read-only + + + GPIO13_EDGE_HIGH + [23:23] + read-only + + + GPIO13_EDGE_LOW + [22:22] + read-only + + + GPIO13_LEVEL_HIGH + [21:21] + read-only + + + GPIO13_LEVEL_LOW + [20:20] + read-only + + + GPIO12_EDGE_HIGH + [19:19] + read-only + + + GPIO12_EDGE_LOW + [18:18] + read-only + + + GPIO12_LEVEL_HIGH + [17:17] + read-only + + + GPIO12_LEVEL_LOW + [16:16] + read-only + + + GPIO11_EDGE_HIGH + [15:15] + read-only + + + GPIO11_EDGE_LOW + [14:14] + read-only + + + GPIO11_LEVEL_HIGH + [13:13] + read-only + + + GPIO11_LEVEL_LOW + [12:12] + read-only + + + GPIO10_EDGE_HIGH + [11:11] + read-only + + + GPIO10_EDGE_LOW + [10:10] + read-only + + + GPIO10_LEVEL_HIGH + [9:9] + read-only + + + GPIO10_LEVEL_LOW + [8:8] + read-only + + + GPIO9_EDGE_HIGH + [7:7] + read-only + + + GPIO9_EDGE_LOW + [6:6] + read-only + + + GPIO9_LEVEL_HIGH + [5:5] + read-only + + + GPIO9_LEVEL_LOW + [4:4] + read-only + + + GPIO8_EDGE_HIGH + [3:3] + read-only + + + GPIO8_EDGE_LOW + [2:2] + read-only + + + GPIO8_LEVEL_HIGH + [1:1] + read-only + + + GPIO8_LEVEL_LOW + [0:0] + read-only + + + + + PROC0_INTS2 + 0x00000280 + Interrupt status after masking & forcing for proc0 + 0x00000000 + + + GPIO23_EDGE_HIGH + [31:31] + read-only + + + GPIO23_EDGE_LOW + [30:30] + read-only + + + GPIO23_LEVEL_HIGH + [29:29] + read-only + + + GPIO23_LEVEL_LOW + [28:28] + read-only + + + GPIO22_EDGE_HIGH + [27:27] + read-only + + + GPIO22_EDGE_LOW + [26:26] + read-only + + + GPIO22_LEVEL_HIGH + [25:25] + read-only + + + GPIO22_LEVEL_LOW + [24:24] + read-only + + + GPIO21_EDGE_HIGH + [23:23] + read-only + + + GPIO21_EDGE_LOW + [22:22] + read-only + + + GPIO21_LEVEL_HIGH + [21:21] + read-only + + + GPIO21_LEVEL_LOW + [20:20] + read-only + + + GPIO20_EDGE_HIGH + [19:19] + read-only + + + GPIO20_EDGE_LOW + [18:18] + read-only + + + GPIO20_LEVEL_HIGH + [17:17] + read-only + + + GPIO20_LEVEL_LOW + [16:16] + read-only + + + GPIO19_EDGE_HIGH + [15:15] + read-only + + + GPIO19_EDGE_LOW + [14:14] + read-only + + + GPIO19_LEVEL_HIGH + [13:13] + read-only + + + GPIO19_LEVEL_LOW + [12:12] + read-only + + + GPIO18_EDGE_HIGH + [11:11] + read-only + + + GPIO18_EDGE_LOW + [10:10] + read-only + + + GPIO18_LEVEL_HIGH + [9:9] + read-only + + + GPIO18_LEVEL_LOW + [8:8] + read-only + + + GPIO17_EDGE_HIGH + [7:7] + read-only + + + GPIO17_EDGE_LOW + [6:6] + read-only + + + GPIO17_LEVEL_HIGH + [5:5] + read-only + + + GPIO17_LEVEL_LOW + [4:4] + read-only + + + GPIO16_EDGE_HIGH + [3:3] + read-only + + + GPIO16_EDGE_LOW + [2:2] + read-only + + + GPIO16_LEVEL_HIGH + [1:1] + read-only + + + GPIO16_LEVEL_LOW + [0:0] + read-only + + + + + PROC0_INTS3 + 0x00000284 + Interrupt status after masking & forcing for proc0 + 0x00000000 + + + GPIO31_EDGE_HIGH + [31:31] + read-only + + + GPIO31_EDGE_LOW + [30:30] + read-only + + + GPIO31_LEVEL_HIGH + [29:29] + read-only + + + GPIO31_LEVEL_LOW + [28:28] + read-only + + + GPIO30_EDGE_HIGH + [27:27] + read-only + + + GPIO30_EDGE_LOW + [26:26] + read-only + + + GPIO30_LEVEL_HIGH + [25:25] + read-only + + + GPIO30_LEVEL_LOW + [24:24] + read-only + + + GPIO29_EDGE_HIGH + [23:23] + read-only + + + GPIO29_EDGE_LOW + [22:22] + read-only + + + GPIO29_LEVEL_HIGH + [21:21] + read-only + + + GPIO29_LEVEL_LOW + [20:20] + read-only + + + GPIO28_EDGE_HIGH + [19:19] + read-only + + + GPIO28_EDGE_LOW + [18:18] + read-only + + + GPIO28_LEVEL_HIGH + [17:17] + read-only + + + GPIO28_LEVEL_LOW + [16:16] + read-only + + + GPIO27_EDGE_HIGH + [15:15] + read-only + + + GPIO27_EDGE_LOW + [14:14] + read-only + + + GPIO27_LEVEL_HIGH + [13:13] + read-only + + + GPIO27_LEVEL_LOW + [12:12] + read-only + + + GPIO26_EDGE_HIGH + [11:11] + read-only + + + GPIO26_EDGE_LOW + [10:10] + read-only + + + GPIO26_LEVEL_HIGH + [9:9] + read-only + + + GPIO26_LEVEL_LOW + [8:8] + read-only + + + GPIO25_EDGE_HIGH + [7:7] + read-only + + + GPIO25_EDGE_LOW + [6:6] + read-only + + + GPIO25_LEVEL_HIGH + [5:5] + read-only + + + GPIO25_LEVEL_LOW + [4:4] + read-only + + + GPIO24_EDGE_HIGH + [3:3] + read-only + + + GPIO24_EDGE_LOW + [2:2] + read-only + + + GPIO24_LEVEL_HIGH + [1:1] + read-only + + + GPIO24_LEVEL_LOW + [0:0] + read-only + + + + + PROC0_INTS4 + 0x00000288 + Interrupt status after masking & forcing for proc0 + 0x00000000 + + + GPIO39_EDGE_HIGH + [31:31] + read-only + + + GPIO39_EDGE_LOW + [30:30] + read-only + + + GPIO39_LEVEL_HIGH + [29:29] + read-only + + + GPIO39_LEVEL_LOW + [28:28] + read-only + + + GPIO38_EDGE_HIGH + [27:27] + read-only + + + GPIO38_EDGE_LOW + [26:26] + read-only + + + GPIO38_LEVEL_HIGH + [25:25] + read-only + + + GPIO38_LEVEL_LOW + [24:24] + read-only + + + GPIO37_EDGE_HIGH + [23:23] + read-only + + + GPIO37_EDGE_LOW + [22:22] + read-only + + + GPIO37_LEVEL_HIGH + [21:21] + read-only + + + GPIO37_LEVEL_LOW + [20:20] + read-only + + + GPIO36_EDGE_HIGH + [19:19] + read-only + + + GPIO36_EDGE_LOW + [18:18] + read-only + + + GPIO36_LEVEL_HIGH + [17:17] + read-only + + + GPIO36_LEVEL_LOW + [16:16] + read-only + + + GPIO35_EDGE_HIGH + [15:15] + read-only + + + GPIO35_EDGE_LOW + [14:14] + read-only + + + GPIO35_LEVEL_HIGH + [13:13] + read-only + + + GPIO35_LEVEL_LOW + [12:12] + read-only + + + GPIO34_EDGE_HIGH + [11:11] + read-only + + + GPIO34_EDGE_LOW + [10:10] + read-only + + + GPIO34_LEVEL_HIGH + [9:9] + read-only + + + GPIO34_LEVEL_LOW + [8:8] + read-only + + + GPIO33_EDGE_HIGH + [7:7] + read-only + + + GPIO33_EDGE_LOW + [6:6] + read-only + + + GPIO33_LEVEL_HIGH + [5:5] + read-only + + + GPIO33_LEVEL_LOW + [4:4] + read-only + + + GPIO32_EDGE_HIGH + [3:3] + read-only + + + GPIO32_EDGE_LOW + [2:2] + read-only + + + GPIO32_LEVEL_HIGH + [1:1] + read-only + + + GPIO32_LEVEL_LOW + [0:0] + read-only + + + + + PROC0_INTS5 + 0x0000028c + Interrupt status after masking & forcing for proc0 + 0x00000000 + + + GPIO47_EDGE_HIGH + [31:31] + read-only + + + GPIO47_EDGE_LOW + [30:30] + read-only + + + GPIO47_LEVEL_HIGH + [29:29] + read-only + + + GPIO47_LEVEL_LOW + [28:28] + read-only + + + GPIO46_EDGE_HIGH + [27:27] + read-only + + + GPIO46_EDGE_LOW + [26:26] + read-only + + + GPIO46_LEVEL_HIGH + [25:25] + read-only + + + GPIO46_LEVEL_LOW + [24:24] + read-only + + + GPIO45_EDGE_HIGH + [23:23] + read-only + + + GPIO45_EDGE_LOW + [22:22] + read-only + + + GPIO45_LEVEL_HIGH + [21:21] + read-only + + + GPIO45_LEVEL_LOW + [20:20] + read-only + + + GPIO44_EDGE_HIGH + [19:19] + read-only + + + GPIO44_EDGE_LOW + [18:18] + read-only + + + GPIO44_LEVEL_HIGH + [17:17] + read-only + + + GPIO44_LEVEL_LOW + [16:16] + read-only + + + GPIO43_EDGE_HIGH + [15:15] + read-only + + + GPIO43_EDGE_LOW + [14:14] + read-only + + + GPIO43_LEVEL_HIGH + [13:13] + read-only + + + GPIO43_LEVEL_LOW + [12:12] + read-only + + + GPIO42_EDGE_HIGH + [11:11] + read-only + + + GPIO42_EDGE_LOW + [10:10] + read-only + + + GPIO42_LEVEL_HIGH + [9:9] + read-only + + + GPIO42_LEVEL_LOW + [8:8] + read-only + + + GPIO41_EDGE_HIGH + [7:7] + read-only + + + GPIO41_EDGE_LOW + [6:6] + read-only + + + GPIO41_LEVEL_HIGH + [5:5] + read-only + + + GPIO41_LEVEL_LOW + [4:4] + read-only + + + GPIO40_EDGE_HIGH + [3:3] + read-only + + + GPIO40_EDGE_LOW + [2:2] + read-only + + + GPIO40_LEVEL_HIGH + [1:1] + read-only + + + GPIO40_LEVEL_LOW + [0:0] + read-only + + + + + PROC1_INTE0 + 0x00000290 + Interrupt Enable for proc1 + 0x00000000 + + + GPIO7_EDGE_HIGH + [31:31] + read-write + + + GPIO7_EDGE_LOW + [30:30] + read-write + + + GPIO7_LEVEL_HIGH + [29:29] + read-write + + + GPIO7_LEVEL_LOW + [28:28] + read-write + + + GPIO6_EDGE_HIGH + [27:27] + read-write + + + GPIO6_EDGE_LOW + [26:26] + read-write + + + GPIO6_LEVEL_HIGH + [25:25] + read-write + + + GPIO6_LEVEL_LOW + [24:24] + read-write + + + GPIO5_EDGE_HIGH + [23:23] + read-write + + + GPIO5_EDGE_LOW + [22:22] + read-write + + + GPIO5_LEVEL_HIGH + [21:21] + read-write + + + GPIO5_LEVEL_LOW + [20:20] + read-write + + + GPIO4_EDGE_HIGH + [19:19] + read-write + + + GPIO4_EDGE_LOW + [18:18] + read-write + + + GPIO4_LEVEL_HIGH + [17:17] + read-write + + + GPIO4_LEVEL_LOW + [16:16] + read-write + + + GPIO3_EDGE_HIGH + [15:15] + read-write + + + GPIO3_EDGE_LOW + [14:14] + read-write + + + GPIO3_LEVEL_HIGH + [13:13] + read-write + + + GPIO3_LEVEL_LOW + [12:12] + read-write + + + GPIO2_EDGE_HIGH + [11:11] + read-write + + + GPIO2_EDGE_LOW + [10:10] + read-write + + + GPIO2_LEVEL_HIGH + [9:9] + read-write + + + GPIO2_LEVEL_LOW + [8:8] + read-write + + + GPIO1_EDGE_HIGH + [7:7] + read-write + + + GPIO1_EDGE_LOW + [6:6] + read-write + + + GPIO1_LEVEL_HIGH + [5:5] + read-write + + + GPIO1_LEVEL_LOW + [4:4] + read-write + + + GPIO0_EDGE_HIGH + [3:3] + read-write + + + GPIO0_EDGE_LOW + [2:2] + read-write + + + GPIO0_LEVEL_HIGH + [1:1] + read-write + + + GPIO0_LEVEL_LOW + [0:0] + read-write + + + + + PROC1_INTE1 + 0x00000294 + Interrupt Enable for proc1 + 0x00000000 + + + GPIO15_EDGE_HIGH + [31:31] + read-write + + + GPIO15_EDGE_LOW + [30:30] + read-write + + + GPIO15_LEVEL_HIGH + [29:29] + read-write + + + GPIO15_LEVEL_LOW + [28:28] + read-write + + + GPIO14_EDGE_HIGH + [27:27] + read-write + + + GPIO14_EDGE_LOW + [26:26] + read-write + + + GPIO14_LEVEL_HIGH + [25:25] + read-write + + + GPIO14_LEVEL_LOW + [24:24] + read-write + + + GPIO13_EDGE_HIGH + [23:23] + read-write + + + GPIO13_EDGE_LOW + [22:22] + read-write + + + GPIO13_LEVEL_HIGH + [21:21] + read-write + + + GPIO13_LEVEL_LOW + [20:20] + read-write + + + GPIO12_EDGE_HIGH + [19:19] + read-write + + + GPIO12_EDGE_LOW + [18:18] + read-write + + + GPIO12_LEVEL_HIGH + [17:17] + read-write + + + GPIO12_LEVEL_LOW + [16:16] + read-write + + + GPIO11_EDGE_HIGH + [15:15] + read-write + + + GPIO11_EDGE_LOW + [14:14] + read-write + + + GPIO11_LEVEL_HIGH + [13:13] + read-write + + + GPIO11_LEVEL_LOW + [12:12] + read-write + + + GPIO10_EDGE_HIGH + [11:11] + read-write + + + GPIO10_EDGE_LOW + [10:10] + read-write + + + GPIO10_LEVEL_HIGH + [9:9] + read-write + + + GPIO10_LEVEL_LOW + [8:8] + read-write + + + GPIO9_EDGE_HIGH + [7:7] + read-write + + + GPIO9_EDGE_LOW + [6:6] + read-write + + + GPIO9_LEVEL_HIGH + [5:5] + read-write + + + GPIO9_LEVEL_LOW + [4:4] + read-write + + + GPIO8_EDGE_HIGH + [3:3] + read-write + + + GPIO8_EDGE_LOW + [2:2] + read-write + + + GPIO8_LEVEL_HIGH + [1:1] + read-write + + + GPIO8_LEVEL_LOW + [0:0] + read-write + + + + + PROC1_INTE2 + 0x00000298 + Interrupt Enable for proc1 + 0x00000000 + + + GPIO23_EDGE_HIGH + [31:31] + read-write + + + GPIO23_EDGE_LOW + [30:30] + read-write + + + GPIO23_LEVEL_HIGH + [29:29] + read-write + + + GPIO23_LEVEL_LOW + [28:28] + read-write + + + GPIO22_EDGE_HIGH + [27:27] + read-write + + + GPIO22_EDGE_LOW + [26:26] + read-write + + + GPIO22_LEVEL_HIGH + [25:25] + read-write + + + GPIO22_LEVEL_LOW + [24:24] + read-write + + + GPIO21_EDGE_HIGH + [23:23] + read-write + + + GPIO21_EDGE_LOW + [22:22] + read-write + + + GPIO21_LEVEL_HIGH + [21:21] + read-write + + + GPIO21_LEVEL_LOW + [20:20] + read-write + + + GPIO20_EDGE_HIGH + [19:19] + read-write + + + GPIO20_EDGE_LOW + [18:18] + read-write + + + GPIO20_LEVEL_HIGH + [17:17] + read-write + + + GPIO20_LEVEL_LOW + [16:16] + read-write + + + GPIO19_EDGE_HIGH + [15:15] + read-write + + + GPIO19_EDGE_LOW + [14:14] + read-write + + + GPIO19_LEVEL_HIGH + [13:13] + read-write + + + GPIO19_LEVEL_LOW + [12:12] + read-write + + + GPIO18_EDGE_HIGH + [11:11] + read-write + + + GPIO18_EDGE_LOW + [10:10] + read-write + + + GPIO18_LEVEL_HIGH + [9:9] + read-write + + + GPIO18_LEVEL_LOW + [8:8] + read-write + + + GPIO17_EDGE_HIGH + [7:7] + read-write + + + GPIO17_EDGE_LOW + [6:6] + read-write + + + GPIO17_LEVEL_HIGH + [5:5] + read-write + + + GPIO17_LEVEL_LOW + [4:4] + read-write + + + GPIO16_EDGE_HIGH + [3:3] + read-write + + + GPIO16_EDGE_LOW + [2:2] + read-write + + + GPIO16_LEVEL_HIGH + [1:1] + read-write + + + GPIO16_LEVEL_LOW + [0:0] + read-write + + + + + PROC1_INTE3 + 0x0000029c + Interrupt Enable for proc1 + 0x00000000 + + + GPIO31_EDGE_HIGH + [31:31] + read-write + + + GPIO31_EDGE_LOW + [30:30] + read-write + + + GPIO31_LEVEL_HIGH + [29:29] + read-write + + + GPIO31_LEVEL_LOW + [28:28] + read-write + + + GPIO30_EDGE_HIGH + [27:27] + read-write + + + GPIO30_EDGE_LOW + [26:26] + read-write + + + GPIO30_LEVEL_HIGH + [25:25] + read-write + + + GPIO30_LEVEL_LOW + [24:24] + read-write + + + GPIO29_EDGE_HIGH + [23:23] + read-write + + + GPIO29_EDGE_LOW + [22:22] + read-write + + + GPIO29_LEVEL_HIGH + [21:21] + read-write + + + GPIO29_LEVEL_LOW + [20:20] + read-write + + + GPIO28_EDGE_HIGH + [19:19] + read-write + + + GPIO28_EDGE_LOW + [18:18] + read-write + + + GPIO28_LEVEL_HIGH + [17:17] + read-write + + + GPIO28_LEVEL_LOW + [16:16] + read-write + + + GPIO27_EDGE_HIGH + [15:15] + read-write + + + GPIO27_EDGE_LOW + [14:14] + read-write + + + GPIO27_LEVEL_HIGH + [13:13] + read-write + + + GPIO27_LEVEL_LOW + [12:12] + read-write + + + GPIO26_EDGE_HIGH + [11:11] + read-write + + + GPIO26_EDGE_LOW + [10:10] + read-write + + + GPIO26_LEVEL_HIGH + [9:9] + read-write + + + GPIO26_LEVEL_LOW + [8:8] + read-write + + + GPIO25_EDGE_HIGH + [7:7] + read-write + + + GPIO25_EDGE_LOW + [6:6] + read-write + + + GPIO25_LEVEL_HIGH + [5:5] + read-write + + + GPIO25_LEVEL_LOW + [4:4] + read-write + + + GPIO24_EDGE_HIGH + [3:3] + read-write + + + GPIO24_EDGE_LOW + [2:2] + read-write + + + GPIO24_LEVEL_HIGH + [1:1] + read-write + + + GPIO24_LEVEL_LOW + [0:0] + read-write + + + + + PROC1_INTE4 + 0x000002a0 + Interrupt Enable for proc1 + 0x00000000 + + + GPIO39_EDGE_HIGH + [31:31] + read-write + + + GPIO39_EDGE_LOW + [30:30] + read-write + + + GPIO39_LEVEL_HIGH + [29:29] + read-write + + + GPIO39_LEVEL_LOW + [28:28] + read-write + + + GPIO38_EDGE_HIGH + [27:27] + read-write + + + GPIO38_EDGE_LOW + [26:26] + read-write + + + GPIO38_LEVEL_HIGH + [25:25] + read-write + + + GPIO38_LEVEL_LOW + [24:24] + read-write + + + GPIO37_EDGE_HIGH + [23:23] + read-write + + + GPIO37_EDGE_LOW + [22:22] + read-write + + + GPIO37_LEVEL_HIGH + [21:21] + read-write + + + GPIO37_LEVEL_LOW + [20:20] + read-write + + + GPIO36_EDGE_HIGH + [19:19] + read-write + + + GPIO36_EDGE_LOW + [18:18] + read-write + + + GPIO36_LEVEL_HIGH + [17:17] + read-write + + + GPIO36_LEVEL_LOW + [16:16] + read-write + + + GPIO35_EDGE_HIGH + [15:15] + read-write + + + GPIO35_EDGE_LOW + [14:14] + read-write + + + GPIO35_LEVEL_HIGH + [13:13] + read-write + + + GPIO35_LEVEL_LOW + [12:12] + read-write + + + GPIO34_EDGE_HIGH + [11:11] + read-write + + + GPIO34_EDGE_LOW + [10:10] + read-write + + + GPIO34_LEVEL_HIGH + [9:9] + read-write + + + GPIO34_LEVEL_LOW + [8:8] + read-write + + + GPIO33_EDGE_HIGH + [7:7] + read-write + + + GPIO33_EDGE_LOW + [6:6] + read-write + + + GPIO33_LEVEL_HIGH + [5:5] + read-write + + + GPIO33_LEVEL_LOW + [4:4] + read-write + + + GPIO32_EDGE_HIGH + [3:3] + read-write + + + GPIO32_EDGE_LOW + [2:2] + read-write + + + GPIO32_LEVEL_HIGH + [1:1] + read-write + + + GPIO32_LEVEL_LOW + [0:0] + read-write + + + + + PROC1_INTE5 + 0x000002a4 + Interrupt Enable for proc1 + 0x00000000 + + + GPIO47_EDGE_HIGH + [31:31] + read-write + + + GPIO47_EDGE_LOW + [30:30] + read-write + + + GPIO47_LEVEL_HIGH + [29:29] + read-write + + + GPIO47_LEVEL_LOW + [28:28] + read-write + + + GPIO46_EDGE_HIGH + [27:27] + read-write + + + GPIO46_EDGE_LOW + [26:26] + read-write + + + GPIO46_LEVEL_HIGH + [25:25] + read-write + + + GPIO46_LEVEL_LOW + [24:24] + read-write + + + GPIO45_EDGE_HIGH + [23:23] + read-write + + + GPIO45_EDGE_LOW + [22:22] + read-write + + + GPIO45_LEVEL_HIGH + [21:21] + read-write + + + GPIO45_LEVEL_LOW + [20:20] + read-write + + + GPIO44_EDGE_HIGH + [19:19] + read-write + + + GPIO44_EDGE_LOW + [18:18] + read-write + + + GPIO44_LEVEL_HIGH + [17:17] + read-write + + + GPIO44_LEVEL_LOW + [16:16] + read-write + + + GPIO43_EDGE_HIGH + [15:15] + read-write + + + GPIO43_EDGE_LOW + [14:14] + read-write + + + GPIO43_LEVEL_HIGH + [13:13] + read-write + + + GPIO43_LEVEL_LOW + [12:12] + read-write + + + GPIO42_EDGE_HIGH + [11:11] + read-write + + + GPIO42_EDGE_LOW + [10:10] + read-write + + + GPIO42_LEVEL_HIGH + [9:9] + read-write + + + GPIO42_LEVEL_LOW + [8:8] + read-write + + + GPIO41_EDGE_HIGH + [7:7] + read-write + + + GPIO41_EDGE_LOW + [6:6] + read-write + + + GPIO41_LEVEL_HIGH + [5:5] + read-write + + + GPIO41_LEVEL_LOW + [4:4] + read-write + + + GPIO40_EDGE_HIGH + [3:3] + read-write + + + GPIO40_EDGE_LOW + [2:2] + read-write + + + GPIO40_LEVEL_HIGH + [1:1] + read-write + + + GPIO40_LEVEL_LOW + [0:0] + read-write + + + + + PROC1_INTF0 + 0x000002a8 + Interrupt Force for proc1 + 0x00000000 + + + GPIO7_EDGE_HIGH + [31:31] + read-write + + + GPIO7_EDGE_LOW + [30:30] + read-write + + + GPIO7_LEVEL_HIGH + [29:29] + read-write + + + GPIO7_LEVEL_LOW + [28:28] + read-write + + + GPIO6_EDGE_HIGH + [27:27] + read-write + + + GPIO6_EDGE_LOW + [26:26] + read-write + + + GPIO6_LEVEL_HIGH + [25:25] + read-write + + + GPIO6_LEVEL_LOW + [24:24] + read-write + + + GPIO5_EDGE_HIGH + [23:23] + read-write + + + GPIO5_EDGE_LOW + [22:22] + read-write + + + GPIO5_LEVEL_HIGH + [21:21] + read-write + + + GPIO5_LEVEL_LOW + [20:20] + read-write + + + GPIO4_EDGE_HIGH + [19:19] + read-write + + + GPIO4_EDGE_LOW + [18:18] + read-write + + + GPIO4_LEVEL_HIGH + [17:17] + read-write + + + GPIO4_LEVEL_LOW + [16:16] + read-write + + + GPIO3_EDGE_HIGH + [15:15] + read-write + + + GPIO3_EDGE_LOW + [14:14] + read-write + + + GPIO3_LEVEL_HIGH + [13:13] + read-write + + + GPIO3_LEVEL_LOW + [12:12] + read-write + + + GPIO2_EDGE_HIGH + [11:11] + read-write + + + GPIO2_EDGE_LOW + [10:10] + read-write + + + GPIO2_LEVEL_HIGH + [9:9] + read-write + + + GPIO2_LEVEL_LOW + [8:8] + read-write + + + GPIO1_EDGE_HIGH + [7:7] + read-write + + + GPIO1_EDGE_LOW + [6:6] + read-write + + + GPIO1_LEVEL_HIGH + [5:5] + read-write + + + GPIO1_LEVEL_LOW + [4:4] + read-write + + + GPIO0_EDGE_HIGH + [3:3] + read-write + + + GPIO0_EDGE_LOW + [2:2] + read-write + + + GPIO0_LEVEL_HIGH + [1:1] + read-write + + + GPIO0_LEVEL_LOW + [0:0] + read-write + + + + + PROC1_INTF1 + 0x000002ac + Interrupt Force for proc1 + 0x00000000 + + + GPIO15_EDGE_HIGH + [31:31] + read-write + + + GPIO15_EDGE_LOW + [30:30] + read-write + + + GPIO15_LEVEL_HIGH + [29:29] + read-write + + + GPIO15_LEVEL_LOW + [28:28] + read-write + + + GPIO14_EDGE_HIGH + [27:27] + read-write + + + GPIO14_EDGE_LOW + [26:26] + read-write + + + GPIO14_LEVEL_HIGH + [25:25] + read-write + + + GPIO14_LEVEL_LOW + [24:24] + read-write + + + GPIO13_EDGE_HIGH + [23:23] + read-write + + + GPIO13_EDGE_LOW + [22:22] + read-write + + + GPIO13_LEVEL_HIGH + [21:21] + read-write + + + GPIO13_LEVEL_LOW + [20:20] + read-write + + + GPIO12_EDGE_HIGH + [19:19] + read-write + + + GPIO12_EDGE_LOW + [18:18] + read-write + + + GPIO12_LEVEL_HIGH + [17:17] + read-write + + + GPIO12_LEVEL_LOW + [16:16] + read-write + + + GPIO11_EDGE_HIGH + [15:15] + read-write + + + GPIO11_EDGE_LOW + [14:14] + read-write + + + GPIO11_LEVEL_HIGH + [13:13] + read-write + + + GPIO11_LEVEL_LOW + [12:12] + read-write + + + GPIO10_EDGE_HIGH + [11:11] + read-write + + + GPIO10_EDGE_LOW + [10:10] + read-write + + + GPIO10_LEVEL_HIGH + [9:9] + read-write + + + GPIO10_LEVEL_LOW + [8:8] + read-write + + + GPIO9_EDGE_HIGH + [7:7] + read-write + + + GPIO9_EDGE_LOW + [6:6] + read-write + + + GPIO9_LEVEL_HIGH + [5:5] + read-write + + + GPIO9_LEVEL_LOW + [4:4] + read-write + + + GPIO8_EDGE_HIGH + [3:3] + read-write + + + GPIO8_EDGE_LOW + [2:2] + read-write + + + GPIO8_LEVEL_HIGH + [1:1] + read-write + + + GPIO8_LEVEL_LOW + [0:0] + read-write + + + + + PROC1_INTF2 + 0x000002b0 + Interrupt Force for proc1 + 0x00000000 + + + GPIO23_EDGE_HIGH + [31:31] + read-write + + + GPIO23_EDGE_LOW + [30:30] + read-write + + + GPIO23_LEVEL_HIGH + [29:29] + read-write + + + GPIO23_LEVEL_LOW + [28:28] + read-write + + + GPIO22_EDGE_HIGH + [27:27] + read-write + + + GPIO22_EDGE_LOW + [26:26] + read-write + + + GPIO22_LEVEL_HIGH + [25:25] + read-write + + + GPIO22_LEVEL_LOW + [24:24] + read-write + + + GPIO21_EDGE_HIGH + [23:23] + read-write + + + GPIO21_EDGE_LOW + [22:22] + read-write + + + GPIO21_LEVEL_HIGH + [21:21] + read-write + + + GPIO21_LEVEL_LOW + [20:20] + read-write + + + GPIO20_EDGE_HIGH + [19:19] + read-write + + + GPIO20_EDGE_LOW + [18:18] + read-write + + + GPIO20_LEVEL_HIGH + [17:17] + read-write + + + GPIO20_LEVEL_LOW + [16:16] + read-write + + + GPIO19_EDGE_HIGH + [15:15] + read-write + + + GPIO19_EDGE_LOW + [14:14] + read-write + + + GPIO19_LEVEL_HIGH + [13:13] + read-write + + + GPIO19_LEVEL_LOW + [12:12] + read-write + + + GPIO18_EDGE_HIGH + [11:11] + read-write + + + GPIO18_EDGE_LOW + [10:10] + read-write + + + GPIO18_LEVEL_HIGH + [9:9] + read-write + + + GPIO18_LEVEL_LOW + [8:8] + read-write + + + GPIO17_EDGE_HIGH + [7:7] + read-write + + + GPIO17_EDGE_LOW + [6:6] + read-write + + + GPIO17_LEVEL_HIGH + [5:5] + read-write + + + GPIO17_LEVEL_LOW + [4:4] + read-write + + + GPIO16_EDGE_HIGH + [3:3] + read-write + + + GPIO16_EDGE_LOW + [2:2] + read-write + + + GPIO16_LEVEL_HIGH + [1:1] + read-write + + + GPIO16_LEVEL_LOW + [0:0] + read-write + + + + + PROC1_INTF3 + 0x000002b4 + Interrupt Force for proc1 + 0x00000000 + + + GPIO31_EDGE_HIGH + [31:31] + read-write + + + GPIO31_EDGE_LOW + [30:30] + read-write + + + GPIO31_LEVEL_HIGH + [29:29] + read-write + + + GPIO31_LEVEL_LOW + [28:28] + read-write + + + GPIO30_EDGE_HIGH + [27:27] + read-write + + + GPIO30_EDGE_LOW + [26:26] + read-write + + + GPIO30_LEVEL_HIGH + [25:25] + read-write + + + GPIO30_LEVEL_LOW + [24:24] + read-write + + + GPIO29_EDGE_HIGH + [23:23] + read-write + + + GPIO29_EDGE_LOW + [22:22] + read-write + + + GPIO29_LEVEL_HIGH + [21:21] + read-write + + + GPIO29_LEVEL_LOW + [20:20] + read-write + + + GPIO28_EDGE_HIGH + [19:19] + read-write + + + GPIO28_EDGE_LOW + [18:18] + read-write + + + GPIO28_LEVEL_HIGH + [17:17] + read-write + + + GPIO28_LEVEL_LOW + [16:16] + read-write + + + GPIO27_EDGE_HIGH + [15:15] + read-write + + + GPIO27_EDGE_LOW + [14:14] + read-write + + + GPIO27_LEVEL_HIGH + [13:13] + read-write + + + GPIO27_LEVEL_LOW + [12:12] + read-write + + + GPIO26_EDGE_HIGH + [11:11] + read-write + + + GPIO26_EDGE_LOW + [10:10] + read-write + + + GPIO26_LEVEL_HIGH + [9:9] + read-write + + + GPIO26_LEVEL_LOW + [8:8] + read-write + + + GPIO25_EDGE_HIGH + [7:7] + read-write + + + GPIO25_EDGE_LOW + [6:6] + read-write + + + GPIO25_LEVEL_HIGH + [5:5] + read-write + + + GPIO25_LEVEL_LOW + [4:4] + read-write + + + GPIO24_EDGE_HIGH + [3:3] + read-write + + + GPIO24_EDGE_LOW + [2:2] + read-write + + + GPIO24_LEVEL_HIGH + [1:1] + read-write + + + GPIO24_LEVEL_LOW + [0:0] + read-write + + + + + PROC1_INTF4 + 0x000002b8 + Interrupt Force for proc1 + 0x00000000 + + + GPIO39_EDGE_HIGH + [31:31] + read-write + + + GPIO39_EDGE_LOW + [30:30] + read-write + + + GPIO39_LEVEL_HIGH + [29:29] + read-write + + + GPIO39_LEVEL_LOW + [28:28] + read-write + + + GPIO38_EDGE_HIGH + [27:27] + read-write + + + GPIO38_EDGE_LOW + [26:26] + read-write + + + GPIO38_LEVEL_HIGH + [25:25] + read-write + + + GPIO38_LEVEL_LOW + [24:24] + read-write + + + GPIO37_EDGE_HIGH + [23:23] + read-write + + + GPIO37_EDGE_LOW + [22:22] + read-write + + + GPIO37_LEVEL_HIGH + [21:21] + read-write + + + GPIO37_LEVEL_LOW + [20:20] + read-write + + + GPIO36_EDGE_HIGH + [19:19] + read-write + + + GPIO36_EDGE_LOW + [18:18] + read-write + + + GPIO36_LEVEL_HIGH + [17:17] + read-write + + + GPIO36_LEVEL_LOW + [16:16] + read-write + + + GPIO35_EDGE_HIGH + [15:15] + read-write + + + GPIO35_EDGE_LOW + [14:14] + read-write + + + GPIO35_LEVEL_HIGH + [13:13] + read-write + + + GPIO35_LEVEL_LOW + [12:12] + read-write + + + GPIO34_EDGE_HIGH + [11:11] + read-write + + + GPIO34_EDGE_LOW + [10:10] + read-write + + + GPIO34_LEVEL_HIGH + [9:9] + read-write + + + GPIO34_LEVEL_LOW + [8:8] + read-write + + + GPIO33_EDGE_HIGH + [7:7] + read-write + + + GPIO33_EDGE_LOW + [6:6] + read-write + + + GPIO33_LEVEL_HIGH + [5:5] + read-write + + + GPIO33_LEVEL_LOW + [4:4] + read-write + + + GPIO32_EDGE_HIGH + [3:3] + read-write + + + GPIO32_EDGE_LOW + [2:2] + read-write + + + GPIO32_LEVEL_HIGH + [1:1] + read-write + + + GPIO32_LEVEL_LOW + [0:0] + read-write + + + + + PROC1_INTF5 + 0x000002bc + Interrupt Force for proc1 + 0x00000000 + + + GPIO47_EDGE_HIGH + [31:31] + read-write + + + GPIO47_EDGE_LOW + [30:30] + read-write + + + GPIO47_LEVEL_HIGH + [29:29] + read-write + + + GPIO47_LEVEL_LOW + [28:28] + read-write + + + GPIO46_EDGE_HIGH + [27:27] + read-write + + + GPIO46_EDGE_LOW + [26:26] + read-write + + + GPIO46_LEVEL_HIGH + [25:25] + read-write + + + GPIO46_LEVEL_LOW + [24:24] + read-write + + + GPIO45_EDGE_HIGH + [23:23] + read-write + + + GPIO45_EDGE_LOW + [22:22] + read-write + + + GPIO45_LEVEL_HIGH + [21:21] + read-write + + + GPIO45_LEVEL_LOW + [20:20] + read-write + + + GPIO44_EDGE_HIGH + [19:19] + read-write + + + GPIO44_EDGE_LOW + [18:18] + read-write + + + GPIO44_LEVEL_HIGH + [17:17] + read-write + + + GPIO44_LEVEL_LOW + [16:16] + read-write + + + GPIO43_EDGE_HIGH + [15:15] + read-write + + + GPIO43_EDGE_LOW + [14:14] + read-write + + + GPIO43_LEVEL_HIGH + [13:13] + read-write + + + GPIO43_LEVEL_LOW + [12:12] + read-write + + + GPIO42_EDGE_HIGH + [11:11] + read-write + + + GPIO42_EDGE_LOW + [10:10] + read-write + + + GPIO42_LEVEL_HIGH + [9:9] + read-write + + + GPIO42_LEVEL_LOW + [8:8] + read-write + + + GPIO41_EDGE_HIGH + [7:7] + read-write + + + GPIO41_EDGE_LOW + [6:6] + read-write + + + GPIO41_LEVEL_HIGH + [5:5] + read-write + + + GPIO41_LEVEL_LOW + [4:4] + read-write + + + GPIO40_EDGE_HIGH + [3:3] + read-write + + + GPIO40_EDGE_LOW + [2:2] + read-write + + + GPIO40_LEVEL_HIGH + [1:1] + read-write + + + GPIO40_LEVEL_LOW + [0:0] + read-write + + + + + PROC1_INTS0 + 0x000002c0 + Interrupt status after masking & forcing for proc1 + 0x00000000 + + + GPIO7_EDGE_HIGH + [31:31] + read-only + + + GPIO7_EDGE_LOW + [30:30] + read-only + + + GPIO7_LEVEL_HIGH + [29:29] + read-only + + + GPIO7_LEVEL_LOW + [28:28] + read-only + + + GPIO6_EDGE_HIGH + [27:27] + read-only + + + GPIO6_EDGE_LOW + [26:26] + read-only + + + GPIO6_LEVEL_HIGH + [25:25] + read-only + + + GPIO6_LEVEL_LOW + [24:24] + read-only + + + GPIO5_EDGE_HIGH + [23:23] + read-only + + + GPIO5_EDGE_LOW + [22:22] + read-only + + + GPIO5_LEVEL_HIGH + [21:21] + read-only + + + GPIO5_LEVEL_LOW + [20:20] + read-only + + + GPIO4_EDGE_HIGH + [19:19] + read-only + + + GPIO4_EDGE_LOW + [18:18] + read-only + + + GPIO4_LEVEL_HIGH + [17:17] + read-only + + + GPIO4_LEVEL_LOW + [16:16] + read-only + + + GPIO3_EDGE_HIGH + [15:15] + read-only + + + GPIO3_EDGE_LOW + [14:14] + read-only + + + GPIO3_LEVEL_HIGH + [13:13] + read-only + + + GPIO3_LEVEL_LOW + [12:12] + read-only + + + GPIO2_EDGE_HIGH + [11:11] + read-only + + + GPIO2_EDGE_LOW + [10:10] + read-only + + + GPIO2_LEVEL_HIGH + [9:9] + read-only + + + GPIO2_LEVEL_LOW + [8:8] + read-only + + + GPIO1_EDGE_HIGH + [7:7] + read-only + + + GPIO1_EDGE_LOW + [6:6] + read-only + + + GPIO1_LEVEL_HIGH + [5:5] + read-only + + + GPIO1_LEVEL_LOW + [4:4] + read-only + + + GPIO0_EDGE_HIGH + [3:3] + read-only + + + GPIO0_EDGE_LOW + [2:2] + read-only + + + GPIO0_LEVEL_HIGH + [1:1] + read-only + + + GPIO0_LEVEL_LOW + [0:0] + read-only + + + + + PROC1_INTS1 + 0x000002c4 + Interrupt status after masking & forcing for proc1 + 0x00000000 + + + GPIO15_EDGE_HIGH + [31:31] + read-only + + + GPIO15_EDGE_LOW + [30:30] + read-only + + + GPIO15_LEVEL_HIGH + [29:29] + read-only + + + GPIO15_LEVEL_LOW + [28:28] + read-only + + + GPIO14_EDGE_HIGH + [27:27] + read-only + + + GPIO14_EDGE_LOW + [26:26] + read-only + + + GPIO14_LEVEL_HIGH + [25:25] + read-only + + + GPIO14_LEVEL_LOW + [24:24] + read-only + + + GPIO13_EDGE_HIGH + [23:23] + read-only + + + GPIO13_EDGE_LOW + [22:22] + read-only + + + GPIO13_LEVEL_HIGH + [21:21] + read-only + + + GPIO13_LEVEL_LOW + [20:20] + read-only + + + GPIO12_EDGE_HIGH + [19:19] + read-only + + + GPIO12_EDGE_LOW + [18:18] + read-only + + + GPIO12_LEVEL_HIGH + [17:17] + read-only + + + GPIO12_LEVEL_LOW + [16:16] + read-only + + + GPIO11_EDGE_HIGH + [15:15] + read-only + + + GPIO11_EDGE_LOW + [14:14] + read-only + + + GPIO11_LEVEL_HIGH + [13:13] + read-only + + + GPIO11_LEVEL_LOW + [12:12] + read-only + + + GPIO10_EDGE_HIGH + [11:11] + read-only + + + GPIO10_EDGE_LOW + [10:10] + read-only + + + GPIO10_LEVEL_HIGH + [9:9] + read-only + + + GPIO10_LEVEL_LOW + [8:8] + read-only + + + GPIO9_EDGE_HIGH + [7:7] + read-only + + + GPIO9_EDGE_LOW + [6:6] + read-only + + + GPIO9_LEVEL_HIGH + [5:5] + read-only + + + GPIO9_LEVEL_LOW + [4:4] + read-only + + + GPIO8_EDGE_HIGH + [3:3] + read-only + + + GPIO8_EDGE_LOW + [2:2] + read-only + + + GPIO8_LEVEL_HIGH + [1:1] + read-only + + + GPIO8_LEVEL_LOW + [0:0] + read-only + + + + + PROC1_INTS2 + 0x000002c8 + Interrupt status after masking & forcing for proc1 + 0x00000000 + + + GPIO23_EDGE_HIGH + [31:31] + read-only + + + GPIO23_EDGE_LOW + [30:30] + read-only + + + GPIO23_LEVEL_HIGH + [29:29] + read-only + + + GPIO23_LEVEL_LOW + [28:28] + read-only + + + GPIO22_EDGE_HIGH + [27:27] + read-only + + + GPIO22_EDGE_LOW + [26:26] + read-only + + + GPIO22_LEVEL_HIGH + [25:25] + read-only + + + GPIO22_LEVEL_LOW + [24:24] + read-only + + + GPIO21_EDGE_HIGH + [23:23] + read-only + + + GPIO21_EDGE_LOW + [22:22] + read-only + + + GPIO21_LEVEL_HIGH + [21:21] + read-only + + + GPIO21_LEVEL_LOW + [20:20] + read-only + + + GPIO20_EDGE_HIGH + [19:19] + read-only + + + GPIO20_EDGE_LOW + [18:18] + read-only + + + GPIO20_LEVEL_HIGH + [17:17] + read-only + + + GPIO20_LEVEL_LOW + [16:16] + read-only + + + GPIO19_EDGE_HIGH + [15:15] + read-only + + + GPIO19_EDGE_LOW + [14:14] + read-only + + + GPIO19_LEVEL_HIGH + [13:13] + read-only + + + GPIO19_LEVEL_LOW + [12:12] + read-only + + + GPIO18_EDGE_HIGH + [11:11] + read-only + + + GPIO18_EDGE_LOW + [10:10] + read-only + + + GPIO18_LEVEL_HIGH + [9:9] + read-only + + + GPIO18_LEVEL_LOW + [8:8] + read-only + + + GPIO17_EDGE_HIGH + [7:7] + read-only + + + GPIO17_EDGE_LOW + [6:6] + read-only + + + GPIO17_LEVEL_HIGH + [5:5] + read-only + + + GPIO17_LEVEL_LOW + [4:4] + read-only + + + GPIO16_EDGE_HIGH + [3:3] + read-only + + + GPIO16_EDGE_LOW + [2:2] + read-only + + + GPIO16_LEVEL_HIGH + [1:1] + read-only + + + GPIO16_LEVEL_LOW + [0:0] + read-only + + + + + PROC1_INTS3 + 0x000002cc + Interrupt status after masking & forcing for proc1 + 0x00000000 + + + GPIO31_EDGE_HIGH + [31:31] + read-only + + + GPIO31_EDGE_LOW + [30:30] + read-only + + + GPIO31_LEVEL_HIGH + [29:29] + read-only + + + GPIO31_LEVEL_LOW + [28:28] + read-only + + + GPIO30_EDGE_HIGH + [27:27] + read-only + + + GPIO30_EDGE_LOW + [26:26] + read-only + + + GPIO30_LEVEL_HIGH + [25:25] + read-only + + + GPIO30_LEVEL_LOW + [24:24] + read-only + + + GPIO29_EDGE_HIGH + [23:23] + read-only + + + GPIO29_EDGE_LOW + [22:22] + read-only + + + GPIO29_LEVEL_HIGH + [21:21] + read-only + + + GPIO29_LEVEL_LOW + [20:20] + read-only + + + GPIO28_EDGE_HIGH + [19:19] + read-only + + + GPIO28_EDGE_LOW + [18:18] + read-only + + + GPIO28_LEVEL_HIGH + [17:17] + read-only + + + GPIO28_LEVEL_LOW + [16:16] + read-only + + + GPIO27_EDGE_HIGH + [15:15] + read-only + + + GPIO27_EDGE_LOW + [14:14] + read-only + + + GPIO27_LEVEL_HIGH + [13:13] + read-only + + + GPIO27_LEVEL_LOW + [12:12] + read-only + + + GPIO26_EDGE_HIGH + [11:11] + read-only + + + GPIO26_EDGE_LOW + [10:10] + read-only + + + GPIO26_LEVEL_HIGH + [9:9] + read-only + + + GPIO26_LEVEL_LOW + [8:8] + read-only + + + GPIO25_EDGE_HIGH + [7:7] + read-only + + + GPIO25_EDGE_LOW + [6:6] + read-only + + + GPIO25_LEVEL_HIGH + [5:5] + read-only + + + GPIO25_LEVEL_LOW + [4:4] + read-only + + + GPIO24_EDGE_HIGH + [3:3] + read-only + + + GPIO24_EDGE_LOW + [2:2] + read-only + + + GPIO24_LEVEL_HIGH + [1:1] + read-only + + + GPIO24_LEVEL_LOW + [0:0] + read-only + + + + + PROC1_INTS4 + 0x000002d0 + Interrupt status after masking & forcing for proc1 + 0x00000000 + + + GPIO39_EDGE_HIGH + [31:31] + read-only + + + GPIO39_EDGE_LOW + [30:30] + read-only + + + GPIO39_LEVEL_HIGH + [29:29] + read-only + + + GPIO39_LEVEL_LOW + [28:28] + read-only + + + GPIO38_EDGE_HIGH + [27:27] + read-only + + + GPIO38_EDGE_LOW + [26:26] + read-only + + + GPIO38_LEVEL_HIGH + [25:25] + read-only + + + GPIO38_LEVEL_LOW + [24:24] + read-only + + + GPIO37_EDGE_HIGH + [23:23] + read-only + + + GPIO37_EDGE_LOW + [22:22] + read-only + + + GPIO37_LEVEL_HIGH + [21:21] + read-only + + + GPIO37_LEVEL_LOW + [20:20] + read-only + + + GPIO36_EDGE_HIGH + [19:19] + read-only + + + GPIO36_EDGE_LOW + [18:18] + read-only + + + GPIO36_LEVEL_HIGH + [17:17] + read-only + + + GPIO36_LEVEL_LOW + [16:16] + read-only + + + GPIO35_EDGE_HIGH + [15:15] + read-only + + + GPIO35_EDGE_LOW + [14:14] + read-only + + + GPIO35_LEVEL_HIGH + [13:13] + read-only + + + GPIO35_LEVEL_LOW + [12:12] + read-only + + + GPIO34_EDGE_HIGH + [11:11] + read-only + + + GPIO34_EDGE_LOW + [10:10] + read-only + + + GPIO34_LEVEL_HIGH + [9:9] + read-only + + + GPIO34_LEVEL_LOW + [8:8] + read-only + + + GPIO33_EDGE_HIGH + [7:7] + read-only + + + GPIO33_EDGE_LOW + [6:6] + read-only + + + GPIO33_LEVEL_HIGH + [5:5] + read-only + + + GPIO33_LEVEL_LOW + [4:4] + read-only + + + GPIO32_EDGE_HIGH + [3:3] + read-only + + + GPIO32_EDGE_LOW + [2:2] + read-only + + + GPIO32_LEVEL_HIGH + [1:1] + read-only + + + GPIO32_LEVEL_LOW + [0:0] + read-only + + + + + PROC1_INTS5 + 0x000002d4 + Interrupt status after masking & forcing for proc1 + 0x00000000 + + + GPIO47_EDGE_HIGH + [31:31] + read-only + + + GPIO47_EDGE_LOW + [30:30] + read-only + + + GPIO47_LEVEL_HIGH + [29:29] + read-only + + + GPIO47_LEVEL_LOW + [28:28] + read-only + + + GPIO46_EDGE_HIGH + [27:27] + read-only + + + GPIO46_EDGE_LOW + [26:26] + read-only + + + GPIO46_LEVEL_HIGH + [25:25] + read-only + + + GPIO46_LEVEL_LOW + [24:24] + read-only + + + GPIO45_EDGE_HIGH + [23:23] + read-only + + + GPIO45_EDGE_LOW + [22:22] + read-only + + + GPIO45_LEVEL_HIGH + [21:21] + read-only + + + GPIO45_LEVEL_LOW + [20:20] + read-only + + + GPIO44_EDGE_HIGH + [19:19] + read-only + + + GPIO44_EDGE_LOW + [18:18] + read-only + + + GPIO44_LEVEL_HIGH + [17:17] + read-only + + + GPIO44_LEVEL_LOW + [16:16] + read-only + + + GPIO43_EDGE_HIGH + [15:15] + read-only + + + GPIO43_EDGE_LOW + [14:14] + read-only + + + GPIO43_LEVEL_HIGH + [13:13] + read-only + + + GPIO43_LEVEL_LOW + [12:12] + read-only + + + GPIO42_EDGE_HIGH + [11:11] + read-only + + + GPIO42_EDGE_LOW + [10:10] + read-only + + + GPIO42_LEVEL_HIGH + [9:9] + read-only + + + GPIO42_LEVEL_LOW + [8:8] + read-only + + + GPIO41_EDGE_HIGH + [7:7] + read-only + + + GPIO41_EDGE_LOW + [6:6] + read-only + + + GPIO41_LEVEL_HIGH + [5:5] + read-only + + + GPIO41_LEVEL_LOW + [4:4] + read-only + + + GPIO40_EDGE_HIGH + [3:3] + read-only + + + GPIO40_EDGE_LOW + [2:2] + read-only + + + GPIO40_LEVEL_HIGH + [1:1] + read-only + + + GPIO40_LEVEL_LOW + [0:0] + read-only + + + + + DORMANT_WAKE_INTE0 + 0x000002d8 + Interrupt Enable for dormant_wake + 0x00000000 + + + GPIO7_EDGE_HIGH + [31:31] + read-write + + + GPIO7_EDGE_LOW + [30:30] + read-write + + + GPIO7_LEVEL_HIGH + [29:29] + read-write + + + GPIO7_LEVEL_LOW + [28:28] + read-write + + + GPIO6_EDGE_HIGH + [27:27] + read-write + + + GPIO6_EDGE_LOW + [26:26] + read-write + + + GPIO6_LEVEL_HIGH + [25:25] + read-write + + + GPIO6_LEVEL_LOW + [24:24] + read-write + + + GPIO5_EDGE_HIGH + [23:23] + read-write + + + GPIO5_EDGE_LOW + [22:22] + read-write + + + GPIO5_LEVEL_HIGH + [21:21] + read-write + + + GPIO5_LEVEL_LOW + [20:20] + read-write + + + GPIO4_EDGE_HIGH + [19:19] + read-write + + + GPIO4_EDGE_LOW + [18:18] + read-write + + + GPIO4_LEVEL_HIGH + [17:17] + read-write + + + GPIO4_LEVEL_LOW + [16:16] + read-write + + + GPIO3_EDGE_HIGH + [15:15] + read-write + + + GPIO3_EDGE_LOW + [14:14] + read-write + + + GPIO3_LEVEL_HIGH + [13:13] + read-write + + + GPIO3_LEVEL_LOW + [12:12] + read-write + + + GPIO2_EDGE_HIGH + [11:11] + read-write + + + GPIO2_EDGE_LOW + [10:10] + read-write + + + GPIO2_LEVEL_HIGH + [9:9] + read-write + + + GPIO2_LEVEL_LOW + [8:8] + read-write + + + GPIO1_EDGE_HIGH + [7:7] + read-write + + + GPIO1_EDGE_LOW + [6:6] + read-write + + + GPIO1_LEVEL_HIGH + [5:5] + read-write + + + GPIO1_LEVEL_LOW + [4:4] + read-write + + + GPIO0_EDGE_HIGH + [3:3] + read-write + + + GPIO0_EDGE_LOW + [2:2] + read-write + + + GPIO0_LEVEL_HIGH + [1:1] + read-write + + + GPIO0_LEVEL_LOW + [0:0] + read-write + + + + + DORMANT_WAKE_INTE1 + 0x000002dc + Interrupt Enable for dormant_wake + 0x00000000 + + + GPIO15_EDGE_HIGH + [31:31] + read-write + + + GPIO15_EDGE_LOW + [30:30] + read-write + + + GPIO15_LEVEL_HIGH + [29:29] + read-write + + + GPIO15_LEVEL_LOW + [28:28] + read-write + + + GPIO14_EDGE_HIGH + [27:27] + read-write + + + GPIO14_EDGE_LOW + [26:26] + read-write + + + GPIO14_LEVEL_HIGH + [25:25] + read-write + + + GPIO14_LEVEL_LOW + [24:24] + read-write + + + GPIO13_EDGE_HIGH + [23:23] + read-write + + + GPIO13_EDGE_LOW + [22:22] + read-write + + + GPIO13_LEVEL_HIGH + [21:21] + read-write + + + GPIO13_LEVEL_LOW + [20:20] + read-write + + + GPIO12_EDGE_HIGH + [19:19] + read-write + + + GPIO12_EDGE_LOW + [18:18] + read-write + + + GPIO12_LEVEL_HIGH + [17:17] + read-write + + + GPIO12_LEVEL_LOW + [16:16] + read-write + + + GPIO11_EDGE_HIGH + [15:15] + read-write + + + GPIO11_EDGE_LOW + [14:14] + read-write + + + GPIO11_LEVEL_HIGH + [13:13] + read-write + + + GPIO11_LEVEL_LOW + [12:12] + read-write + + + GPIO10_EDGE_HIGH + [11:11] + read-write + + + GPIO10_EDGE_LOW + [10:10] + read-write + + + GPIO10_LEVEL_HIGH + [9:9] + read-write + + + GPIO10_LEVEL_LOW + [8:8] + read-write + + + GPIO9_EDGE_HIGH + [7:7] + read-write + + + GPIO9_EDGE_LOW + [6:6] + read-write + + + GPIO9_LEVEL_HIGH + [5:5] + read-write + + + GPIO9_LEVEL_LOW + [4:4] + read-write + + + GPIO8_EDGE_HIGH + [3:3] + read-write + + + GPIO8_EDGE_LOW + [2:2] + read-write + + + GPIO8_LEVEL_HIGH + [1:1] + read-write + + + GPIO8_LEVEL_LOW + [0:0] + read-write + + + + + DORMANT_WAKE_INTE2 + 0x000002e0 + Interrupt Enable for dormant_wake + 0x00000000 + + + GPIO23_EDGE_HIGH + [31:31] + read-write + + + GPIO23_EDGE_LOW + [30:30] + read-write + + + GPIO23_LEVEL_HIGH + [29:29] + read-write + + + GPIO23_LEVEL_LOW + [28:28] + read-write + + + GPIO22_EDGE_HIGH + [27:27] + read-write + + + GPIO22_EDGE_LOW + [26:26] + read-write + + + GPIO22_LEVEL_HIGH + [25:25] + read-write + + + GPIO22_LEVEL_LOW + [24:24] + read-write + + + GPIO21_EDGE_HIGH + [23:23] + read-write + + + GPIO21_EDGE_LOW + [22:22] + read-write + + + GPIO21_LEVEL_HIGH + [21:21] + read-write + + + GPIO21_LEVEL_LOW + [20:20] + read-write + + + GPIO20_EDGE_HIGH + [19:19] + read-write + + + GPIO20_EDGE_LOW + [18:18] + read-write + + + GPIO20_LEVEL_HIGH + [17:17] + read-write + + + GPIO20_LEVEL_LOW + [16:16] + read-write + + + GPIO19_EDGE_HIGH + [15:15] + read-write + + + GPIO19_EDGE_LOW + [14:14] + read-write + + + GPIO19_LEVEL_HIGH + [13:13] + read-write + + + GPIO19_LEVEL_LOW + [12:12] + read-write + + + GPIO18_EDGE_HIGH + [11:11] + read-write + + + GPIO18_EDGE_LOW + [10:10] + read-write + + + GPIO18_LEVEL_HIGH + [9:9] + read-write + + + GPIO18_LEVEL_LOW + [8:8] + read-write + + + GPIO17_EDGE_HIGH + [7:7] + read-write + + + GPIO17_EDGE_LOW + [6:6] + read-write + + + GPIO17_LEVEL_HIGH + [5:5] + read-write + + + GPIO17_LEVEL_LOW + [4:4] + read-write + + + GPIO16_EDGE_HIGH + [3:3] + read-write + + + GPIO16_EDGE_LOW + [2:2] + read-write + + + GPIO16_LEVEL_HIGH + [1:1] + read-write + + + GPIO16_LEVEL_LOW + [0:0] + read-write + + + + + DORMANT_WAKE_INTE3 + 0x000002e4 + Interrupt Enable for dormant_wake + 0x00000000 + + + GPIO31_EDGE_HIGH + [31:31] + read-write + + + GPIO31_EDGE_LOW + [30:30] + read-write + + + GPIO31_LEVEL_HIGH + [29:29] + read-write + + + GPIO31_LEVEL_LOW + [28:28] + read-write + + + GPIO30_EDGE_HIGH + [27:27] + read-write + + + GPIO30_EDGE_LOW + [26:26] + read-write + + + GPIO30_LEVEL_HIGH + [25:25] + read-write + + + GPIO30_LEVEL_LOW + [24:24] + read-write + + + GPIO29_EDGE_HIGH + [23:23] + read-write + + + GPIO29_EDGE_LOW + [22:22] + read-write + + + GPIO29_LEVEL_HIGH + [21:21] + read-write + + + GPIO29_LEVEL_LOW + [20:20] + read-write + + + GPIO28_EDGE_HIGH + [19:19] + read-write + + + GPIO28_EDGE_LOW + [18:18] + read-write + + + GPIO28_LEVEL_HIGH + [17:17] + read-write + + + GPIO28_LEVEL_LOW + [16:16] + read-write + + + GPIO27_EDGE_HIGH + [15:15] + read-write + + + GPIO27_EDGE_LOW + [14:14] + read-write + + + GPIO27_LEVEL_HIGH + [13:13] + read-write + + + GPIO27_LEVEL_LOW + [12:12] + read-write + + + GPIO26_EDGE_HIGH + [11:11] + read-write + + + GPIO26_EDGE_LOW + [10:10] + read-write + + + GPIO26_LEVEL_HIGH + [9:9] + read-write + + + GPIO26_LEVEL_LOW + [8:8] + read-write + + + GPIO25_EDGE_HIGH + [7:7] + read-write + + + GPIO25_EDGE_LOW + [6:6] + read-write + + + GPIO25_LEVEL_HIGH + [5:5] + read-write + + + GPIO25_LEVEL_LOW + [4:4] + read-write + + + GPIO24_EDGE_HIGH + [3:3] + read-write + + + GPIO24_EDGE_LOW + [2:2] + read-write + + + GPIO24_LEVEL_HIGH + [1:1] + read-write + + + GPIO24_LEVEL_LOW + [0:0] + read-write + + + + + DORMANT_WAKE_INTE4 + 0x000002e8 + Interrupt Enable for dormant_wake + 0x00000000 + + + GPIO39_EDGE_HIGH + [31:31] + read-write + + + GPIO39_EDGE_LOW + [30:30] + read-write + + + GPIO39_LEVEL_HIGH + [29:29] + read-write + + + GPIO39_LEVEL_LOW + [28:28] + read-write + + + GPIO38_EDGE_HIGH + [27:27] + read-write + + + GPIO38_EDGE_LOW + [26:26] + read-write + + + GPIO38_LEVEL_HIGH + [25:25] + read-write + + + GPIO38_LEVEL_LOW + [24:24] + read-write + + + GPIO37_EDGE_HIGH + [23:23] + read-write + + + GPIO37_EDGE_LOW + [22:22] + read-write + + + GPIO37_LEVEL_HIGH + [21:21] + read-write + + + GPIO37_LEVEL_LOW + [20:20] + read-write + + + GPIO36_EDGE_HIGH + [19:19] + read-write + + + GPIO36_EDGE_LOW + [18:18] + read-write + + + GPIO36_LEVEL_HIGH + [17:17] + read-write + + + GPIO36_LEVEL_LOW + [16:16] + read-write + + + GPIO35_EDGE_HIGH + [15:15] + read-write + + + GPIO35_EDGE_LOW + [14:14] + read-write + + + GPIO35_LEVEL_HIGH + [13:13] + read-write + + + GPIO35_LEVEL_LOW + [12:12] + read-write + + + GPIO34_EDGE_HIGH + [11:11] + read-write + + + GPIO34_EDGE_LOW + [10:10] + read-write + + + GPIO34_LEVEL_HIGH + [9:9] + read-write + + + GPIO34_LEVEL_LOW + [8:8] + read-write + + + GPIO33_EDGE_HIGH + [7:7] + read-write + + + GPIO33_EDGE_LOW + [6:6] + read-write + + + GPIO33_LEVEL_HIGH + [5:5] + read-write + + + GPIO33_LEVEL_LOW + [4:4] + read-write + + + GPIO32_EDGE_HIGH + [3:3] + read-write + + + GPIO32_EDGE_LOW + [2:2] + read-write + + + GPIO32_LEVEL_HIGH + [1:1] + read-write + + + GPIO32_LEVEL_LOW + [0:0] + read-write + + + + + DORMANT_WAKE_INTE5 + 0x000002ec + Interrupt Enable for dormant_wake + 0x00000000 + + + GPIO47_EDGE_HIGH + [31:31] + read-write + + + GPIO47_EDGE_LOW + [30:30] + read-write + + + GPIO47_LEVEL_HIGH + [29:29] + read-write + + + GPIO47_LEVEL_LOW + [28:28] + read-write + + + GPIO46_EDGE_HIGH + [27:27] + read-write + + + GPIO46_EDGE_LOW + [26:26] + read-write + + + GPIO46_LEVEL_HIGH + [25:25] + read-write + + + GPIO46_LEVEL_LOW + [24:24] + read-write + + + GPIO45_EDGE_HIGH + [23:23] + read-write + + + GPIO45_EDGE_LOW + [22:22] + read-write + + + GPIO45_LEVEL_HIGH + [21:21] + read-write + + + GPIO45_LEVEL_LOW + [20:20] + read-write + + + GPIO44_EDGE_HIGH + [19:19] + read-write + + + GPIO44_EDGE_LOW + [18:18] + read-write + + + GPIO44_LEVEL_HIGH + [17:17] + read-write + + + GPIO44_LEVEL_LOW + [16:16] + read-write + + + GPIO43_EDGE_HIGH + [15:15] + read-write + + + GPIO43_EDGE_LOW + [14:14] + read-write + + + GPIO43_LEVEL_HIGH + [13:13] + read-write + + + GPIO43_LEVEL_LOW + [12:12] + read-write + + + GPIO42_EDGE_HIGH + [11:11] + read-write + + + GPIO42_EDGE_LOW + [10:10] + read-write + + + GPIO42_LEVEL_HIGH + [9:9] + read-write + + + GPIO42_LEVEL_LOW + [8:8] + read-write + + + GPIO41_EDGE_HIGH + [7:7] + read-write + + + GPIO41_EDGE_LOW + [6:6] + read-write + + + GPIO41_LEVEL_HIGH + [5:5] + read-write + + + GPIO41_LEVEL_LOW + [4:4] + read-write + + + GPIO40_EDGE_HIGH + [3:3] + read-write + + + GPIO40_EDGE_LOW + [2:2] + read-write + + + GPIO40_LEVEL_HIGH + [1:1] + read-write + + + GPIO40_LEVEL_LOW + [0:0] + read-write + + + + + DORMANT_WAKE_INTF0 + 0x000002f0 + Interrupt Force for dormant_wake + 0x00000000 + + + GPIO7_EDGE_HIGH + [31:31] + read-write + + + GPIO7_EDGE_LOW + [30:30] + read-write + + + GPIO7_LEVEL_HIGH + [29:29] + read-write + + + GPIO7_LEVEL_LOW + [28:28] + read-write + + + GPIO6_EDGE_HIGH + [27:27] + read-write + + + GPIO6_EDGE_LOW + [26:26] + read-write + + + GPIO6_LEVEL_HIGH + [25:25] + read-write + + + GPIO6_LEVEL_LOW + [24:24] + read-write + + + GPIO5_EDGE_HIGH + [23:23] + read-write + + + GPIO5_EDGE_LOW + [22:22] + read-write + + + GPIO5_LEVEL_HIGH + [21:21] + read-write + + + GPIO5_LEVEL_LOW + [20:20] + read-write + + + GPIO4_EDGE_HIGH + [19:19] + read-write + + + GPIO4_EDGE_LOW + [18:18] + read-write + + + GPIO4_LEVEL_HIGH + [17:17] + read-write + + + GPIO4_LEVEL_LOW + [16:16] + read-write + + + GPIO3_EDGE_HIGH + [15:15] + read-write + + + GPIO3_EDGE_LOW + [14:14] + read-write + + + GPIO3_LEVEL_HIGH + [13:13] + read-write + + + GPIO3_LEVEL_LOW + [12:12] + read-write + + + GPIO2_EDGE_HIGH + [11:11] + read-write + + + GPIO2_EDGE_LOW + [10:10] + read-write + + + GPIO2_LEVEL_HIGH + [9:9] + read-write + + + GPIO2_LEVEL_LOW + [8:8] + read-write + + + GPIO1_EDGE_HIGH + [7:7] + read-write + + + GPIO1_EDGE_LOW + [6:6] + read-write + + + GPIO1_LEVEL_HIGH + [5:5] + read-write + + + GPIO1_LEVEL_LOW + [4:4] + read-write + + + GPIO0_EDGE_HIGH + [3:3] + read-write + + + GPIO0_EDGE_LOW + [2:2] + read-write + + + GPIO0_LEVEL_HIGH + [1:1] + read-write + + + GPIO0_LEVEL_LOW + [0:0] + read-write + + + + + DORMANT_WAKE_INTF1 + 0x000002f4 + Interrupt Force for dormant_wake + 0x00000000 + + + GPIO15_EDGE_HIGH + [31:31] + read-write + + + GPIO15_EDGE_LOW + [30:30] + read-write + + + GPIO15_LEVEL_HIGH + [29:29] + read-write + + + GPIO15_LEVEL_LOW + [28:28] + read-write + + + GPIO14_EDGE_HIGH + [27:27] + read-write + + + GPIO14_EDGE_LOW + [26:26] + read-write + + + GPIO14_LEVEL_HIGH + [25:25] + read-write + + + GPIO14_LEVEL_LOW + [24:24] + read-write + + + GPIO13_EDGE_HIGH + [23:23] + read-write + + + GPIO13_EDGE_LOW + [22:22] + read-write + + + GPIO13_LEVEL_HIGH + [21:21] + read-write + + + GPIO13_LEVEL_LOW + [20:20] + read-write + + + GPIO12_EDGE_HIGH + [19:19] + read-write + + + GPIO12_EDGE_LOW + [18:18] + read-write + + + GPIO12_LEVEL_HIGH + [17:17] + read-write + + + GPIO12_LEVEL_LOW + [16:16] + read-write + + + GPIO11_EDGE_HIGH + [15:15] + read-write + + + GPIO11_EDGE_LOW + [14:14] + read-write + + + GPIO11_LEVEL_HIGH + [13:13] + read-write + + + GPIO11_LEVEL_LOW + [12:12] + read-write + + + GPIO10_EDGE_HIGH + [11:11] + read-write + + + GPIO10_EDGE_LOW + [10:10] + read-write + + + GPIO10_LEVEL_HIGH + [9:9] + read-write + + + GPIO10_LEVEL_LOW + [8:8] + read-write + + + GPIO9_EDGE_HIGH + [7:7] + read-write + + + GPIO9_EDGE_LOW + [6:6] + read-write + + + GPIO9_LEVEL_HIGH + [5:5] + read-write + + + GPIO9_LEVEL_LOW + [4:4] + read-write + + + GPIO8_EDGE_HIGH + [3:3] + read-write + + + GPIO8_EDGE_LOW + [2:2] + read-write + + + GPIO8_LEVEL_HIGH + [1:1] + read-write + + + GPIO8_LEVEL_LOW + [0:0] + read-write + + + + + DORMANT_WAKE_INTF2 + 0x000002f8 + Interrupt Force for dormant_wake + 0x00000000 + + + GPIO23_EDGE_HIGH + [31:31] + read-write + + + GPIO23_EDGE_LOW + [30:30] + read-write + + + GPIO23_LEVEL_HIGH + [29:29] + read-write + + + GPIO23_LEVEL_LOW + [28:28] + read-write + + + GPIO22_EDGE_HIGH + [27:27] + read-write + + + GPIO22_EDGE_LOW + [26:26] + read-write + + + GPIO22_LEVEL_HIGH + [25:25] + read-write + + + GPIO22_LEVEL_LOW + [24:24] + read-write + + + GPIO21_EDGE_HIGH + [23:23] + read-write + + + GPIO21_EDGE_LOW + [22:22] + read-write + + + GPIO21_LEVEL_HIGH + [21:21] + read-write + + + GPIO21_LEVEL_LOW + [20:20] + read-write + + + GPIO20_EDGE_HIGH + [19:19] + read-write + + + GPIO20_EDGE_LOW + [18:18] + read-write + + + GPIO20_LEVEL_HIGH + [17:17] + read-write + + + GPIO20_LEVEL_LOW + [16:16] + read-write + + + GPIO19_EDGE_HIGH + [15:15] + read-write + + + GPIO19_EDGE_LOW + [14:14] + read-write + + + GPIO19_LEVEL_HIGH + [13:13] + read-write + + + GPIO19_LEVEL_LOW + [12:12] + read-write + + + GPIO18_EDGE_HIGH + [11:11] + read-write + + + GPIO18_EDGE_LOW + [10:10] + read-write + + + GPIO18_LEVEL_HIGH + [9:9] + read-write + + + GPIO18_LEVEL_LOW + [8:8] + read-write + + + GPIO17_EDGE_HIGH + [7:7] + read-write + + + GPIO17_EDGE_LOW + [6:6] + read-write + + + GPIO17_LEVEL_HIGH + [5:5] + read-write + + + GPIO17_LEVEL_LOW + [4:4] + read-write + + + GPIO16_EDGE_HIGH + [3:3] + read-write + + + GPIO16_EDGE_LOW + [2:2] + read-write + + + GPIO16_LEVEL_HIGH + [1:1] + read-write + + + GPIO16_LEVEL_LOW + [0:0] + read-write + + + + + DORMANT_WAKE_INTF3 + 0x000002fc + Interrupt Force for dormant_wake + 0x00000000 + + + GPIO31_EDGE_HIGH + [31:31] + read-write + + + GPIO31_EDGE_LOW + [30:30] + read-write + + + GPIO31_LEVEL_HIGH + [29:29] + read-write + + + GPIO31_LEVEL_LOW + [28:28] + read-write + + + GPIO30_EDGE_HIGH + [27:27] + read-write + + + GPIO30_EDGE_LOW + [26:26] + read-write + + + GPIO30_LEVEL_HIGH + [25:25] + read-write + + + GPIO30_LEVEL_LOW + [24:24] + read-write + + + GPIO29_EDGE_HIGH + [23:23] + read-write + + + GPIO29_EDGE_LOW + [22:22] + read-write + + + GPIO29_LEVEL_HIGH + [21:21] + read-write + + + GPIO29_LEVEL_LOW + [20:20] + read-write + + + GPIO28_EDGE_HIGH + [19:19] + read-write + + + GPIO28_EDGE_LOW + [18:18] + read-write + + + GPIO28_LEVEL_HIGH + [17:17] + read-write + + + GPIO28_LEVEL_LOW + [16:16] + read-write + + + GPIO27_EDGE_HIGH + [15:15] + read-write + + + GPIO27_EDGE_LOW + [14:14] + read-write + + + GPIO27_LEVEL_HIGH + [13:13] + read-write + + + GPIO27_LEVEL_LOW + [12:12] + read-write + + + GPIO26_EDGE_HIGH + [11:11] + read-write + + + GPIO26_EDGE_LOW + [10:10] + read-write + + + GPIO26_LEVEL_HIGH + [9:9] + read-write + + + GPIO26_LEVEL_LOW + [8:8] + read-write + + + GPIO25_EDGE_HIGH + [7:7] + read-write + + + GPIO25_EDGE_LOW + [6:6] + read-write + + + GPIO25_LEVEL_HIGH + [5:5] + read-write + + + GPIO25_LEVEL_LOW + [4:4] + read-write + + + GPIO24_EDGE_HIGH + [3:3] + read-write + + + GPIO24_EDGE_LOW + [2:2] + read-write + + + GPIO24_LEVEL_HIGH + [1:1] + read-write + + + GPIO24_LEVEL_LOW + [0:0] + read-write + + + + + DORMANT_WAKE_INTF4 + 0x00000300 + Interrupt Force for dormant_wake + 0x00000000 + + + GPIO39_EDGE_HIGH + [31:31] + read-write + + + GPIO39_EDGE_LOW + [30:30] + read-write + + + GPIO39_LEVEL_HIGH + [29:29] + read-write + + + GPIO39_LEVEL_LOW + [28:28] + read-write + + + GPIO38_EDGE_HIGH + [27:27] + read-write + + + GPIO38_EDGE_LOW + [26:26] + read-write + + + GPIO38_LEVEL_HIGH + [25:25] + read-write + + + GPIO38_LEVEL_LOW + [24:24] + read-write + + + GPIO37_EDGE_HIGH + [23:23] + read-write + + + GPIO37_EDGE_LOW + [22:22] + read-write + + + GPIO37_LEVEL_HIGH + [21:21] + read-write + + + GPIO37_LEVEL_LOW + [20:20] + read-write + + + GPIO36_EDGE_HIGH + [19:19] + read-write + + + GPIO36_EDGE_LOW + [18:18] + read-write + + + GPIO36_LEVEL_HIGH + [17:17] + read-write + + + GPIO36_LEVEL_LOW + [16:16] + read-write + + + GPIO35_EDGE_HIGH + [15:15] + read-write + + + GPIO35_EDGE_LOW + [14:14] + read-write + + + GPIO35_LEVEL_HIGH + [13:13] + read-write + + + GPIO35_LEVEL_LOW + [12:12] + read-write + + + GPIO34_EDGE_HIGH + [11:11] + read-write + + + GPIO34_EDGE_LOW + [10:10] + read-write + + + GPIO34_LEVEL_HIGH + [9:9] + read-write + + + GPIO34_LEVEL_LOW + [8:8] + read-write + + + GPIO33_EDGE_HIGH + [7:7] + read-write + + + GPIO33_EDGE_LOW + [6:6] + read-write + + + GPIO33_LEVEL_HIGH + [5:5] + read-write + + + GPIO33_LEVEL_LOW + [4:4] + read-write + + + GPIO32_EDGE_HIGH + [3:3] + read-write + + + GPIO32_EDGE_LOW + [2:2] + read-write + + + GPIO32_LEVEL_HIGH + [1:1] + read-write + + + GPIO32_LEVEL_LOW + [0:0] + read-write + + + + + DORMANT_WAKE_INTF5 + 0x00000304 + Interrupt Force for dormant_wake + 0x00000000 + + + GPIO47_EDGE_HIGH + [31:31] + read-write + + + GPIO47_EDGE_LOW + [30:30] + read-write + + + GPIO47_LEVEL_HIGH + [29:29] + read-write + + + GPIO47_LEVEL_LOW + [28:28] + read-write + + + GPIO46_EDGE_HIGH + [27:27] + read-write + + + GPIO46_EDGE_LOW + [26:26] + read-write + + + GPIO46_LEVEL_HIGH + [25:25] + read-write + + + GPIO46_LEVEL_LOW + [24:24] + read-write + + + GPIO45_EDGE_HIGH + [23:23] + read-write + + + GPIO45_EDGE_LOW + [22:22] + read-write + + + GPIO45_LEVEL_HIGH + [21:21] + read-write + + + GPIO45_LEVEL_LOW + [20:20] + read-write + + + GPIO44_EDGE_HIGH + [19:19] + read-write + + + GPIO44_EDGE_LOW + [18:18] + read-write + + + GPIO44_LEVEL_HIGH + [17:17] + read-write + + + GPIO44_LEVEL_LOW + [16:16] + read-write + + + GPIO43_EDGE_HIGH + [15:15] + read-write + + + GPIO43_EDGE_LOW + [14:14] + read-write + + + GPIO43_LEVEL_HIGH + [13:13] + read-write + + + GPIO43_LEVEL_LOW + [12:12] + read-write + + + GPIO42_EDGE_HIGH + [11:11] + read-write + + + GPIO42_EDGE_LOW + [10:10] + read-write + + + GPIO42_LEVEL_HIGH + [9:9] + read-write + + + GPIO42_LEVEL_LOW + [8:8] + read-write + + + GPIO41_EDGE_HIGH + [7:7] + read-write + + + GPIO41_EDGE_LOW + [6:6] + read-write + + + GPIO41_LEVEL_HIGH + [5:5] + read-write + + + GPIO41_LEVEL_LOW + [4:4] + read-write + + + GPIO40_EDGE_HIGH + [3:3] + read-write + + + GPIO40_EDGE_LOW + [2:2] + read-write + + + GPIO40_LEVEL_HIGH + [1:1] + read-write + + + GPIO40_LEVEL_LOW + [0:0] + read-write + + + + + DORMANT_WAKE_INTS0 + 0x00000308 + Interrupt status after masking & forcing for dormant_wake + 0x00000000 + + + GPIO7_EDGE_HIGH + [31:31] + read-only + + + GPIO7_EDGE_LOW + [30:30] + read-only + + + GPIO7_LEVEL_HIGH + [29:29] + read-only + + + GPIO7_LEVEL_LOW + [28:28] + read-only + + + GPIO6_EDGE_HIGH + [27:27] + read-only + + + GPIO6_EDGE_LOW + [26:26] + read-only + + + GPIO6_LEVEL_HIGH + [25:25] + read-only + + + GPIO6_LEVEL_LOW + [24:24] + read-only + + + GPIO5_EDGE_HIGH + [23:23] + read-only + + + GPIO5_EDGE_LOW + [22:22] + read-only + + + GPIO5_LEVEL_HIGH + [21:21] + read-only + + + GPIO5_LEVEL_LOW + [20:20] + read-only + + + GPIO4_EDGE_HIGH + [19:19] + read-only + + + GPIO4_EDGE_LOW + [18:18] + read-only + + + GPIO4_LEVEL_HIGH + [17:17] + read-only + + + GPIO4_LEVEL_LOW + [16:16] + read-only + + + GPIO3_EDGE_HIGH + [15:15] + read-only + + + GPIO3_EDGE_LOW + [14:14] + read-only + + + GPIO3_LEVEL_HIGH + [13:13] + read-only + + + GPIO3_LEVEL_LOW + [12:12] + read-only + + + GPIO2_EDGE_HIGH + [11:11] + read-only + + + GPIO2_EDGE_LOW + [10:10] + read-only + + + GPIO2_LEVEL_HIGH + [9:9] + read-only + + + GPIO2_LEVEL_LOW + [8:8] + read-only + + + GPIO1_EDGE_HIGH + [7:7] + read-only + + + GPIO1_EDGE_LOW + [6:6] + read-only + + + GPIO1_LEVEL_HIGH + [5:5] + read-only + + + GPIO1_LEVEL_LOW + [4:4] + read-only + + + GPIO0_EDGE_HIGH + [3:3] + read-only + + + GPIO0_EDGE_LOW + [2:2] + read-only + + + GPIO0_LEVEL_HIGH + [1:1] + read-only + + + GPIO0_LEVEL_LOW + [0:0] + read-only + + + + + DORMANT_WAKE_INTS1 + 0x0000030c + Interrupt status after masking & forcing for dormant_wake + 0x00000000 + + + GPIO15_EDGE_HIGH + [31:31] + read-only + + + GPIO15_EDGE_LOW + [30:30] + read-only + + + GPIO15_LEVEL_HIGH + [29:29] + read-only + + + GPIO15_LEVEL_LOW + [28:28] + read-only + + + GPIO14_EDGE_HIGH + [27:27] + read-only + + + GPIO14_EDGE_LOW + [26:26] + read-only + + + GPIO14_LEVEL_HIGH + [25:25] + read-only + + + GPIO14_LEVEL_LOW + [24:24] + read-only + + + GPIO13_EDGE_HIGH + [23:23] + read-only + + + GPIO13_EDGE_LOW + [22:22] + read-only + + + GPIO13_LEVEL_HIGH + [21:21] + read-only + + + GPIO13_LEVEL_LOW + [20:20] + read-only + + + GPIO12_EDGE_HIGH + [19:19] + read-only + + + GPIO12_EDGE_LOW + [18:18] + read-only + + + GPIO12_LEVEL_HIGH + [17:17] + read-only + + + GPIO12_LEVEL_LOW + [16:16] + read-only + + + GPIO11_EDGE_HIGH + [15:15] + read-only + + + GPIO11_EDGE_LOW + [14:14] + read-only + + + GPIO11_LEVEL_HIGH + [13:13] + read-only + + + GPIO11_LEVEL_LOW + [12:12] + read-only + + + GPIO10_EDGE_HIGH + [11:11] + read-only + + + GPIO10_EDGE_LOW + [10:10] + read-only + + + GPIO10_LEVEL_HIGH + [9:9] + read-only + + + GPIO10_LEVEL_LOW + [8:8] + read-only + + + GPIO9_EDGE_HIGH + [7:7] + read-only + + + GPIO9_EDGE_LOW + [6:6] + read-only + + + GPIO9_LEVEL_HIGH + [5:5] + read-only + + + GPIO9_LEVEL_LOW + [4:4] + read-only + + + GPIO8_EDGE_HIGH + [3:3] + read-only + + + GPIO8_EDGE_LOW + [2:2] + read-only + + + GPIO8_LEVEL_HIGH + [1:1] + read-only + + + GPIO8_LEVEL_LOW + [0:0] + read-only + + + + + DORMANT_WAKE_INTS2 + 0x00000310 + Interrupt status after masking & forcing for dormant_wake + 0x00000000 + + + GPIO23_EDGE_HIGH + [31:31] + read-only + + + GPIO23_EDGE_LOW + [30:30] + read-only + + + GPIO23_LEVEL_HIGH + [29:29] + read-only + + + GPIO23_LEVEL_LOW + [28:28] + read-only + + + GPIO22_EDGE_HIGH + [27:27] + read-only + + + GPIO22_EDGE_LOW + [26:26] + read-only + + + GPIO22_LEVEL_HIGH + [25:25] + read-only + + + GPIO22_LEVEL_LOW + [24:24] + read-only + + + GPIO21_EDGE_HIGH + [23:23] + read-only + + + GPIO21_EDGE_LOW + [22:22] + read-only + + + GPIO21_LEVEL_HIGH + [21:21] + read-only + + + GPIO21_LEVEL_LOW + [20:20] + read-only + + + GPIO20_EDGE_HIGH + [19:19] + read-only + + + GPIO20_EDGE_LOW + [18:18] + read-only + + + GPIO20_LEVEL_HIGH + [17:17] + read-only + + + GPIO20_LEVEL_LOW + [16:16] + read-only + + + GPIO19_EDGE_HIGH + [15:15] + read-only + + + GPIO19_EDGE_LOW + [14:14] + read-only + + + GPIO19_LEVEL_HIGH + [13:13] + read-only + + + GPIO19_LEVEL_LOW + [12:12] + read-only + + + GPIO18_EDGE_HIGH + [11:11] + read-only + + + GPIO18_EDGE_LOW + [10:10] + read-only + + + GPIO18_LEVEL_HIGH + [9:9] + read-only + + + GPIO18_LEVEL_LOW + [8:8] + read-only + + + GPIO17_EDGE_HIGH + [7:7] + read-only + + + GPIO17_EDGE_LOW + [6:6] + read-only + + + GPIO17_LEVEL_HIGH + [5:5] + read-only + + + GPIO17_LEVEL_LOW + [4:4] + read-only + + + GPIO16_EDGE_HIGH + [3:3] + read-only + + + GPIO16_EDGE_LOW + [2:2] + read-only + + + GPIO16_LEVEL_HIGH + [1:1] + read-only + + + GPIO16_LEVEL_LOW + [0:0] + read-only + + + + + DORMANT_WAKE_INTS3 + 0x00000314 + Interrupt status after masking & forcing for dormant_wake + 0x00000000 + + + GPIO31_EDGE_HIGH + [31:31] + read-only + + + GPIO31_EDGE_LOW + [30:30] + read-only + + + GPIO31_LEVEL_HIGH + [29:29] + read-only + + + GPIO31_LEVEL_LOW + [28:28] + read-only + + + GPIO30_EDGE_HIGH + [27:27] + read-only + + + GPIO30_EDGE_LOW + [26:26] + read-only + + + GPIO30_LEVEL_HIGH + [25:25] + read-only + + + GPIO30_LEVEL_LOW + [24:24] + read-only + + + GPIO29_EDGE_HIGH + [23:23] + read-only + + + GPIO29_EDGE_LOW + [22:22] + read-only + + + GPIO29_LEVEL_HIGH + [21:21] + read-only + + + GPIO29_LEVEL_LOW + [20:20] + read-only + + + GPIO28_EDGE_HIGH + [19:19] + read-only + + + GPIO28_EDGE_LOW + [18:18] + read-only + + + GPIO28_LEVEL_HIGH + [17:17] + read-only + + + GPIO28_LEVEL_LOW + [16:16] + read-only + + + GPIO27_EDGE_HIGH + [15:15] + read-only + + + GPIO27_EDGE_LOW + [14:14] + read-only + + + GPIO27_LEVEL_HIGH + [13:13] + read-only + + + GPIO27_LEVEL_LOW + [12:12] + read-only + + + GPIO26_EDGE_HIGH + [11:11] + read-only + + + GPIO26_EDGE_LOW + [10:10] + read-only + + + GPIO26_LEVEL_HIGH + [9:9] + read-only + + + GPIO26_LEVEL_LOW + [8:8] + read-only + + + GPIO25_EDGE_HIGH + [7:7] + read-only + + + GPIO25_EDGE_LOW + [6:6] + read-only + + + GPIO25_LEVEL_HIGH + [5:5] + read-only + + + GPIO25_LEVEL_LOW + [4:4] + read-only + + + GPIO24_EDGE_HIGH + [3:3] + read-only + + + GPIO24_EDGE_LOW + [2:2] + read-only + + + GPIO24_LEVEL_HIGH + [1:1] + read-only + + + GPIO24_LEVEL_LOW + [0:0] + read-only + + + + + DORMANT_WAKE_INTS4 + 0x00000318 + Interrupt status after masking & forcing for dormant_wake + 0x00000000 + + + GPIO39_EDGE_HIGH + [31:31] + read-only + + + GPIO39_EDGE_LOW + [30:30] + read-only + + + GPIO39_LEVEL_HIGH + [29:29] + read-only + + + GPIO39_LEVEL_LOW + [28:28] + read-only + + + GPIO38_EDGE_HIGH + [27:27] + read-only + + + GPIO38_EDGE_LOW + [26:26] + read-only + + + GPIO38_LEVEL_HIGH + [25:25] + read-only + + + GPIO38_LEVEL_LOW + [24:24] + read-only + + + GPIO37_EDGE_HIGH + [23:23] + read-only + + + GPIO37_EDGE_LOW + [22:22] + read-only + + + GPIO37_LEVEL_HIGH + [21:21] + read-only + + + GPIO37_LEVEL_LOW + [20:20] + read-only + + + GPIO36_EDGE_HIGH + [19:19] + read-only + + + GPIO36_EDGE_LOW + [18:18] + read-only + + + GPIO36_LEVEL_HIGH + [17:17] + read-only + + + GPIO36_LEVEL_LOW + [16:16] + read-only + + + GPIO35_EDGE_HIGH + [15:15] + read-only + + + GPIO35_EDGE_LOW + [14:14] + read-only + + + GPIO35_LEVEL_HIGH + [13:13] + read-only + + + GPIO35_LEVEL_LOW + [12:12] + read-only + + + GPIO34_EDGE_HIGH + [11:11] + read-only + + + GPIO34_EDGE_LOW + [10:10] + read-only + + + GPIO34_LEVEL_HIGH + [9:9] + read-only + + + GPIO34_LEVEL_LOW + [8:8] + read-only + + + GPIO33_EDGE_HIGH + [7:7] + read-only + + + GPIO33_EDGE_LOW + [6:6] + read-only + + + GPIO33_LEVEL_HIGH + [5:5] + read-only + + + GPIO33_LEVEL_LOW + [4:4] + read-only + + + GPIO32_EDGE_HIGH + [3:3] + read-only + + + GPIO32_EDGE_LOW + [2:2] + read-only + + + GPIO32_LEVEL_HIGH + [1:1] + read-only + + + GPIO32_LEVEL_LOW + [0:0] + read-only + + + + + DORMANT_WAKE_INTS5 + 0x0000031c + Interrupt status after masking & forcing for dormant_wake + 0x00000000 + + + GPIO47_EDGE_HIGH + [31:31] + read-only + + + GPIO47_EDGE_LOW + [30:30] + read-only + + + GPIO47_LEVEL_HIGH + [29:29] + read-only + + + GPIO47_LEVEL_LOW + [28:28] + read-only + + + GPIO46_EDGE_HIGH + [27:27] + read-only + + + GPIO46_EDGE_LOW + [26:26] + read-only + + + GPIO46_LEVEL_HIGH + [25:25] + read-only + + + GPIO46_LEVEL_LOW + [24:24] + read-only + + + GPIO45_EDGE_HIGH + [23:23] + read-only + + + GPIO45_EDGE_LOW + [22:22] + read-only + + + GPIO45_LEVEL_HIGH + [21:21] + read-only + + + GPIO45_LEVEL_LOW + [20:20] + read-only + + + GPIO44_EDGE_HIGH + [19:19] + read-only + + + GPIO44_EDGE_LOW + [18:18] + read-only + + + GPIO44_LEVEL_HIGH + [17:17] + read-only + + + GPIO44_LEVEL_LOW + [16:16] + read-only + + + GPIO43_EDGE_HIGH + [15:15] + read-only + + + GPIO43_EDGE_LOW + [14:14] + read-only + + + GPIO43_LEVEL_HIGH + [13:13] + read-only + + + GPIO43_LEVEL_LOW + [12:12] + read-only + + + GPIO42_EDGE_HIGH + [11:11] + read-only + + + GPIO42_EDGE_LOW + [10:10] + read-only + + + GPIO42_LEVEL_HIGH + [9:9] + read-only + + + GPIO42_LEVEL_LOW + [8:8] + read-only + + + GPIO41_EDGE_HIGH + [7:7] + read-only + + + GPIO41_EDGE_LOW + [6:6] + read-only + + + GPIO41_LEVEL_HIGH + [5:5] + read-only + + + GPIO41_LEVEL_LOW + [4:4] + read-only + + + GPIO40_EDGE_HIGH + [3:3] + read-only + + + GPIO40_EDGE_LOW + [2:2] + read-only + + + GPIO40_LEVEL_HIGH + [1:1] + read-only + + + GPIO40_LEVEL_LOW + [0:0] + read-only + + + + + + + SYSINFO + 0x40000000 + + 0 + 24 + registers + + + + CHIP_ID + 0x00000000 + JEDEC JEP-106 compliant chip identifier. + 0x00000001 + + + REVISION + [31:28] + read-only + + + PART + [27:12] + read-only + + + MANUFACTURER + [11:1] + read-only + + + STOP_BIT + [0:0] + read-only + + + + + PACKAGE_SEL + 0x00000004 + 0x00000000 + + + PACKAGE_SEL + [0:0] + read-only + + + + + PLATFORM + 0x00000008 + Platform register. Allows software to know what environment it is running in during pre-production development. Post-production, the PLATFORM is always ASIC, non-SIM. + 0x00000000 + + + GATESIM + [4:4] + read-only + + + BATCHSIM + [3:3] + read-only + + + HDLSIM + [2:2] + read-only + + + ASIC + [1:1] + read-only + + + FPGA + [0:0] + read-only + + + + + GITREF_RP2350 + 0x00000014 + Git hash of the chip source. Used to identify chip version. + 0x00000000 + + + GITREF_RP2350 + [31:0] + read-only + + + + + + + SHA256 + SHA-256 hash function implementation + 0x400f8000 + + 0 + 40 + registers + + + + CSR + 0x00000000 + Control and status register + 0x00001206 + + + BSWAP + Enable byte swapping of 32-bit values at the point they are committed to the SHA message scheduler. + + This block's bus interface assembles byte/halfword data into message words in little-endian order, so that DMAing the same buffer with different transfer sizes always gives the same result on a little-endian system like RP2350. + + However, when marshalling bytes into blocks, SHA expects that the first byte is the *most significant* in each message word. To resolve this, once the bus interface has accumulated 32 bits of data (either a word write, two halfword writes in little-endian order, or four byte writes in little-endian order) the final value can be byte-swapped before passing to the actual SHA core. + + This feature is enabled by default because using the SHA core to checksum byte buffers is expected to be more common than having preformatted SHA message words lying around. + [12:12] + read-write + + + DMA_SIZE + Configure DREQ logic for the correct DMA data size. Must be configured before the DMA channel is triggered. + + The SHA-256 core's DREQ logic requests one entire block of data at once, since there is no FIFO, and data goes straight into the core's message schedule and digest hardware. Therefore, when transferring data with DMA, CSR_DMA_SIZE must be configured in advance so that the correct number of transfers can be requested per block. + [9:8] + read-write + + + 8bit + 0 + + + 16bit + 1 + + + 32bit + 2 + + + + + ERR_WDATA_NOT_RDY + Set when a write occurs whilst the SHA-256 core is not ready for data (WDATA_RDY is low). Write one to clear. + [4:4] + read-write + oneToClear + + + SUM_VLD + If 1, the SHA-256 checksum presented in registers SUM0 through SUM7 is currently valid. + + Goes low when WDATA is first written, then returns high once 16 words have been written and the digest of the current 512-bit block has subsequently completed. + [2:2] + read-only + + + WDATA_RDY + If 1, the SHA-256 core is ready to accept more data through the WDATA register. + + After writing 16 words, this flag will go low for 57 cycles whilst the core completes its digest. + [1:1] + read-only + + + START + Write 1 to prepare the SHA-256 core for a new checksum. + + The SUMx registers are initialised to the proper values (fractional bits of square roots of first 8 primes) and internal counters are cleared. This immediately forces WDATA_RDY and SUM_VLD high. + + START must be written before initiating a DMA transfer to the SHA-256 core, because the core will always request 16 transfers at a time (1 512-bit block). Additionally, the DMA channel should be configured for a multiple of 16 32-bit transfers. + [0:0] + write-only + + + + + WDATA + 0x00000004 + Write data register + 0x00000000 + + + WDATA + After pulsing START and writing 16 words of data to this register, WDATA_RDY will go low and the SHA-256 core will complete the digest of the current 512-bit block. + + Software is responsible for ensuring the data is correctly padded and terminated to a whole number of 512-bit blocks. + + After this, WDATA_RDY will return high, and more data can be written (if any). + + This register supports word, halfword and byte writes, so that DMA from non-word-aligned buffers can be supported. The total amount of data per block remains the same (16 words, 32 halfwords or 64 bytes) and byte/halfword transfers must not be mixed within a block. + [31:0] + write-only + + + + + SUM0 + 0x00000008 + 256-bit checksum result. Contents are undefined when CSR_SUM_VLD is 0. + 0x00000000 + + + SUM0 + [31:0] + read-only + + + + + SUM1 + 0x0000000c + 256-bit checksum result. Contents are undefined when CSR_SUM_VLD is 0. + 0x00000000 + + + SUM1 + [31:0] + read-only + + + + + SUM2 + 0x00000010 + 256-bit checksum result. Contents are undefined when CSR_SUM_VLD is 0. + 0x00000000 + + + SUM2 + [31:0] + read-only + + + + + SUM3 + 0x00000014 + 256-bit checksum result. Contents are undefined when CSR_SUM_VLD is 0. + 0x00000000 + + + SUM3 + [31:0] + read-only + + + + + SUM4 + 0x00000018 + 256-bit checksum result. Contents are undefined when CSR_SUM_VLD is 0. + 0x00000000 + + + SUM4 + [31:0] + read-only + + + + + SUM5 + 0x0000001c + 256-bit checksum result. Contents are undefined when CSR_SUM_VLD is 0. + 0x00000000 + + + SUM5 + [31:0] + read-only + + + + + SUM6 + 0x00000020 + 256-bit checksum result. Contents are undefined when CSR_SUM_VLD is 0. + 0x00000000 + + + SUM6 + [31:0] + read-only + + + + + SUM7 + 0x00000024 + 256-bit checksum result. Contents are undefined when CSR_SUM_VLD is 0. + 0x00000000 + + + SUM7 + [31:0] + read-only + + + + + + + HSTX_FIFO + FIFO status and write access for HSTX + 0x50600000 + + 0 + 8 + registers + + + + STAT + 0x00000000 + FIFO status + 0x00000000 + + + WOF + FIFO was written when full. Write 1 to clear. + [10:10] + read-write + oneToClear + + + EMPTY + [9:9] + read-only + + + FULL + [8:8] + read-only + + + LEVEL + [7:0] + read-only + + + + + FIFO + 0x00000004 + Write access to FIFO + 0x00000000 + + + FIFO + [31:0] + write-only + + + + + + + HSTX_CTRL + Control interface to HSTX. For FIFO write access and status, see the HSTX_FIFO register block. + 0x400c0000 + + 0 + 44 + registers + + + + CSR + 0x00000000 + 0x10050600 + + + CLKDIV + Clock period of the generated clock, measured in HSTX clock cycles. Can be odd or even. The generated clock advances only on cycles where the shift register shifts. + + For example, a clkdiv of 5 would generate a complete output clock period for every 5 HSTX clocks (or every 10 half-clocks). + + A CLKDIV value of 0 is mapped to a period of 16 HSTX clock cycles. + [31:28] + read-write + + + CLKPHASE + Set the initial phase of the generated clock. + + A CLKPHASE of 0 means the clock is initially low, and the first rising edge occurs after one half period of the generated clock (i.e. CLKDIV/2 cycles of clk_hstx). Incrementing CLKPHASE by 1 will advance the initial clock phase by one half clk_hstx period. For example, if CLKDIV=2 and CLKPHASE=1: + + * The clock will be initially low + + * The first rising edge will be 0.5 clk_hstx cycles after asserting first data + + * The first falling edge will be 1.5 clk_hstx cycles after asserting first data + + This configuration would be suitable for serialising at a bit rate of clk_hstx with a centre-aligned DDR clock. + + When the HSTX is halted by clearing CSR_EN, the clock generator will return to its initial phase as configured by the CLKPHASE field. + + Note CLKPHASE must be strictly less than double the value of CLKDIV (one full period), else its operation is undefined. + [27:24] + read-write + + + N_SHIFTS + Number of times to shift the shift register before refilling it from the FIFO. (A count of how many times it has been shifted, *not* the total shift distance.) + + A register value of 0 means shift 32 times. + [20:16] + read-write + + + SHIFT + How many bits to right-rotate the shift register by each cycle. + + The use of a rotate rather than a shift allows left shifts to be emulated, by subtracting the left-shift amount from 32. It also allows data to be repeated, when the product of SHIFT and N_SHIFTS is greater than 32. + [12:8] + read-write + + + COUPLED_SEL + Select which PIO to use for coupled mode operation. + [6:5] + read-write + + + COUPLED_MODE + Enable the PIO-to-HSTX 1:1 connection. The HSTX must be clocked *directly* from the system clock (not just from some other clock source of the same frequency) for this synchronous interface to function correctly. + + When COUPLED_MODE is set, BITx_SEL_P and SEL_N indices 24 through 31 will select bits from the 8-bit PIO-to-HSTX path, rather than shifter bits. Indices of 0 through 23 will still index the shift register as normal. + + The PIO outputs connected to the PIO-to-HSTX bus are those same outputs that would appear on the HSTX-capable pins if those pins' FUNCSELs were set to PIO instead of HSTX. + + For example, if HSTX is on GPIOs 12 through 19, then PIO outputs 12 through 19 are connected to the HSTX when coupled mode is engaged. + [4:4] + read-write + + + EXPAND_EN + Enable the command expander. When 0, raw FIFO data is passed directly to the output shift register. When 1, the command expander can perform simple operations such as run length decoding on data between the FIFO and the shift register. + + Do not change CXPD_EN whilst EN is set. It's safe to set CXPD_EN simultaneously with setting EN. + [1:1] + read-write + + + EN + When EN is 1, the HSTX will shift out data as it appears in the FIFO. As long as there is data, the HSTX shift register will shift once per clock cycle, and the frequency of popping from the FIFO is determined by the ratio of SHIFT and SHIFT_THRESH. + + When EN is 0, the FIFO is not popped. The shift counter and clock generator are also reset to their initial state for as long as EN is low. Note the initial phase of the clock generator can be configured by the CLKPHASE field. + + Once the HSTX is enabled again, and data is pushed to the FIFO, the generated clock's first rising edge will be one half-period after the first data is launched. + [0:0] + read-write + + + + + BIT0 + 0x00000004 + Data control register for output bit 0 + 0x00000000 + + + CLK + Connect this output to the generated clock, rather than the data shift register. SEL_P and SEL_N are ignored if this bit is set, but INV can still be set to generate an antiphase clock. + [17:17] + read-write + + + INV + Invert this data output (logical NOT) + [16:16] + read-write + + + SEL_N + Shift register data bit select for the second half of the HSTX clock cycle + [12:8] + read-write + + + SEL_P + Shift register data bit select for the first half of the HSTX clock cycle + [4:0] + read-write + + + + + BIT1 + 0x00000008 + Data control register for output bit 1 + 0x00000000 + + + CLK + Connect this output to the generated clock, rather than the data shift register. SEL_P and SEL_N are ignored if this bit is set, but INV can still be set to generate an antiphase clock. + [17:17] + read-write + + + INV + Invert this data output (logical NOT) + [16:16] + read-write + + + SEL_N + Shift register data bit select for the second half of the HSTX clock cycle + [12:8] + read-write + + + SEL_P + Shift register data bit select for the first half of the HSTX clock cycle + [4:0] + read-write + + + + + BIT2 + 0x0000000c + Data control register for output bit 2 + 0x00000000 + + + CLK + Connect this output to the generated clock, rather than the data shift register. SEL_P and SEL_N are ignored if this bit is set, but INV can still be set to generate an antiphase clock. + [17:17] + read-write + + + INV + Invert this data output (logical NOT) + [16:16] + read-write + + + SEL_N + Shift register data bit select for the second half of the HSTX clock cycle + [12:8] + read-write + + + SEL_P + Shift register data bit select for the first half of the HSTX clock cycle + [4:0] + read-write + + + + + BIT3 + 0x00000010 + Data control register for output bit 3 + 0x00000000 + + + CLK + Connect this output to the generated clock, rather than the data shift register. SEL_P and SEL_N are ignored if this bit is set, but INV can still be set to generate an antiphase clock. + [17:17] + read-write + + + INV + Invert this data output (logical NOT) + [16:16] + read-write + + + SEL_N + Shift register data bit select for the second half of the HSTX clock cycle + [12:8] + read-write + + + SEL_P + Shift register data bit select for the first half of the HSTX clock cycle + [4:0] + read-write + + + + + BIT4 + 0x00000014 + Data control register for output bit 4 + 0x00000000 + + + CLK + Connect this output to the generated clock, rather than the data shift register. SEL_P and SEL_N are ignored if this bit is set, but INV can still be set to generate an antiphase clock. + [17:17] + read-write + + + INV + Invert this data output (logical NOT) + [16:16] + read-write + + + SEL_N + Shift register data bit select for the second half of the HSTX clock cycle + [12:8] + read-write + + + SEL_P + Shift register data bit select for the first half of the HSTX clock cycle + [4:0] + read-write + + + + + BIT5 + 0x00000018 + Data control register for output bit 5 + 0x00000000 + + + CLK + Connect this output to the generated clock, rather than the data shift register. SEL_P and SEL_N are ignored if this bit is set, but INV can still be set to generate an antiphase clock. + [17:17] + read-write + + + INV + Invert this data output (logical NOT) + [16:16] + read-write + + + SEL_N + Shift register data bit select for the second half of the HSTX clock cycle + [12:8] + read-write + + + SEL_P + Shift register data bit select for the first half of the HSTX clock cycle + [4:0] + read-write + + + + + BIT6 + 0x0000001c + Data control register for output bit 6 + 0x00000000 + + + CLK + Connect this output to the generated clock, rather than the data shift register. SEL_P and SEL_N are ignored if this bit is set, but INV can still be set to generate an antiphase clock. + [17:17] + read-write + + + INV + Invert this data output (logical NOT) + [16:16] + read-write + + + SEL_N + Shift register data bit select for the second half of the HSTX clock cycle + [12:8] + read-write + + + SEL_P + Shift register data bit select for the first half of the HSTX clock cycle + [4:0] + read-write + + + + + BIT7 + 0x00000020 + Data control register for output bit 7 + 0x00000000 + + + CLK + Connect this output to the generated clock, rather than the data shift register. SEL_P and SEL_N are ignored if this bit is set, but INV can still be set to generate an antiphase clock. + [17:17] + read-write + + + INV + Invert this data output (logical NOT) + [16:16] + read-write + + + SEL_N + Shift register data bit select for the second half of the HSTX clock cycle + [12:8] + read-write + + + SEL_P + Shift register data bit select for the first half of the HSTX clock cycle + [4:0] + read-write + + + + + EXPAND_SHIFT + 0x00000024 + Configure the optional shifter inside the command expander + 0x01000100 + + + ENC_N_SHIFTS + Number of times to consume from the shift register before refilling it from the FIFO, when the current command is an encoded data command (e.g. TMDS). A register value of 0 means shift 32 times. + [28:24] + read-write + + + ENC_SHIFT + How many bits to right-rotate the shift register by each time data is pushed to the output shifter, when the current command is an encoded data command (e.g. TMDS). + [20:16] + read-write + + + RAW_N_SHIFTS + Number of times to consume from the shift register before refilling it from the FIFO, when the current command is a raw data command. A register value of 0 means shift 32 times. + [12:8] + read-write + + + RAW_SHIFT + How many bits to right-rotate the shift register by each time data is pushed to the output shifter, when the current command is a raw data command. + [4:0] + read-write + + + + + EXPAND_TMDS + 0x00000028 + Configure the optional TMDS encoder inside the command expander + 0x00000000 + + + L2_NBITS + Number of valid data bits for the lane 2 TMDS encoder, starting from bit 7 of the rotated data. Field values of 0 -> 7 encode counts of 1 -> 8 bits. + [23:21] + read-write + + + L2_ROT + Right-rotate applied to the current shifter data before the lane 2 TMDS encoder. + [20:16] + read-write + + + L1_NBITS + Number of valid data bits for the lane 1 TMDS encoder, starting from bit 7 of the rotated data. Field values of 0 -> 7 encode counts of 1 -> 8 bits. + [15:13] + read-write + + + L1_ROT + Right-rotate applied to the current shifter data before the lane 1 TMDS encoder. + [12:8] + read-write + + + L0_NBITS + Number of valid data bits for the lane 0 TMDS encoder, starting from bit 7 of the rotated data. Field values of 0 -> 7 encode counts of 1 -> 8 bits. + [7:5] + read-write + + + L0_ROT + Right-rotate applied to the current shifter data before the lane 0 TMDS encoder. + [4:0] + read-write + + + + + + + EPPB + Cortex-M33 EPPB vendor register block for RP2350 + 0xe0080000 + + 0 + 12 + registers + + + + NMI_MASK0 + 0x00000000 + NMI mask for IRQs 0 through 31. This register is core-local, and is reset by a processor warm reset. + 0x00000000 + + + NMI_MASK0 + [31:0] + read-write + + + + + NMI_MASK1 + 0x00000004 + NMI mask for IRQs 0 though 51. This register is core-local, and is reset by a processor warm reset. + 0x00000000 + + + NMI_MASK1 + [19:0] + read-write + + + + + SLEEPCTRL + 0x00000008 + Nonstandard sleep control register + 0x00000002 + + + WICENACK + Status signal from the processor's interrupt controller. Changes to WICENREQ are eventually reflected in WICENACK. + [2:2] + read-only + + + WICENREQ + Request that the next processor deep sleep is a WIC sleep. After setting this bit, before sleeping, poll WICENACK to ensure the processor interrupt controller has acknowledged the change. + [1:1] + read-write + + + LIGHT_SLEEP + By default, any processor sleep will deassert the system-level clock request. Reenabling the clocks incurs 5 cycles of additional latency on wakeup. + + Setting LIGHT_SLEEP to 1 keeps the clock request asserted during a normal sleep (Arm SCR.SLEEPDEEP = 0), for faster wakeup. Processor deep sleep (Arm SCR.SLEEPDEEP = 1) is not affected, and will always deassert the system-level clock request. + [0:0] + read-write + + + + + + + PPB + TEAL registers accessible through the debug interface + 0xe0000000 + + 0 + 274432 + registers + + + + ITM_STIM0 + 0x00000000 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM1 + 0x00000004 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM2 + 0x00000008 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM3 + 0x0000000c + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM4 + 0x00000010 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM5 + 0x00000014 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM6 + 0x00000018 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM7 + 0x0000001c + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM8 + 0x00000020 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM9 + 0x00000024 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM10 + 0x00000028 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM11 + 0x0000002c + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM12 + 0x00000030 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM13 + 0x00000034 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM14 + 0x00000038 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM15 + 0x0000003c + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM16 + 0x00000040 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM17 + 0x00000044 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM18 + 0x00000048 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM19 + 0x0000004c + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM20 + 0x00000050 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM21 + 0x00000054 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM22 + 0x00000058 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM23 + 0x0000005c + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM24 + 0x00000060 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM25 + 0x00000064 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM26 + 0x00000068 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM27 + 0x0000006c + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM28 + 0x00000070 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM29 + 0x00000074 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM30 + 0x00000078 + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_STIM31 + 0x0000007c + Provides the interface for generating Instrumentation packets + 0x00000000 + + + STIMULUS + Data to write to the Stimulus Port FIFO, for forwarding as an Instrumentation packet. The size of write access determines the type of Instrumentation packet generated. + [31:0] + read-write + + + + + ITM_TER0 + 0x00000e00 + Provide an individual enable bit for each ITM_STIM register + 0x00000000 + + + STIMENA + For STIMENA[m] in ITM_TER*n, controls whether ITM_STIM(32*n + m) is enabled + [31:0] + read-write + + + + + ITM_TPR + 0x00000e40 + Controls which stimulus ports can be accessed by unprivileged code + 0x00000000 + + + PRIVMASK + Bit mask to enable tracing on ITM stimulus ports + [3:0] + read-write + + + + + ITM_TCR + 0x00000e80 + Configures and controls transfers through the ITM interface + 0x00000000 + + + BUSY + Indicates whether the ITM is currently processing events + [23:23] + read-only + + + TRACEBUSID + Identifier for multi-source trace stream formatting. If multi-source trace is in use, the debugger must write a unique non-zero trace ID value to this field + [22:16] + read-write + + + GTSFREQ + Defines how often the ITM generates a global timestamp, based on the global timestamp clock frequency, or disables generation of global timestamps + [11:10] + read-write + + + TSPRESCALE + Local timestamp prescaler, used with the trace packet reference clock + [9:8] + read-write + + + STALLENA + Stall the PE to guarantee delivery of Data Trace packets. + [5:5] + read-write + + + SWOENA + Enables asynchronous clocking of the timestamp counter + [4:4] + read-write + + + TXENA + Enables forwarding of hardware event packet from the DWT unit to the ITM for output to the TPIU + [3:3] + read-write + + + SYNCENA + Enables Synchronization packet transmission for a synchronous TPIU + [2:2] + read-write + + + TSENA + Enables Local timestamp generation + [1:1] + read-write + + + ITMENA + Enables the ITM + [0:0] + read-write + + + + + INT_ATREADY + 0x00000ef0 + Integration Mode: Read ATB Ready + 0x00000000 + + + AFVALID + A read of this bit returns the value of AFVALID + [1:1] + read-only + + + ATREADY + A read of this bit returns the value of ATREADY + [0:0] + read-only + + + + + INT_ATVALID + 0x00000ef8 + Integration Mode: Write ATB Valid + 0x00000000 + + + AFREADY + A write to this bit gives the value of AFREADY + [1:1] + read-write + + + ATREADY + A write to this bit gives the value of ATVALID + [0:0] + read-write + + + + + ITM_ITCTRL + 0x00000f00 + Integration Mode Control Register + 0x00000000 + + + IME + Integration mode enable bit - The possible values are: 0 - The trace unit is not in integration mode. 1 - The trace unit is in integration mode. This mode enables: A debug agent to perform topology detection. SoC test software to perform integration testing. + [0:0] + read-write + + + + + ITM_DEVARCH + 0x00000fbc + Provides CoreSight discovery information for the ITM + 0x47701a01 + + + ARCHITECT + Defines the architect of the component. Bits [31:28] are the JEP106 continuation code (JEP106 bank ID, minus 1) and bits [27:21] are the JEP106 ID code. + [31:21] + read-only + + + PRESENT + Defines that the DEVARCH register is present + [20:20] + read-only + + + REVISION + Defines the architecture revision of the component + [19:16] + read-only + + + ARCHVER + Defines the architecture version of the component + [15:12] + read-only + + + ARCHPART + Defines the architecture of the component + [11:0] + read-only + + + + + ITM_DEVTYPE + 0x00000fcc + Provides CoreSight discovery information for the ITM + 0x00000043 + + + SUB + Component sub-type + [7:4] + read-only + + + MAJOR + Component major type + [3:0] + read-only + + + + + ITM_PIDR4 + 0x00000fd0 + Provides CoreSight discovery information for the ITM + 0x00000004 + + + SIZE + See CoreSight Architecture Specification + [7:4] + read-only + + + DES_2 + See CoreSight Architecture Specification + [3:0] + read-only + + + + + ITM_PIDR5 + 0x00000fd4 + Provides CoreSight discovery information for the ITM + 0x00000000 + + + ITM_PIDR5 + [31:0] + read-write + + + + + ITM_PIDR6 + 0x00000fd8 + Provides CoreSight discovery information for the ITM + 0x00000000 + + + ITM_PIDR6 + [31:0] + read-write + + + + + ITM_PIDR7 + 0x00000fdc + Provides CoreSight discovery information for the ITM + 0x00000000 + + + ITM_PIDR7 + [31:0] + read-write + + + + + ITM_PIDR0 + 0x00000fe0 + Provides CoreSight discovery information for the ITM + 0x00000021 + + + PART_0 + See CoreSight Architecture Specification + [7:0] + read-only + + + + + ITM_PIDR1 + 0x00000fe4 + Provides CoreSight discovery information for the ITM + 0x000000bd + + + DES_0 + See CoreSight Architecture Specification + [7:4] + read-only + + + PART_1 + See CoreSight Architecture Specification + [3:0] + read-only + + + + + ITM_PIDR2 + 0x00000fe8 + Provides CoreSight discovery information for the ITM + 0x0000000b + + + REVISION + See CoreSight Architecture Specification + [7:4] + read-only + + + JEDEC + See CoreSight Architecture Specification + [3:3] + read-only + + + DES_1 + See CoreSight Architecture Specification + [2:0] + read-only + + + + + ITM_PIDR3 + 0x00000fec + Provides CoreSight discovery information for the ITM + 0x00000000 + + + REVAND + See CoreSight Architecture Specification + [7:4] + read-only + + + CMOD + See CoreSight Architecture Specification + [3:0] + read-only + + + + + ITM_CIDR0 + 0x00000ff0 + Provides CoreSight discovery information for the ITM + 0x0000000d + + + PRMBL_0 + See CoreSight Architecture Specification + [7:0] + read-only + + + + + ITM_CIDR1 + 0x00000ff4 + Provides CoreSight discovery information for the ITM + 0x00000090 + + + CLASS + See CoreSight Architecture Specification + [7:4] + read-only + + + PRMBL_1 + See CoreSight Architecture Specification + [3:0] + read-only + + + + + ITM_CIDR2 + 0x00000ff8 + Provides CoreSight discovery information for the ITM + 0x00000005 + + + PRMBL_2 + See CoreSight Architecture Specification + [7:0] + read-only + + + + + ITM_CIDR3 + 0x00000ffc + Provides CoreSight discovery information for the ITM + 0x000000b1 + + + PRMBL_3 + See CoreSight Architecture Specification + [7:0] + read-only + + + + + DWT_CTRL + 0x00001000 + Provides configuration and status information for the DWT unit, and used to control features of the unit + 0x73741824 + + + NUMCOMP + Number of DWT comparators implemented + [31:28] + read-only + + + NOTRCPKT + Indicates whether the implementation does not support trace + [27:27] + read-only + + + NOEXTTRIG + Reserved, RAZ + [26:26] + read-only + + + NOCYCCNT + Indicates whether the implementation does not include a cycle counter + [25:25] + read-only + + + NOPRFCNT + Indicates whether the implementation does not include the profiling counters + [24:24] + read-only + + + CYCDISS + Controls whether the cycle counter is disabled in Secure state + [23:23] + read-write + + + CYCEVTENA + Enables Event Counter packet generation on POSTCNT underflow + [22:22] + read-write + + + FOLDEVTENA + Enables DWT_FOLDCNT counter + [21:21] + read-write + + + LSUEVTENA + Enables DWT_LSUCNT counter + [20:20] + read-write + + + SLEEPEVTENA + Enable DWT_SLEEPCNT counter + [19:19] + read-write + + + EXCEVTENA + Enables DWT_EXCCNT counter + [18:18] + read-write + + + CPIEVTENA + Enables DWT_CPICNT counter + [17:17] + read-write + + + EXTTRCENA + Enables generation of Exception Trace packets + [16:16] + read-write + + + PCSAMPLENA + Enables use of POSTCNT counter as a timer for Periodic PC Sample packet generation + [12:12] + read-write + + + SYNCTAP + Selects the position of the synchronization packet counter tap on the CYCCNT counter. This determines the Synchronization packet rate + [11:10] + read-write + + + CYCTAP + Selects the position of the POSTCNT tap on the CYCCNT counter + [9:9] + read-write + + + POSTINIT + Initial value for the POSTCNT counter + [8:5] + read-write + + + POSTPRESET + Reload value for the POSTCNT counter + [4:1] + read-write + + + CYCCNTENA + Enables CYCCNT + [0:0] + read-write + + + + + DWT_CYCCNT + 0x00001004 + Shows or sets the value of the processor cycle counter, CYCCNT + 0x00000000 + + + CYCCNT + Increments one on each processor clock cycle when DWT_CTRL.CYCCNTENA == 1 and DEMCR.TRCENA == 1. On overflow, CYCCNT wraps to zero + [31:0] + read-write + + + + + DWT_EXCCNT + 0x0000100c + Counts the total cycles spent in exception processing + 0x00000000 + + + EXCCNT + Counts one on each cycle when all of the following are true: - DWT_CTRL.EXCEVTENA == 1 and DEMCR.TRCENA == 1. - No instruction is executed, see DWT_CPICNT. - An exception-entry or exception-exit related operation is in progress. - Either SecureNoninvasiveDebugAllowed() == TRUE, or NS-Req for the operation is set to Non-secure and NoninvasiveDebugAllowed() == TRUE. + [7:0] + read-write + + + + + DWT_LSUCNT + 0x00001014 + Increments on the additional cycles required to execute all load or store instructions + 0x00000000 + + + LSUCNT + Counts one on each cycle when all of the following are true: - DWT_CTRL.LSUEVTENA == 1 and DEMCR.TRCENA == 1. - No instruction is executed, see DWT_CPICNT. - No exception-entry or exception-exit operation is in progress, see DWT_EXCCNT. - A load-store operation is in progress. - Either SecureNoninvasiveDebugAllowed() == TRUE, or NS-Req for the operation is set to Non-secure and NoninvasiveDebugAllowed() == TRUE. + [7:0] + read-write + + + + + DWT_FOLDCNT + 0x00001018 + Increments on the additional cycles required to execute all load or store instructions + 0x00000000 + + + FOLDCNT + Counts on each cycle when all of the following are true: - DWT_CTRL.FOLDEVTENA == 1 and DEMCR.TRCENA == 1. - At least two instructions are executed, see DWT_CPICNT. - Either SecureNoninvasiveDebugAllowed() == TRUE, or the PE is in Non-secure state and NoninvasiveDebugAllowed() == TRUE. The counter is incremented by the number of instructions executed, minus one + [7:0] + read-write + + + + + DWT_COMP0 + 0x00001020 + Provides a reference value for use by watchpoint comparator 0 + 0x00000000 + + + DWT_COMP0 + [31:0] + read-write + + + + + DWT_FUNCTION0 + 0x00001028 + Controls the operation of watchpoint comparator 0 + 0x58000000 + + + ID + Identifies the capabilities for MATCH for comparator *n + [31:27] + read-only + + + MATCHED + Set to 1 when the comparator matches + [24:24] + read-only + + + DATAVSIZE + Defines the size of the object being watched for by Data Value and Data Address comparators + [11:10] + read-write + + + ACTION + Defines the action on a match. This field is ignored and the comparator generates no actions if it is disabled by MATCH + [5:4] + read-write + + + MATCH + Controls the type of match generated by this comparator + [3:0] + read-write + + + + + DWT_COMP1 + 0x00001030 + Provides a reference value for use by watchpoint comparator 1 + 0x00000000 + + + DWT_COMP1 + [31:0] + read-write + + + + + DWT_FUNCTION1 + 0x00001038 + Controls the operation of watchpoint comparator 1 + 0x89000828 + + + ID + Identifies the capabilities for MATCH for comparator *n + [31:27] + read-only + + + MATCHED + Set to 1 when the comparator matches + [24:24] + read-only + + + DATAVSIZE + Defines the size of the object being watched for by Data Value and Data Address comparators + [11:10] + read-write + + + ACTION + Defines the action on a match. This field is ignored and the comparator generates no actions if it is disabled by MATCH + [5:4] + read-write + + + MATCH + Controls the type of match generated by this comparator + [3:0] + read-write + + + + + DWT_COMP2 + 0x00001040 + Provides a reference value for use by watchpoint comparator 2 + 0x00000000 + + + DWT_COMP2 + [31:0] + read-write + + + + + DWT_FUNCTION2 + 0x00001048 + Controls the operation of watchpoint comparator 2 + 0x50000000 + + + ID + Identifies the capabilities for MATCH for comparator *n + [31:27] + read-only + + + MATCHED + Set to 1 when the comparator matches + [24:24] + read-only + + + DATAVSIZE + Defines the size of the object being watched for by Data Value and Data Address comparators + [11:10] + read-write + + + ACTION + Defines the action on a match. This field is ignored and the comparator generates no actions if it is disabled by MATCH + [5:4] + read-write + + + MATCH + Controls the type of match generated by this comparator + [3:0] + read-write + + + + + DWT_COMP3 + 0x00001050 + Provides a reference value for use by watchpoint comparator 3 + 0x00000000 + + + DWT_COMP3 + [31:0] + read-write + + + + + DWT_FUNCTION3 + 0x00001058 + Controls the operation of watchpoint comparator 3 + 0x20000800 + + + ID + Identifies the capabilities for MATCH for comparator *n + [31:27] + read-only + + + MATCHED + Set to 1 when the comparator matches + [24:24] + read-only + + + DATAVSIZE + Defines the size of the object being watched for by Data Value and Data Address comparators + [11:10] + read-write + + + ACTION + Defines the action on a match. This field is ignored and the comparator generates no actions if it is disabled by MATCH + [5:4] + read-write + + + MATCH + Controls the type of match generated by this comparator + [3:0] + read-write + + + + + DWT_DEVARCH + 0x00001fbc + Provides CoreSight discovery information for the DWT + 0x47701a02 + + + ARCHITECT + Defines the architect of the component. Bits [31:28] are the JEP106 continuation code (JEP106 bank ID, minus 1) and bits [27:21] are the JEP106 ID code. + [31:21] + read-only + + + PRESENT + Defines that the DEVARCH register is present + [20:20] + read-only + + + REVISION + Defines the architecture revision of the component + [19:16] + read-only + + + ARCHVER + Defines the architecture version of the component + [15:12] + read-only + + + ARCHPART + Defines the architecture of the component + [11:0] + read-only + + + + + DWT_DEVTYPE + 0x00001fcc + Provides CoreSight discovery information for the DWT + 0x00000000 + + + SUB + Component sub-type + [7:4] + read-only + + + MAJOR + Component major type + [3:0] + read-only + + + + + DWT_PIDR4 + 0x00001fd0 + Provides CoreSight discovery information for the DWT + 0x00000004 + + + SIZE + See CoreSight Architecture Specification + [7:4] + read-only + + + DES_2 + See CoreSight Architecture Specification + [3:0] + read-only + + + + + DWT_PIDR5 + 0x00001fd4 + Provides CoreSight discovery information for the DWT + 0x00000000 + + + DWT_PIDR5 + [31:0] + read-write + + + + + DWT_PIDR6 + 0x00001fd8 + Provides CoreSight discovery information for the DWT + 0x00000000 + + + DWT_PIDR6 + [31:0] + read-write + + + + + DWT_PIDR7 + 0x00001fdc + Provides CoreSight discovery information for the DWT + 0x00000000 + + + DWT_PIDR7 + [31:0] + read-write + + + + + DWT_PIDR0 + 0x00001fe0 + Provides CoreSight discovery information for the DWT + 0x00000021 + + + PART_0 + See CoreSight Architecture Specification + [7:0] + read-only + + + + + DWT_PIDR1 + 0x00001fe4 + Provides CoreSight discovery information for the DWT + 0x000000bd + + + DES_0 + See CoreSight Architecture Specification + [7:4] + read-only + + + PART_1 + See CoreSight Architecture Specification + [3:0] + read-only + + + + + DWT_PIDR2 + 0x00001fe8 + Provides CoreSight discovery information for the DWT + 0x0000000b + + + REVISION + See CoreSight Architecture Specification + [7:4] + read-only + + + JEDEC + See CoreSight Architecture Specification + [3:3] + read-only + + + DES_1 + See CoreSight Architecture Specification + [2:0] + read-only + + + + + DWT_PIDR3 + 0x00001fec + Provides CoreSight discovery information for the DWT + 0x00000000 + + + REVAND + See CoreSight Architecture Specification + [7:4] + read-only + + + CMOD + See CoreSight Architecture Specification + [3:0] + read-only + + + + + DWT_CIDR0 + 0x00001ff0 + Provides CoreSight discovery information for the DWT + 0x0000000d + + + PRMBL_0 + See CoreSight Architecture Specification + [7:0] + read-only + + + + + DWT_CIDR1 + 0x00001ff4 + Provides CoreSight discovery information for the DWT + 0x00000090 + + + CLASS + See CoreSight Architecture Specification + [7:4] + read-only + + + PRMBL_1 + See CoreSight Architecture Specification + [3:0] + read-only + + + + + DWT_CIDR2 + 0x00001ff8 + Provides CoreSight discovery information for the DWT + 0x00000005 + + + PRMBL_2 + See CoreSight Architecture Specification + [7:0] + read-only + + + + + DWT_CIDR3 + 0x00001ffc + Provides CoreSight discovery information for the DWT + 0x000000b1 + + + PRMBL_3 + See CoreSight Architecture Specification + [7:0] + read-only + + + + + FP_CTRL + 0x00002000 + Provides FPB implementation information, and the global enable for the FPB unit + 0x60005580 + + + REV + Flash Patch and Breakpoint Unit architecture revision + [31:28] + read-only + + + NUM_CODE_14_12_ + Indicates the number of implemented instruction address comparators. Zero indicates no Instruction Address comparators are implemented. The Instruction Address comparators are numbered from 0 to NUM_CODE - 1 + [14:12] + read-only + + + NUM_LIT + Indicates the number of implemented literal address comparators. The Literal Address comparators are numbered from NUM_CODE to NUM_CODE + NUM_LIT - 1 + [11:8] + read-only + + + NUM_CODE_7_4_ + Indicates the number of implemented instruction address comparators. Zero indicates no Instruction Address comparators are implemented. The Instruction Address comparators are numbered from 0 to NUM_CODE - 1 + [7:4] + read-only + + + KEY + Writes to the FP_CTRL are ignored unless KEY is concurrently written to one + [1:1] + read-write + + + ENABLE + Enables the FPB + [0:0] + read-write + + + + + FP_REMAP + 0x00002004 + Indicates whether the implementation supports Flash Patch remap and, if it does, holds the target address for remap + 0x00000000 + + + RMPSPT + Indicates whether the FPB unit supports the Flash Patch remap function + [29:29] + read-only + + + REMAP + Holds the bits[28:5] of the Flash Patch remap address + [28:5] + read-only + + + + + FP_COMP0 + 0x00002008 + Holds an address for comparison. The effect of the match depends on the configuration of the FPB and whether the comparator is an instruction address comparator or a literal address comparator + 0x00000000 + + + BE + Selects between flashpatch and breakpoint functionality + [0:0] + read-write + + + + + FP_COMP1 + 0x0000200c + Holds an address for comparison. The effect of the match depends on the configuration of the FPB and whether the comparator is an instruction address comparator or a literal address comparator + 0x00000000 + + + BE + Selects between flashpatch and breakpoint functionality + [0:0] + read-write + + + + + FP_COMP2 + 0x00002010 + Holds an address for comparison. The effect of the match depends on the configuration of the FPB and whether the comparator is an instruction address comparator or a literal address comparator + 0x00000000 + + + BE + Selects between flashpatch and breakpoint functionality + [0:0] + read-write + + + + + FP_COMP3 + 0x00002014 + Holds an address for comparison. The effect of the match depends on the configuration of the FPB and whether the comparator is an instruction address comparator or a literal address comparator + 0x00000000 + + + BE + Selects between flashpatch and breakpoint functionality + [0:0] + read-write + + + + + FP_COMP4 + 0x00002018 + Holds an address for comparison. The effect of the match depends on the configuration of the FPB and whether the comparator is an instruction address comparator or a literal address comparator + 0x00000000 + + + BE + Selects between flashpatch and breakpoint functionality + [0:0] + read-write + + + + + FP_COMP5 + 0x0000201c + Holds an address for comparison. The effect of the match depends on the configuration of the FPB and whether the comparator is an instruction address comparator or a literal address comparator + 0x00000000 + + + BE + Selects between flashpatch and breakpoint functionality + [0:0] + read-write + + + + + FP_COMP6 + 0x00002020 + Holds an address for comparison. The effect of the match depends on the configuration of the FPB and whether the comparator is an instruction address comparator or a literal address comparator + 0x00000000 + + + BE + Selects between flashpatch and breakpoint functionality + [0:0] + read-write + + + + + FP_COMP7 + 0x00002024 + Holds an address for comparison. The effect of the match depends on the configuration of the FPB and whether the comparator is an instruction address comparator or a literal address comparator + 0x00000000 + + + BE + Selects between flashpatch and breakpoint functionality + [0:0] + read-write + + + + + FP_DEVARCH + 0x00002fbc + Provides CoreSight discovery information for the FPB + 0x47701a03 + + + ARCHITECT + Defines the architect of the component. Bits [31:28] are the JEP106 continuation code (JEP106 bank ID, minus 1) and bits [27:21] are the JEP106 ID code. + [31:21] + read-only + + + PRESENT + Defines that the DEVARCH register is present + [20:20] + read-only + + + REVISION + Defines the architecture revision of the component + [19:16] + read-only + + + ARCHVER + Defines the architecture version of the component + [15:12] + read-only + + + ARCHPART + Defines the architecture of the component + [11:0] + read-only + + + + + FP_DEVTYPE + 0x00002fcc + Provides CoreSight discovery information for the FPB + 0x00000000 + + + SUB + Component sub-type + [7:4] + read-only + + + MAJOR + Component major type + [3:0] + read-only + + + + + FP_PIDR4 + 0x00002fd0 + Provides CoreSight discovery information for the FP + 0x00000004 + + + SIZE + See CoreSight Architecture Specification + [7:4] + read-only + + + DES_2 + See CoreSight Architecture Specification + [3:0] + read-only + + + + + FP_PIDR5 + 0x00002fd4 + Provides CoreSight discovery information for the FP + 0x00000000 + + + FP_PIDR5 + [31:0] + read-write + + + + + FP_PIDR6 + 0x00002fd8 + Provides CoreSight discovery information for the FP + 0x00000000 + + + FP_PIDR6 + [31:0] + read-write + + + + + FP_PIDR7 + 0x00002fdc + Provides CoreSight discovery information for the FP + 0x00000000 + + + FP_PIDR7 + [31:0] + read-write + + + + + FP_PIDR0 + 0x00002fe0 + Provides CoreSight discovery information for the FP + 0x00000021 + + + PART_0 + See CoreSight Architecture Specification + [7:0] + read-only + + + + + FP_PIDR1 + 0x00002fe4 + Provides CoreSight discovery information for the FP + 0x000000bd + + + DES_0 + See CoreSight Architecture Specification + [7:4] + read-only + + + PART_1 + See CoreSight Architecture Specification + [3:0] + read-only + + + + + FP_PIDR2 + 0x00002fe8 + Provides CoreSight discovery information for the FP + 0x0000000b + + + REVISION + See CoreSight Architecture Specification + [7:4] + read-only + + + JEDEC + See CoreSight Architecture Specification + [3:3] + read-only + + + DES_1 + See CoreSight Architecture Specification + [2:0] + read-only + + + + + FP_PIDR3 + 0x00002fec + Provides CoreSight discovery information for the FP + 0x00000000 + + + REVAND + See CoreSight Architecture Specification + [7:4] + read-only + + + CMOD + See CoreSight Architecture Specification + [3:0] + read-only + + + + + FP_CIDR0 + 0x00002ff0 + Provides CoreSight discovery information for the FP + 0x0000000d + + + PRMBL_0 + See CoreSight Architecture Specification + [7:0] + read-only + + + + + FP_CIDR1 + 0x00002ff4 + Provides CoreSight discovery information for the FP + 0x00000090 + + + CLASS + See CoreSight Architecture Specification + [7:4] + read-only + + + PRMBL_1 + See CoreSight Architecture Specification + [3:0] + read-only + + + + + FP_CIDR2 + 0x00002ff8 + Provides CoreSight discovery information for the FP + 0x00000005 + + + PRMBL_2 + See CoreSight Architecture Specification + [7:0] + read-only + + + + + FP_CIDR3 + 0x00002ffc + Provides CoreSight discovery information for the FP + 0x000000b1 + + + PRMBL_3 + See CoreSight Architecture Specification + [7:0] + read-only + + + + + ICTR + 0x0000e004 + Provides information about the interrupt controller + 0x00000001 + + + INTLINESNUM + Indicates the number of the highest implemented register in each of the NVIC control register sets, or in the case of NVIC_IPR*n, 4×INTLINESNUM + [3:0] + read-only + + + + + ACTLR + 0x0000e008 + Provides IMPLEMENTATION DEFINED configuration and control options + 0x00000000 + + + EXTEXCLALL + External Exclusives Allowed with no MPU + [29:29] + read-write + + + DISITMATBFLUSH + Disable ATB Flush + [12:12] + read-write + + + FPEXCODIS + Disable FPU exception outputs + [10:10] + read-write + + + DISOOFP + Disable out-of-order FP instruction completion + [9:9] + read-write + + + DISFOLD + Disable dual-issue. + [2:2] + read-write + + + DISMCYCINT + Disable dual-issue. + [0:0] + read-write + + + + + SYST_CSR + 0x0000e010 + Use the SysTick Control and Status Register to enable the SysTick features. + 0x00000000 + + + COUNTFLAG + Returns 1 if timer counted to 0 since last time this was read. Clears on read by application or debugger. + [16:16] + read-only + + + CLKSOURCE + SysTick clock source. Always reads as one if SYST_CALIB reports NOREF. + Selects the SysTick timer clock source: + 0 = External reference clock. + 1 = Processor clock. + [2:2] + read-write + + + TICKINT + Enables SysTick exception request: + 0 = Counting down to zero does not assert the SysTick exception request. + 1 = Counting down to zero to asserts the SysTick exception request. + [1:1] + read-write + + + ENABLE + Enable SysTick counter: + 0 = Counter disabled. + 1 = Counter enabled. + [0:0] + read-write + + + + + SYST_RVR + 0x0000e014 + Use the SysTick Reload Value Register to specify the start value to load into the current value register when the counter reaches 0. It can be any value between 0 and 0x00FFFFFF. A start value of 0 is possible, but has no effect because the SysTick interrupt and COUNTFLAG are activated when counting from 1 to 0. The reset value of this register is UNKNOWN. + To generate a multi-shot timer with a period of N processor clock cycles, use a RELOAD value of N-1. For example, if the SysTick interrupt is required every 100 clock pulses, set RELOAD to 99. + 0x00000000 + + + RELOAD + Value to load into the SysTick Current Value Register when the counter reaches 0. + [23:0] + read-write + + + + + SYST_CVR + 0x0000e018 + Use the SysTick Current Value Register to find the current value in the register. The reset value of this register is UNKNOWN. + 0x00000000 + + + CURRENT + Reads return the current value of the SysTick counter. This register is write-clear. Writing to it with any value clears the register to 0. Clearing this register also clears the COUNTFLAG bit of the SysTick Control and Status Register. + [23:0] + read-write + + + + + SYST_CALIB + 0x0000e01c + Use the SysTick Calibration Value Register to enable software to scale to any required speed using divide and multiply. + 0x00000000 + + + NOREF + If reads as 1, the Reference clock is not provided - the CLKSOURCE bit of the SysTick Control and Status register will be forced to 1 and cannot be cleared to 0. + [31:31] + read-only + + + SKEW + If reads as 1, the calibration value for 10ms is inexact (due to clock frequency). + [30:30] + read-only + + + TENMS + An optional Reload value to be used for 10ms (100Hz) timing, subject to system clock skew errors. If the value reads as 0, the calibration value is not known. + [23:0] + read-only + + + + + NVIC_ISER0 + 0x0000e100 + Enables or reads the enabled state of each group of 32 interrupts + 0x00000000 + + + SETENA + For SETENA[m] in NVIC_ISER*n, indicates whether interrupt 32*n + m is enabled + [31:0] + read-write + + + + + NVIC_ISER1 + 0x0000e104 + Enables or reads the enabled state of each group of 32 interrupts + 0x00000000 + + + SETENA + For SETENA[m] in NVIC_ISER*n, indicates whether interrupt 32*n + m is enabled + [31:0] + read-write + + + + + NVIC_ICER0 + 0x0000e180 + Clears or reads the enabled state of each group of 32 interrupts + 0x00000000 + + + CLRENA + For CLRENA[m] in NVIC_ICER*n, indicates whether interrupt 32*n + m is enabled + [31:0] + read-write + + + + + NVIC_ICER1 + 0x0000e184 + Clears or reads the enabled state of each group of 32 interrupts + 0x00000000 + + + CLRENA + For CLRENA[m] in NVIC_ICER*n, indicates whether interrupt 32*n + m is enabled + [31:0] + read-write + + + + + NVIC_ISPR0 + 0x0000e200 + Enables or reads the pending state of each group of 32 interrupts + 0x00000000 + + + SETPEND + For SETPEND[m] in NVIC_ISPR*n, indicates whether interrupt 32*n + m is pending + [31:0] + read-write + + + + + NVIC_ISPR1 + 0x0000e204 + Enables or reads the pending state of each group of 32 interrupts + 0x00000000 + + + SETPEND + For SETPEND[m] in NVIC_ISPR*n, indicates whether interrupt 32*n + m is pending + [31:0] + read-write + + + + + NVIC_ICPR0 + 0x0000e280 + Clears or reads the pending state of each group of 32 interrupts + 0x00000000 + + + CLRPEND + For CLRPEND[m] in NVIC_ICPR*n, indicates whether interrupt 32*n + m is pending + [31:0] + read-write + + + + + NVIC_ICPR1 + 0x0000e284 + Clears or reads the pending state of each group of 32 interrupts + 0x00000000 + + + CLRPEND + For CLRPEND[m] in NVIC_ICPR*n, indicates whether interrupt 32*n + m is pending + [31:0] + read-write + + + + + NVIC_IABR0 + 0x0000e300 + For each group of 32 interrupts, shows the active state of each interrupt + 0x00000000 + + + ACTIVE + For ACTIVE[m] in NVIC_IABR*n, indicates the active state for interrupt 32*n+m + [31:0] + read-write + + + + + NVIC_IABR1 + 0x0000e304 + For each group of 32 interrupts, shows the active state of each interrupt + 0x00000000 + + + ACTIVE + For ACTIVE[m] in NVIC_IABR*n, indicates the active state for interrupt 32*n+m + [31:0] + read-write + + + + + NVIC_ITNS0 + 0x0000e380 + For each group of 32 interrupts, determines whether each interrupt targets Non-secure or Secure state + 0x00000000 + + + ITNS + For ITNS[m] in NVIC_ITNS*n, `IAAMO the target Security state for interrupt 32*n+m + [31:0] + read-write + + + + + NVIC_ITNS1 + 0x0000e384 + For each group of 32 interrupts, determines whether each interrupt targets Non-secure or Secure state + 0x00000000 + + + ITNS + For ITNS[m] in NVIC_ITNS*n, `IAAMO the target Security state for interrupt 32*n+m + [31:0] + read-write + + + + + NVIC_IPR0 + 0x0000e400 + Sets or reads interrupt priorities + 0x00000000 + + + PRI_N3 + For register NVIC_IPRn, the priority of interrupt number 4*n+3, or RES0 if the PE does not implement this interrupt + [31:28] + read-write + + + PRI_N2 + For register NVIC_IPRn, the priority of interrupt number 4*n+2, or RES0 if the PE does not implement this interrupt + [23:20] + read-write + + + PRI_N1 + For register NVIC_IPRn, the priority of interrupt number 4*n+1, or RES0 if the PE does not implement this interrupt + [15:12] + read-write + + + PRI_N0 + For register NVIC_IPRn, the priority of interrupt number 4*n+0, or RES0 if the PE does not implement this interrupt + [7:4] + read-write + + + + + NVIC_IPR1 + 0x0000e404 + Sets or reads interrupt priorities + 0x00000000 + + + PRI_N3 + For register NVIC_IPRn, the priority of interrupt number 4*n+3, or RES0 if the PE does not implement this interrupt + [31:28] + read-write + + + PRI_N2 + For register NVIC_IPRn, the priority of interrupt number 4*n+2, or RES0 if the PE does not implement this interrupt + [23:20] + read-write + + + PRI_N1 + For register NVIC_IPRn, the priority of interrupt number 4*n+1, or RES0 if the PE does not implement this interrupt + [15:12] + read-write + + + PRI_N0 + For register NVIC_IPRn, the priority of interrupt number 4*n+0, or RES0 if the PE does not implement this interrupt + [7:4] + read-write + + + + + NVIC_IPR2 + 0x0000e408 + Sets or reads interrupt priorities + 0x00000000 + + + PRI_N3 + For register NVIC_IPRn, the priority of interrupt number 4*n+3, or RES0 if the PE does not implement this interrupt + [31:28] + read-write + + + PRI_N2 + For register NVIC_IPRn, the priority of interrupt number 4*n+2, or RES0 if the PE does not implement this interrupt + [23:20] + read-write + + + PRI_N1 + For register NVIC_IPRn, the priority of interrupt number 4*n+1, or RES0 if the PE does not implement this interrupt + [15:12] + read-write + + + PRI_N0 + For register NVIC_IPRn, the priority of interrupt number 4*n+0, or RES0 if the PE does not implement this interrupt + [7:4] + read-write + + + + + NVIC_IPR3 + 0x0000e40c + Sets or reads interrupt priorities + 0x00000000 + + + PRI_N3 + For register NVIC_IPRn, the priority of interrupt number 4*n+3, or RES0 if the PE does not implement this interrupt + [31:28] + read-write + + + PRI_N2 + For register NVIC_IPRn, the priority of interrupt number 4*n+2, or RES0 if the PE does not implement this interrupt + [23:20] + read-write + + + PRI_N1 + For register NVIC_IPRn, the priority of interrupt number 4*n+1, or RES0 if the PE does not implement this interrupt + [15:12] + read-write + + + PRI_N0 + For register NVIC_IPRn, the priority of interrupt number 4*n+0, or RES0 if the PE does not implement this interrupt + [7:4] + read-write + + + + + NVIC_IPR4 + 0x0000e410 + Sets or reads interrupt priorities + 0x00000000 + + + PRI_N3 + For register NVIC_IPRn, the priority of interrupt number 4*n+3, or RES0 if the PE does not implement this interrupt + [31:28] + read-write + + + PRI_N2 + For register NVIC_IPRn, the priority of interrupt number 4*n+2, or RES0 if the PE does not implement this interrupt + [23:20] + read-write + + + PRI_N1 + For register NVIC_IPRn, the priority of interrupt number 4*n+1, or RES0 if the PE does not implement this interrupt + [15:12] + read-write + + + PRI_N0 + For register NVIC_IPRn, the priority of interrupt number 4*n+0, or RES0 if the PE does not implement this interrupt + [7:4] + read-write + + + + + NVIC_IPR5 + 0x0000e414 + Sets or reads interrupt priorities + 0x00000000 + + + PRI_N3 + For register NVIC_IPRn, the priority of interrupt number 4*n+3, or RES0 if the PE does not implement this interrupt + [31:28] + read-write + + + PRI_N2 + For register NVIC_IPRn, the priority of interrupt number 4*n+2, or RES0 if the PE does not implement this interrupt + [23:20] + read-write + + + PRI_N1 + For register NVIC_IPRn, the priority of interrupt number 4*n+1, or RES0 if the PE does not implement this interrupt + [15:12] + read-write + + + PRI_N0 + For register NVIC_IPRn, the priority of interrupt number 4*n+0, or RES0 if the PE does not implement this interrupt + [7:4] + read-write + + + + + NVIC_IPR6 + 0x0000e418 + Sets or reads interrupt priorities + 0x00000000 + + + PRI_N3 + For register NVIC_IPRn, the priority of interrupt number 4*n+3, or RES0 if the PE does not implement this interrupt + [31:28] + read-write + + + PRI_N2 + For register NVIC_IPRn, the priority of interrupt number 4*n+2, or RES0 if the PE does not implement this interrupt + [23:20] + read-write + + + PRI_N1 + For register NVIC_IPRn, the priority of interrupt number 4*n+1, or RES0 if the PE does not implement this interrupt + [15:12] + read-write + + + PRI_N0 + For register NVIC_IPRn, the priority of interrupt number 4*n+0, or RES0 if the PE does not implement this interrupt + [7:4] + read-write + + + + + NVIC_IPR7 + 0x0000e41c + Sets or reads interrupt priorities + 0x00000000 + + + PRI_N3 + For register NVIC_IPRn, the priority of interrupt number 4*n+3, or RES0 if the PE does not implement this interrupt + [31:28] + read-write + + + PRI_N2 + For register NVIC_IPRn, the priority of interrupt number 4*n+2, or RES0 if the PE does not implement this interrupt + [23:20] + read-write + + + PRI_N1 + For register NVIC_IPRn, the priority of interrupt number 4*n+1, or RES0 if the PE does not implement this interrupt + [15:12] + read-write + + + PRI_N0 + For register NVIC_IPRn, the priority of interrupt number 4*n+0, or RES0 if the PE does not implement this interrupt + [7:4] + read-write + + + + + NVIC_IPR8 + 0x0000e420 + Sets or reads interrupt priorities + 0x00000000 + + + PRI_N3 + For register NVIC_IPRn, the priority of interrupt number 4*n+3, or RES0 if the PE does not implement this interrupt + [31:28] + read-write + + + PRI_N2 + For register NVIC_IPRn, the priority of interrupt number 4*n+2, or RES0 if the PE does not implement this interrupt + [23:20] + read-write + + + PRI_N1 + For register NVIC_IPRn, the priority of interrupt number 4*n+1, or RES0 if the PE does not implement this interrupt + [15:12] + read-write + + + PRI_N0 + For register NVIC_IPRn, the priority of interrupt number 4*n+0, or RES0 if the PE does not implement this interrupt + [7:4] + read-write + + + + + NVIC_IPR9 + 0x0000e424 + Sets or reads interrupt priorities + 0x00000000 + + + PRI_N3 + For register NVIC_IPRn, the priority of interrupt number 4*n+3, or RES0 if the PE does not implement this interrupt + [31:28] + read-write + + + PRI_N2 + For register NVIC_IPRn, the priority of interrupt number 4*n+2, or RES0 if the PE does not implement this interrupt + [23:20] + read-write + + + PRI_N1 + For register NVIC_IPRn, the priority of interrupt number 4*n+1, or RES0 if the PE does not implement this interrupt + [15:12] + read-write + + + PRI_N0 + For register NVIC_IPRn, the priority of interrupt number 4*n+0, or RES0 if the PE does not implement this interrupt + [7:4] + read-write + + + + + NVIC_IPR10 + 0x0000e428 + Sets or reads interrupt priorities + 0x00000000 + + + PRI_N3 + For register NVIC_IPRn, the priority of interrupt number 4*n+3, or RES0 if the PE does not implement this interrupt + [31:28] + read-write + + + PRI_N2 + For register NVIC_IPRn, the priority of interrupt number 4*n+2, or RES0 if the PE does not implement this interrupt + [23:20] + read-write + + + PRI_N1 + For register NVIC_IPRn, the priority of interrupt number 4*n+1, or RES0 if the PE does not implement this interrupt + [15:12] + read-write + + + PRI_N0 + For register NVIC_IPRn, the priority of interrupt number 4*n+0, or RES0 if the PE does not implement this interrupt + [7:4] + read-write + + + + + NVIC_IPR11 + 0x0000e42c + Sets or reads interrupt priorities + 0x00000000 + + + PRI_N3 + For register NVIC_IPRn, the priority of interrupt number 4*n+3, or RES0 if the PE does not implement this interrupt + [31:28] + read-write + + + PRI_N2 + For register NVIC_IPRn, the priority of interrupt number 4*n+2, or RES0 if the PE does not implement this interrupt + [23:20] + read-write + + + PRI_N1 + For register NVIC_IPRn, the priority of interrupt number 4*n+1, or RES0 if the PE does not implement this interrupt + [15:12] + read-write + + + PRI_N0 + For register NVIC_IPRn, the priority of interrupt number 4*n+0, or RES0 if the PE does not implement this interrupt + [7:4] + read-write + + + + + NVIC_IPR12 + 0x0000e430 + Sets or reads interrupt priorities + 0x00000000 + + + PRI_N3 + For register NVIC_IPRn, the priority of interrupt number 4*n+3, or RES0 if the PE does not implement this interrupt + [31:28] + read-write + + + PRI_N2 + For register NVIC_IPRn, the priority of interrupt number 4*n+2, or RES0 if the PE does not implement this interrupt + [23:20] + read-write + + + PRI_N1 + For register NVIC_IPRn, the priority of interrupt number 4*n+1, or RES0 if the PE does not implement this interrupt + [15:12] + read-write + + + PRI_N0 + For register NVIC_IPRn, the priority of interrupt number 4*n+0, or RES0 if the PE does not implement this interrupt + [7:4] + read-write + + + + + NVIC_IPR13 + 0x0000e434 + Sets or reads interrupt priorities + 0x00000000 + + + PRI_N3 + For register NVIC_IPRn, the priority of interrupt number 4*n+3, or RES0 if the PE does not implement this interrupt + [31:28] + read-write + + + PRI_N2 + For register NVIC_IPRn, the priority of interrupt number 4*n+2, or RES0 if the PE does not implement this interrupt + [23:20] + read-write + + + PRI_N1 + For register NVIC_IPRn, the priority of interrupt number 4*n+1, or RES0 if the PE does not implement this interrupt + [15:12] + read-write + + + PRI_N0 + For register NVIC_IPRn, the priority of interrupt number 4*n+0, or RES0 if the PE does not implement this interrupt + [7:4] + read-write + + + + + NVIC_IPR14 + 0x0000e438 + Sets or reads interrupt priorities + 0x00000000 + + + PRI_N3 + For register NVIC_IPRn, the priority of interrupt number 4*n+3, or RES0 if the PE does not implement this interrupt + [31:28] + read-write + + + PRI_N2 + For register NVIC_IPRn, the priority of interrupt number 4*n+2, or RES0 if the PE does not implement this interrupt + [23:20] + read-write + + + PRI_N1 + For register NVIC_IPRn, the priority of interrupt number 4*n+1, or RES0 if the PE does not implement this interrupt + [15:12] + read-write + + + PRI_N0 + For register NVIC_IPRn, the priority of interrupt number 4*n+0, or RES0 if the PE does not implement this interrupt + [7:4] + read-write + + + + + NVIC_IPR15 + 0x0000e43c + Sets or reads interrupt priorities + 0x00000000 + + + PRI_N3 + For register NVIC_IPRn, the priority of interrupt number 4*n+3, or RES0 if the PE does not implement this interrupt + [31:28] + read-write + + + PRI_N2 + For register NVIC_IPRn, the priority of interrupt number 4*n+2, or RES0 if the PE does not implement this interrupt + [23:20] + read-write + + + PRI_N1 + For register NVIC_IPRn, the priority of interrupt number 4*n+1, or RES0 if the PE does not implement this interrupt + [15:12] + read-write + + + PRI_N0 + For register NVIC_IPRn, the priority of interrupt number 4*n+0, or RES0 if the PE does not implement this interrupt + [7:4] + read-write + + + + + CPUID + 0x0000ed00 + Provides identification information for the PE, including an implementer code for the device and a device ID number + 0x411fd210 + + + IMPLEMENTER + This field must hold an implementer code that has been assigned by ARM + [31:24] + read-only + + + VARIANT + IMPLEMENTATION DEFINED variant number. Typically, this field is used to distinguish between different product variants, or major revisions of a product + [23:20] + read-only + + + ARCHITECTURE + Defines the Architecture implemented by the PE + [19:16] + read-only + + + PARTNO + IMPLEMENTATION DEFINED primary part number for the device + [15:4] + read-only + + + REVISION + IMPLEMENTATION DEFINED revision number for the device + [3:0] + read-only + + + + + ICSR + 0x0000ed04 + Controls and provides status information for NMI, PendSV, SysTick and interrupts + 0x00000000 + + + PENDNMISET + Indicates whether the NMI exception is pending + [31:31] + read-only + + + PENDNMICLR + Allows the NMI exception pend state to be cleared + [30:30] + read-write + + + PENDSVSET + Indicates whether the PendSV `FTSSS exception is pending + [28:28] + read-only + + + PENDSVCLR + Allows the PendSV exception pend state to be cleared `FTSSS + [27:27] + read-write + + + PENDSTSET + Indicates whether the SysTick `FTSSS exception is pending + [26:26] + read-only + + + PENDSTCLR + Allows the SysTick exception pend state to be cleared `FTSSS + [25:25] + read-write + + + STTNS + Controls whether in a single SysTick implementation, the SysTick is Secure or Non-secure + [24:24] + read-write + + + ISRPREEMPT + Indicates whether a pending exception will be serviced on exit from debug halt state + [23:23] + read-only + + + ISRPENDING + Indicates whether an external interrupt, generated by the NVIC, is pending + [22:22] + read-only + + + VECTPENDING + The exception number of the highest priority pending and enabled interrupt + [20:12] + read-only + + + RETTOBASE + In Handler mode, indicates whether there is more than one active exception + [11:11] + read-only + + + VECTACTIVE + The exception number of the current executing exception + [8:0] + read-only + + + + + VTOR + 0x0000ed08 + The VTOR indicates the offset of the vector table base address from memory address 0x00000000. + 0x00000000 + + + TBLOFF + Vector table base offset field. It contains bits[31:7] of the offset of the table base from the bottom of the memory map. + [31:7] + read-write + + + + + AIRCR + 0x0000ed0c + Use the Application Interrupt and Reset Control Register to: determine data endianness, clear all active state information from debug halt mode, request a system reset. + 0x00000000 + + + VECTKEY + Register key: + Reads as Unknown + On writes, write 0x05FA to VECTKEY, otherwise the write is ignored. + [31:16] + read-write + + + ENDIANESS + Data endianness implemented: + 0 = Little-endian. + [15:15] + read-only + + + PRIS + Prioritize Secure exceptions. The value of this bit defines whether Secure exception priority boosting is enabled. + 0 Priority ranges of Secure and Non-secure exceptions are identical. + 1 Non-secure exceptions are de-prioritized. + [14:14] + read-write + + + BFHFNMINS + BusFault, HardFault, and NMI Non-secure enable. + 0 BusFault, HardFault, and NMI are Secure. + 1 BusFault and NMI are Non-secure and exceptions can target Non-secure HardFault. + [13:13] + read-write + + + PRIGROUP + Interrupt priority grouping field. This field determines the split of group priority from subpriority. + See https://developer.arm.com/documentation/100235/0004/the-cortex-m33-peripherals/system-control-block/application-interrupt-and-reset-control-register?lang=en + [10:8] + read-write + + + SYSRESETREQS + System reset request, Secure state only. + 0 SYSRESETREQ functionality is available to both Security states. + 1 SYSRESETREQ functionality is only available to Secure state. + [3:3] + read-write + + + SYSRESETREQ + Writing 1 to this bit causes the SYSRESETREQ signal to the outer system to be asserted to request a reset. The intention is to force a large system reset of all major components except for debug. The C_HALT bit in the DHCSR is cleared as a result of the system reset requested. The debugger does not lose contact with the device. + [2:2] + read-write + + + VECTCLRACTIVE + Clears all active state information for fixed and configurable exceptions. This bit: is self-clearing, can only be set by the DAP when the core is halted. When set: clears all active exception status of the processor, forces a return to Thread mode, forces an IPSR of 0. A debugger must re-initialize the stack. + [1:1] + read-write + + + + + SCR + 0x0000ed10 + System Control Register. Use the System Control Register for power-management functions: signal to the system when the processor can enter a low power state, control how the processor enters and exits low power states. + 0x00000000 + + + SEVONPEND + Send Event on Pending bit: + 0 = Only enabled interrupts or events can wakeup the processor, disabled interrupts are excluded. + 1 = Enabled events and all interrupts, including disabled interrupts, can wakeup the processor. + When an event or interrupt becomes pending, the event signal wakes up the processor from WFE. If the + processor is not waiting for an event, the event is registered and affects the next WFE. + The processor also wakes up on execution of an SEV instruction or an external event. + [4:4] + read-write + + + SLEEPDEEPS + 0 SLEEPDEEP is available to both security states + 1 SLEEPDEEP is only available to Secure state + [3:3] + read-write + + + SLEEPDEEP + Controls whether the processor uses sleep or deep sleep as its low power mode: + 0 = Sleep. + 1 = Deep sleep. + [2:2] + read-write + + + SLEEPONEXIT + Indicates sleep-on-exit when returning from Handler mode to Thread mode: + 0 = Do not sleep when returning to Thread mode. + 1 = Enter sleep, or deep sleep, on return from an ISR to Thread mode. + Setting this bit to 1 enables an interrupt driven application to avoid returning to an empty main application. + [1:1] + read-write + + + + + CCR + 0x0000ed14 + Sets or returns configuration and control data + 0x00000201 + + + BP + Enables program flow prediction `FTSSS + [18:18] + read-only + + + IC + This is a global enable bit for instruction caches in the selected Security state + [17:17] + read-only + + + DC + Enables data caching of all data accesses to Normal memory `FTSSS + [16:16] + read-only + + + STKOFHFNMIGN + Controls the effect of a stack limit violation while executing at a requested priority less than 0 + [10:10] + read-write + + + RES1 + Reserved, RES1 + [9:9] + read-only + + + BFHFNMIGN + Determines the effect of precise BusFaults on handlers running at a requested priority less than 0 + [8:8] + read-write + + + DIV_0_TRP + Controls the generation of a DIVBYZERO UsageFault when attempting to perform integer division by zero + [4:4] + read-write + + + UNALIGN_TRP + Controls the trapping of unaligned word or halfword accesses + [3:3] + read-write + + + USERSETMPEND + Determines whether unprivileged accesses are permitted to pend interrupts via the STIR + [1:1] + read-write + + + RES1_1 + Reserved, RES1 + [0:0] + read-only + + + + + SHPR1 + 0x0000ed18 + Sets or returns priority for system handlers 4 - 7 + 0x00000000 + + + PRI_7_3 + Priority of system handler 7, SecureFault + [31:29] + read-write + + + PRI_6_3 + Priority of system handler 6, SecureFault + [23:21] + read-write + + + PRI_5_3 + Priority of system handler 5, SecureFault + [15:13] + read-write + + + PRI_4_3 + Priority of system handler 4, SecureFault + [7:5] + read-write + + + + + SHPR2 + 0x0000ed1c + Sets or returns priority for system handlers 8 - 11 + 0x00000000 + + + PRI_11_3 + Priority of system handler 11, SecureFault + [31:29] + read-write + + + PRI_10 + Reserved, RES0 + [23:16] + read-only + + + PRI_9 + Reserved, RES0 + [15:8] + read-only + + + PRI_8 + Reserved, RES0 + [7:0] + read-only + + + + + SHPR3 + 0x0000ed20 + Sets or returns priority for system handlers 12 - 15 + 0x00000000 + + + PRI_15_3 + Priority of system handler 15, SecureFault + [31:29] + read-write + + + PRI_14_3 + Priority of system handler 14, SecureFault + [23:21] + read-write + + + PRI_13 + Reserved, RES0 + [15:8] + read-only + + + PRI_12_3 + Priority of system handler 12, SecureFault + [7:5] + read-write + + + + + SHCSR + 0x0000ed24 + Provides access to the active and pending status of system exceptions + 0x00000000 + + + HARDFAULTPENDED + `IAAMO the pending state of the HardFault exception `CTTSSS + [21:21] + read-write + + + SECUREFAULTPENDED + `IAAMO the pending state of the SecureFault exception + [20:20] + read-write + + + SECUREFAULTENA + `DW the SecureFault exception is enabled + [19:19] + read-write + + + USGFAULTENA + `DW the UsageFault exception is enabled `FTSSS + [18:18] + read-write + + + BUSFAULTENA + `DW the BusFault exception is enabled + [17:17] + read-write + + + MEMFAULTENA + `DW the MemManage exception is enabled `FTSSS + [16:16] + read-write + + + SVCALLPENDED + `IAAMO the pending state of the SVCall exception `FTSSS + [15:15] + read-write + + + BUSFAULTPENDED + `IAAMO the pending state of the BusFault exception + [14:14] + read-write + + + MEMFAULTPENDED + `IAAMO the pending state of the MemManage exception `FTSSS + [13:13] + read-write + + + USGFAULTPENDED + The UsageFault exception is banked between Security states, `IAAMO the pending state of the UsageFault exception `FTSSS + [12:12] + read-write + + + SYSTICKACT + `IAAMO the active state of the SysTick exception `FTSSS + [11:11] + read-write + + + PENDSVACT + `IAAMO the active state of the PendSV exception `FTSSS + [10:10] + read-write + + + MONITORACT + `IAAMO the active state of the DebugMonitor exception + [8:8] + read-write + + + SVCALLACT + `IAAMO the active state of the SVCall exception `FTSSS + [7:7] + read-write + + + NMIACT + `IAAMO the active state of the NMI exception + [5:5] + read-write + + + SECUREFAULTACT + `IAAMO the active state of the SecureFault exception + [4:4] + read-write + + + USGFAULTACT + `IAAMO the active state of the UsageFault exception `FTSSS + [3:3] + read-write + + + HARDFAULTACT + Indicates and allows limited modification of the active state of the HardFault exception `FTSSS + [2:2] + read-write + + + BUSFAULTACT + `IAAMO the active state of the BusFault exception + [1:1] + read-write + + + MEMFAULTACT + `IAAMO the active state of the MemManage exception `FTSSS + [0:0] + read-write + + + + + CFSR + 0x0000ed28 + Contains the three Configurable Fault Status Registers. + + 31:16 UFSR: Provides information on UsageFault exceptions + + 15:8 BFSR: Provides information on BusFault exceptions + + 7:0 MMFSR: Provides information on MemManage exceptions + 0x00000000 + + + UFSR_DIVBYZERO + Sticky flag indicating whether an integer division by zero error has occurred + [25:25] + read-write + + + UFSR_UNALIGNED + Sticky flag indicating whether an unaligned access error has occurred + [24:24] + read-write + + + UFSR_STKOF + Sticky flag indicating whether a stack overflow error has occurred + [20:20] + read-write + + + UFSR_NOCP + Sticky flag indicating whether a coprocessor disabled or not present error has occurred + [19:19] + read-write + + + UFSR_INVPC + Sticky flag indicating whether an integrity check error has occurred + [18:18] + read-write + + + UFSR_INVSTATE + Sticky flag indicating whether an EPSR.T or EPSR.IT validity error has occurred + [17:17] + read-write + + + UFSR_UNDEFINSTR + Sticky flag indicating whether an undefined instruction error has occurred + [16:16] + read-write + + + BFSR_BFARVALID + Indicates validity of the contents of the BFAR register + [15:15] + read-write + + + BFSR_LSPERR + Records whether a BusFault occurred during FP lazy state preservation + [13:13] + read-write + + + BFSR_STKERR + Records whether a derived BusFault occurred during exception entry stacking + [12:12] + read-write + + + BFSR_UNSTKERR + Records whether a derived BusFault occurred during exception return unstacking + [11:11] + read-write + + + BFSR_IMPRECISERR + Records whether an imprecise data access error has occurred + [10:10] + read-write + + + BFSR_PRECISERR + Records whether a precise data access error has occurred + [9:9] + read-write + + + BFSR_IBUSERR + Records whether a BusFault on an instruction prefetch has occurred + [8:8] + read-write + + + MMFSR + Provides information on MemManage exceptions + [7:0] + read-write + + + + + HFSR + 0x0000ed2c + Shows the cause of any HardFaults + 0x00000000 + + + DEBUGEVT + Indicates when a Debug event has occurred + [31:31] + read-write + + + FORCED + Indicates that a fault with configurable priority has been escalated to a HardFault exception, because it could not be made active, because of priority, or because it was disabled + [30:30] + read-write + + + VECTTBL + Indicates when a fault has occurred because of a vector table read error on exception processing + [1:1] + read-write + + + + + DFSR + 0x0000ed30 + Shows which debug event occurred + 0x00000000 + + + EXTERNAL + Sticky flag indicating whether an External debug request debug event has occurred + [4:4] + read-write + + + VCATCH + Sticky flag indicating whether a Vector catch debug event has occurred + [3:3] + read-write + + + DWTTRAP + Sticky flag indicating whether a Watchpoint debug event has occurred + [2:2] + read-write + + + BKPT + Sticky flag indicating whether a Breakpoint debug event has occurred + [1:1] + read-write + + + HALTED + Sticky flag indicating that a Halt request debug event or Step debug event has occurred + [0:0] + read-write + + + + + MMFAR + 0x0000ed34 + Shows the address of the memory location that caused an MPU fault + 0x00000000 + + + ADDRESS + This register is updated with the address of a location that produced a MemManage fault. The MMFSR shows the cause of the fault, and whether this field is valid. This field is valid only when MMFSR.MMARVALID is set, otherwise it is UNKNOWN + [31:0] + read-write + + + + + BFAR + 0x0000ed38 + Shows the address associated with a precise data access BusFault + 0x00000000 + + + ADDRESS + This register is updated with the address of a location that produced a BusFault. The BFSR shows the reason for the fault. This field is valid only when BFSR.BFARVALID is set, otherwise it is UNKNOWN + [31:0] + read-write + + + + + ID_PFR0 + 0x0000ed40 + Gives top-level information about the instruction set supported by the PE + 0x00000030 + + + STATE1 + T32 instruction set support + [7:4] + read-only + + + STATE0 + A32 instruction set support + [3:0] + read-only + + + + + ID_PFR1 + 0x0000ed44 + Gives information about the programmers' model and Extensions support + 0x00000520 + + + MPROGMOD + Identifies support for the M-Profile programmers' model support + [11:8] + read-only + + + SECURITY + Identifies whether the Security Extension is implemented + [7:4] + read-only + + + + + ID_DFR0 + 0x0000ed48 + Provides top level information about the debug system + 0x00200000 + + + MPROFDBG + Indicates the supported M-profile debug architecture + [23:20] + read-only + + + + + ID_AFR0 + 0x0000ed4c + Provides information about the IMPLEMENTATION DEFINED features of the PE + 0x00000000 + + + IMPDEF3 + IMPLEMENTATION DEFINED meaning + [15:12] + read-only + + + IMPDEF2 + IMPLEMENTATION DEFINED meaning + [11:8] + read-only + + + IMPDEF1 + IMPLEMENTATION DEFINED meaning + [7:4] + read-only + + + IMPDEF0 + IMPLEMENTATION DEFINED meaning + [3:0] + read-only + + + + + ID_MMFR0 + 0x0000ed50 + Provides information about the implemented memory model and memory management support + 0x00101f40 + + + AUXREG + Indicates support for Auxiliary Control Registers + [23:20] + read-only + + + TCM + Indicates support for tightly coupled memories (TCMs) + [19:16] + read-only + + + SHARELVL + Indicates the number of shareability levels implemented + [15:12] + read-only + + + OUTERSHR + Indicates the outermost shareability domain implemented + [11:8] + read-only + + + PMSA + Indicates support for the protected memory system architecture (PMSA) + [7:4] + read-only + + + + + ID_MMFR1 + 0x0000ed54 + Provides information about the implemented memory model and memory management support + 0x00000000 + + + ID_MMFR1 + [31:0] + read-write + + + + + ID_MMFR2 + 0x0000ed58 + Provides information about the implemented memory model and memory management support + 0x01000000 + + + WFISTALL + Indicates the support for Wait For Interrupt (WFI) stalling + [27:24] + read-only + + + + + ID_MMFR3 + 0x0000ed5c + Provides information about the implemented memory model and memory management support + 0x00000000 + + + BPMAINT + Indicates the supported branch predictor maintenance + [11:8] + read-only + + + CMAINTSW + Indicates the supported cache maintenance operations by set/way + [7:4] + read-only + + + CMAINTVA + Indicates the supported cache maintenance operations by address + [3:0] + read-only + + + + + ID_ISAR0 + 0x0000ed60 + Provides information about the instruction set implemented by the PE + 0x08092300 + + + DIVIDE + Indicates the supported Divide instructions + [27:24] + read-only + + + DEBUG + Indicates the implemented Debug instructions + [23:20] + read-only + + + COPROC + Indicates the supported Coprocessor instructions + [19:16] + read-only + + + CMPBRANCH + Indicates the supported combined Compare and Branch instructions + [15:12] + read-only + + + BITFIELD + Indicates the supported bit field instructions + [11:8] + read-only + + + BITCOUNT + Indicates the supported bit count instructions + [7:4] + read-only + + + + + ID_ISAR1 + 0x0000ed64 + Provides information about the instruction set implemented by the PE + 0x05725000 + + + INTERWORK + Indicates the implemented Interworking instructions + [27:24] + read-only + + + IMMEDIATE + Indicates the implemented for data-processing instructions with long immediates + [23:20] + read-only + + + IFTHEN + Indicates the implemented If-Then instructions + [19:16] + read-only + + + EXTEND + Indicates the implemented Extend instructions + [15:12] + read-only + + + + + ID_ISAR2 + 0x0000ed68 + Provides information about the instruction set implemented by the PE + 0x30173426 + + + REVERSAL + Indicates the implemented Reversal instructions + [31:28] + read-only + + + MULTU + Indicates the implemented advanced unsigned Multiply instructions + [23:20] + read-only + + + MULTS + Indicates the implemented advanced signed Multiply instructions + [19:16] + read-only + + + MULT + Indicates the implemented additional Multiply instructions + [15:12] + read-only + + + MULTIACCESSINT + Indicates the support for interruptible multi-access instructions + [11:8] + read-only + + + MEMHINT + Indicates the implemented Memory Hint instructions + [7:4] + read-only + + + LOADSTORE + Indicates the implemented additional load/store instructions + [3:0] + read-only + + + + + ID_ISAR3 + 0x0000ed6c + Provides information about the instruction set implemented by the PE + 0x07895729 + + + TRUENOP + Indicates the implemented true NOP instructions + [27:24] + read-only + + + T32COPY + Indicates the support for T32 non flag-setting MOV instructions + [23:20] + read-only + + + TABBRANCH + Indicates the implemented Table Branch instructions + [19:16] + read-only + + + SYNCHPRIM + Used in conjunction with ID_ISAR4.SynchPrim_frac to indicate the implemented Synchronization Primitive instructions + [15:12] + read-only + + + SVC + Indicates the implemented SVC instructions + [11:8] + read-only + + + SIMD + Indicates the implemented SIMD instructions + [7:4] + read-only + + + SATURATE + Indicates the implemented saturating instructions + [3:0] + read-only + + + + + ID_ISAR4 + 0x0000ed70 + Provides information about the instruction set implemented by the PE + 0x01310132 + + + PSR_M + Indicates the implemented M profile instructions to modify the PSRs + [27:24] + read-only + + + SYNCPRIM_FRAC + Used in conjunction with ID_ISAR3.SynchPrim to indicate the implemented Synchronization Primitive instructions + [23:20] + read-only + + + BARRIER + Indicates the implemented Barrier instructions + [19:16] + read-only + + + WRITEBACK + Indicates the support for writeback addressing modes + [11:8] + read-only + + + WITHSHIFTS + Indicates the support for writeback addressing modes + [7:4] + read-only + + + UNPRIV + Indicates the implemented unprivileged instructions + [3:0] + read-only + + + + + ID_ISAR5 + 0x0000ed74 + Provides information about the instruction set implemented by the PE + 0x00000000 + + + ID_ISAR5 + [31:0] + read-write + + + + + CTR + 0x0000ed7c + Provides information about the architecture of the caches. CTR is RES0 if CLIDR is zero. + 0x8000c000 + + + RES1 + Reserved, RES1 + [31:31] + read-only + + + CWG + Log2 of the number of words of the maximum size of memory that can be overwritten as a result of the eviction of a cache entry that has had a memory location in it modified + [27:24] + read-only + + + ERG + Log2 of the number of words of the maximum size of the reservation granule that has been implemented for the Load-Exclusive and Store-Exclusive instructions + [23:20] + read-only + + + DMINLINE + Log2 of the number of words in the smallest cache line of all the data caches and unified caches that are controlled by the PE + [19:16] + read-only + + + RES1_1 + Reserved, RES1 + [15:14] + read-only + + + IMINLINE + Log2 of the number of words in the smallest cache line of all the instruction caches that are controlled by the PE + [3:0] + read-only + + + + + CPACR + 0x0000ed88 + Specifies the access privileges for coprocessors and the FP Extension + 0x00000000 + + + CP11 + The value in this field is ignored. If the implementation does not include the FP Extension, this field is RAZ/WI. If the value of this bit is not programmed to the same value as the CP10 field, then the value is UNKNOWN + [23:22] + read-write + + + CP10 + Defines the access rights for the floating-point functionality + [21:20] + read-write + + + CP7 + Controls access privileges for coprocessor 7 + [15:14] + read-write + + + CP6 + Controls access privileges for coprocessor 6 + [13:12] + read-write + + + CP5 + Controls access privileges for coprocessor 5 + [11:10] + read-write + + + CP4 + Controls access privileges for coprocessor 4 + [9:8] + read-write + + + CP3 + Controls access privileges for coprocessor 3 + [7:6] + read-write + + + CP2 + Controls access privileges for coprocessor 2 + [5:4] + read-write + + + CP1 + Controls access privileges for coprocessor 1 + [3:2] + read-write + + + CP0 + Controls access privileges for coprocessor 0 + [1:0] + read-write + + + + + NSACR + 0x0000ed8c + Defines the Non-secure access permissions for both the FP Extension and coprocessors CP0 to CP7 + 0x00000000 + + + CP11 + Enables Non-secure access to the Floating-point Extension + [11:11] + read-write + + + CP10 + Enables Non-secure access to the Floating-point Extension + [10:10] + read-write + + + CP7 + Enables Non-secure access to coprocessor CP7 + [7:7] + read-write + + + CP6 + Enables Non-secure access to coprocessor CP6 + [6:6] + read-write + + + CP5 + Enables Non-secure access to coprocessor CP5 + [5:5] + read-write + + + CP4 + Enables Non-secure access to coprocessor CP4 + [4:4] + read-write + + + CP3 + Enables Non-secure access to coprocessor CP3 + [3:3] + read-write + + + CP2 + Enables Non-secure access to coprocessor CP2 + [2:2] + read-write + + + CP1 + Enables Non-secure access to coprocessor CP1 + [1:1] + read-write + + + CP0 + Enables Non-secure access to coprocessor CP0 + [0:0] + read-write + + + + + MPU_TYPE + 0x0000ed90 + The MPU Type Register indicates how many regions the MPU `FTSSS supports + 0x00000800 + + + DREGION + Number of regions supported by the MPU + [15:8] + read-only + + + SEPARATE + Indicates support for separate instructions and data address regions + [0:0] + read-only + + + + + MPU_CTRL + 0x0000ed94 + Enables the MPU and, when the MPU is enabled, controls whether the default memory map is enabled as a background region for privileged accesses, and whether the MPU is enabled for HardFaults, NMIs, and exception handlers when FAULTMASK is set to 1 + 0x00000000 + + + PRIVDEFENA + Controls whether the default memory map is enabled for privileged software + [2:2] + read-write + + + HFNMIENA + Controls whether handlers executing with priority less than 0 access memory with the MPU enabled or disabled. This applies to HardFaults, NMIs, and exception handlers when FAULTMASK is set to 1 + [1:1] + read-write + + + ENABLE + Enables the MPU + [0:0] + read-write + + + + + MPU_RNR + 0x0000ed98 + Selects the region currently accessed by MPU_RBAR and MPU_RLAR + 0x00000000 + + + REGION + Indicates the memory region accessed by MPU_RBAR and MPU_RLAR + [2:0] + read-write + + + + + MPU_RBAR + 0x0000ed9c + Provides indirect read and write access to the base address of the currently selected MPU region `FTSSS + 0x00000000 + + + BASE + Contains bits [31:5] of the lower inclusive limit of the selected MPU memory region. This value is zero extended to provide the base address to be checked against + [31:5] + read-write + + + SH + Defines the Shareability domain of this region for Normal memory + [4:3] + read-write + + + AP + Defines the access permissions for this region + [2:1] + read-write + + + XN + Defines whether code can be executed from this region + [0:0] + read-write + + + + + MPU_RLAR + 0x0000eda0 + Provides indirect read and write access to the limit address of the currently selected MPU region `FTSSS + 0x00000000 + + + LIMIT + Contains bits [31:5] of the upper inclusive limit of the selected MPU memory region. This value is postfixed with 0x1F to provide the limit address to be checked against + [31:5] + read-write + + + ATTRINDX + Associates a set of attributes in the MPU_MAIR0 and MPU_MAIR1 fields + [3:1] + read-write + + + EN + Region enable + [0:0] + read-write + + + + + MPU_RBAR_A1 + 0x0000eda4 + Provides indirect read and write access to the base address of the MPU region selected by MPU_RNR[7:2]:(1[1:0]) `FTSSS + 0x00000000 + + + BASE + Contains bits [31:5] of the lower inclusive limit of the selected MPU memory region. This value is zero extended to provide the base address to be checked against + [31:5] + read-write + + + SH + Defines the Shareability domain of this region for Normal memory + [4:3] + read-write + + + AP + Defines the access permissions for this region + [2:1] + read-write + + + XN + Defines whether code can be executed from this region + [0:0] + read-write + + + + + MPU_RLAR_A1 + 0x0000eda8 + Provides indirect read and write access to the limit address of the currently selected MPU region selected by MPU_RNR[7:2]:(1[1:0]) `FTSSS + 0x00000000 + + + LIMIT + Contains bits [31:5] of the upper inclusive limit of the selected MPU memory region. This value is postfixed with 0x1F to provide the limit address to be checked against + [31:5] + read-write + + + ATTRINDX + Associates a set of attributes in the MPU_MAIR0 and MPU_MAIR1 fields + [3:1] + read-write + + + EN + Region enable + [0:0] + read-write + + + + + MPU_RBAR_A2 + 0x0000edac + Provides indirect read and write access to the base address of the MPU region selected by MPU_RNR[7:2]:(2[1:0]) `FTSSS + 0x00000000 + + + BASE + Contains bits [31:5] of the lower inclusive limit of the selected MPU memory region. This value is zero extended to provide the base address to be checked against + [31:5] + read-write + + + SH + Defines the Shareability domain of this region for Normal memory + [4:3] + read-write + + + AP + Defines the access permissions for this region + [2:1] + read-write + + + XN + Defines whether code can be executed from this region + [0:0] + read-write + + + + + MPU_RLAR_A2 + 0x0000edb0 + Provides indirect read and write access to the limit address of the currently selected MPU region selected by MPU_RNR[7:2]:(2[1:0]) `FTSSS + 0x00000000 + + + LIMIT + Contains bits [31:5] of the upper inclusive limit of the selected MPU memory region. This value is postfixed with 0x1F to provide the limit address to be checked against + [31:5] + read-write + + + ATTRINDX + Associates a set of attributes in the MPU_MAIR0 and MPU_MAIR1 fields + [3:1] + read-write + + + EN + Region enable + [0:0] + read-write + + + + + MPU_RBAR_A3 + 0x0000edb4 + Provides indirect read and write access to the base address of the MPU region selected by MPU_RNR[7:2]:(3[1:0]) `FTSSS + 0x00000000 + + + BASE + Contains bits [31:5] of the lower inclusive limit of the selected MPU memory region. This value is zero extended to provide the base address to be checked against + [31:5] + read-write + + + SH + Defines the Shareability domain of this region for Normal memory + [4:3] + read-write + + + AP + Defines the access permissions for this region + [2:1] + read-write + + + XN + Defines whether code can be executed from this region + [0:0] + read-write + + + + + MPU_RLAR_A3 + 0x0000edb8 + Provides indirect read and write access to the limit address of the currently selected MPU region selected by MPU_RNR[7:2]:(3[1:0]) `FTSSS + 0x00000000 + + + LIMIT + Contains bits [31:5] of the upper inclusive limit of the selected MPU memory region. This value is postfixed with 0x1F to provide the limit address to be checked against + [31:5] + read-write + + + ATTRINDX + Associates a set of attributes in the MPU_MAIR0 and MPU_MAIR1 fields + [3:1] + read-write + + + EN + Region enable + [0:0] + read-write + + + + + MPU_MAIR0 + 0x0000edc0 + Along with MPU_MAIR1, provides the memory attribute encodings corresponding to the AttrIndex values + 0x00000000 + + + ATTR3 + Memory attribute encoding for MPU regions with an AttrIndex of 3 + [31:24] + read-write + + + ATTR2 + Memory attribute encoding for MPU regions with an AttrIndex of 2 + [23:16] + read-write + + + ATTR1 + Memory attribute encoding for MPU regions with an AttrIndex of 1 + [15:8] + read-write + + + ATTR0 + Memory attribute encoding for MPU regions with an AttrIndex of 0 + [7:0] + read-write + + + + + MPU_MAIR1 + 0x0000edc4 + Along with MPU_MAIR0, provides the memory attribute encodings corresponding to the AttrIndex values + 0x00000000 + + + ATTR7 + Memory attribute encoding for MPU regions with an AttrIndex of 7 + [31:24] + read-write + + + ATTR6 + Memory attribute encoding for MPU regions with an AttrIndex of 6 + [23:16] + read-write + + + ATTR5 + Memory attribute encoding for MPU regions with an AttrIndex of 5 + [15:8] + read-write + + + ATTR4 + Memory attribute encoding for MPU regions with an AttrIndex of 4 + [7:0] + read-write + + + + + SAU_CTRL + 0x0000edd0 + Allows enabling of the Security Attribution Unit + 0x00000000 + + + ALLNS + When SAU_CTRL.ENABLE is 0 this bit controls if the memory is marked as Non-secure or Secure + [1:1] + read-write + + + ENABLE + Enables the SAU + [0:0] + read-write + + + + + SAU_TYPE + 0x0000edd4 + Indicates the number of regions implemented by the Security Attribution Unit + 0x00000008 + + + SREGION + The number of implemented SAU regions + [7:0] + read-only + + + + + SAU_RNR + 0x0000edd8 + Selects the region currently accessed by SAU_RBAR and SAU_RLAR + 0x00000000 + + + REGION + Indicates the SAU region accessed by SAU_RBAR and SAU_RLAR + [7:0] + read-write + + + + + SAU_RBAR + 0x0000eddc + Provides indirect read and write access to the base address of the currently selected SAU region + 0x00000000 + + + BADDR + Holds bits [31:5] of the base address for the selected SAU region + [31:5] + read-write + + + + + SAU_RLAR + 0x0000ede0 + Provides indirect read and write access to the limit address of the currently selected SAU region + 0x00000000 + + + LADDR + Holds bits [31:5] of the limit address for the selected SAU region + [31:5] + read-write + + + NSC + Controls whether Non-secure state is permitted to execute an SG instruction from this region + [1:1] + read-write + + + ENABLE + SAU region enable + [0:0] + read-write + + + + + SFSR + 0x0000ede4 + Provides information about any security related faults + 0x00000000 + + + LSERR + Sticky flag indicating that an error occurred during lazy state activation or deactivation + [7:7] + read-write + + + SFARVALID + This bit is set when the SFAR register contains a valid value. As with similar fields, such as BFSR.BFARVALID and MMFSR.MMARVALID, this bit can be cleared by other exceptions, such as BusFault + [6:6] + read-write + + + LSPERR + Stick flag indicating that an SAU or IDAU violation occurred during the lazy preservation of floating-point state + [5:5] + read-write + + + INVTRAN + Sticky flag indicating that an exception was raised due to a branch that was not flagged as being domain crossing causing a transition from Secure to Non-secure memory + [4:4] + read-write + + + AUVIOL + Sticky flag indicating that an attempt was made to access parts of the address space that are marked as Secure with NS-Req for the transaction set to Non-secure. This bit is not set if the violation occurred during lazy state preservation. See LSPERR + [3:3] + read-write + + + INVER + This can be caused by EXC_RETURN.DCRS being set to 0 when returning from an exception in the Non-secure state, or by EXC_RETURN.ES being set to 1 when returning from an exception in the Non-secure state + [2:2] + read-write + + + INVIS + This bit is set if the integrity signature in an exception stack frame is found to be invalid during the unstacking operation + [1:1] + read-write + + + INVEP + This bit is set if a function call from the Non-secure state or exception targets a non-SG instruction in the Secure state. This bit is also set if the target address is a SG instruction, but there is no matching SAU/IDAU region with the NSC flag set + [0:0] + read-write + + + + + SFAR + 0x0000ede8 + Shows the address of the memory location that caused a Security violation + 0x00000000 + + + ADDRESS + The address of an access that caused a attribution unit violation. This field is only valid when SFSR.SFARVALID is set. This allows the actual flip flops associated with this register to be shared with other fault address registers. If an implementation chooses to share the storage in this way, care must be taken to not leak Secure address information to the Non-secure state. One way of achieving this is to share the SFAR register with the MMFAR_S register, which is not accessible to the Non-secure state + [31:0] + read-write + + + + + DHCSR + 0x0000edf0 + Controls halting debug + 0x00000000 + + + S_RESTART_ST + Indicates the PE has processed a request to clear DHCSR.C_HALT to 0. That is, either a write to DHCSR that clears DHCSR.C_HALT from 1 to 0, or an External Restart Request + [26:26] + read-only + + + S_RESET_ST + Indicates whether the PE has been reset since the last read of the DHCSR + [25:25] + read-only + + + S_RETIRE_ST + Set to 1 every time the PE retires one of more instructions + [24:24] + read-only + + + S_SDE + Indicates whether Secure invasive debug is allowed + [20:20] + read-only + + + S_LOCKUP + Indicates whether the PE is in Lockup state + [19:19] + read-only + + + S_SLEEP + Indicates whether the PE is sleeping + [18:18] + read-only + + + S_HALT + Indicates whether the PE is in Debug state + [17:17] + read-only + + + S_REGRDY + Handshake flag to transfers through the DCRDR + [16:16] + read-only + + + C_SNAPSTALL + Allow imprecise entry to Debug state + [5:5] + read-write + + + C_MASKINTS + When debug is enabled, the debugger can write to this bit to mask PendSV, SysTick and external configurable interrupts + [3:3] + read-write + + + C_STEP + Enable single instruction step + [2:2] + read-write + + + C_HALT + PE enter Debug state halt request + [1:1] + read-write + + + C_DEBUGEN + Enable Halting debug + [0:0] + read-write + + + + + DCRSR + 0x0000edf4 + With the DCRDR, provides debug access to the general-purpose registers, special-purpose registers, and the FP extension registers. A write to the DCRSR specifies the register to transfer, whether the transfer is a read or write, and starts the transfer + 0x00000000 + + + REGWNR + Specifies the access type for the transfer + [16:16] + read-write + + + REGSEL + Specifies the general-purpose register, special-purpose register, or FP register to transfer + [6:0] + read-write + + + + + DCRDR + 0x0000edf8 + With the DCRSR, provides debug access to the general-purpose registers, special-purpose registers, and the FP Extension registers. If the Main Extension is implemented, it can also be used for message passing between an external debugger and a debug agent running on the PE + 0x00000000 + + + DBGTMP + Provides debug access for reading and writing the general-purpose registers, special-purpose registers, and Floating-point Extension registers + [31:0] + read-write + + + + + DEMCR + 0x0000edfc + Manages vector catch behavior and DebugMonitor handling when debugging + 0x00000000 + + + TRCENA + Global enable for all DWT and ITM features + [24:24] + read-write + + + SDME + Indicates whether the DebugMonitor targets the Secure or the Non-secure state and whether debug events are allowed in Secure state + [20:20] + read-only + + + MON_REQ + DebugMonitor semaphore bit + [19:19] + read-write + + + MON_STEP + Enable DebugMonitor stepping + [18:18] + read-write + + + MON_PEND + Sets or clears the pending state of the DebugMonitor exception + [17:17] + read-write + + + MON_EN + Enable the DebugMonitor exception + [16:16] + read-write + + + VC_SFERR + SecureFault exception halting debug vector catch enable + [11:11] + read-write + + + VC_HARDERR + HardFault exception halting debug vector catch enable + [10:10] + read-write + + + VC_INTERR + Enable halting debug vector catch for faults during exception entry and return + [9:9] + read-write + + + VC_BUSERR + BusFault exception halting debug vector catch enable + [8:8] + read-write + + + VC_STATERR + Enable halting debug trap on a UsageFault exception caused by a state information error, for example an Undefined Instruction exception + [7:7] + read-write + + + VC_CHKERR + Enable halting debug trap on a UsageFault exception caused by a checking error, for example an alignment check error + [6:6] + read-write + + + VC_NOCPERR + Enable halting debug trap on a UsageFault caused by an access to a coprocessor + [5:5] + read-write + + + VC_MMERR + Enable halting debug trap on a MemManage exception + [4:4] + read-write + + + VC_CORERESET + Enable Reset Vector Catch. This causes a warm reset to halt a running system + [0:0] + read-write + + + + + DSCSR + 0x0000ee08 + Provides control and status information for Secure debug + 0x00000000 + + + CDSKEY + Writes to the CDS bit are ignored unless CDSKEY is concurrently written to zero + [17:17] + read-write + + + CDS + This field indicates the current Security state of the processor + [16:16] + read-write + + + SBRSEL + If SBRSELEN is 1 this bit selects whether the Non-secure or the Secure version of the memory-mapped Banked registers are accessible to the debugger + [1:1] + read-write + + + SBRSELEN + Controls whether the SBRSEL field or the current Security state of the processor selects which version of the memory-mapped Banked registers are accessed to the debugger + [0:0] + read-write + + + + + STIR + 0x0000ef00 + Provides a mechanism for software to generate an interrupt + 0x00000000 + + + INTID + Indicates the interrupt to be pended. The value written is (ExceptionNumber - 16) + [8:0] + read-write + + + + + FPCCR + 0x0000ef34 + Holds control data for the Floating-point extension + 0x20000472 + + + ASPEN + When this bit is set to 1, execution of a floating-point instruction sets the CONTROL.FPCA bit to 1 + [31:31] + read-write + + + LSPEN + Enables lazy context save of floating-point state + [30:30] + read-write + + + LSPENS + This bit controls whether the LSPEN bit is writeable from the Non-secure state + [29:29] + read-write + + + CLRONRET + Clear floating-point caller saved registers on exception return + [28:28] + read-write + + + CLRONRETS + This bit controls whether the CLRONRET bit is writeable from the Non-secure state + [27:27] + read-write + + + TS + Treat floating-point registers as Secure enable + [26:26] + read-write + + + UFRDY + Indicates whether the software executing when the PE allocated the floating-point stack frame was able to set the UsageFault exception to pending + [10:10] + read-write + + + SPLIMVIOL + This bit is banked between the Security states and indicates whether the floating-point context violates the stack pointer limit that was active when lazy state preservation was activated. SPLIMVIOL modifies the lazy floating-point state preservation behavior + [9:9] + read-write + + + MONRDY + Indicates whether the software executing when the PE allocated the floating-point stack frame was able to set the DebugMonitor exception to pending + [8:8] + read-write + + + SFRDY + Indicates whether the software executing when the PE allocated the floating-point stack frame was able to set the SecureFault exception to pending. This bit is only present in the Secure version of the register, and behaves as RAZ/WI when accessed from the Non-secure state + [7:7] + read-write + + + BFRDY + Indicates whether the software executing when the PE allocated the floating-point stack frame was able to set the BusFault exception to pending + [6:6] + read-write + + + MMRDY + Indicates whether the software executing when the PE allocated the floating-point stack frame was able to set the MemManage exception to pending + [5:5] + read-write + + + HFRDY + Indicates whether the software executing when the PE allocated the floating-point stack frame was able to set the HardFault exception to pending + [4:4] + read-write + + + THREAD + Indicates the PE mode when it allocated the floating-point stack frame + [3:3] + read-write + + + S + Security status of the floating-point context. This bit is only present in the Secure version of the register, and behaves as RAZ/WI when accessed from the Non-secure state. This bit is updated whenever lazy state preservation is activated, or when a floating-point instruction is executed + [2:2] + read-write + + + USER + Indicates the privilege level of the software executing when the PE allocated the floating-point stack frame + [1:1] + read-write + + + LSPACT + Indicates whether lazy preservation of the floating-point state is active + [0:0] + read-write + + + + + FPCAR + 0x0000ef38 + Holds the location of the unpopulated floating-point register space allocated on an exception stack frame + 0x00000000 + + + ADDRESS + The location of the unpopulated floating-point register space allocated on an exception stack frame + [31:3] + read-write + + + + + FPDSCR + 0x0000ef3c + Holds the default values for the floating-point status control data that the PE assigns to the FPSCR when it creates a new floating-point context + 0x00000000 + + + AHP + Default value for FPSCR.AHP + [26:26] + read-write + + + DN + Default value for FPSCR.DN + [25:25] + read-write + + + FZ + Default value for FPSCR.FZ + [24:24] + read-write + + + RMODE + Default value for FPSCR.RMode + [23:22] + read-write + + + + + MVFR0 + 0x0000ef40 + Describes the features provided by the Floating-point Extension + 0x60540601 + + + FPROUND + Indicates the rounding modes supported by the FP Extension + [31:28] + read-only + + + FPSQRT + Indicates the support for FP square root operations + [23:20] + read-only + + + FPDIVIDE + Indicates the support for FP divide operations + [19:16] + read-only + + + FPDP + Indicates support for FP double-precision operations + [11:8] + read-only + + + FPSP + Indicates support for FP single-precision operations + [7:4] + read-only + + + SIMDREG + Indicates size of FP register file + [3:0] + read-only + + + + + MVFR1 + 0x0000ef44 + Describes the features provided by the Floating-point Extension + 0x85000089 + + + FMAC + Indicates whether the FP Extension implements the fused multiply accumulate instructions + [31:28] + read-only + + + FPHP + Indicates whether the FP Extension implements half-precision FP conversion instructions + [27:24] + read-only + + + FPDNAN + Indicates whether the FP hardware implementation supports NaN propagation + [7:4] + read-only + + + FPFTZ + Indicates whether subnormals are always flushed-to-zero + [3:0] + read-only + + + + + MVFR2 + 0x0000ef48 + Describes the features provided by the Floating-point Extension + 0x00000060 + + + FPMISC + Indicates support for miscellaneous FP features + [7:4] + read-only + + + + + DDEVARCH + 0x0000efbc + Provides CoreSight discovery information for the SCS + 0x47702a04 + + + ARCHITECT + Defines the architect of the component. Bits [31:28] are the JEP106 continuation code (JEP106 bank ID, minus 1) and bits [27:21] are the JEP106 ID code. + [31:21] + read-only + + + PRESENT + Defines that the DEVARCH register is present + [20:20] + read-only + + + REVISION + Defines the architecture revision of the component + [19:16] + read-only + + + ARCHVER + Defines the architecture version of the component + [15:12] + read-only + + + ARCHPART + Defines the architecture of the component + [11:0] + read-only + + + + + DDEVTYPE + 0x0000efcc + Provides CoreSight discovery information for the SCS + 0x00000000 + + + SUB + Component sub-type + [7:4] + read-only + + + MAJOR + CoreSight major type + [3:0] + read-only + + + + + DPIDR4 + 0x0000efd0 + Provides CoreSight discovery information for the SCS + 0x00000004 + + + SIZE + See CoreSight Architecture Specification + [7:4] + read-only + + + DES_2 + See CoreSight Architecture Specification + [3:0] + read-only + + + + + DPIDR5 + 0x0000efd4 + Provides CoreSight discovery information for the SCS + 0x00000000 + + + DPIDR5 + [31:0] + read-write + + + + + DPIDR6 + 0x0000efd8 + Provides CoreSight discovery information for the SCS + 0x00000000 + + + DPIDR6 + [31:0] + read-write + + + + + DPIDR7 + 0x0000efdc + Provides CoreSight discovery information for the SCS + 0x00000000 + + + DPIDR7 + [31:0] + read-write + + + + + DPIDR0 + 0x0000efe0 + Provides CoreSight discovery information for the SCS + 0x00000021 + + + PART_0 + See CoreSight Architecture Specification + [7:0] + read-only + + + + + DPIDR1 + 0x0000efe4 + Provides CoreSight discovery information for the SCS + 0x000000bd + + + DES_0 + See CoreSight Architecture Specification + [7:4] + read-only + + + PART_1 + See CoreSight Architecture Specification + [3:0] + read-only + + + + + DPIDR2 + 0x0000efe8 + Provides CoreSight discovery information for the SCS + 0x0000000b + + + REVISION + See CoreSight Architecture Specification + [7:4] + read-only + + + JEDEC + See CoreSight Architecture Specification + [3:3] + read-only + + + DES_1 + See CoreSight Architecture Specification + [2:0] + read-only + + + + + DPIDR3 + 0x0000efec + Provides CoreSight discovery information for the SCS + 0x00000000 + + + REVAND + See CoreSight Architecture Specification + [7:4] + read-only + + + CMOD + See CoreSight Architecture Specification + [3:0] + read-only + + + + + DCIDR0 + 0x0000eff0 + Provides CoreSight discovery information for the SCS + 0x0000000d + + + PRMBL_0 + See CoreSight Architecture Specification + [7:0] + read-only + + + + + DCIDR1 + 0x0000eff4 + Provides CoreSight discovery information for the SCS + 0x00000090 + + + CLASS + See CoreSight Architecture Specification + [7:4] + read-only + + + PRMBL_1 + See CoreSight Architecture Specification + [3:0] + read-only + + + + + DCIDR2 + 0x0000eff8 + Provides CoreSight discovery information for the SCS + 0x00000005 + + + PRMBL_2 + See CoreSight Architecture Specification + [7:0] + read-only + + + + + DCIDR3 + 0x0000effc + Provides CoreSight discovery information for the SCS + 0x000000b1 + + + PRMBL_3 + See CoreSight Architecture Specification + [7:0] + read-only + + + + + TRCPRGCTLR + 0x00041004 + Programming Control Register + 0x00000000 + + + EN + Trace Unit Enable + [0:0] + read-write + + + + + TRCSTATR + 0x0004100c + The TRCSTATR indicates the ETM-Teal status + 0x00000000 + + + PMSTABLE + Indicates whether the ETM-Teal registers are stable and can be read + [1:1] + read-only + + + IDLE + Indicates that the trace unit is inactive + [0:0] + read-only + + + + + TRCCONFIGR + 0x00041010 + The TRCCONFIGR sets the basic tracing options for the trace unit + 0x00000000 + + + RS + Return stack enable + [12:12] + read-write + + + TS + Global timestamp tracing + [11:11] + read-write + + + COND + Conditional instruction tracing + [10:5] + read-write + + + CCI + Cycle counting in instruction trace + [4:4] + read-write + + + BB + Branch broadcast mode + [3:3] + read-write + + + + + TRCEVENTCTL0R + 0x00041020 + The TRCEVENTCTL0R controls the tracing of events in the trace stream. The events also drive the ETM-Teal external outputs. + 0x00000000 + + + TYPE1 + Selects the resource type for event 1 + [15:15] + read-write + + + SEL1 + Selects the resource number, based on the value of TYPE1: When TYPE1 is 0, selects a single selected resource from 0-15 defined by SEL1[2:0]. When TYPE1 is 1, selects a Boolean combined resource pair from 0-7 defined by SEL1[2:0] + [10:8] + read-write + + + TYPE0 + Selects the resource type for event 0 + [7:7] + read-write + + + SEL0 + Selects the resource number, based on the value of TYPE0: When TYPE1 is 0, selects a single selected resource from 0-15 defined by SEL0[2:0]. When TYPE1 is 1, selects a Boolean combined resource pair from 0-7 defined by SEL0[2:0] + [2:0] + read-write + + + + + TRCEVENTCTL1R + 0x00041024 + The TRCEVENTCTL1R controls how the events selected by TRCEVENTCTL0R behave + 0x00000000 + + + LPOVERRIDE + Low power state behavior override + [12:12] + read-write + + + ATB + ATB enabled + [11:11] + read-write + + + INSTEN1 + One bit per event, to enable generation of an event element in the instruction trace stream when the selected event occurs + [1:1] + read-write + + + INSTEN0 + One bit per event, to enable generation of an event element in the instruction trace stream when the selected event occurs + [0:0] + read-write + + + + + TRCSTALLCTLR + 0x0004102c + The TRCSTALLCTLR enables ETM-Teal to stall the processor if the ETM-Teal FIFO goes over the programmed level to minimize risk of overflow + 0x00000000 + + + INSTPRIORITY + Reserved, RES0 + [10:10] + read-only + + + ISTALL + Stall processor based on instruction trace buffer space + [8:8] + read-write + + + LEVEL + Threshold at which stalling becomes active. This provides four levels. This level can be varied to optimize the level of invasion caused by stalling, balanced against the risk of a FIFO overflow + [3:2] + read-write + + + + + TRCTSCTLR + 0x00041030 + The TRCTSCTLR controls the insertion of global timestamps into the trace stream. A timestamp is always inserted into the instruction trace stream + 0x00000000 + + + TYPE0 + Selects the resource type for event 0 + [7:7] + read-write + + + SEL0 + Selects the resource number, based on the value of TYPE0: When TYPE1 is 0, selects a single selected resource from 0-15 defined by SEL0[2:0]. When TYPE1 is 1, selects a Boolean combined resource pair from 0-7 defined by SEL0[2:0] + [1:0] + read-write + + + + + TRCSYNCPR + 0x00041034 + The TRCSYNCPR specifies the period of trace synchronization of the trace streams. TRCSYNCPR defines a number of bytes of trace between requests for trace synchronization. This value is always a power of two + 0x0000000a + + + PERIOD + Defines the number of bytes of trace between trace synchronization requests as a total of the number of bytes generated by the instruction stream. The number of bytes is 2N where N is the value of this field: - A value of zero disables these periodic trace synchronization requests, but does not disable other trace synchronization requests. - The minimum value that can be programmed, other than zero, is 8, providing a minimum trace synchronization period of 256 bytes. - The maximum value is 20, providing a maximum trace synchronization period of 2^20 bytes + [4:0] + read-only + + + + + TRCCCCTLR + 0x00041038 + The TRCCCCTLR sets the threshold value for instruction trace cycle counting. The threshold represents the minimum interval between cycle count trace packets + 0x00000000 + + + THRESHOLD + Instruction trace cycle count threshold + [11:0] + read-write + + + + + TRCVICTLR + 0x00041080 + The TRCVICTLR controls instruction trace filtering + 0x00000000 + + + EXLEVEL_S3 + In Secure state, each bit controls whether instruction tracing is enabled for the corresponding exception level + [19:19] + read-write + + + EXLEVEL_S0 + In Secure state, each bit controls whether instruction tracing is enabled for the corresponding exception level + [16:16] + read-write + + + TRCERR + Selects whether a system error exception must always be traced + [11:11] + read-write + + + TRCRESET + Selects whether a reset exception must always be traced + [10:10] + read-write + + + SSSTATUS + Indicates the current status of the start/stop logic + [9:9] + read-write + + + TYPE0 + Selects the resource type for event 0 + [7:7] + read-write + + + SEL0 + Selects the resource number, based on the value of TYPE0: When TYPE1 is 0, selects a single selected resource from 0-15 defined by SEL0[2:0]. When TYPE1 is 1, selects a Boolean combined resource pair from 0-7 defined by SEL0[2:0] + [1:0] + read-write + + + + + TRCCNTRLDVR0 + 0x00041140 + The TRCCNTRLDVR defines the reload value for the reduced function counter + 0x00000000 + + + VALUE + Defines the reload value for the counter. This value is loaded into the counter each time the reload event occurs + [15:0] + read-write + + + + + TRCIDR8 + 0x00041180 + TRCIDR8 + 0x00000000 + + + MAXSPEC + reads as `ImpDef + [31:0] + read-only + + + + + TRCIDR9 + 0x00041184 + TRCIDR9 + 0x00000000 + + + NUMP0KEY + reads as `ImpDef + [31:0] + read-only + + + + + TRCIDR10 + 0x00041188 + TRCIDR10 + 0x00000000 + + + NUMP1KEY + reads as `ImpDef + [31:0] + read-only + + + + + TRCIDR11 + 0x0004118c + TRCIDR11 + 0x00000000 + + + NUMP1SPC + reads as `ImpDef + [31:0] + read-only + + + + + TRCIDR12 + 0x00041190 + TRCIDR12 + 0x00000001 + + + NUMCONDKEY + reads as `ImpDef + [31:0] + read-only + + + + + TRCIDR13 + 0x00041194 + TRCIDR13 + 0x00000000 + + + NUMCONDSPC + reads as `ImpDef + [31:0] + read-only + + + + + TRCIMSPEC + 0x000411c0 + The TRCIMSPEC shows the presence of any IMPLEMENTATION SPECIFIC features, and enables any features that are provided + 0x00000000 + + + SUPPORT + Reserved, RES0 + [3:0] + read-only + + + + + TRCIDR0 + 0x000411e0 + TRCIDR0 + 0x280006e1 + + + COMMOPT + reads as `ImpDef + [29:29] + read-only + + + TSSIZE + reads as `ImpDef + [28:24] + read-only + + + TRCEXDATA + reads as `ImpDef + [17:17] + read-only + + + QSUPP + reads as `ImpDef + [16:15] + read-only + + + QFILT + reads as `ImpDef + [14:14] + read-only + + + CONDTYPE + reads as `ImpDef + [13:12] + read-only + + + NUMEVENT + reads as `ImpDef + [11:10] + read-only + + + RETSTACK + reads as `ImpDef + [9:9] + read-only + + + TRCCCI + reads as `ImpDef + [7:7] + read-only + + + TRCCOND + reads as `ImpDef + [6:6] + read-only + + + TRCBB + reads as `ImpDef + [5:5] + read-only + + + TRCDATA + reads as `ImpDef + [4:3] + read-only + + + INSTP0 + reads as `ImpDef + [2:1] + read-only + + + RES1 + Reserved, RES1 + [0:0] + read-only + + + + + TRCIDR1 + 0x000411e4 + TRCIDR1 + 0x4100f421 + + + DESIGNER + reads as `ImpDef + [31:24] + read-only + + + RES1 + Reserved, RES1 + [15:12] + read-only + + + TRCARCHMAJ + reads as 0b0100 + [11:8] + read-only + + + TRCARCHMIN + reads as 0b0000 + [7:4] + read-only + + + REVISION + reads as `ImpDef + [3:0] + read-only + + + + + TRCIDR2 + 0x000411e8 + TRCIDR2 + 0x00000004 + + + CCSIZE + reads as `ImpDef + [28:25] + read-only + + + DVSIZE + reads as `ImpDef + [24:20] + read-only + + + DASIZE + reads as `ImpDef + [19:15] + read-only + + + VMIDSIZE + reads as `ImpDef + [14:10] + read-only + + + CIDSIZE + reads as `ImpDef + [9:5] + read-only + + + IASIZE + reads as `ImpDef + [4:0] + read-only + + + + + TRCIDR3 + 0x000411ec + TRCIDR3 + 0x0f090004 + + + NOOVERFLOW + reads as `ImpDef + [31:31] + read-only + + + NUMPROC + reads as `ImpDef + [30:28] + read-only + + + SYSSTALL + reads as `ImpDef + [27:27] + read-only + + + STALLCTL + reads as `ImpDef + [26:26] + read-only + + + SYNCPR + reads as `ImpDef + [25:25] + read-only + + + TRCERR + reads as `ImpDef + [24:24] + read-only + + + EXLEVEL_NS + reads as `ImpDef + [23:20] + read-only + + + EXLEVEL_S + reads as `ImpDef + [19:16] + read-only + + + CCITMIN + reads as `ImpDef + [11:0] + read-only + + + + + TRCIDR4 + 0x000411f0 + TRCIDR4 + 0x00114000 + + + NUMVMIDC + reads as `ImpDef + [31:28] + read-only + + + NUMCIDC + reads as `ImpDef + [27:24] + read-only + + + NUMSSCC + reads as `ImpDef + [23:20] + read-only + + + NUMRSPAIR + reads as `ImpDef + [19:16] + read-only + + + NUMPC + reads as `ImpDef + [15:12] + read-only + + + SUPPDAC + reads as `ImpDef + [8:8] + read-only + + + NUMDVC + reads as `ImpDef + [7:4] + read-only + + + NUMACPAIRS + reads as `ImpDef + [3:0] + read-only + + + + + TRCIDR5 + 0x000411f4 + TRCIDR5 + 0x90c70004 + + + REDFUNCNTR + reads as `ImpDef + [31:31] + read-only + + + NUMCNTR + reads as `ImpDef + [30:28] + read-only + + + NUMSEQSTATE + reads as `ImpDef + [27:25] + read-only + + + LPOVERRIDE + reads as `ImpDef + [23:23] + read-only + + + ATBTRIG + reads as `ImpDef + [22:22] + read-only + + + TRACEIDSIZE + reads as 0x07 + [21:16] + read-only + + + NUMEXTINSEL + reads as `ImpDef + [11:9] + read-only + + + NUMEXTIN + reads as `ImpDef + [8:0] + read-only + + + + + TRCIDR6 + 0x000411f8 + TRCIDR6 + 0x00000000 + + + TRCIDR6 + [31:0] + read-write + + + + + TRCIDR7 + 0x000411fc + TRCIDR7 + 0x00000000 + + + TRCIDR7 + [31:0] + read-write + + + + + TRCRSCTLR2 + 0x00041208 + The TRCRSCTLR controls the trace resources + 0x00000000 + + + PAIRINV + Inverts the result of a combined pair of resources. This bit is only implemented on the lower register for a pair of resource selectors + [21:21] + read-write + + + INV + Inverts the selected resources + [20:20] + read-write + + + GROUP + Selects a group of resource + [18:16] + read-write + + + SELECT + Selects one or more resources from the wanted group. One bit is provided per resource from the group + [7:0] + read-write + + + + + TRCRSCTLR3 + 0x0004120c + The TRCRSCTLR controls the trace resources + 0x00000000 + + + PAIRINV + Inverts the result of a combined pair of resources. This bit is only implemented on the lower register for a pair of resource selectors + [21:21] + read-write + + + INV + Inverts the selected resources + [20:20] + read-write + + + GROUP + Selects a group of resource + [18:16] + read-write + + + SELECT + Selects one or more resources from the wanted group. One bit is provided per resource from the group + [7:0] + read-write + + + + + TRCSSCSR + 0x000412a0 + Controls the corresponding single-shot comparator resource + 0x00000000 + + + STATUS + Single-shot status bit. Indicates if any of the comparators, that TRCSSCCRn.SAC or TRCSSCCRn.ARC selects, have matched + [31:31] + read-write + + + PC + Reserved, RES1 + [3:3] + read-only + + + DV + Reserved, RES0 + [2:2] + read-only + + + DA + Reserved, RES0 + [1:1] + read-only + + + INST + Reserved, RES0 + [0:0] + read-only + + + + + TRCSSPCICR + 0x000412c0 + Selects the PE comparator inputs for Single-shot control + 0x00000000 + + + PC + Selects one or more PE comparator inputs for Single-shot control. TRCIDR4.NUMPC defines the size of the PC field. 1 bit is provided for each implemented PE comparator input. For example, if bit[1] == 1 this selects PE comparator input 1 for Single-shot control + [3:0] + read-write + + + + + TRCPDCR + 0x00041310 + Requests the system to provide power to the trace unit + 0x00000000 + + + PU + Powerup request bit: + [3:3] + read-write + + + + + TRCPDSR + 0x00041314 + Returns the following information about the trace unit: - OS Lock status. - Core power domain status. - Power interruption status + 0x00000003 + + + OSLK + OS Lock status bit: + [5:5] + read-only + + + STICKYPD + Sticky powerdown status bit. Indicates whether the trace register state is valid: + [1:1] + read-only + + + POWER + Power status bit: + [0:0] + read-only + + + + + TRCITATBIDR + 0x00041ee4 + Trace Integration ATB Identification Register + 0x00000000 + + + ID + Trace ID + [6:0] + read-write + + + + + TRCITIATBINR + 0x00041ef4 + Trace Integration Instruction ATB In Register + 0x00000000 + + + AFVALIDM + Integration Mode instruction AFVALIDM in + [1:1] + read-write + + + ATREADYM + Integration Mode instruction ATREADYM in + [0:0] + read-write + + + + + TRCITIATBOUTR + 0x00041efc + Trace Integration Instruction ATB Out Register + 0x00000000 + + + AFREADY + Integration Mode instruction AFREADY out + [1:1] + read-write + + + ATVALID + Integration Mode instruction ATVALID out + [0:0] + read-write + + + + + TRCCLAIMSET + 0x00041fa0 + Claim Tag Set Register + 0x0000000f + + + SET3 + When a write to one of these bits occurs, with the value: + [3:3] + read-write + + + SET2 + When a write to one of these bits occurs, with the value: + [2:2] + read-write + + + SET1 + When a write to one of these bits occurs, with the value: + [1:1] + read-write + + + SET0 + When a write to one of these bits occurs, with the value: + [0:0] + read-write + + + + + TRCCLAIMCLR + 0x00041fa4 + Claim Tag Clear Register + 0x00000000 + + + CLR3 + When a write to one of these bits occurs, with the value: + [3:3] + read-write + + + CLR2 + When a write to one of these bits occurs, with the value: + [2:2] + read-write + + + CLR1 + When a write to one of these bits occurs, with the value: + [1:1] + read-write + + + CLR0 + When a write to one of these bits occurs, with the value: + [0:0] + read-write + + + + + TRCAUTHSTATUS + 0x00041fb8 + Returns the level of tracing that the trace unit can support + 0x00000000 + + + SNID + Indicates whether the system enables the trace unit to support Secure non-invasive debug: + [7:6] + read-only + + + SID + Indicates whether the trace unit supports Secure invasive debug: + [5:4] + read-only + + + NSNID + Indicates whether the system enables the trace unit to support Non-secure non-invasive debug: + [3:2] + read-only + + + NSID + Indicates whether the trace unit supports Non-secure invasive debug: + [1:0] + read-only + + + + + TRCDEVARCH + 0x00041fbc + TRCDEVARCH + 0x47724a13 + + + ARCHITECT + reads as 0b01000111011 + [31:21] + read-only + + + PRESENT + reads as 0b1 + [20:20] + read-only + + + REVISION + reads as 0b0000 + [19:16] + read-only + + + ARCHID + reads as 0b0100101000010011 + [15:0] + read-only + + + + + TRCDEVID + 0x00041fc8 + TRCDEVID + 0x00000000 + + + TRCDEVID + [31:0] + read-write + + + + + TRCDEVTYPE + 0x00041fcc + TRCDEVTYPE + 0x00000013 + + + SUB + reads as 0b0001 + [7:4] + read-only + + + MAJOR + reads as 0b0011 + [3:0] + read-only + + + + + TRCPIDR4 + 0x00041fd0 + TRCPIDR4 + 0x00000004 + + + SIZE + reads as `ImpDef + [7:4] + read-only + + + DES_2 + reads as `ImpDef + [3:0] + read-only + + + + + TRCPIDR5 + 0x00041fd4 + TRCPIDR5 + 0x00000000 + + + TRCPIDR5 + [31:0] + read-write + + + + + TRCPIDR6 + 0x00041fd8 + TRCPIDR6 + 0x00000000 + + + TRCPIDR6 + [31:0] + read-write + + + + + TRCPIDR7 + 0x00041fdc + TRCPIDR7 + 0x00000000 + + + TRCPIDR7 + [31:0] + read-write + + + + + TRCPIDR0 + 0x00041fe0 + TRCPIDR0 + 0x00000021 + + + PART_0 + reads as `ImpDef + [7:0] + read-only + + + + + TRCPIDR1 + 0x00041fe4 + TRCPIDR1 + 0x000000bd + + + DES_0 + reads as `ImpDef + [7:4] + read-only + + + PART_0 + reads as `ImpDef + [3:0] + read-only + + + + + TRCPIDR2 + 0x00041fe8 + TRCPIDR2 + 0x0000002b + + + REVISION + reads as `ImpDef + [7:4] + read-only + + + JEDEC + reads as 0b1 + [3:3] + read-only + + + DES_0 + reads as `ImpDef + [2:0] + read-only + + + + + TRCPIDR3 + 0x00041fec + TRCPIDR3 + 0x00000000 + + + REVAND + reads as `ImpDef + [7:4] + read-only + + + CMOD + reads as `ImpDef + [3:0] + read-only + + + + + TRCCIDR0 + 0x00041ff0 + TRCCIDR0 + 0x0000000d + + + PRMBL_0 + reads as 0b00001101 + [7:0] + read-only + + + + + TRCCIDR1 + 0x00041ff4 + TRCCIDR1 + 0x00000090 + + + CLASS + reads as 0b1001 + [7:4] + read-only + + + PRMBL_1 + reads as 0b0000 + [3:0] + read-only + + + + + TRCCIDR2 + 0x00041ff8 + TRCCIDR2 + 0x00000005 + + + PRMBL_2 + reads as 0b00000101 + [7:0] + read-only + + + + + TRCCIDR3 + 0x00041ffc + TRCCIDR3 + 0x000000b1 + + + PRMBL_3 + reads as 0b10110001 + [7:0] + read-only + + + + + CTICONTROL + 0x00042000 + CTI Control Register + 0x00000000 + + + GLBEN + Enables or disables the CTI + [0:0] + read-write + + + + + CTIINTACK + 0x00042010 + CTI Interrupt Acknowledge Register + 0x00000000 + + + INTACK + Acknowledges the corresponding ctitrigout output. There is one bit of the register for each ctitrigout output. When a 1 is written to a bit in this register, the corresponding ctitrigout is acknowledged, causing it to be cleared. + [7:0] + read-write + + + + + CTIAPPSET + 0x00042014 + CTI Application Trigger Set Register + 0x00000000 + + + APPSET + Setting a bit HIGH generates a channel event for the selected channel. There is one bit of the register for each channel + [3:0] + read-write + + + + + CTIAPPCLEAR + 0x00042018 + CTI Application Trigger Clear Register + 0x00000000 + + + APPCLEAR + Sets the corresponding bits in the CTIAPPSET to 0. There is one bit of the register for each channel. + [3:0] + read-write + + + + + CTIAPPPULSE + 0x0004201c + CTI Application Pulse Register + 0x00000000 + + + APPULSE + Setting a bit HIGH generates a channel event pulse for the selected channel. There is one bit of the register for each channel. + [3:0] + read-write + + + + + CTIINEN0 + 0x00042020 + CTI Trigger to Channel Enable Registers + 0x00000000 + + + TRIGINEN + Enables a cross trigger event to the corresponding channel when a ctitrigin input is activated. There is one bit of the field for each of the four channels + [3:0] + read-write + + + + + CTIINEN1 + 0x00042024 + CTI Trigger to Channel Enable Registers + 0x00000000 + + + TRIGINEN + Enables a cross trigger event to the corresponding channel when a ctitrigin input is activated. There is one bit of the field for each of the four channels + [3:0] + read-write + + + + + CTIINEN2 + 0x00042028 + CTI Trigger to Channel Enable Registers + 0x00000000 + + + TRIGINEN + Enables a cross trigger event to the corresponding channel when a ctitrigin input is activated. There is one bit of the field for each of the four channels + [3:0] + read-write + + + + + CTIINEN3 + 0x0004202c + CTI Trigger to Channel Enable Registers + 0x00000000 + + + TRIGINEN + Enables a cross trigger event to the corresponding channel when a ctitrigin input is activated. There is one bit of the field for each of the four channels + [3:0] + read-write + + + + + CTIINEN4 + 0x00042030 + CTI Trigger to Channel Enable Registers + 0x00000000 + + + TRIGINEN + Enables a cross trigger event to the corresponding channel when a ctitrigin input is activated. There is one bit of the field for each of the four channels + [3:0] + read-write + + + + + CTIINEN5 + 0x00042034 + CTI Trigger to Channel Enable Registers + 0x00000000 + + + TRIGINEN + Enables a cross trigger event to the corresponding channel when a ctitrigin input is activated. There is one bit of the field for each of the four channels + [3:0] + read-write + + + + + CTIINEN6 + 0x00042038 + CTI Trigger to Channel Enable Registers + 0x00000000 + + + TRIGINEN + Enables a cross trigger event to the corresponding channel when a ctitrigin input is activated. There is one bit of the field for each of the four channels + [3:0] + read-write + + + + + CTIINEN7 + 0x0004203c + CTI Trigger to Channel Enable Registers + 0x00000000 + + + TRIGINEN + Enables a cross trigger event to the corresponding channel when a ctitrigin input is activated. There is one bit of the field for each of the four channels + [3:0] + read-write + + + + + CTIOUTEN0 + 0x000420a0 + CTI Trigger to Channel Enable Registers + 0x00000000 + + + TRIGOUTEN + Enables a cross trigger event to ctitrigout when the corresponding channel is activated. There is one bit of the field for each of the four channels. + [3:0] + read-write + + + + + CTIOUTEN1 + 0x000420a4 + CTI Trigger to Channel Enable Registers + 0x00000000 + + + TRIGOUTEN + Enables a cross trigger event to ctitrigout when the corresponding channel is activated. There is one bit of the field for each of the four channels. + [3:0] + read-write + + + + + CTIOUTEN2 + 0x000420a8 + CTI Trigger to Channel Enable Registers + 0x00000000 + + + TRIGOUTEN + Enables a cross trigger event to ctitrigout when the corresponding channel is activated. There is one bit of the field for each of the four channels. + [3:0] + read-write + + + + + CTIOUTEN3 + 0x000420ac + CTI Trigger to Channel Enable Registers + 0x00000000 + + + TRIGOUTEN + Enables a cross trigger event to ctitrigout when the corresponding channel is activated. There is one bit of the field for each of the four channels. + [3:0] + read-write + + + + + CTIOUTEN4 + 0x000420b0 + CTI Trigger to Channel Enable Registers + 0x00000000 + + + TRIGOUTEN + Enables a cross trigger event to ctitrigout when the corresponding channel is activated. There is one bit of the field for each of the four channels. + [3:0] + read-write + + + + + CTIOUTEN5 + 0x000420b4 + CTI Trigger to Channel Enable Registers + 0x00000000 + + + TRIGOUTEN + Enables a cross trigger event to ctitrigout when the corresponding channel is activated. There is one bit of the field for each of the four channels. + [3:0] + read-write + + + + + CTIOUTEN6 + 0x000420b8 + CTI Trigger to Channel Enable Registers + 0x00000000 + + + TRIGOUTEN + Enables a cross trigger event to ctitrigout when the corresponding channel is activated. There is one bit of the field for each of the four channels. + [3:0] + read-write + + + + + CTIOUTEN7 + 0x000420bc + CTI Trigger to Channel Enable Registers + 0x00000000 + + + TRIGOUTEN + Enables a cross trigger event to ctitrigout when the corresponding channel is activated. There is one bit of the field for each of the four channels. + [3:0] + read-write + + + + + CTITRIGINSTATUS + 0x00042130 + CTI Trigger to Channel Enable Registers + 0x00000000 + + + TRIGINSTATUS + Shows the status of the ctitrigin inputs. There is one bit of the field for each trigger input.Because the register provides a view of the raw ctitrigin inputs, the reset value is UNKNOWN. + [7:0] + read-only + + + + + CTITRIGOUTSTATUS + 0x00042134 + CTI Trigger In Status Register + 0x00000000 + + + TRIGOUTSTATUS + Shows the status of the ctitrigout outputs. There is one bit of the field for each trigger output. + [7:0] + read-only + + + + + CTICHINSTATUS + 0x00042138 + CTI Channel In Status Register + 0x00000000 + + + CTICHOUTSTATUS + Shows the status of the ctichout outputs. There is one bit of the field for each channel output + [3:0] + read-only + + + + + CTIGATE + 0x00042140 + Enable CTI Channel Gate register + 0x0000000f + + + CTIGATEEN3 + Enable ctichout3. Set to 0 to disable channel propagation. + [3:3] + read-write + + + CTIGATEEN2 + Enable ctichout2. Set to 0 to disable channel propagation. + [2:2] + read-write + + + CTIGATEEN1 + Enable ctichout1. Set to 0 to disable channel propagation. + [1:1] + read-write + + + CTIGATEEN0 + Enable ctichout0. Set to 0 to disable channel propagation. + [0:0] + read-write + + + + + ASICCTL + 0x00042144 + External Multiplexer Control register + 0x00000000 + + + ASICCTL + [31:0] + read-write + + + + + ITCHOUT + 0x00042ee4 + Integration Test Channel Output register + 0x00000000 + + + CTCHOUT + Sets the value of the ctichout outputs + [3:0] + read-write + + + + + ITTRIGOUT + 0x00042ee8 + Integration Test Trigger Output register + 0x00000000 + + + CTTRIGOUT + Sets the value of the ctitrigout outputs + [7:0] + read-write + + + + + ITCHIN + 0x00042ef4 + Integration Test Channel Input register + 0x00000000 + + + CTCHIN + Reads the value of the ctichin inputs. + [3:0] + read-only + + + + + ITCTRL + 0x00042f00 + Integration Mode Control register + 0x00000000 + + + IME + Integration Mode Enable + [0:0] + read-write + + + + + DEVARCH + 0x00042fbc + Device Architecture register + 0x47701a14 + + + ARCHITECT + Indicates the component architect + [31:21] + read-only + + + PRESENT + Indicates whether the DEVARCH register is present + [20:20] + read-only + + + REVISION + Indicates the architecture revision + [19:16] + read-only + + + ARCHID + Indicates the component + [15:0] + read-only + + + + + DEVID + 0x00042fc8 + Device Configuration register + 0x00040800 + + + NUMCH + Number of ECT channels available + [19:16] + read-only + + + NUMTRIG + Number of ECT triggers available. + [15:8] + read-only + + + EXTMUXNUM + Indicates the number of multiplexers available on Trigger Inputs and Trigger Outputs that are using asicctl. The default value of 0b00000 indicates that no multiplexing is present. This value of this bit depends on the Verilog define EXTMUXNUM that you must change accordingly. + [4:0] + read-only + + + + + DEVTYPE + 0x00042fcc + Device Type Identifier register + 0x00000014 + + + SUB + Sub-classification of the type of the debug component as specified in the ARM Architecture Specification within the major classification as specified in the MAJOR field. + [7:4] + read-only + + + MAJOR + Major classification of the type of the debug component as specified in the ARM Architecture Specification for this debug and trace component. + [3:0] + read-only + + + + + PIDR4 + 0x00042fd0 + CoreSight Peripheral ID4 + 0x00000004 + + + SIZE + Always 0b0000. Indicates that the device only occupies 4KB of memory + [7:4] + read-only + + + DES_2 + Together, PIDR1.DES_0, PIDR2.DES_1, and PIDR4.DES_2 identify the designer of the component. + [3:0] + read-only + + + + + PIDR5 + 0x00042fd4 + CoreSight Peripheral ID5 + 0x00000000 + + + PIDR5 + [31:0] + read-write + + + + + PIDR6 + 0x00042fd8 + CoreSight Peripheral ID6 + 0x00000000 + + + PIDR6 + [31:0] + read-write + + + + + PIDR7 + 0x00042fdc + CoreSight Peripheral ID7 + 0x00000000 + + + PIDR7 + [31:0] + read-write + + + + + PIDR0 + 0x00042fe0 + CoreSight Peripheral ID0 + 0x00000021 + + + PART_0 + Bits[7:0] of the 12-bit part number of the component. The designer of the component assigns this part number. + [7:0] + read-only + + + + + PIDR1 + 0x00042fe4 + CoreSight Peripheral ID1 + 0x000000bd + + + DES_0 + Together, PIDR1.DES_0, PIDR2.DES_1, and PIDR4.DES_2 identify the designer of the component. + [7:4] + read-only + + + PART_1 + Bits[11:8] of the 12-bit part number of the component. The designer of the component assigns this part number. + [3:0] + read-only + + + + + PIDR2 + 0x00042fe8 + CoreSight Peripheral ID2 + 0x0000000b + + + REVISION + This device is at r1p0 + [7:4] + read-only + + + JEDEC + Always 1. Indicates that the JEDEC-assigned designer ID is used. + [3:3] + read-only + + + DES_1 + Together, PIDR1.DES_0, PIDR2.DES_1, and PIDR4.DES_2 identify the designer of the component. + [2:0] + read-only + + + + + PIDR3 + 0x00042fec + CoreSight Peripheral ID3 + 0x00000000 + + + REVAND + Indicates minor errata fixes specific to the revision of the component being used, for example metal fixes after implementation. In most cases, this field is 0b0000. ARM recommends that the component designers ensure that a metal fix can change this field if required, for example, by driving it from registers that reset to 0b0000. + [7:4] + read-only + + + CMOD + Customer Modified. Indicates whether the customer has modified the behavior of the component. In most cases, this field is 0b0000. Customers change this value when they make authorized modifications to this component. + [3:0] + read-only + + + + + CIDR0 + 0x00042ff0 + CoreSight Component ID0 + 0x0000000d + + + PRMBL_0 + Preamble[0]. Contains bits[7:0] of the component identification code + [7:0] + read-only + + + + + CIDR1 + 0x00042ff4 + CoreSight Component ID1 + 0x00000090 + + + CLASS + Class of the component, for example, whether the component is a ROM table or a generic CoreSight component. Contains bits[15:12] of the component identification code. + [7:4] + read-only + + + PRMBL_1 + Preamble[1]. Contains bits[11:8] of the component identification code. + [3:0] + read-only + + + + + CIDR2 + 0x00042ff8 + CoreSight Component ID2 + 0x00000005 + + + PRMBL_2 + Preamble[2]. Contains bits[23:16] of the component identification code. + [7:0] + read-only + + + + + CIDR3 + 0x00042ffc + CoreSight Component ID3 + 0x000000b1 + + + PRMBL_3 + Preamble[3]. Contains bits[31:24] of the component identification code. + [7:0] + read-only + + + + + + + PPB_NS + 0xe0020000 + + + QMI + QSPI Memory Interface. + + Provides a memory-mapped interface to up to two SPI/DSPI/QSPI flash or PSRAM devices. Also provides a serial interface for programming and configuration of the external device. + 0x400d0000 + + 0 + 84 + registers + + + + DIRECT_CSR + 0x00000000 + Control and status for direct serial mode + + Direct serial mode allows the processor to send and receive raw serial frames, for programming, configuration and control of the external memory devices. Only SPI mode 0 (CPOL=0 CPHA=0) is supported. + 0x01800000 + + + RXDELAY + Delay the read data sample timing, in units of one half of a system clock cycle. (Not necessarily half of an SCK cycle.) + [31:30] + read-write + + + CLKDIV + Clock divisor for direct serial mode. Divisors of 1..255 are encoded directly, and the maximum divisor of 256 is encoded by a value of CLKDIV=0. + + The clock divisor can be changed on-the-fly by software, without halting or otherwise coordinating with the serial interface. The serial interface will sample the latest clock divisor each time it begins the transmission of a new byte. + [29:22] + read-write + + + RXLEVEL + Current level of DIRECT_RX FIFO + [20:18] + read-only + + + RXFULL + When 1, the DIRECT_RX FIFO is currently full. The serial interface will be stalled until data is popped; the interface will not begin a new serial frame when the DIRECT_TX FIFO is empty or the DIRECT_RX FIFO is full. + [17:17] + read-only + + + RXEMPTY + When 1, the DIRECT_RX FIFO is currently empty. If the processor attempts to read more data, the FIFO state is not affected, but the value returned to the processor is undefined. + [16:16] + read-only + + + TXLEVEL + Current level of DIRECT_TX FIFO + [14:12] + read-only + + + TXEMPTY + When 1, the DIRECT_TX FIFO is currently empty. Unless the processor pushes more data, transmission will stop and BUSY will go low once the current 8-bit serial frame completes. + [11:11] + read-only + + + TXFULL + When 1, the DIRECT_TX FIFO is currently full. If the processor tries to write more data, that data will be ignored. + [10:10] + read-only + + + AUTO_CS1N + When 1, automatically assert the CS1n chip select line whenever the BUSY flag is set. + [7:7] + read-write + + + AUTO_CS0N + When 1, automatically assert the CS0n chip select line whenever the BUSY flag is set. + [6:6] + read-write + + + ASSERT_CS1N + When 1, assert (i.e. drive low) the CS1n chip select line. + + Note that this applies even when DIRECT_CSR_EN is 0. + [3:3] + read-write + + + ASSERT_CS0N + When 1, assert (i.e. drive low) the CS0n chip select line. + + Note that this applies even when DIRECT_CSR_EN is 0. + [2:2] + read-write + + + BUSY + Direct mode busy flag. If 1, data is currently being shifted in/out (or would be if the interface were not stalled on the RX FIFO), and the chip select must not yet be deasserted. + + The busy flag will also be set to 1 if a memory-mapped transfer is still in progress when direct mode is enabled. Direct mode blocks new memory-mapped transfers, but can't halt a transfer that is already in progress. If there is a chance that memory-mapped transfers may be in progress, the busy flag should be polled for 0 before asserting the chip select. + + (In practice you will usually discover this timing condition through other means, because any subsequent memory-mapped transfers when direct mode is enabled will return bus errors, which are difficult to ignore.) + [1:1] + read-only + + + EN + Enable direct mode. + + In direct mode, software controls the chip select lines, and can perform direct SPI transfers by pushing data to the DIRECT_TX FIFO, and popping the same amount of data from the DIRECT_RX FIFO. + + Memory-mapped accesses will generate bus errors when direct serial mode is enabled. + [0:0] + read-write + + + + + DIRECT_TX + 0x00000004 + Transmit FIFO for direct mode + 0x00000000 + + + NOPUSH + Inhibit the RX FIFO push that would correspond to this TX FIFO entry. + + Useful to avoid garbage appearing in the RX FIFO when pushing the command at the beginning of a SPI transfer. + [20:20] + write-only + + + OE + Output enable (active-high). For single width (SPI), this field is ignored, and SD0 is always set to output, with SD1 always set to input. + + For dual and quad width (DSPI/QSPI), this sets whether the relevant SDx pads are set to output whilst transferring this FIFO record. In this case the command/address should have OE set, and the data transfer should have OE set or clear depending on the direction of the transfer. + [19:19] + write-only + + + DWIDTH + Data width. If 0, hardware will transmit the 8 LSBs of the DIRECT_TX DATA field, and return an 8-bit value in the 8 LSBs of DIRECT_RX. If 1, the full 16-bit width is used. 8-bit and 16-bit transfers can be mixed freely. + [18:18] + write-only + + + IWIDTH + Configure whether this FIFO record is transferred with single/dual/quad interface width (0/1/2). Different widths can be mixed freely. + [17:16] + write-only + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + DATA + Data pushed here will be clocked out falling edges of SCK (or before the very first rising edge of SCK, if this is the first pulse). For each byte clocked out, the interface will simultaneously sample one byte, on rising edges of SCK, and push this to the DIRECT_RX FIFO. + + For 16-bit data, the least-significant byte is transmitted first. + [15:0] + write-only + + + + + DIRECT_RX + 0x00000008 + Receive FIFO for direct mode + 0x00000000 + + + DIRECT_RX + With each byte clocked out on the serial interface, one byte will simultaneously be clocked in, and will appear in this FIFO. The serial interface will stall when this FIFO is full, to avoid dropping data. + + When 16-bit data is pushed into the TX FIFO, the corresponding RX FIFO push will also contain 16 bits of data. The least-significant byte is the first one received. + [15:0] + read-only + modify + + + + + M0_TIMING + 0x0000000c + Timing configuration register for memory address window 0. + 0x40000004 + + + COOLDOWN + Chip select cooldown period. When a memory transfer finishes, the chip select remains asserted for 64 x COOLDOWN system clock cycles, plus half an SCK clock period (rounded up for odd SCK divisors). After this cooldown expires, the chip select is always deasserted to save power. + + If the next memory access arrives within the cooldown period, the QMI may be able to append more SCK cycles to the currently ongoing SPI transfer, rather than starting a new transfer. This reduces access latency and increases bus throughput. + + Specifically, the next access must be in the same direction (read/write), access the same memory window (chip select 0/1), and follow sequentially the address of the last transfer. If any of these are false, the new access will first deassert the chip select, then begin a new transfer. + + If COOLDOWN is 0, the address alignment configured by PAGEBREAK has been reached, or the total chip select assertion limit MAX_SELECT has been reached, the cooldown period is skipped, and the chip select will always be deasserted one half SCK period after the transfer finishes. + [31:30] + read-write + + + PAGEBREAK + When page break is enabled, chip select will automatically deassert when crossing certain power-of-2-aligned address boundaries. The next access will always begin a new read/write SPI burst, even if the address of the next access follows in sequence with the last access before the page boundary. + + Some flash and PSRAM devices forbid crossing page boundaries with a single read/write transfer, or restrict the operating frequency for transfers that do cross page a boundary. This option allows the QMI to safely support those devices. + + This field has no effect when COOLDOWN is disabled. + [29:28] + read-write + + + NONE + 0 + No page boundary is enforced + + + 256 + 1 + Break bursts crossing a 256-byte page boundary + + + 1024 + 2 + Break bursts crossing a 1024-byte quad-page boundary + + + 4096 + 3 + Break bursts crossing a 4096-byte sector boundary + + + + + SELECT_SETUP + Add up to one additional system clock cycle of setup between chip select assertion and the first rising edge of SCK. + + The default setup time is one half SCK period, which is usually sufficient except for very high SCK frequencies with some flash devices. + [25:25] + read-write + + + SELECT_HOLD + Add up to three additional system clock cycles of active hold between the last falling edge of SCK and the deassertion of this window's chip select. + + The default hold time is one system clock cycle. Note that flash datasheets usually give chip select active hold time from the last *rising* edge of SCK, and so even zero hold from the last falling edge would be safe. + + Note that this is a minimum hold time guaranteed by the QMI: the actual chip select active hold may be slightly longer for read transfers with low clock divisors and/or high sample delays. Specifically, if the point two cycles after the last RX data sample is later than the last SCK falling edge, then the hold time is measured from *this* point. + + Note also that, in case the final SCK pulse is masked to save energy (true for non-DTR reads when COOLDOWN is disabled or PAGE_BREAK is reached), all of QMI's timing logic behaves as though the clock pulse were still present. The SELECT_HOLD time is applied from the point where the last SCK falling edge would be if the clock pulse were not masked. + [24:23] + read-write + + + MAX_SELECT + Enforce a maximum assertion duration for this window's chip select, in units of 64 system clock cycles. If 0, the QMI is permitted to keep the chip select asserted indefinitely when servicing sequential memory accesses (see COOLDOWN). + + This feature is required to meet timing constraints of PSRAM devices, which specify a maximum chip select assertion so they can perform DRAM refresh cycles. See also MIN_DESELECT, which can enforce a minimum deselect time. + + If a memory access is in progress at the time MAX_SELECT is reached, the QMI will wait for the access to complete before deasserting the chip select. This additional time must be accounted for to calculate a safe MAX_SELECT value. In the worst case, this may be a fully-formed serial transfer, including command prefix and address, with a data payload as large as one cache line. + [22:17] + read-write + + + MIN_DESELECT + After this window's chip select is deasserted, it remains deasserted for half an SCK cycle (rounded up to an integer number of system clock cycles), plus MIN_DESELECT additional system clock cycles, before the QMI reasserts either chip select pin. + + Nonzero values may be required for PSRAM devices which enforce a longer minimum CS deselect time, so that they can perform internal DRAM refresh cycles whilst deselected. + [16:12] + read-write + + + RXDELAY + Delay the read data sample timing, in units of one half of a system clock cycle. (Not necessarily half of an SCK cycle.) An RXDELAY of 0 means the sample is captured at the SDI input registers simultaneously with the rising edge of SCK launched from the SCK output register. + + At higher SCK frequencies, RXDELAY may need to be increased to account for the round trip delay of the pads, and the clock-to-Q delay of the QSPI memory device. + [10:8] + read-write + + + CLKDIV + Clock divisor. Odd and even divisors are supported. Defines the SCK clock period in units of 1 system clock cycle. Divisors 1..255 are encoded directly, and a divisor of 256 is encoded with a value of CLKDIV=0. + + The clock divisor can be changed on-the-fly, even when the QMI is currently accessing memory in this address window. All other parameters must only be changed when the QMI is idle. + + If software is increasing CLKDIV in anticipation of an increase in the system clock frequency, a dummy access to either memory window (and appropriate processor barriers/fences) must be inserted after the Mx_TIMING write to ensure the SCK divisor change is in effect _before_ the system clock is changed. + [7:0] + read-write + + + + + M0_RFMT + 0x00000010 + Read transfer format configuration for memory address window 0. + + Configure the bus width of each transfer phase individually, and configure the length or presence of the command prefix, command suffix and dummy/turnaround transfer phases. Only 24-bit addresses are supported. + + The reset value of the M0_RFMT register is configured to support a basic 03h serial read transfer with no additional configuration. + 0x00001000 + + + DTR + Enable double transfer rate (DTR) for read commands: address, suffix and read data phases are active on both edges of SCK. SDO data is launched centre-aligned on each SCK edge, and SDI data is captured on the SCK edge that follows its launch. + + DTR is implemented by halving the clock rate; SCK has a period of 2 x CLK_DIV throughout the transfer. The prefix and dummy phases are still single transfer rate. + + If the suffix is quad-width, it must be 0 or 8 bits in length, to ensure an even number of SCK edges. + [28:28] + read-write + + + DUMMY_LEN + Length of dummy phase between command suffix and data phase, in units of 4 bits. (i.e. 1 cycle for quad width, 2 for dual, 4 for single) + [18:16] + read-write + + + NONE + 0 + No dummy phase + + + 4 + 1 + 4 dummy bits + + + 8 + 2 + 8 dummy bits + + + 12 + 3 + 12 dummy bits + + + 16 + 4 + 16 dummy bits + + + 20 + 5 + 20 dummy bits + + + 24 + 6 + 24 dummy bits + + + 28 + 7 + 28 dummy bits + + + + + SUFFIX_LEN + Length of post-address command suffix, in units of 4 bits. (i.e. 1 cycle for quad width, 2 for dual, 4 for single) + + Only values of 0 and 8 bits are supported. + [15:14] + read-write + + + NONE + 0 + No suffix + + + 8 + 2 + 8-bit suffix + + + + + PREFIX_LEN + Length of command prefix, in units of 8 bits. (i.e. 2 cycles for quad width, 4 for dual, 8 for single) + [12:12] + read-write + + + NONE + 0 + No prefix + + + 8 + 1 + 8-bit prefix + + + + + DATA_WIDTH + The width used for the data transfer + [9:8] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + DUMMY_WIDTH + The width used for the dummy phase, if any. + + If width is single, SD0/MOSI is held asserted low during the dummy phase, and SD1...SD3 are tristated. If width is dual/quad, all IOs are tristated during the dummy phase. + [7:6] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + SUFFIX_WIDTH + The width used for the post-address command suffix, if any + [5:4] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + ADDR_WIDTH + The transfer width used for the address. The address phase always transfers 24 bits in total. + [3:2] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + PREFIX_WIDTH + The transfer width used for the command prefix, if any + [1:0] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + + + M0_RCMD + 0x00000014 + Command constants used for reads from memory address window 0. + + The reset value of the M0_RCMD register is configured to support a basic 03h serial read transfer with no additional configuration. + 0x0000a003 + + + SUFFIX + The command suffix bits following the address, if Mx_RFMT_SUFFIX_LEN is nonzero. + [15:8] + read-write + + + PREFIX + The command prefix bits to prepend on each new transfer, if Mx_RFMT_PREFIX_LEN is nonzero. + [7:0] + read-write + + + + + M0_WFMT + 0x00000018 + Write transfer format configuration for memory address window 0. + + Configure the bus width of each transfer phase individually, and configure the length or presence of the command prefix, command suffix and dummy/turnaround transfer phases. Only 24-bit addresses are supported. + + The reset value of the M0_WFMT register is configured to support a basic 02h serial write transfer. However, writes to this window must first be enabled via the XIP_CTRL_WRITABLE_M0 bit, as XIP memory is read-only by default. + 0x00001000 + + + DTR + Enable double transfer rate (DTR) for write commands: address, suffix and write data phases are active on both edges of SCK. SDO data is launched centre-aligned on each SCK edge, and SDI data is captured on the SCK edge that follows its launch. + + DTR is implemented by halving the clock rate; SCK has a period of 2 x CLK_DIV throughout the transfer. The prefix and dummy phases are still single transfer rate. + + If the suffix is quad-width, it must be 0 or 8 bits in length, to ensure an even number of SCK edges. + [28:28] + read-write + + + DUMMY_LEN + Length of dummy phase between command suffix and data phase, in units of 4 bits. (i.e. 1 cycle for quad width, 2 for dual, 4 for single) + [18:16] + read-write + + + NONE + 0 + No dummy phase + + + 4 + 1 + 4 dummy bits + + + 8 + 2 + 8 dummy bits + + + 12 + 3 + 12 dummy bits + + + 16 + 4 + 16 dummy bits + + + 20 + 5 + 20 dummy bits + + + 24 + 6 + 24 dummy bits + + + 28 + 7 + 28 dummy bits + + + + + SUFFIX_LEN + Length of post-address command suffix, in units of 4 bits. (i.e. 1 cycle for quad width, 2 for dual, 4 for single) + + Only values of 0 and 8 bits are supported. + [15:14] + read-write + + + NONE + 0 + No suffix + + + 8 + 2 + 8-bit suffix + + + + + PREFIX_LEN + Length of command prefix, in units of 8 bits. (i.e. 2 cycles for quad width, 4 for dual, 8 for single) + [12:12] + read-write + + + NONE + 0 + No prefix + + + 8 + 1 + 8-bit prefix + + + + + DATA_WIDTH + The width used for the data transfer + [9:8] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + DUMMY_WIDTH + The width used for the dummy phase, if any. + + If width is single, SD0/MOSI is held asserted low during the dummy phase, and SD1...SD3 are tristated. If width is dual/quad, all IOs are tristated during the dummy phase. + [7:6] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + SUFFIX_WIDTH + The width used for the post-address command suffix, if any + [5:4] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + ADDR_WIDTH + The transfer width used for the address. The address phase always transfers 24 bits in total. + [3:2] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + PREFIX_WIDTH + The transfer width used for the command prefix, if any + [1:0] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + + + M0_WCMD + 0x0000001c + Command constants used for writes to memory address window 0. + + The reset value of the M0_WCMD register is configured to support a basic 02h serial write transfer with no additional configuration. + 0x0000a002 + + + SUFFIX + The command suffix bits following the address, if Mx_WFMT_SUFFIX_LEN is nonzero. + [15:8] + read-write + + + PREFIX + The command prefix bits to prepend on each new transfer, if Mx_WFMT_PREFIX_LEN is nonzero. + [7:0] + read-write + + + + + M1_TIMING + 0x00000020 + Timing configuration register for memory address window 1. + 0x40000004 + + + COOLDOWN + Chip select cooldown period. When a memory transfer finishes, the chip select remains asserted for 64 x COOLDOWN system clock cycles, plus half an SCK clock period (rounded up for odd SCK divisors). After this cooldown expires, the chip select is always deasserted to save power. + + If the next memory access arrives within the cooldown period, the QMI may be able to append more SCK cycles to the currently ongoing SPI transfer, rather than starting a new transfer. This reduces access latency and increases bus throughput. + + Specifically, the next access must be in the same direction (read/write), access the same memory window (chip select 0/1), and follow sequentially the address of the last transfer. If any of these are false, the new access will first deassert the chip select, then begin a new transfer. + + If COOLDOWN is 0, the address alignment configured by PAGEBREAK has been reached, or the total chip select assertion limit MAX_SELECT has been reached, the cooldown period is skipped, and the chip select will always be deasserted one half SCK period after the transfer finishes. + [31:30] + read-write + + + PAGEBREAK + When page break is enabled, chip select will automatically deassert when crossing certain power-of-2-aligned address boundaries. The next access will always begin a new read/write SPI burst, even if the address of the next access follows in sequence with the last access before the page boundary. + + Some flash and PSRAM devices forbid crossing page boundaries with a single read/write transfer, or restrict the operating frequency for transfers that do cross page a boundary. This option allows the QMI to safely support those devices. + + This field has no effect when COOLDOWN is disabled. + [29:28] + read-write + + + NONE + 0 + No page boundary is enforced + + + 256 + 1 + Break bursts crossing a 256-byte page boundary + + + 1024 + 2 + Break bursts crossing a 1024-byte quad-page boundary + + + 4096 + 3 + Break bursts crossing a 4096-byte sector boundary + + + + + SELECT_SETUP + Add up to one additional system clock cycle of setup between chip select assertion and the first rising edge of SCK. + + The default setup time is one half SCK period, which is usually sufficient except for very high SCK frequencies with some flash devices. + [25:25] + read-write + + + SELECT_HOLD + Add up to three additional system clock cycles of active hold between the last falling edge of SCK and the deassertion of this window's chip select. + + The default hold time is one system clock cycle. Note that flash datasheets usually give chip select active hold time from the last *rising* edge of SCK, and so even zero hold from the last falling edge would be safe. + + Note that this is a minimum hold time guaranteed by the QMI: the actual chip select active hold may be slightly longer for read transfers with low clock divisors and/or high sample delays. Specifically, if the point two cycles after the last RX data sample is later than the last SCK falling edge, then the hold time is measured from *this* point. + + Note also that, in case the final SCK pulse is masked to save energy (true for non-DTR reads when COOLDOWN is disabled or PAGE_BREAK is reached), all of QMI's timing logic behaves as though the clock pulse were still present. The SELECT_HOLD time is applied from the point where the last SCK falling edge would be if the clock pulse were not masked. + [24:23] + read-write + + + MAX_SELECT + Enforce a maximum assertion duration for this window's chip select, in units of 64 system clock cycles. If 0, the QMI is permitted to keep the chip select asserted indefinitely when servicing sequential memory accesses (see COOLDOWN). + + This feature is required to meet timing constraints of PSRAM devices, which specify a maximum chip select assertion so they can perform DRAM refresh cycles. See also MIN_DESELECT, which can enforce a minimum deselect time. + + If a memory access is in progress at the time MAX_SELECT is reached, the QMI will wait for the access to complete before deasserting the chip select. This additional time must be accounted for to calculate a safe MAX_SELECT value. In the worst case, this may be a fully-formed serial transfer, including command prefix and address, with a data payload as large as one cache line. + [22:17] + read-write + + + MIN_DESELECT + After this window's chip select is deasserted, it remains deasserted for half an SCK cycle (rounded up to an integer number of system clock cycles), plus MIN_DESELECT additional system clock cycles, before the QMI reasserts either chip select pin. + + Nonzero values may be required for PSRAM devices which enforce a longer minimum CS deselect time, so that they can perform internal DRAM refresh cycles whilst deselected. + [16:12] + read-write + + + RXDELAY + Delay the read data sample timing, in units of one half of a system clock cycle. (Not necessarily half of an SCK cycle.) An RXDELAY of 0 means the sample is captured at the SDI input registers simultaneously with the rising edge of SCK launched from the SCK output register. + + At higher SCK frequencies, RXDELAY may need to be increased to account for the round trip delay of the pads, and the clock-to-Q delay of the QSPI memory device. + [10:8] + read-write + + + CLKDIV + Clock divisor. Odd and even divisors are supported. Defines the SCK clock period in units of 1 system clock cycle. Divisors 1..255 are encoded directly, and a divisor of 256 is encoded with a value of CLKDIV=0. + + The clock divisor can be changed on-the-fly, even when the QMI is currently accessing memory in this address window. All other parameters must only be changed when the QMI is idle. + + If software is increasing CLKDIV in anticipation of an increase in the system clock frequency, a dummy access to either memory window (and appropriate processor barriers/fences) must be inserted after the Mx_TIMING write to ensure the SCK divisor change is in effect _before_ the system clock is changed. + [7:0] + read-write + + + + + M1_RFMT + 0x00000024 + Read transfer format configuration for memory address window 1. + + Configure the bus width of each transfer phase individually, and configure the length or presence of the command prefix, command suffix and dummy/turnaround transfer phases. Only 24-bit addresses are supported. + + The reset value of the M1_RFMT register is configured to support a basic 03h serial read transfer with no additional configuration. + 0x00001000 + + + DTR + Enable double transfer rate (DTR) for read commands: address, suffix and read data phases are active on both edges of SCK. SDO data is launched centre-aligned on each SCK edge, and SDI data is captured on the SCK edge that follows its launch. + + DTR is implemented by halving the clock rate; SCK has a period of 2 x CLK_DIV throughout the transfer. The prefix and dummy phases are still single transfer rate. + + If the suffix is quad-width, it must be 0 or 8 bits in length, to ensure an even number of SCK edges. + [28:28] + read-write + + + DUMMY_LEN + Length of dummy phase between command suffix and data phase, in units of 4 bits. (i.e. 1 cycle for quad width, 2 for dual, 4 for single) + [18:16] + read-write + + + NONE + 0 + No dummy phase + + + 4 + 1 + 4 dummy bits + + + 8 + 2 + 8 dummy bits + + + 12 + 3 + 12 dummy bits + + + 16 + 4 + 16 dummy bits + + + 20 + 5 + 20 dummy bits + + + 24 + 6 + 24 dummy bits + + + 28 + 7 + 28 dummy bits + + + + + SUFFIX_LEN + Length of post-address command suffix, in units of 4 bits. (i.e. 1 cycle for quad width, 2 for dual, 4 for single) + + Only values of 0 and 8 bits are supported. + [15:14] + read-write + + + NONE + 0 + No suffix + + + 8 + 2 + 8-bit suffix + + + + + PREFIX_LEN + Length of command prefix, in units of 8 bits. (i.e. 2 cycles for quad width, 4 for dual, 8 for single) + [12:12] + read-write + + + NONE + 0 + No prefix + + + 8 + 1 + 8-bit prefix + + + + + DATA_WIDTH + The width used for the data transfer + [9:8] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + DUMMY_WIDTH + The width used for the dummy phase, if any. + + If width is single, SD0/MOSI is held asserted low during the dummy phase, and SD1...SD3 are tristated. If width is dual/quad, all IOs are tristated during the dummy phase. + [7:6] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + SUFFIX_WIDTH + The width used for the post-address command suffix, if any + [5:4] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + ADDR_WIDTH + The transfer width used for the address. The address phase always transfers 24 bits in total. + [3:2] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + PREFIX_WIDTH + The transfer width used for the command prefix, if any + [1:0] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + + + M1_RCMD + 0x00000028 + Command constants used for reads from memory address window 1. + + The reset value of the M1_RCMD register is configured to support a basic 03h serial read transfer with no additional configuration. + 0x0000a003 + + + SUFFIX + The command suffix bits following the address, if Mx_RFMT_SUFFIX_LEN is nonzero. + [15:8] + read-write + + + PREFIX + The command prefix bits to prepend on each new transfer, if Mx_RFMT_PREFIX_LEN is nonzero. + [7:0] + read-write + + + + + M1_WFMT + 0x0000002c + Write transfer format configuration for memory address window 1. + + Configure the bus width of each transfer phase individually, and configure the length or presence of the command prefix, command suffix and dummy/turnaround transfer phases. Only 24-bit addresses are supported. + + The reset value of the M1_WFMT register is configured to support a basic 02h serial write transfer. However, writes to this window must first be enabled via the XIP_CTRL_WRITABLE_M1 bit, as XIP memory is read-only by default. + 0x00001000 + + + DTR + Enable double transfer rate (DTR) for write commands: address, suffix and write data phases are active on both edges of SCK. SDO data is launched centre-aligned on each SCK edge, and SDI data is captured on the SCK edge that follows its launch. + + DTR is implemented by halving the clock rate; SCK has a period of 2 x CLK_DIV throughout the transfer. The prefix and dummy phases are still single transfer rate. + + If the suffix is quad-width, it must be 0 or 8 bits in length, to ensure an even number of SCK edges. + [28:28] + read-write + + + DUMMY_LEN + Length of dummy phase between command suffix and data phase, in units of 4 bits. (i.e. 1 cycle for quad width, 2 for dual, 4 for single) + [18:16] + read-write + + + NONE + 0 + No dummy phase + + + 4 + 1 + 4 dummy bits + + + 8 + 2 + 8 dummy bits + + + 12 + 3 + 12 dummy bits + + + 16 + 4 + 16 dummy bits + + + 20 + 5 + 20 dummy bits + + + 24 + 6 + 24 dummy bits + + + 28 + 7 + 28 dummy bits + + + + + SUFFIX_LEN + Length of post-address command suffix, in units of 4 bits. (i.e. 1 cycle for quad width, 2 for dual, 4 for single) + + Only values of 0 and 8 bits are supported. + [15:14] + read-write + + + NONE + 0 + No suffix + + + 8 + 2 + 8-bit suffix + + + + + PREFIX_LEN + Length of command prefix, in units of 8 bits. (i.e. 2 cycles for quad width, 4 for dual, 8 for single) + [12:12] + read-write + + + NONE + 0 + No prefix + + + 8 + 1 + 8-bit prefix + + + + + DATA_WIDTH + The width used for the data transfer + [9:8] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + DUMMY_WIDTH + The width used for the dummy phase, if any. + + If width is single, SD0/MOSI is held asserted low during the dummy phase, and SD1...SD3 are tristated. If width is dual/quad, all IOs are tristated during the dummy phase. + [7:6] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + SUFFIX_WIDTH + The width used for the post-address command suffix, if any + [5:4] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + ADDR_WIDTH + The transfer width used for the address. The address phase always transfers 24 bits in total. + [3:2] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + PREFIX_WIDTH + The transfer width used for the command prefix, if any + [1:0] + read-write + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + + + M1_WCMD + 0x00000030 + Command constants used for writes to memory address window 1. + + The reset value of the M1_WCMD register is configured to support a basic 02h serial write transfer with no additional configuration. + 0x0000a002 + + + SUFFIX + The command suffix bits following the address, if Mx_WFMT_SUFFIX_LEN is nonzero. + [15:8] + read-write + + + PREFIX + The command prefix bits to prepend on each new transfer, if Mx_WFMT_PREFIX_LEN is nonzero. + [7:0] + read-write + + + + + ATRANS0 + 0x00000034 + Configure address translation for XIP virtual addresses 0x000000 through 0x3fffff (a 4 MiB window starting at +0 MiB). + + Address translation allows a program image to be executed in place at multiple physical flash addresses (for example, a double-buffered flash image for over-the-air updates), without the overhead of position-independent code. + + At reset, the address translation registers are initialised to an identity mapping, so that they can be ignored if address translation is not required. + + Note that the XIP cache is fully virtually addressed, so a cache flush is required after changing the address translation. + 0x04000000 + + + SIZE + Translation aperture size for this virtual address range, in units of 4 kiB (one flash sector). + + Bits 21:12 of the virtual address are compared to SIZE. Offsets greater than SIZE return a bus error, and do not cause a QSPI access. + [26:16] + read-write + + + BASE + Physical address base for this virtual address range, in units of 4 kiB (one flash sector). + + Taking a 24-bit virtual address, firstly bits 23:22 (the two MSBs) are masked to zero, and then BASE is added to bits 23:12 (the upper 12 bits) to form the physical address. Translation wraps on a 16 MiB boundary. + [11:0] + read-write + + + + + ATRANS1 + 0x00000038 + Configure address translation for XIP virtual addresses 0x400000 through 0x7fffff (a 4 MiB window starting at +4 MiB). + + Address translation allows a program image to be executed in place at multiple physical flash addresses (for example, a double-buffered flash image for over-the-air updates), without the overhead of position-independent code. + + At reset, the address translation registers are initialised to an identity mapping, so that they can be ignored if address translation is not required. + + Note that the XIP cache is fully virtually addressed, so a cache flush is required after changing the address translation. + 0x04000400 + + + SIZE + Translation aperture size for this virtual address range, in units of 4 kiB (one flash sector). + + Bits 21:12 of the virtual address are compared to SIZE. Offsets greater than SIZE return a bus error, and do not cause a QSPI access. + [26:16] + read-write + + + BASE + Physical address base for this virtual address range, in units of 4 kiB (one flash sector). + + Taking a 24-bit virtual address, firstly bits 23:22 (the two MSBs) are masked to zero, and then BASE is added to bits 23:12 (the upper 12 bits) to form the physical address. Translation wraps on a 16 MiB boundary. + [11:0] + read-write + + + + + ATRANS2 + 0x0000003c + Configure address translation for XIP virtual addresses 0x800000 through 0xbfffff (a 4 MiB window starting at +8 MiB). + + Address translation allows a program image to be executed in place at multiple physical flash addresses (for example, a double-buffered flash image for over-the-air updates), without the overhead of position-independent code. + + At reset, the address translation registers are initialised to an identity mapping, so that they can be ignored if address translation is not required. + + Note that the XIP cache is fully virtually addressed, so a cache flush is required after changing the address translation. + 0x04000800 + + + SIZE + Translation aperture size for this virtual address range, in units of 4 kiB (one flash sector). + + Bits 21:12 of the virtual address are compared to SIZE. Offsets greater than SIZE return a bus error, and do not cause a QSPI access. + [26:16] + read-write + + + BASE + Physical address base for this virtual address range, in units of 4 kiB (one flash sector). + + Taking a 24-bit virtual address, firstly bits 23:22 (the two MSBs) are masked to zero, and then BASE is added to bits 23:12 (the upper 12 bits) to form the physical address. Translation wraps on a 16 MiB boundary. + [11:0] + read-write + + + + + ATRANS3 + 0x00000040 + Configure address translation for XIP virtual addresses 0xc00000 through 0xffffff (a 4 MiB window starting at +12 MiB). + + Address translation allows a program image to be executed in place at multiple physical flash addresses (for example, a double-buffered flash image for over-the-air updates), without the overhead of position-independent code. + + At reset, the address translation registers are initialised to an identity mapping, so that they can be ignored if address translation is not required. + + Note that the XIP cache is fully virtually addressed, so a cache flush is required after changing the address translation. + 0x04000c00 + + + SIZE + Translation aperture size for this virtual address range, in units of 4 kiB (one flash sector). + + Bits 21:12 of the virtual address are compared to SIZE. Offsets greater than SIZE return a bus error, and do not cause a QSPI access. + [26:16] + read-write + + + BASE + Physical address base for this virtual address range, in units of 4 kiB (one flash sector). + + Taking a 24-bit virtual address, firstly bits 23:22 (the two MSBs) are masked to zero, and then BASE is added to bits 23:12 (the upper 12 bits) to form the physical address. Translation wraps on a 16 MiB boundary. + [11:0] + read-write + + + + + ATRANS4 + 0x00000044 + Configure address translation for XIP virtual addresses 0x1000000 through 0x13fffff (a 4 MiB window starting at +16 MiB). + + Address translation allows a program image to be executed in place at multiple physical flash addresses (for example, a double-buffered flash image for over-the-air updates), without the overhead of position-independent code. + + At reset, the address translation registers are initialised to an identity mapping, so that they can be ignored if address translation is not required. + + Note that the XIP cache is fully virtually addressed, so a cache flush is required after changing the address translation. + 0x04000000 + + + SIZE + Translation aperture size for this virtual address range, in units of 4 kiB (one flash sector). + + Bits 21:12 of the virtual address are compared to SIZE. Offsets greater than SIZE return a bus error, and do not cause a QSPI access. + [26:16] + read-write + + + BASE + Physical address base for this virtual address range, in units of 4 kiB (one flash sector). + + Taking a 24-bit virtual address, firstly bits 23:22 (the two MSBs) are masked to zero, and then BASE is added to bits 23:12 (the upper 12 bits) to form the physical address. Translation wraps on a 16 MiB boundary. + [11:0] + read-write + + + + + ATRANS5 + 0x00000048 + Configure address translation for XIP virtual addresses 0x1400000 through 0x17fffff (a 4 MiB window starting at +20 MiB). + + Address translation allows a program image to be executed in place at multiple physical flash addresses (for example, a double-buffered flash image for over-the-air updates), without the overhead of position-independent code. + + At reset, the address translation registers are initialised to an identity mapping, so that they can be ignored if address translation is not required. + + Note that the XIP cache is fully virtually addressed, so a cache flush is required after changing the address translation. + 0x04000400 + + + SIZE + Translation aperture size for this virtual address range, in units of 4 kiB (one flash sector). + + Bits 21:12 of the virtual address are compared to SIZE. Offsets greater than SIZE return a bus error, and do not cause a QSPI access. + [26:16] + read-write + + + BASE + Physical address base for this virtual address range, in units of 4 kiB (one flash sector). + + Taking a 24-bit virtual address, firstly bits 23:22 (the two MSBs) are masked to zero, and then BASE is added to bits 23:12 (the upper 12 bits) to form the physical address. Translation wraps on a 16 MiB boundary. + [11:0] + read-write + + + + + ATRANS6 + 0x0000004c + Configure address translation for XIP virtual addresses 0x1800000 through 0x1bfffff (a 4 MiB window starting at +24 MiB). + + Address translation allows a program image to be executed in place at multiple physical flash addresses (for example, a double-buffered flash image for over-the-air updates), without the overhead of position-independent code. + + At reset, the address translation registers are initialised to an identity mapping, so that they can be ignored if address translation is not required. + + Note that the XIP cache is fully virtually addressed, so a cache flush is required after changing the address translation. + 0x04000800 + + + SIZE + Translation aperture size for this virtual address range, in units of 4 kiB (one flash sector). + + Bits 21:12 of the virtual address are compared to SIZE. Offsets greater than SIZE return a bus error, and do not cause a QSPI access. + [26:16] + read-write + + + BASE + Physical address base for this virtual address range, in units of 4 kiB (one flash sector). + + Taking a 24-bit virtual address, firstly bits 23:22 (the two MSBs) are masked to zero, and then BASE is added to bits 23:12 (the upper 12 bits) to form the physical address. Translation wraps on a 16 MiB boundary. + [11:0] + read-write + + + + + ATRANS7 + 0x00000050 + Configure address translation for XIP virtual addresses 0x1c00000 through 0x1ffffff (a 4 MiB window starting at +28 MiB). + + Address translation allows a program image to be executed in place at multiple physical flash addresses (for example, a double-buffered flash image for over-the-air updates), without the overhead of position-independent code. + + At reset, the address translation registers are initialised to an identity mapping, so that they can be ignored if address translation is not required. + + Note that the XIP cache is fully virtually addressed, so a cache flush is required after changing the address translation. + 0x04000c00 + + + SIZE + Translation aperture size for this virtual address range, in units of 4 kiB (one flash sector). + + Bits 21:12 of the virtual address are compared to SIZE. Offsets greater than SIZE return a bus error, and do not cause a QSPI access. + [26:16] + read-write + + + BASE + Physical address base for this virtual address range, in units of 4 kiB (one flash sector). + + Taking a 24-bit virtual address, firstly bits 23:22 (the two MSBs) are masked to zero, and then BASE is added to bits 23:12 (the upper 12 bits) to form the physical address. Translation wraps on a 16 MiB boundary. + [11:0] + read-write + + + + + + + XIP_CTRL + QSPI flash execute-in-place block + 0x400c8000 + + 0 + 32 + registers + + + + CTRL + 0x00000000 + Cache control register. Read-only from a Non-secure context. + 0x00000083 + + + WRITABLE_M1 + If 1, enable writes to XIP memory window 1 (addresses 0x11000000 through 0x11ffffff, and their uncached mirrors). If 0, this region is read-only. + + XIP memory is *read-only by default*. This bit must be set to enable writes if a RAM device is attached on QSPI chip select 1. + + The default read-only behaviour avoids two issues with writing to a read-only QSPI device (e.g. flash). First, a write will initially appear to succeed due to caching, but the data will eventually be lost when the written line is evicted, causing unpredictable behaviour. + + Second, when a written line is evicted, it will cause a write command to be issued to the flash, which can break the flash out of its continuous read mode. After this point, flash reads will return garbage. This is a security concern, as it allows Non-secure software to break Secure flash reads if it has permission to write to any flash address. + + Note the read-only behaviour is implemented by downgrading writes to reads, so writes will still cause allocation of an address, but have no other effect. + [11:11] + read-write + + + WRITABLE_M0 + If 1, enable writes to XIP memory window 0 (addresses 0x10000000 through 0x10ffffff, and their uncached mirrors). If 0, this region is read-only. + + XIP memory is *read-only by default*. This bit must be set to enable writes if a RAM device is attached on QSPI chip select 0. + + The default read-only behaviour avoids two issues with writing to a read-only QSPI device (e.g. flash). First, a write will initially appear to succeed due to caching, but the data will eventually be lost when the written line is evicted, causing unpredictable behaviour. + + Second, when a written line is evicted, it will cause a write command to be issued to the flash, which can break the flash out of its continuous read mode. After this point, flash reads will return garbage. This is a security concern, as it allows Non-secure software to break Secure flash reads if it has permission to write to any flash address. + + Note the read-only behaviour is implemented by downgrading writes to reads, so writes will still cause allocation of an address, but have no other effect. + [10:10] + read-write + + + SPLIT_WAYS + When 1, route all cached+Secure accesses to way 0 of the cache, and route all cached+Non-secure accesses to way 1 of the cache. + + This partitions the cache into two half-sized direct-mapped regions, such that Non-secure code can not observe cache line state changes caused by Secure execution. + + A full cache flush is required when changing the value of SPLIT_WAYS. The flush should be performed whilst SPLIT_WAYS is 0, so that both cache ways are accessible for invalidation. + [9:9] + read-write + + + MAINT_NONSEC + When 0, Non-secure accesses to the cache maintenance address window (addr[27] == 1, addr[26] == 0) will generate a bus error. When 1, Non-secure accesses can perform cache maintenance operations by writing to the cache maintenance address window. + + Cache maintenance operations may be used to corrupt Secure data by invalidating cache lines inappropriately, or map Secure content into a Non-secure region by pinning cache lines. Therefore this bit should generally be set to 0, unless Secure code is not using the cache. + + Care should also be taken to clear the cache data memory and tag memory before granting maintenance operations to Non-secure code. + [8:8] + read-write + + + NO_UNTRANSLATED_NONSEC + When 1, Non-secure accesses to the uncached, untranslated window (addr[27:26] == 3) will generate a bus error. + [7:7] + read-write + + + NO_UNTRANSLATED_SEC + When 1, Secure accesses to the uncached, untranslated window (addr[27:26] == 3) will generate a bus error. + [6:6] + read-write + + + NO_UNCACHED_NONSEC + When 1, Non-secure accesses to the uncached window (addr[27:26] == 1) will generate a bus error. This may reduce the number of SAU/MPU/PMP regions required to protect flash contents. + + Note this does not disable access to the uncached, untranslated window -- see NO_UNTRANSLATED_SEC. + [5:5] + read-write + + + NO_UNCACHED_SEC + When 1, Secure accesses to the uncached window (addr[27:26] == 1) will generate a bus error. This may reduce the number of SAU/MPU/PMP regions required to protect flash contents. + + Note this does not disable access to the uncached, untranslated window -- see NO_UNTRANSLATED_SEC. + [4:4] + read-write + + + POWER_DOWN + When 1, the cache memories are powered down. They retain state, but can not be accessed. This reduces static power dissipation. Writing 1 to this bit forces CTRL_EN_SECURE and CTRL_EN_NONSECURE to 0, i.e. the cache cannot be enabled when powered down. + [3:3] + read-write + + + EN_NONSECURE + When 1, enable the cache for Non-secure accesses. When enabled, Non-secure XIP accesses to the cached (addr[26] == 0) window will query the cache, and QSPI accesses are performed only if the requested data is not present. When disabled, Secure access ignore the cache contents, and always access the QSPI interface. + + Accesses to the uncached (addr[26] == 1) window will never query the cache, irrespective of this bit. + [1:1] + read-write + + + EN_SECURE + When 1, enable the cache for Secure accesses. When enabled, Secure XIP accesses to the cached (addr[26] == 0) window will query the cache, and QSPI accesses are performed only if the requested data is not present. When disabled, Secure access ignore the cache contents, and always access the QSPI interface. + + Accesses to the uncached (addr[26] == 1) window will never query the cache, irrespective of this bit. + + There is no cache-as-SRAM address window. Cache lines are allocated for SRAM-like use by individually pinning them, and keeping the cache enabled. + [0:0] + read-write + + + + + STAT + 0x00000008 + 0x00000002 + + + FIFO_FULL + When 1, indicates the XIP streaming FIFO is completely full. + The streaming FIFO is 2 entries deep, so the full and empty + flag allow its level to be ascertained. + [2:2] + read-only + + + FIFO_EMPTY + When 1, indicates the XIP streaming FIFO is completely empty. + [1:1] + read-only + + + + + CTR_HIT + 0x0000000c + Cache Hit counter + 0x00000000 + + + CTR_HIT + A 32 bit saturating counter that increments upon each cache hit, + i.e. when an XIP access is serviced directly from cached data. + Write any value to clear. + [31:0] + read-write + oneToClear + + + + + CTR_ACC + 0x00000010 + Cache Access counter + 0x00000000 + + + CTR_ACC + A 32 bit saturating counter that increments upon each XIP access, + whether the cache is hit or not. This includes noncacheable accesses. + Write any value to clear. + [31:0] + read-write + oneToClear + + + + + STREAM_ADDR + 0x00000014 + FIFO stream address + 0x00000000 + + + STREAM_ADDR + The address of the next word to be streamed from flash to the streaming FIFO. + Increments automatically after each flash access. + Write the initial access address here before starting a streaming read. + [31:2] + read-write + + + + + STREAM_CTR + 0x00000018 + FIFO stream control + 0x00000000 + + + STREAM_CTR + Write a nonzero value to start a streaming read. This will then + progress in the background, using flash idle cycles to transfer + a linear data block from flash to the streaming FIFO. + Decrements automatically (1 at a time) as the stream + progresses, and halts on reaching 0. + Write 0 to halt an in-progress stream, and discard any in-flight + read, so that a new stream can immediately be started (after + draining the FIFO and reinitialising STREAM_ADDR) + [21:0] + read-write + + + + + STREAM_FIFO + 0x0000001c + FIFO stream data + 0x00000000 + + + STREAM_FIFO + Streamed data is buffered here, for retrieval by the system DMA. + This FIFO can also be accessed via the XIP_AUX slave, to avoid exposing + the DMA to bus stalls caused by other XIP traffic. + [31:0] + read-only + modify + + + + + + + XIP_AUX + Auxiliary DMA access to XIP FIFOs, via fast AHB bus access + 0x50500000 + + 0 + 12 + registers + + + + STREAM + 0x00000000 + Read the XIP stream FIFO (fast bus access to XIP_CTRL_STREAM_FIFO) + 0x00000000 + + + STREAM + [31:0] + read-only + modify + + + + + QMI_DIRECT_TX + 0x00000004 + Write to the QMI direct-mode TX FIFO (fast bus access to QMI_DIRECT_TX) + 0x00000000 + + + NOPUSH + Inhibit the RX FIFO push that would correspond to this TX FIFO entry. + + Useful to avoid garbage appearing in the RX FIFO when pushing the command at the beginning of a SPI transfer. + [20:20] + write-only + + + OE + Output enable (active-high). For single width (SPI), this field is ignored, and SD0 is always set to output, with SD1 always set to input. + + For dual and quad width (DSPI/QSPI), this sets whether the relevant SDx pads are set to output whilst transferring this FIFO record. In this case the command/address should have OE set, and the data transfer should have OE set or clear depending on the direction of the transfer. + [19:19] + write-only + + + DWIDTH + Data width. If 0, hardware will transmit the 8 LSBs of the DIRECT_TX DATA field, and return an 8-bit value in the 8 LSBs of DIRECT_RX. If 1, the full 16-bit width is used. 8-bit and 16-bit transfers can be mixed freely. + [18:18] + write-only + + + IWIDTH + Configure whether this FIFO record is transferred with single/dual/quad interface width (0/1/2). Different widths can be mixed freely. + [17:16] + write-only + + + S + 0 + Single width + + + D + 1 + Dual width + + + Q + 2 + Quad width + + + + + DATA + Data pushed here will be clocked out falling edges of SCK (or before the very first rising edge of SCK, if this is the first pulse). For each byte clocked out, the interface will simultaneously sample one byte, on rising edges of SCK, and push this to the DIRECT_RX FIFO. + + For 16-bit data, the least-significant byte is transmitted first. + [15:0] + write-only + + + + + QMI_DIRECT_RX + 0x00000008 + Read from the QMI direct-mode RX FIFO (fast bus access to QMI_DIRECT_RX) + 0x00000000 + + + QMI_DIRECT_RX + With each byte clocked out on the serial interface, one byte will simultaneously be clocked in, and will appear in this FIFO. The serial interface will stall when this FIFO is full, to avoid dropping data. + + When 16-bit data is pushed into the TX FIFO, the corresponding RX FIFO push will also contain 16 bits of data. The least-significant byte is the first one received. + [15:0] + read-only + modify + + + + + + + SYSCFG + Register block for various chip control signals + 0x40008000 + + 0 + 24 + registers + + + + PROC_CONFIG + 0x00000000 + Configuration for processors + 0x00000000 + + + PROC1_HALTED + Indication that proc1 has halted + [1:1] + read-only + + + PROC0_HALTED + Indication that proc0 has halted + [0:0] + read-only + + + + + PROC_IN_SYNC_BYPASS + 0x00000004 + For each bit, if 1, bypass the input synchronizer between that GPIO + and the GPIO input register in the SIO. The input synchronizers should + generally be unbypassed, to avoid injecting metastabilities into processors. + If you're feeling brave, you can bypass to save two cycles of input + latency. This register applies to GPIO 0...31. + 0x00000000 + + + GPIO + [31:0] + read-write + + + + + PROC_IN_SYNC_BYPASS_HI + 0x00000008 + For each bit, if 1, bypass the input synchronizer between that GPIO + and the GPIO input register in the SIO. The input synchronizers should + generally be unbypassed, to avoid injecting metastabilities into processors. + If you're feeling brave, you can bypass to save two cycles of input + latency. This register applies to GPIO 32...47. USB GPIO 56..57 QSPI GPIO 58..63 + 0x00000000 + + + QSPI_SD + [31:28] + read-write + + + QSPI_CSN + [27:27] + read-write + + + QSPI_SCK + [26:26] + read-write + + + USB_DM + [25:25] + read-write + + + USB_DP + [24:24] + read-write + + + GPIO + [15:0] + read-write + + + + + DBGFORCE + 0x0000000c + Directly control the chip SWD debug port + 0x00000006 + + + ATTACH + Attach chip debug port to syscfg controls, and disconnect it from external SWD pads. + [3:3] + read-write + + + SWCLK + Directly drive SWCLK, if ATTACH is set + [2:2] + read-write + + + SWDI + Directly drive SWDIO input, if ATTACH is set + [1:1] + read-write + + + SWDO + Observe the value of SWDIO output. + [0:0] + read-only + + + + + MEMPOWERDOWN + 0x00000010 + Control PD pins to memories. + Set high to put memories to a low power state. In this state the memories will retain contents but not be accessible + Use with caution + 0x00000000 + + + BOOTRAM + [12:12] + read-write + + + ROM + [11:11] + read-write + + + USB + [10:10] + read-write + + + SRAM9 + [9:9] + read-write + + + SRAM8 + [8:8] + read-write + + + SRAM7 + [7:7] + read-write + + + SRAM6 + [6:6] + read-write + + + SRAM5 + [5:5] + read-write + + + SRAM4 + [4:4] + read-write + + + SRAM3 + [3:3] + read-write + + + SRAM2 + [2:2] + read-write + + + SRAM1 + [1:1] + read-write + + + SRAM0 + [0:0] + read-write + + + + + AUXCTRL + 0x00000014 + Auxiliary system control register + 0x00000000 + + + AUXCTRL + * Bits 7:2: Reserved + + * Bit 1: When clear, the LPOSC output is XORed into the TRNG ROSC output as an additional, uncorrelated entropy source. When set, this behaviour is disabled. + + * Bit 0: Force POWMAN clock to switch to LPOSC, by asserting its WDRESET input. This must be set before initiating a watchdog reset of the RSM from a stage that includes CLOCKS, if POWMAN is running from clk_ref at the point that the watchdog reset takes place. Otherwise, the short pulse generated on clk_ref by the reset of the CLOCKS block may affect POWMAN register state. + [7:0] + read-write + + + + + + + XOSC + Controls the crystal oscillator + 0x40048000 + + 0 + 20 + registers + + + + CTRL + 0x00000000 + Crystal Oscillator Control + 0x00000000 + + + ENABLE + On power-up this field is initialised to DISABLE and the chip runs from the ROSC. + If the chip has subsequently been programmed to run from the XOSC then setting this field to DISABLE may lock-up the chip. If this is a concern then run the clk_ref from the ROSC and enable the clk_sys RESUS feature. + The 12-bit code is intended to give some protection against accidental writes. An invalid setting will retain the previous value. The actual value being used can be read from STATUS_ENABLED + [23:12] + read-write + + + DISABLE + 3358 + + + ENABLE + 4011 + + + + + FREQ_RANGE + The 12-bit code is intended to give some protection against accidental writes. An invalid setting will retain the previous value. The actual value being used can be read from STATUS_FREQ_RANGE + [11:0] + read-write + + + 1_15MHZ + 2720 + + + 10_30MHZ + 2721 + + + 25_60MHZ + 2722 + + + 40_100MHZ + 2723 + + + + + + + STATUS + 0x00000004 + Crystal Oscillator Status + 0x00000000 + + + STABLE + Oscillator is running and stable + [31:31] + read-only + + + BADWRITE + An invalid value has been written to CTRL_ENABLE or CTRL_FREQ_RANGE or DORMANT + [24:24] + read-write + oneToClear + + + ENABLED + Oscillator is enabled but not necessarily running and stable, resets to 0 + [12:12] + read-only + + + FREQ_RANGE + The current frequency range setting + [1:0] + read-only + + + 1_15MHZ + 0 + + + 10_30MHZ + 1 + + + 25_60MHZ + 2 + + + 40_100MHZ + 3 + + + + + + + DORMANT + 0x00000008 + Crystal Oscillator pause control + 0x00000000 + + + DORMANT + This is used to save power by pausing the XOSC + On power-up this field is initialised to WAKE + An invalid write will also select WAKE + Warning: stop the PLLs before selecting dormant mode + Warning: setup the irq before selecting dormant mode + [31:0] + read-write + + + dormant + 1668246881 + + + WAKE + 2002873189 + + + + + + + STARTUP + 0x0000000c + Controls the startup delay + 0x00000000 + + + X4 + Multiplies the startup_delay by 4, just in case. The reset value is controlled by a mask-programmable tiecell and is provided in case we are booting from XOSC and the default startup delay is insufficient. The reset value is 0x0. + [20:20] + read-write + + + DELAY + in multiples of 256*xtal_period. The reset value of 0xc4 corresponds to approx 50 000 cycles. + [13:0] + read-write + + + + + COUNT + 0x00000010 + A down counter running at the xosc frequency which counts to zero and stops. + Can be used for short software pauses when setting up time sensitive hardware. + To start the counter, write a non-zero value. Reads will return 1 while the count is running and 0 when it has finished. + Minimum count value is 4. Count values <4 will be treated as count value =4. + Note that synchronisation to the register clock domain costs 2 register clock cycles and the counter cannot compensate for that. + 0x00000000 + + + COUNT + [15:0] + read-write + + + + + + + PLL_SYS + 0x40050000 + + 0 + 32 + registers + + + PLL_SYS_IRQ + 42 + + + + CS + 0x00000000 + Control and Status + GENERAL CONSTRAINTS: + Reference clock frequency min=5MHz, max=800MHz + Feedback divider min=16, max=320 + VCO frequency min=750MHz, max=1600MHz + 0x00000001 + + + LOCK + PLL is locked + [31:31] + read-only + + + LOCK_N + PLL is not locked + Ideally this is cleared when PLL lock is seen and this should never normally be set + [30:30] + read-write + oneToClear + + + BYPASS + Passes the reference clock to the output instead of the divided VCO. The VCO continues to run so the user can switch between the reference clock and the divided VCO but the output will glitch when doing so. + [8:8] + read-write + + + REFDIV + Divides the PLL input reference clock. + Behaviour is undefined for div=0. + PLL output will be unpredictable during refdiv changes, wait for lock=1 before using it. + [5:0] + read-write + + + + + PWR + 0x00000004 + Controls the PLL power modes. + 0x0000002d + + + VCOPD + PLL VCO powerdown + To save power set high when PLL output not required or bypass=1. + [5:5] + read-write + + + POSTDIVPD + PLL post divider powerdown + To save power set high when PLL output not required or bypass=1. + [3:3] + read-write + + + DSMPD + PLL DSM powerdown + Nothing is achieved by setting this low. + [2:2] + read-write + + + PD + PLL powerdown + To save power set high when PLL output not required. + [0:0] + read-write + + + + + FBDIV_INT + 0x00000008 + Feedback divisor + (note: this PLL does not support fractional division) + 0x00000000 + + + FBDIV_INT + see ctrl reg description for constraints + [11:0] + read-write + + + + + PRIM + 0x0000000c + Controls the PLL post dividers for the primary output + (note: this PLL does not have a secondary output) + the primary output is driven from VCO divided by postdiv1*postdiv2 + 0x00077000 + + + POSTDIV1 + divide by 1-7 + [18:16] + read-write + + + POSTDIV2 + divide by 1-7 + [14:12] + read-write + + + + + INTR + 0x00000010 + Raw Interrupts + 0x00000000 + + + LOCK_N_STICKY + [0:0] + read-write + oneToClear + + + + + INTE + 0x00000014 + Interrupt Enable + 0x00000000 + + + LOCK_N_STICKY + [0:0] + read-write + + + + + INTF + 0x00000018 + Interrupt Force + 0x00000000 + + + LOCK_N_STICKY + [0:0] + read-write + + + + + INTS + 0x0000001c + Interrupt status after masking & forcing + 0x00000000 + + + LOCK_N_STICKY + [0:0] + read-only + + + + + + + PLL_USB + 0x40058000 + + PLL_USB_IRQ + 43 + + + + ACCESSCTRL + Hardware access control registers + 0x40060000 + + 0 + 236 + registers + + + + LOCK + 0x00000000 + Once a LOCK bit is written to 1, ACCESSCTRL silently ignores writes from that master. LOCK is writable only by a Secure, Privileged processor or debugger. + + LOCK bits are only writable when their value is zero. Once set, they can never be cleared, except by a full reset of ACCESSCTRL + + Setting the LOCK bit does not affect whether an access raises a bus error. Unprivileged writes, or writes from the DMA, will continue to raise bus errors. All other accesses will continue not to. + 0x00000004 + + + DEBUG + [3:3] + read-write + + + DMA + [2:2] + read-only + + + CORE1 + [1:1] + read-write + + + CORE0 + [0:0] + read-write + + + + + FORCE_CORE_NS + 0x00000004 + Force core 1's bus accesses to always be Non-secure, no matter the core's internal state. + + Useful for schemes where one core is designated as the Non-secure core, since some peripherals may filter individual registers internally based on security state but not on master ID. + 0x00000000 + + + CORE1 + [1:1] + read-write + + + + + CFGRESET + 0x00000008 + Write 1 to reset all ACCESSCTRL configuration, except for the LOCK and FORCE_CORE_NS registers. + + This bit is used in the RP2350 bootrom to quickly restore ACCESSCTRL to a known state during the boot path. + + Note that, like all registers in ACCESSCTRL, this register is not writable when the writer's corresponding LOCK bit is set, therefore a master which has been locked out of ACCESSCTRL can not use the CFGRESET register to disturb its contents. + 0x00000000 + + + CFGRESET + [0:0] + write-only + + + + + GPIO_NSMASK0 + 0x0000000c + Control whether GPIO0...31 are accessible to Non-secure code. Writable only by a Secure, Privileged processor or debugger. + + 0 -> Secure access only + + 1 -> Secure + Non-secure access + 0x00000000 + + + GPIO_NSMASK0 + [31:0] + read-write + + + + + GPIO_NSMASK1 + 0x00000010 + Control whether GPIO32..47 are accessible to Non-secure code, and whether QSPI and USB bitbang are accessible through the Non-secure SIO. Writable only by a Secure, Privileged processor or debugger. + 0x00000000 + + + QSPI_SD + [31:28] + read-write + + + QSPI_CSN + [27:27] + read-write + + + QSPI_SCK + [26:26] + read-write + + + USB_DM + [25:25] + read-write + + + USB_DP + [24:24] + read-write + + + GPIO + [15:0] + read-write + + + + + ROM + 0x00000014 + Control whether debugger, DMA, core 0 and core 1 can access ROM, and at what security/privilege levels they can do so. + + Defaults to fully open access. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000ff + + + DBG + If 1, ROM can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, ROM can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, ROM can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, ROM can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, ROM can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, ROM can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, ROM can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, ROM can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + XIP_MAIN + 0x00000018 + Control whether debugger, DMA, core 0 and core 1 can access XIP_MAIN, and at what security/privilege levels they can do so. + + Defaults to fully open access. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000ff + + + DBG + If 1, XIP_MAIN can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, XIP_MAIN can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, XIP_MAIN can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, XIP_MAIN can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, XIP_MAIN can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, XIP_MAIN can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, XIP_MAIN can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, XIP_MAIN can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + SRAM0 + 0x0000001c + Control whether debugger, DMA, core 0 and core 1 can access SRAM0, and at what security/privilege levels they can do so. + + Defaults to fully open access. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000ff + + + DBG + If 1, SRAM0 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, SRAM0 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, SRAM0 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, SRAM0 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, SRAM0 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, SRAM0 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, SRAM0 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, SRAM0 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + SRAM1 + 0x00000020 + Control whether debugger, DMA, core 0 and core 1 can access SRAM1, and at what security/privilege levels they can do so. + + Defaults to fully open access. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000ff + + + DBG + If 1, SRAM1 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, SRAM1 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, SRAM1 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, SRAM1 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, SRAM1 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, SRAM1 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, SRAM1 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, SRAM1 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + SRAM2 + 0x00000024 + Control whether debugger, DMA, core 0 and core 1 can access SRAM2, and at what security/privilege levels they can do so. + + Defaults to fully open access. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000ff + + + DBG + If 1, SRAM2 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, SRAM2 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, SRAM2 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, SRAM2 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, SRAM2 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, SRAM2 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, SRAM2 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, SRAM2 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + SRAM3 + 0x00000028 + Control whether debugger, DMA, core 0 and core 1 can access SRAM3, and at what security/privilege levels they can do so. + + Defaults to fully open access. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000ff + + + DBG + If 1, SRAM3 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, SRAM3 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, SRAM3 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, SRAM3 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, SRAM3 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, SRAM3 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, SRAM3 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, SRAM3 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + SRAM4 + 0x0000002c + Control whether debugger, DMA, core 0 and core 1 can access SRAM4, and at what security/privilege levels they can do so. + + Defaults to fully open access. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000ff + + + DBG + If 1, SRAM4 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, SRAM4 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, SRAM4 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, SRAM4 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, SRAM4 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, SRAM4 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, SRAM4 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, SRAM4 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + SRAM5 + 0x00000030 + Control whether debugger, DMA, core 0 and core 1 can access SRAM5, and at what security/privilege levels they can do so. + + Defaults to fully open access. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000ff + + + DBG + If 1, SRAM5 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, SRAM5 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, SRAM5 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, SRAM5 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, SRAM5 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, SRAM5 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, SRAM5 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, SRAM5 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + SRAM6 + 0x00000034 + Control whether debugger, DMA, core 0 and core 1 can access SRAM6, and at what security/privilege levels they can do so. + + Defaults to fully open access. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000ff + + + DBG + If 1, SRAM6 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, SRAM6 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, SRAM6 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, SRAM6 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, SRAM6 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, SRAM6 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, SRAM6 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, SRAM6 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + SRAM7 + 0x00000038 + Control whether debugger, DMA, core 0 and core 1 can access SRAM7, and at what security/privilege levels they can do so. + + Defaults to fully open access. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000ff + + + DBG + If 1, SRAM7 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, SRAM7 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, SRAM7 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, SRAM7 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, SRAM7 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, SRAM7 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, SRAM7 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, SRAM7 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + SRAM8 + 0x0000003c + Control whether debugger, DMA, core 0 and core 1 can access SRAM8, and at what security/privilege levels they can do so. + + Defaults to fully open access. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000ff + + + DBG + If 1, SRAM8 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, SRAM8 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, SRAM8 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, SRAM8 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, SRAM8 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, SRAM8 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, SRAM8 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, SRAM8 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + SRAM9 + 0x00000040 + Control whether debugger, DMA, core 0 and core 1 can access SRAM9, and at what security/privilege levels they can do so. + + Defaults to fully open access. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000ff + + + DBG + If 1, SRAM9 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, SRAM9 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, SRAM9 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, SRAM9 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, SRAM9 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, SRAM9 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, SRAM9 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, SRAM9 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + DMA + 0x00000044 + Control whether debugger, DMA, core 0 and core 1 can access DMA, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, DMA can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, DMA can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, DMA can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, DMA can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, DMA can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, DMA can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, DMA can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, DMA can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + USBCTRL + 0x00000048 + Control whether debugger, DMA, core 0 and core 1 can access USBCTRL, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, USBCTRL can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, USBCTRL can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, USBCTRL can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, USBCTRL can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, USBCTRL can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, USBCTRL can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, USBCTRL can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, USBCTRL can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + PIO0 + 0x0000004c + Control whether debugger, DMA, core 0 and core 1 can access PIO0, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, PIO0 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, PIO0 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, PIO0 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, PIO0 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, PIO0 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, PIO0 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, PIO0 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, PIO0 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + PIO1 + 0x00000050 + Control whether debugger, DMA, core 0 and core 1 can access PIO1, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, PIO1 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, PIO1 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, PIO1 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, PIO1 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, PIO1 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, PIO1 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, PIO1 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, PIO1 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + PIO2 + 0x00000054 + Control whether debugger, DMA, core 0 and core 1 can access PIO2, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, PIO2 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, PIO2 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, PIO2 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, PIO2 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, PIO2 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, PIO2 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, PIO2 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, PIO2 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + CORESIGHT_TRACE + 0x00000058 + Control whether debugger, DMA, core 0 and core 1 can access CORESIGHT_TRACE, and at what security/privilege levels they can do so. + + Defaults to Secure, Privileged processor or debug access only. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000b8 + + + DBG + If 1, CORESIGHT_TRACE can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, CORESIGHT_TRACE can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, CORESIGHT_TRACE can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, CORESIGHT_TRACE can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, CORESIGHT_TRACE can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, CORESIGHT_TRACE can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, CORESIGHT_TRACE can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, CORESIGHT_TRACE can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + CORESIGHT_PERIPH + 0x0000005c + Control whether debugger, DMA, core 0 and core 1 can access CORESIGHT_PERIPH, and at what security/privilege levels they can do so. + + Defaults to Secure, Privileged processor or debug access only. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000b8 + + + DBG + If 1, CORESIGHT_PERIPH can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, CORESIGHT_PERIPH can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, CORESIGHT_PERIPH can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, CORESIGHT_PERIPH can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, CORESIGHT_PERIPH can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, CORESIGHT_PERIPH can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, CORESIGHT_PERIPH can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, CORESIGHT_PERIPH can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + SYSINFO + 0x00000060 + Control whether debugger, DMA, core 0 and core 1 can access SYSINFO, and at what security/privilege levels they can do so. + + Defaults to fully open access. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000ff + + + DBG + If 1, SYSINFO can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, SYSINFO can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, SYSINFO can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, SYSINFO can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, SYSINFO can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, SYSINFO can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, SYSINFO can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, SYSINFO can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + RESETS + 0x00000064 + Control whether debugger, DMA, core 0 and core 1 can access RESETS, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, RESETS can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, RESETS can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, RESETS can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, RESETS can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, RESETS can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, RESETS can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, RESETS can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, RESETS can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + IO_BANK0 + 0x00000068 + Control whether debugger, DMA, core 0 and core 1 can access IO_BANK0, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, IO_BANK0 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, IO_BANK0 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, IO_BANK0 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, IO_BANK0 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, IO_BANK0 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, IO_BANK0 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, IO_BANK0 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, IO_BANK0 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + IO_BANK1 + 0x0000006c + Control whether debugger, DMA, core 0 and core 1 can access IO_BANK1, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, IO_BANK1 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, IO_BANK1 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, IO_BANK1 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, IO_BANK1 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, IO_BANK1 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, IO_BANK1 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, IO_BANK1 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, IO_BANK1 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + PADS_BANK0 + 0x00000070 + Control whether debugger, DMA, core 0 and core 1 can access PADS_BANK0, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, PADS_BANK0 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, PADS_BANK0 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, PADS_BANK0 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, PADS_BANK0 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, PADS_BANK0 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, PADS_BANK0 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, PADS_BANK0 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, PADS_BANK0 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + PADS_QSPI + 0x00000074 + Control whether debugger, DMA, core 0 and core 1 can access PADS_QSPI, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, PADS_QSPI can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, PADS_QSPI can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, PADS_QSPI can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, PADS_QSPI can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, PADS_QSPI can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, PADS_QSPI can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, PADS_QSPI can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, PADS_QSPI can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + BUSCTRL + 0x00000078 + Control whether debugger, DMA, core 0 and core 1 can access BUSCTRL, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, BUSCTRL can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, BUSCTRL can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, BUSCTRL can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, BUSCTRL can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, BUSCTRL can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, BUSCTRL can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, BUSCTRL can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, BUSCTRL can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + ADC0 + 0x0000007c + Control whether debugger, DMA, core 0 and core 1 can access ADC0, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, ADC0 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, ADC0 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, ADC0 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, ADC0 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, ADC0 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, ADC0 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, ADC0 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, ADC0 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + HSTX + 0x00000080 + Control whether debugger, DMA, core 0 and core 1 can access HSTX, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, HSTX can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, HSTX can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, HSTX can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, HSTX can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, HSTX can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, HSTX can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, HSTX can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, HSTX can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + I2C0 + 0x00000084 + Control whether debugger, DMA, core 0 and core 1 can access I2C0, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, I2C0 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, I2C0 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, I2C0 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, I2C0 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, I2C0 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, I2C0 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, I2C0 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, I2C0 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + I2C1 + 0x00000088 + Control whether debugger, DMA, core 0 and core 1 can access I2C1, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, I2C1 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, I2C1 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, I2C1 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, I2C1 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, I2C1 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, I2C1 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, I2C1 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, I2C1 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + PWM + 0x0000008c + Control whether debugger, DMA, core 0 and core 1 can access PWM, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, PWM can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, PWM can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, PWM can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, PWM can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, PWM can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, PWM can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, PWM can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, PWM can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + SPI0 + 0x00000090 + Control whether debugger, DMA, core 0 and core 1 can access SPI0, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, SPI0 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, SPI0 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, SPI0 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, SPI0 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, SPI0 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, SPI0 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, SPI0 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, SPI0 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + SPI1 + 0x00000094 + Control whether debugger, DMA, core 0 and core 1 can access SPI1, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, SPI1 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, SPI1 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, SPI1 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, SPI1 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, SPI1 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, SPI1 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, SPI1 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, SPI1 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + TIMER0 + 0x00000098 + Control whether debugger, DMA, core 0 and core 1 can access TIMER0, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, TIMER0 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, TIMER0 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, TIMER0 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, TIMER0 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, TIMER0 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, TIMER0 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, TIMER0 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, TIMER0 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + TIMER1 + 0x0000009c + Control whether debugger, DMA, core 0 and core 1 can access TIMER1, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, TIMER1 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, TIMER1 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, TIMER1 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, TIMER1 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, TIMER1 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, TIMER1 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, TIMER1 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, TIMER1 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + UART0 + 0x000000a0 + Control whether debugger, DMA, core 0 and core 1 can access UART0, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, UART0 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, UART0 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, UART0 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, UART0 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, UART0 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, UART0 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, UART0 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, UART0 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + UART1 + 0x000000a4 + Control whether debugger, DMA, core 0 and core 1 can access UART1, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, UART1 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, UART1 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, UART1 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, UART1 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, UART1 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, UART1 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, UART1 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, UART1 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + OTP + 0x000000a8 + Control whether debugger, DMA, core 0 and core 1 can access OTP, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, OTP can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, OTP can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, OTP can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, OTP can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, OTP can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, OTP can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, OTP can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, OTP can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + TBMAN + 0x000000ac + Control whether debugger, DMA, core 0 and core 1 can access TBMAN, and at what security/privilege levels they can do so. + + Defaults to Secure access from any master. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000fc + + + DBG + If 1, TBMAN can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, TBMAN can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, TBMAN can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, TBMAN can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, TBMAN can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, TBMAN can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, TBMAN can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, TBMAN can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + POWMAN + 0x000000b0 + Control whether debugger, DMA, core 0 and core 1 can access POWMAN, and at what security/privilege levels they can do so. + + Defaults to Secure, Privileged processor or debug access only. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000b8 + + + DBG + If 1, POWMAN can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, POWMAN can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, POWMAN can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, POWMAN can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, POWMAN can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, POWMAN can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, POWMAN can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, POWMAN can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + TRNG + 0x000000b4 + Control whether debugger, DMA, core 0 and core 1 can access TRNG, and at what security/privilege levels they can do so. + + Defaults to Secure, Privileged processor or debug access only. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000b8 + + + DBG + If 1, TRNG can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, TRNG can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, TRNG can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, TRNG can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, TRNG can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, TRNG can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, TRNG can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, TRNG can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + SHA256 + 0x000000b8 + Control whether debugger, DMA, core 0 and core 1 can access SHA256, and at what security/privilege levels they can do so. + + Defaults to Secure, Privileged access only. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000f8 + + + DBG + If 1, SHA256 can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, SHA256 can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, SHA256 can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, SHA256 can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, SHA256 can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, SHA256 can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, SHA256 can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, SHA256 can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + SYSCFG + 0x000000bc + Control whether debugger, DMA, core 0 and core 1 can access SYSCFG, and at what security/privilege levels they can do so. + + Defaults to Secure, Privileged processor or debug access only. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000b8 + + + DBG + If 1, SYSCFG can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, SYSCFG can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, SYSCFG can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, SYSCFG can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, SYSCFG can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, SYSCFG can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, SYSCFG can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, SYSCFG can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + CLOCKS + 0x000000c0 + Control whether debugger, DMA, core 0 and core 1 can access CLOCKS, and at what security/privilege levels they can do so. + + Defaults to Secure, Privileged processor or debug access only. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000b8 + + + DBG + If 1, CLOCKS can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, CLOCKS can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, CLOCKS can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, CLOCKS can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, CLOCKS can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, CLOCKS can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, CLOCKS can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, CLOCKS can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + XOSC + 0x000000c4 + Control whether debugger, DMA, core 0 and core 1 can access XOSC, and at what security/privilege levels they can do so. + + Defaults to Secure, Privileged processor or debug access only. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000b8 + + + DBG + If 1, XOSC can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, XOSC can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, XOSC can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, XOSC can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, XOSC can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, XOSC can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, XOSC can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, XOSC can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + ROSC + 0x000000c8 + Control whether debugger, DMA, core 0 and core 1 can access ROSC, and at what security/privilege levels they can do so. + + Defaults to Secure, Privileged processor or debug access only. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000b8 + + + DBG + If 1, ROSC can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, ROSC can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, ROSC can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, ROSC can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, ROSC can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, ROSC can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, ROSC can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, ROSC can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + PLL_SYS + 0x000000cc + Control whether debugger, DMA, core 0 and core 1 can access PLL_SYS, and at what security/privilege levels they can do so. + + Defaults to Secure, Privileged processor or debug access only. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000b8 + + + DBG + If 1, PLL_SYS can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, PLL_SYS can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, PLL_SYS can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, PLL_SYS can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, PLL_SYS can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, PLL_SYS can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, PLL_SYS can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, PLL_SYS can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + PLL_USB + 0x000000d0 + Control whether debugger, DMA, core 0 and core 1 can access PLL_USB, and at what security/privilege levels they can do so. + + Defaults to Secure, Privileged processor or debug access only. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000b8 + + + DBG + If 1, PLL_USB can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, PLL_USB can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, PLL_USB can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, PLL_USB can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, PLL_USB can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, PLL_USB can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, PLL_USB can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, PLL_USB can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + TICKS + 0x000000d4 + Control whether debugger, DMA, core 0 and core 1 can access TICKS, and at what security/privilege levels they can do so. + + Defaults to Secure, Privileged processor or debug access only. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000b8 + + + DBG + If 1, TICKS can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, TICKS can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, TICKS can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, TICKS can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, TICKS can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, TICKS can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, TICKS can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, TICKS can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + WATCHDOG + 0x000000d8 + Control whether debugger, DMA, core 0 and core 1 can access WATCHDOG, and at what security/privilege levels they can do so. + + Defaults to Secure, Privileged processor or debug access only. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000b8 + + + DBG + If 1, WATCHDOG can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, WATCHDOG can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, WATCHDOG can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, WATCHDOG can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, WATCHDOG can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, WATCHDOG can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, WATCHDOG can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, WATCHDOG can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + RSM + 0x000000dc + Control whether debugger, DMA, core 0 and core 1 can access RSM, and at what security/privilege levels they can do so. + + Defaults to Secure, Privileged processor or debug access only. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000b8 + + + DBG + If 1, RSM can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, RSM can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, RSM can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, RSM can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, RSM can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, RSM can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, RSM can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, RSM can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + XIP_CTRL + 0x000000e0 + Control whether debugger, DMA, core 0 and core 1 can access XIP_CTRL, and at what security/privilege levels they can do so. + + Defaults to Secure, Privileged processor or debug access only. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000b8 + + + DBG + If 1, XIP_CTRL can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, XIP_CTRL can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, XIP_CTRL can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, XIP_CTRL can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, XIP_CTRL can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, XIP_CTRL can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, XIP_CTRL can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, XIP_CTRL can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + XIP_QMI + 0x000000e4 + Control whether debugger, DMA, core 0 and core 1 can access XIP_QMI, and at what security/privilege levels they can do so. + + Defaults to Secure, Privileged processor or debug access only. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000b8 + + + DBG + If 1, XIP_QMI can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, XIP_QMI can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, XIP_QMI can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, XIP_QMI can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, XIP_QMI can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, XIP_QMI can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, XIP_QMI can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, XIP_QMI can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + XIP_AUX + 0x000000e8 + Control whether debugger, DMA, core 0 and core 1 can access XIP_AUX, and at what security/privilege levels they can do so. + + Defaults to Secure, Privileged access only. + + This register is writable only from a Secure, Privileged processor or debugger, with the exception of the NSU bit, which becomes Non-secure-Privileged-writable when the NSP bit is set. + 0x000000f8 + + + DBG + If 1, XIP_AUX can be accessed by the debugger, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [7:7] + read-write + + + DMA + If 1, XIP_AUX can be accessed by the DMA, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [6:6] + read-write + + + CORE1 + If 1, XIP_AUX can be accessed by core 1, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [5:5] + read-write + + + CORE0 + If 1, XIP_AUX can be accessed by core 0, at security/privilege levels permitted by SP/NSP/SU/NSU in this register. + [4:4] + read-write + + + SP + If 1, XIP_AUX can be accessed from a Secure, Privileged context. + [3:3] + read-write + + + SU + If 1, and SP is also set, XIP_AUX can be accessed from a Secure, Unprivileged context. + [2:2] + read-write + + + NSP + If 1, XIP_AUX can be accessed from a Non-secure, Privileged context. + [1:1] + read-write + + + NSU + If 1, and NSP is also set, XIP_AUX can be accessed from a Non-secure, Unprivileged context. + + This bit is writable from a Non-secure, Privileged context, if and only if the NSP bit is set. + [0:0] + read-write + + + + + + + UART0 + 0x40070000 + + 0 + 4096 + registers + + + UART0_IRQ + 33 + + + + UARTDR + 0x00000000 + Data Register, UARTDR + 0x00000000 + + + OE + Overrun error. This bit is set to 1 if data is received and the receive FIFO is already full. This is cleared to 0 once there is an empty space in the FIFO and a new character can be written to it. + [11:11] + read-only + + + BE + Break error. This bit is set to 1 if a break condition was detected, indicating that the received data input was held LOW for longer than a full-word transmission time (defined as start, data, parity and stop bits). In FIFO mode, this error is associated with the character at the top of the FIFO. When a break occurs, only one 0 character is loaded into the FIFO. The next character is only enabled after the receive data input goes to a 1 (marking state), and the next valid start bit is received. + [10:10] + read-only + + + PE + Parity error. When set to 1, it indicates that the parity of the received data character does not match the parity that the EPS and SPS bits in the Line Control Register, UARTLCR_H. In FIFO mode, this error is associated with the character at the top of the FIFO. + [9:9] + read-only + + + FE + Framing error. When set to 1, it indicates that the received character did not have a valid stop bit (a valid stop bit is 1). In FIFO mode, this error is associated with the character at the top of the FIFO. + [8:8] + read-only + + + DATA + Receive (read) data character. Transmit (write) data character. + [7:0] + read-write + modify + + + + + UARTRSR + 0x00000004 + Receive Status Register/Error Clear Register, UARTRSR/UARTECR + 0x00000000 + + + OE + Overrun error. This bit is set to 1 if data is received and the FIFO is already full. This bit is cleared to 0 by a write to UARTECR. The FIFO contents remain valid because no more data is written when the FIFO is full, only the contents of the shift register are overwritten. The CPU must now read the data, to empty the FIFO. + [3:3] + read-write + oneToClear + + + BE + Break error. This bit is set to 1 if a break condition was detected, indicating that the received data input was held LOW for longer than a full-word transmission time (defined as start, data, parity, and stop bits). This bit is cleared to 0 after a write to UARTECR. In FIFO mode, this error is associated with the character at the top of the FIFO. When a break occurs, only one 0 character is loaded into the FIFO. The next character is only enabled after the receive data input goes to a 1 (marking state) and the next valid start bit is received. + [2:2] + read-write + oneToClear + + + PE + Parity error. When set to 1, it indicates that the parity of the received data character does not match the parity that the EPS and SPS bits in the Line Control Register, UARTLCR_H. This bit is cleared to 0 by a write to UARTECR. In FIFO mode, this error is associated with the character at the top of the FIFO. + [1:1] + read-write + oneToClear + + + FE + Framing error. When set to 1, it indicates that the received character did not have a valid stop bit (a valid stop bit is 1). This bit is cleared to 0 by a write to UARTECR. In FIFO mode, this error is associated with the character at the top of the FIFO. + [0:0] + read-write + oneToClear + + + + + UARTFR + 0x00000018 + Flag Register, UARTFR + 0x00000090 + + + RI + Ring indicator. This bit is the complement of the UART ring indicator, nUARTRI, modem status input. That is, the bit is 1 when nUARTRI is LOW. + [8:8] + read-only + + + TXFE + Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the Line Control Register, UARTLCR_H. If the FIFO is disabled, this bit is set when the transmit holding register is empty. If the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does not indicate if there is data in the transmit shift register. + [7:7] + read-only + + + RXFF + Receive FIFO full. The meaning of this bit depends on the state of the FEN bit in the UARTLCR_H Register. If the FIFO is disabled, this bit is set when the receive holding register is full. If the FIFO is enabled, the RXFF bit is set when the receive FIFO is full. + [6:6] + read-only + + + TXFF + Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the UARTLCR_H Register. If the FIFO is disabled, this bit is set when the transmit holding register is full. If the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full. + [5:5] + read-only + + + RXFE + Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the UARTLCR_H Register. If the FIFO is disabled, this bit is set when the receive holding register is empty. If the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty. + [4:4] + read-only + + + BUSY + UART busy. If this bit is set to 1, the UART is busy transmitting data. This bit remains set until the complete byte, including all the stop bits, has been sent from the shift register. This bit is set as soon as the transmit FIFO becomes non-empty, regardless of whether the UART is enabled or not. + [3:3] + read-only + + + DCD + Data carrier detect. This bit is the complement of the UART data carrier detect, nUARTDCD, modem status input. That is, the bit is 1 when nUARTDCD is LOW. + [2:2] + read-only + + + DSR + Data set ready. This bit is the complement of the UART data set ready, nUARTDSR, modem status input. That is, the bit is 1 when nUARTDSR is LOW. + [1:1] + read-only + + + CTS + Clear to send. This bit is the complement of the UART clear to send, nUARTCTS, modem status input. That is, the bit is 1 when nUARTCTS is LOW. + [0:0] + read-only + + + + + UARTILPR + 0x00000020 + IrDA Low-Power Counter Register, UARTILPR + 0x00000000 + + + ILPDVSR + 8-bit low-power divisor value. These bits are cleared to 0 at reset. + [7:0] + read-write + + + + + UARTIBRD + 0x00000024 + Integer Baud Rate Register, UARTIBRD + 0x00000000 + + + BAUD_DIVINT + The integer baud rate divisor. These bits are cleared to 0 on reset. + [15:0] + read-write + + + + + UARTFBRD + 0x00000028 + Fractional Baud Rate Register, UARTFBRD + 0x00000000 + + + BAUD_DIVFRAC + The fractional baud rate divisor. These bits are cleared to 0 on reset. + [5:0] + read-write + + + + + UARTLCR_H + 0x0000002c + Line Control Register, UARTLCR_H + 0x00000000 + + + SPS + Stick parity select. 0 = stick parity is disabled 1 = either: * if the EPS bit is 0 then the parity bit is transmitted and checked as a 1 * if the EPS bit is 1 then the parity bit is transmitted and checked as a 0. This bit has no effect when the PEN bit disables parity checking and generation. + [7:7] + read-write + + + WLEN + Word length. These bits indicate the number of data bits transmitted or received in a frame as follows: b11 = 8 bits b10 = 7 bits b01 = 6 bits b00 = 5 bits. + [6:5] + read-write + + + FEN + Enable FIFOs: 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding registers 1 = transmit and receive FIFO buffers are enabled (FIFO mode). + [4:4] + read-write + + + STP2 + Two stop bits select. If this bit is set to 1, two stop bits are transmitted at the end of the frame. The receive logic does not check for two stop bits being received. + [3:3] + read-write + + + EPS + Even parity select. Controls the type of parity the UART uses during transmission and reception: 0 = odd parity. The UART generates or checks for an odd number of 1s in the data and parity bits. 1 = even parity. The UART generates or checks for an even number of 1s in the data and parity bits. This bit has no effect when the PEN bit disables parity checking and generation. + [2:2] + read-write + + + PEN + Parity enable: 0 = parity is disabled and no parity bit added to the data frame 1 = parity checking and generation is enabled. + [1:1] + read-write + + + BRK + Send break. If this bit is set to 1, a low-level is continually output on the UARTTXD output, after completing transmission of the current character. For the proper execution of the break command, the software must set this bit for at least two complete frames. For normal use, this bit must be cleared to 0. + [0:0] + read-write + + + + + UARTCR + 0x00000030 + Control Register, UARTCR + 0x00000300 + + + CTSEN + CTS hardware flow control enable. If this bit is set to 1, CTS hardware flow control is enabled. Data is only transmitted when the nUARTCTS signal is asserted. + [15:15] + read-write + + + RTSEN + RTS hardware flow control enable. If this bit is set to 1, RTS hardware flow control is enabled. Data is only requested when there is space in the receive FIFO for it to be received. + [14:14] + read-write + + + OUT2 + This bit is the complement of the UART Out2 (nUARTOut2) modem status output. That is, when the bit is programmed to a 1, the output is 0. For DTE this can be used as Ring Indicator (RI). + [13:13] + read-write + + + OUT1 + This bit is the complement of the UART Out1 (nUARTOut1) modem status output. That is, when the bit is programmed to a 1 the output is 0. For DTE this can be used as Data Carrier Detect (DCD). + [12:12] + read-write + + + RTS + Request to send. This bit is the complement of the UART request to send, nUARTRTS, modem status output. That is, when the bit is programmed to a 1 then nUARTRTS is LOW. + [11:11] + read-write + + + DTR + Data transmit ready. This bit is the complement of the UART data transmit ready, nUARTDTR, modem status output. That is, when the bit is programmed to a 1 then nUARTDTR is LOW. + [10:10] + read-write + + + RXE + Receive enable. If this bit is set to 1, the receive section of the UART is enabled. Data reception occurs for either UART signals or SIR signals depending on the setting of the SIREN bit. When the UART is disabled in the middle of reception, it completes the current character before stopping. + [9:9] + read-write + + + TXE + Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled. Data transmission occurs for either UART signals, or SIR signals depending on the setting of the SIREN bit. When the UART is disabled in the middle of transmission, it completes the current character before stopping. + [8:8] + read-write + + + LBE + Loopback enable. If this bit is set to 1 and the SIREN bit is set to 1 and the SIRTEST bit in the Test Control Register, UARTTCR is set to 1, then the nSIROUT path is inverted, and fed through to the SIRIN path. The SIRTEST bit in the test register must be set to 1 to override the normal half-duplex SIR operation. This must be the requirement for accessing the test registers during normal operation, and SIRTEST must be cleared to 0 when loopback testing is finished. This feature reduces the amount of external coupling required during system test. If this bit is set to 1, and the SIRTEST bit is set to 0, the UARTTXD path is fed through to the UARTRXD path. In either SIR mode or UART mode, when this bit is set, the modem outputs are also fed through to the modem inputs. This bit is cleared to 0 on reset, to disable loopback. + [7:7] + read-write + + + SIRLP + SIR low-power IrDA mode. This bit selects the IrDA encoding mode. If this bit is cleared to 0, low-level bits are transmitted as an active high pulse with a width of 3 / 16th of the bit period. If this bit is set to 1, low-level bits are transmitted with a pulse width that is 3 times the period of the IrLPBaud16 input signal, regardless of the selected bit rate. Setting this bit uses less power, but might reduce transmission distances. + [2:2] + read-write + + + SIREN + SIR enable: 0 = IrDA SIR ENDEC is disabled. nSIROUT remains LOW (no light pulse generated), and signal transitions on SIRIN have no effect. 1 = IrDA SIR ENDEC is enabled. Data is transmitted and received on nSIROUT and SIRIN. UARTTXD remains HIGH, in the marking state. Signal transitions on UARTRXD or modem status inputs have no effect. This bit has no effect if the UARTEN bit disables the UART. + [1:1] + read-write + + + UARTEN + UART enable: 0 = UART is disabled. If the UART is disabled in the middle of transmission or reception, it completes the current character before stopping. 1 = the UART is enabled. Data transmission and reception occurs for either UART signals or SIR signals depending on the setting of the SIREN bit. + [0:0] + read-write + + + + + UARTIFLS + 0x00000034 + Interrupt FIFO Level Select Register, UARTIFLS + 0x00000012 + + + RXIFLSEL + Receive interrupt FIFO level select. The trigger points for the receive interrupt are as follows: b000 = Receive FIFO becomes >= 1 / 8 full b001 = Receive FIFO becomes >= 1 / 4 full b010 = Receive FIFO becomes >= 1 / 2 full b011 = Receive FIFO becomes >= 3 / 4 full b100 = Receive FIFO becomes >= 7 / 8 full b101-b111 = reserved. + [5:3] + read-write + + + TXIFLSEL + Transmit interrupt FIFO level select. The trigger points for the transmit interrupt are as follows: b000 = Transmit FIFO becomes <= 1 / 8 full b001 = Transmit FIFO becomes <= 1 / 4 full b010 = Transmit FIFO becomes <= 1 / 2 full b011 = Transmit FIFO becomes <= 3 / 4 full b100 = Transmit FIFO becomes <= 7 / 8 full b101-b111 = reserved. + [2:0] + read-write + + + + + UARTIMSC + 0x00000038 + Interrupt Mask Set/Clear Register, UARTIMSC + 0x00000000 + + + OEIM + Overrun error interrupt mask. A read returns the current mask for the UARTOEINTR interrupt. On a write of 1, the mask of the UARTOEINTR interrupt is set. A write of 0 clears the mask. + [10:10] + read-write + + + BEIM + Break error interrupt mask. A read returns the current mask for the UARTBEINTR interrupt. On a write of 1, the mask of the UARTBEINTR interrupt is set. A write of 0 clears the mask. + [9:9] + read-write + + + PEIM + Parity error interrupt mask. A read returns the current mask for the UARTPEINTR interrupt. On a write of 1, the mask of the UARTPEINTR interrupt is set. A write of 0 clears the mask. + [8:8] + read-write + + + FEIM + Framing error interrupt mask. A read returns the current mask for the UARTFEINTR interrupt. On a write of 1, the mask of the UARTFEINTR interrupt is set. A write of 0 clears the mask. + [7:7] + read-write + + + RTIM + Receive timeout interrupt mask. A read returns the current mask for the UARTRTINTR interrupt. On a write of 1, the mask of the UARTRTINTR interrupt is set. A write of 0 clears the mask. + [6:6] + read-write + + + TXIM + Transmit interrupt mask. A read returns the current mask for the UARTTXINTR interrupt. On a write of 1, the mask of the UARTTXINTR interrupt is set. A write of 0 clears the mask. + [5:5] + read-write + + + RXIM + Receive interrupt mask. A read returns the current mask for the UARTRXINTR interrupt. On a write of 1, the mask of the UARTRXINTR interrupt is set. A write of 0 clears the mask. + [4:4] + read-write + + + DSRMIM + nUARTDSR modem interrupt mask. A read returns the current mask for the UARTDSRINTR interrupt. On a write of 1, the mask of the UARTDSRINTR interrupt is set. A write of 0 clears the mask. + [3:3] + read-write + + + DCDMIM + nUARTDCD modem interrupt mask. A read returns the current mask for the UARTDCDINTR interrupt. On a write of 1, the mask of the UARTDCDINTR interrupt is set. A write of 0 clears the mask. + [2:2] + read-write + + + CTSMIM + nUARTCTS modem interrupt mask. A read returns the current mask for the UARTCTSINTR interrupt. On a write of 1, the mask of the UARTCTSINTR interrupt is set. A write of 0 clears the mask. + [1:1] + read-write + + + RIMIM + nUARTRI modem interrupt mask. A read returns the current mask for the UARTRIINTR interrupt. On a write of 1, the mask of the UARTRIINTR interrupt is set. A write of 0 clears the mask. + [0:0] + read-write + + + + + UARTRIS + 0x0000003c + Raw Interrupt Status Register, UARTRIS + 0x00000000 + + + OERIS + Overrun error interrupt status. Returns the raw interrupt state of the UARTOEINTR interrupt. + [10:10] + read-only + + + BERIS + Break error interrupt status. Returns the raw interrupt state of the UARTBEINTR interrupt. + [9:9] + read-only + + + PERIS + Parity error interrupt status. Returns the raw interrupt state of the UARTPEINTR interrupt. + [8:8] + read-only + + + FERIS + Framing error interrupt status. Returns the raw interrupt state of the UARTFEINTR interrupt. + [7:7] + read-only + + + RTRIS + Receive timeout interrupt status. Returns the raw interrupt state of the UARTRTINTR interrupt. a + [6:6] + read-only + + + TXRIS + Transmit interrupt status. Returns the raw interrupt state of the UARTTXINTR interrupt. + [5:5] + read-only + + + RXRIS + Receive interrupt status. Returns the raw interrupt state of the UARTRXINTR interrupt. + [4:4] + read-only + + + DSRRMIS + nUARTDSR modem interrupt status. Returns the raw interrupt state of the UARTDSRINTR interrupt. + [3:3] + read-only + + + DCDRMIS + nUARTDCD modem interrupt status. Returns the raw interrupt state of the UARTDCDINTR interrupt. + [2:2] + read-only + + + CTSRMIS + nUARTCTS modem interrupt status. Returns the raw interrupt state of the UARTCTSINTR interrupt. + [1:1] + read-only + + + RIRMIS + nUARTRI modem interrupt status. Returns the raw interrupt state of the UARTRIINTR interrupt. + [0:0] + read-only + + + + + UARTMIS + 0x00000040 + Masked Interrupt Status Register, UARTMIS + 0x00000000 + + + OEMIS + Overrun error masked interrupt status. Returns the masked interrupt state of the UARTOEINTR interrupt. + [10:10] + read-only + + + BEMIS + Break error masked interrupt status. Returns the masked interrupt state of the UARTBEINTR interrupt. + [9:9] + read-only + + + PEMIS + Parity error masked interrupt status. Returns the masked interrupt state of the UARTPEINTR interrupt. + [8:8] + read-only + + + FEMIS + Framing error masked interrupt status. Returns the masked interrupt state of the UARTFEINTR interrupt. + [7:7] + read-only + + + RTMIS + Receive timeout masked interrupt status. Returns the masked interrupt state of the UARTRTINTR interrupt. + [6:6] + read-only + + + TXMIS + Transmit masked interrupt status. Returns the masked interrupt state of the UARTTXINTR interrupt. + [5:5] + read-only + + + RXMIS + Receive masked interrupt status. Returns the masked interrupt state of the UARTRXINTR interrupt. + [4:4] + read-only + + + DSRMMIS + nUARTDSR modem masked interrupt status. Returns the masked interrupt state of the UARTDSRINTR interrupt. + [3:3] + read-only + + + DCDMMIS + nUARTDCD modem masked interrupt status. Returns the masked interrupt state of the UARTDCDINTR interrupt. + [2:2] + read-only + + + CTSMMIS + nUARTCTS modem masked interrupt status. Returns the masked interrupt state of the UARTCTSINTR interrupt. + [1:1] + read-only + + + RIMMIS + nUARTRI modem masked interrupt status. Returns the masked interrupt state of the UARTRIINTR interrupt. + [0:0] + read-only + + + + + UARTICR + 0x00000044 + Interrupt Clear Register, UARTICR + 0x00000000 + + + OEIC + Overrun error interrupt clear. Clears the UARTOEINTR interrupt. + [10:10] + read-write + oneToClear + + + BEIC + Break error interrupt clear. Clears the UARTBEINTR interrupt. + [9:9] + read-write + oneToClear + + + PEIC + Parity error interrupt clear. Clears the UARTPEINTR interrupt. + [8:8] + read-write + oneToClear + + + FEIC + Framing error interrupt clear. Clears the UARTFEINTR interrupt. + [7:7] + read-write + oneToClear + + + RTIC + Receive timeout interrupt clear. Clears the UARTRTINTR interrupt. + [6:6] + read-write + oneToClear + + + TXIC + Transmit interrupt clear. Clears the UARTTXINTR interrupt. + [5:5] + read-write + oneToClear + + + RXIC + Receive interrupt clear. Clears the UARTRXINTR interrupt. + [4:4] + read-write + oneToClear + + + DSRMIC + nUARTDSR modem interrupt clear. Clears the UARTDSRINTR interrupt. + [3:3] + read-write + oneToClear + + + DCDMIC + nUARTDCD modem interrupt clear. Clears the UARTDCDINTR interrupt. + [2:2] + read-write + oneToClear + + + CTSMIC + nUARTCTS modem interrupt clear. Clears the UARTCTSINTR interrupt. + [1:1] + read-write + oneToClear + + + RIMIC + nUARTRI modem interrupt clear. Clears the UARTRIINTR interrupt. + [0:0] + read-write + oneToClear + + + + + UARTDMACR + 0x00000048 + DMA Control Register, UARTDMACR + 0x00000000 + + + DMAONERR + DMA on error. If this bit is set to 1, the DMA receive request outputs, UARTRXDMASREQ or UARTRXDMABREQ, are disabled when the UART error interrupt is asserted. + [2:2] + read-write + + + TXDMAE + Transmit DMA enable. If this bit is set to 1, DMA for the transmit FIFO is enabled. + [1:1] + read-write + + + RXDMAE + Receive DMA enable. If this bit is set to 1, DMA for the receive FIFO is enabled. + [0:0] + read-write + + + + + UARTPERIPHID0 + 0x00000fe0 + UARTPeriphID0 Register + 0x00000011 + + + PARTNUMBER0 + These bits read back as 0x11 + [7:0] + read-only + + + + + UARTPERIPHID1 + 0x00000fe4 + UARTPeriphID1 Register + 0x00000010 + + + DESIGNER0 + These bits read back as 0x1 + [7:4] + read-only + + + PARTNUMBER1 + These bits read back as 0x0 + [3:0] + read-only + + + + + UARTPERIPHID2 + 0x00000fe8 + UARTPeriphID2 Register + 0x00000034 + + + REVISION + This field depends on the revision of the UART: r1p0 0x0 r1p1 0x1 r1p3 0x2 r1p4 0x2 r1p5 0x3 + [7:4] + read-only + + + DESIGNER1 + These bits read back as 0x4 + [3:0] + read-only + + + + + UARTPERIPHID3 + 0x00000fec + UARTPeriphID3 Register + 0x00000000 + + + CONFIGURATION + These bits read back as 0x00 + [7:0] + read-only + + + + + UARTPCELLID0 + 0x00000ff0 + UARTPCellID0 Register + 0x0000000d + + + UARTPCELLID0 + These bits read back as 0x0D + [7:0] + read-only + + + + + UARTPCELLID1 + 0x00000ff4 + UARTPCellID1 Register + 0x000000f0 + + + UARTPCELLID1 + These bits read back as 0xF0 + [7:0] + read-only + + + + + UARTPCELLID2 + 0x00000ff8 + UARTPCellID2 Register + 0x00000005 + + + UARTPCELLID2 + These bits read back as 0x05 + [7:0] + read-only + + + + + UARTPCELLID3 + 0x00000ffc + UARTPCellID3 Register + 0x000000b1 + + + UARTPCELLID3 + These bits read back as 0xB1 + [7:0] + read-only + + + + + + + UART1 + 0x40078000 + + UART1_IRQ + 34 + + + + ROSC + 0x400e8000 + + 0 + 40 + registers + + + + CTRL + 0x00000000 + Ring Oscillator control + 0x00000aa0 + + + ENABLE + On power-up this field is initialised to ENABLE + The system clock must be switched to another source before setting this field to DISABLE otherwise the chip will lock up + The 12-bit code is intended to give some protection against accidental writes. An invalid setting will enable the oscillator. + [23:12] + read-write + + + DISABLE + 3358 + + + ENABLE + 4011 + + + + + FREQ_RANGE + Controls the number of delay stages in the ROSC ring + LOW uses stages 0 to 7 + MEDIUM uses stages 2 to 7 + HIGH uses stages 4 to 7 + TOOHIGH uses stages 6 to 7 and should not be used because its frequency exceeds design specifications + The clock output will not glitch when changing the range up one step at a time + The clock output will glitch when changing the range down + Note: the values here are gray coded which is why HIGH comes before TOOHIGH + [11:0] + read-write + + + LOW + 4004 + + + MEDIUM + 4005 + + + HIGH + 4007 + + + TOOHIGH + 4006 + + + + + + + FREQA + 0x00000004 + The FREQA & FREQB registers control the frequency by controlling the drive strength of each stage + The drive strength has 4 levels determined by the number of bits set + Increasing the number of bits set increases the drive strength and increases the oscillation frequency + 0 bits set is the default drive strength + 1 bit set doubles the drive strength + 2 bits set triples drive strength + 3 bits set quadruples drive strength + For frequency randomisation set both DS0_RANDOM=1 & DS1_RANDOM=1 + 0x00000000 + + + PASSWD + Set to 0x9696 to apply the settings + Any other value in this field will set all drive strengths to 0 + [31:16] + read-write + + + PASS + 38550 + + + + + DS3 + Stage 3 drive strength + [14:12] + read-write + + + DS2 + Stage 2 drive strength + [10:8] + read-write + + + DS1_RANDOM + Randomises the stage 1 drive strength + [7:7] + read-write + + + DS1 + Stage 1 drive strength + [6:4] + read-write + + + DS0_RANDOM + Randomises the stage 0 drive strength + [3:3] + read-write + + + DS0 + Stage 0 drive strength + [2:0] + read-write + + + + + FREQB + 0x00000008 + For a detailed description see freqa register + 0x00000000 + + + PASSWD + Set to 0x9696 to apply the settings + Any other value in this field will set all drive strengths to 0 + [31:16] + read-write + + + PASS + 38550 + + + + + DS7 + Stage 7 drive strength + [14:12] + read-write + + + DS6 + Stage 6 drive strength + [10:8] + read-write + + + DS5 + Stage 5 drive strength + [6:4] + read-write + + + DS4 + Stage 4 drive strength + [2:0] + read-write + + + + + RANDOM + 0x0000000c + Loads a value to the LFSR randomiser + 0x3f04b16d + + + SEED + [31:0] + read-write + + + + + DORMANT + 0x00000010 + Ring Oscillator pause control + 0x00000000 + + + DORMANT + This is used to save power by pausing the ROSC + On power-up this field is initialised to WAKE + An invalid write will also select WAKE + Warning: setup the irq before selecting dormant mode + [31:0] + read-write + + + dormant + 1668246881 + + + WAKE + 2002873189 + + + + + + + DIV + 0x00000014 + Controls the output divider + 0x00000000 + + + DIV + set to 0xaa00 + div where + div = 0 divides by 128 + div = 1-127 divides by div + any other value sets div=128 + this register resets to div=32 + [15:0] + read-write + + + PASS + 43520 + + + + + + + PHASE + 0x00000018 + Controls the phase shifted output + 0x00000008 + + + PASSWD + set to 0xaa + any other value enables the output with shift=0 + [11:4] + read-write + + + ENABLE + enable the phase-shifted output + this can be changed on-the-fly + [3:3] + read-write + + + FLIP + invert the phase-shifted output + this is ignored when div=1 + [2:2] + read-write + + + SHIFT + phase shift the phase-shifted output by SHIFT input clocks + this can be changed on-the-fly + must be set to 0 before setting div=1 + [1:0] + read-write + + + + + STATUS + 0x0000001c + Ring Oscillator Status + 0x00000000 + + + STABLE + Oscillator is running and stable + [31:31] + read-only + + + BADWRITE + An invalid value has been written to CTRL_ENABLE or CTRL_FREQ_RANGE or FREQA or FREQB or DIV or PHASE or DORMANT + [24:24] + read-write + oneToClear + + + DIV_RUNNING + post-divider is running + this resets to 0 but transitions to 1 during chip startup + [16:16] + read-only + + + ENABLED + Oscillator is enabled but not necessarily running and stable + this resets to 0 but transitions to 1 during chip startup + [12:12] + read-only + + + + + RANDOMBIT + 0x00000020 + This just reads the state of the oscillator output so randomness is compromised if the ring oscillator is stopped or run at a harmonic of the bus frequency + 0x00000001 + + + RANDOMBIT + [0:0] + read-only + + + + + COUNT + 0x00000024 + A down counter running at the ROSC frequency which counts to zero and stops. + To start the counter write a non-zero value. + Can be used for short software pauses when setting up time sensitive hardware. + 0x00000000 + + + COUNT + [15:0] + read-write + + + + + + + POWMAN + Controls vreg, bor, lposc, chip resets & xosc startup, powman and provides scratch register for general use and for bootcode use + 0x40100000 + + 0 + 240 + registers + + + POWMAN_IRQ_POW + 44 + + + POWMAN_IRQ_TIMER + 45 + + + + BADPASSWD + 0x00000000 + Indicates a bad password has been used + 0x00000000 + + + BADPASSWD + [0:0] + read-write + oneToClear + + + + + VREG_CTRL + 0x00000004 + Voltage Regulator Control + 0x00008050 + + + RST_N + returns the regulator to its startup settings + 0 - reset + 1 - not reset (default) + [15:15] + read-write + + + UNLOCK + unlocks the VREG control interface after power up + 0 - Locked (default) + 1 - Unlocked + It cannot be relocked when it is unlocked. + [13:13] + read-write + + + ISOLATE + isolates the VREG control interface + 0 - not isolated (default) + 1 - isolated + [12:12] + read-write + + + DISABLE_VOLTAGE_LIMIT + 0=not disabled, 1=enabled + [8:8] + read-write + + + HT_TH + high temperature protection threshold + regulator power transistors are disabled when junction temperature exceeds threshold + 000 - 100C + 001 - 105C + 010 - 110C + 011 - 115C + 100 - 120C + 101 - 125C + 110 - 135C + 111 - 150C + [6:4] + read-write + + + + + VREG_STS + 0x00000008 + Voltage Regulator Status + 0x00000000 + + + VOUT_OK + output regulation status + 0=not in regulation, 1=in regulation + [4:4] + read-only + + + STARTUP + startup status + 0=startup complete, 1=starting up + [0:0] + read-only + + + + + VREG + 0x0000000c + Voltage Regulator Settings + 0x000000b0 + + + UPDATE_IN_PROGRESS + regulator state is being updated + writes to the vreg register will be ignored when this field is set + [15:15] + read-only + + + VSEL + output voltage select + the regulator output voltage is limited to 1.3V unless the voltage limit + is disabled using the disable_voltage_limit field in the vreg_ctrl register + 00000 - 0.55V + 00001 - 0.60V + 00010 - 0.65V + 00011 - 0.70V + 00100 - 0.75V + 00101 - 0.80V + 00110 - 0.85V + 00111 - 0.90V + 01000 - 0.95V + 01001 - 1.00V + 01010 - 1.05V + 01011 - 1.10V (default) + 01100 - 1.15V + 01101 - 1.20V + 01110 - 1.25V + 01111 - 1.30V + 10000 - 1.35V + 10001 - 1.40V + 10010 - 1.50V + 10011 - 1.60V + 10100 - 1.65V + 10101 - 1.70V + 10110 - 1.80V + 10111 - 1.90V + 11000 - 2.00V + 11001 - 2.35V + 11010 - 2.50V + 11011 - 2.65V + 11100 - 2.80V + 11101 - 3.00V + 11110 - 3.15V + 11111 - 3.30V + [8:4] + read-write + + + HIZ + high impedance mode select + 0=not in high impedance mode, 1=in high impedance mode + [1:1] + read-write + + + + + VREG_LP_ENTRY + 0x00000010 + Voltage Regulator Low Power Entry Settings + 0x000000b4 + + + VSEL + output voltage select + the regulator output voltage is limited to 1.3V unless the voltage limit + is disabled using the disable_voltage_limit field in the vreg_ctrl register + 00000 - 0.55V + 00001 - 0.60V + 00010 - 0.65V + 00011 - 0.70V + 00100 - 0.75V + 00101 - 0.80V + 00110 - 0.85V + 00111 - 0.90V + 01000 - 0.95V + 01001 - 1.00V + 01010 - 1.05V + 01011 - 1.10V (default) + 01100 - 1.15V + 01101 - 1.20V + 01110 - 1.25V + 01111 - 1.30V + 10000 - 1.35V + 10001 - 1.40V + 10010 - 1.50V + 10011 - 1.60V + 10100 - 1.65V + 10101 - 1.70V + 10110 - 1.80V + 10111 - 1.90V + 11000 - 2.00V + 11001 - 2.35V + 11010 - 2.50V + 11011 - 2.65V + 11100 - 2.80V + 11101 - 3.00V + 11110 - 3.15V + 11111 - 3.30V + [8:4] + read-write + + + MODE + selects either normal (switching) mode or low power (linear) mode + low power mode can only be selected for output voltages up to 1.3V + 0 = normal mode (switching) + 1 = low power mode (linear) + [2:2] + read-write + + + HIZ + high impedance mode select + 0=not in high impedance mode, 1=in high impedance mode + [1:1] + read-write + + + + + VREG_LP_EXIT + 0x00000014 + Voltage Regulator Low Power Exit Settings + 0x000000b0 + + + VSEL + output voltage select + the regulator output voltage is limited to 1.3V unless the voltage limit + is disabled using the disable_voltage_limit field in the vreg_ctrl register + 00000 - 0.55V + 00001 - 0.60V + 00010 - 0.65V + 00011 - 0.70V + 00100 - 0.75V + 00101 - 0.80V + 00110 - 0.85V + 00111 - 0.90V + 01000 - 0.95V + 01001 - 1.00V + 01010 - 1.05V + 01011 - 1.10V (default) + 01100 - 1.15V + 01101 - 1.20V + 01110 - 1.25V + 01111 - 1.30V + 10000 - 1.35V + 10001 - 1.40V + 10010 - 1.50V + 10011 - 1.60V + 10100 - 1.65V + 10101 - 1.70V + 10110 - 1.80V + 10111 - 1.90V + 11000 - 2.00V + 11001 - 2.35V + 11010 - 2.50V + 11011 - 2.65V + 11100 - 2.80V + 11101 - 3.00V + 11110 - 3.15V + 11111 - 3.30V + [8:4] + read-write + + + MODE + selects either normal (switching) mode or low power (linear) mode + low power mode can only be selected for output voltages up to 1.3V + 0 = normal mode (switching) + 1 = low power mode (linear) + [2:2] + read-write + + + HIZ + high impedance mode select + 0=not in high impedance mode, 1=in high impedance mode + [1:1] + read-write + + + + + BOD_CTRL + 0x00000018 + Brown-out Detection Control + 0x00000000 + + + ISOLATE + isolates the brown-out detection control interface + 0 - not isolated (default) + 1 - isolated + [12:12] + read-write + + + + + BOD + 0x0000001c + Brown-out Detection Settings + 0x000000b1 + + + VSEL + threshold select + 00000 - 0.473V + 00001 - 0.516V + 00010 - 0.559V + 00011 - 0.602V + 00100 - 0.645VS + 00101 - 0.688V + 00110 - 0.731V + 00111 - 0.774V + 01000 - 0.817V + 01001 - 0.860V (default) + 01010 - 0.903V + 01011 - 0.946V + 01100 - 0.989V + 01101 - 1.032V + 01110 - 1.075V + 01111 - 1.118V + 10000 - 1.161 + 10001 - 1.204V + [8:4] + read-write + + + EN + enable brown-out detection + 0=not enabled, 1=enabled + [0:0] + read-write + + + + + BOD_LP_ENTRY + 0x00000020 + Brown-out Detection Low Power Entry Settings + 0x000000b0 + + + VSEL + threshold select + 00000 - 0.473V + 00001 - 0.516V + 00010 - 0.559V + 00011 - 0.602V + 00100 - 0.645VS + 00101 - 0.688V + 00110 - 0.731V + 00111 - 0.774V + 01000 - 0.817V + 01001 - 0.860V (default) + 01010 - 0.903V + 01011 - 0.946V + 01100 - 0.989V + 01101 - 1.032V + 01110 - 1.075V + 01111 - 1.118V + 10000 - 1.161 + 10001 - 1.204V + [8:4] + read-write + + + EN + enable brown-out detection + 0=not enabled, 1=enabled + [0:0] + read-write + + + + + BOD_LP_EXIT + 0x00000024 + Brown-out Detection Low Power Exit Settings + 0x000000b1 + + + VSEL + threshold select + 00000 - 0.473V + 00001 - 0.516V + 00010 - 0.559V + 00011 - 0.602V + 00100 - 0.645VS + 00101 - 0.688V + 00110 - 0.731V + 00111 - 0.774V + 01000 - 0.817V + 01001 - 0.860V (default) + 01010 - 0.903V + 01011 - 0.946V + 01100 - 0.989V + 01101 - 1.032V + 01110 - 1.075V + 01111 - 1.118V + 10000 - 1.161 + 10001 - 1.204V + [8:4] + read-write + + + EN + enable brown-out detection + 0=not enabled, 1=enabled + [0:0] + read-write + + + + + LPOSC + 0x00000028 + Low power oscillator control register. + 0x00000203 + + + TRIM + Frequency trim - the trim step is typically 1% of the reset frequency, but can be up to 3% + [9:4] + read-write + + + MODE + This feature has been removed + [1:0] + read-write + + + + + CHIP_RESET + 0x0000002c + Chip reset control and status + 0x00000000 + + + HAD_WATCHDOG_RESET_RSM + Last reset was a watchdog timeout which was configured to reset the power-on state machine + This resets: + double_tap flag no + DP no + RPAP no + rescue_flag no + timer no + powman no + swcore no + psm yes + and does not change the power state + [28:28] + read-only + + + HAD_HZD_SYS_RESET_REQ + Last reset was a system reset from the hazard debugger + This resets: + double_tap flag no + DP no + RPAP no + rescue_flag no + timer no + powman no + swcore no + psm yes + and does not change the power state + [27:27] + read-only + + + HAD_GLITCH_DETECT + Last reset was due to a power supply glitch + This resets: + double_tap flag no + DP no + RPAP no + rescue_flag no + timer no + powman no + swcore no + psm yes + and does not change the power state + [26:26] + read-only + + + HAD_SWCORE_PD + Last reset was a switched core powerdown + This resets: + double_tap flag no + DP no + RPAP no + rescue_flag no + timer no + powman no + swcore yes + psm yes + then starts the power sequencer + [25:25] + read-only + + + HAD_WATCHDOG_RESET_SWCORE + Last reset was a watchdog timeout which was configured to reset the switched-core + This resets: + double_tap flag no + DP no + RPAP no + rescue_flag no + timer no + powman no + swcore yes + psm yes + then starts the power sequencer + [24:24] + read-only + + + HAD_WATCHDOG_RESET_POWMAN + Last reset was a watchdog timeout which was configured to reset the power manager + This resets: + double_tap flag no + DP no + RPAP no + rescue_flag no + timer yes + powman yes + swcore yes + psm yes + then starts the power sequencer + [23:23] + read-only + + + HAD_WATCHDOG_RESET_POWMAN_ASYNC + Last reset was a watchdog timeout which was configured to reset the power manager asynchronously + This resets: + double_tap flag no + DP no + RPAP no + rescue_flag no + timer yes + powman yes + swcore yes + psm yes + then starts the power sequencer + [22:22] + read-only + + + HAD_RESCUE + Last reset was a rescue reset from the debugger + This resets: + double_tap flag no + DP no + RPAP no + rescue_flag no, it sets this flag + timer yes + powman yes + swcore yes + psm yes + then starts the power sequencer + [21:21] + read-only + + + HAD_DP_RESET_REQ + Last reset was an reset request from the arm debugger + This resets: + double_tap flag no + DP no + RPAP no + rescue_flag yes + timer yes + powman yes + swcore yes + psm yes + then starts the power sequencer + [19:19] + read-only + + + HAD_RUN_LOW + Last reset was from the RUN pin + This resets: + double_tap flag no + DP yes + RPAP yes + rescue_flag yes + timer yes + powman yes + swcore yes + psm yes + then starts the power sequencer + [18:18] + read-only + + + HAD_BOR + Last reset was from the brown-out detection block + This resets: + double_tap flag yes + DP yes + RPAP yes + rescue_flag yes + timer yes + powman yes + swcore yes + psm yes + then starts the power sequencer + [17:17] + read-only + + + HAD_POR + Last reset was from the power-on reset + This resets: + double_tap flag yes + DP yes + RPAP yes + rescue_flag yes + timer yes + powman yes + swcore yes + psm yes + then starts the power sequencer + [16:16] + read-only + + + RESCUE_FLAG + This is set by a rescue reset from the RP-AP. + Its purpose is to halt before the bootrom before booting from flash in order to recover from a boot lock-up. + The debugger can then attach once the bootrom has been halted and flash some working code that does not lock up. + [4:4] + read-write + oneToClear + + + DOUBLE_TAP + This flag is set by double-tapping RUN. It tells bootcode to go into the bootloader. + [0:0] + read-write + + + + + WDSEL + 0x00000030 + Allows a watchdog reset to reset the internal state of powman in addition to the power-on state machine (PSM). + Note that powman ignores watchdog resets that do not select at least the CLOCKS stage or earlier stages in the PSM. If using these bits, it's recommended to set PSM_WDSEL to all-ones in addition to the desired bits in this register. Failing to select CLOCKS or earlier will result in the POWMAN_WDSEL register having no effect. + 0x00000000 + + + RESET_RSM + If set to 1, a watchdog reset will run the full power-on state machine (PSM) sequence + From a user perspective it is the same as setting RSM_WDSEL_PROC_COLD + From a hardware debug perspective it has the same effect as a reset from a glitch detector + [12:12] + read-write + + + RESET_SWCORE + If set to 1, a watchdog reset will reset the switched core power domain and run the full power-on state machine (PSM) sequence + From a user perspective it is the same as setting RSM_WDSEL_PROC_COLD + From a hardware debug perspective it has the same effect as a power-on reset for the switched core power domain + [8:8] + read-write + + + RESET_POWMAN + If set to 1, a watchdog reset will restore powman defaults, reset the timer, reset the switched core power domain + and run the full power-on state machine (PSM) sequence + This relies on clk_ref running. Use reset_powman_async if that may not be true + [4:4] + read-write + + + RESET_POWMAN_ASYNC + If set to 1, a watchdog reset will restore powman defaults, reset the timer, + reset the switched core domain and run the full power-on state machine (PSM) sequence + This does not rely on clk_ref running + [0:0] + read-write + + + + + SEQ_CFG + 0x00000034 + For configuration of the power sequencer + Writes are ignored while POWMAN_STATE_CHANGING=1 + 0x001011f0 + + + USING_FAST_POWCK + 0 indicates the POWMAN clock is running from the low power oscillator (32kHz) + 1 indicates the POWMAN clock is running from the reference clock (2-50MHz) + [20:20] + read-only + + + USING_BOD_LP + Indicates the brown-out detector (BOD) mode + 0 = BOD high power mode which is the default + 1 = BOD low power mode + [17:17] + read-only + + + USING_VREG_LP + Indicates the voltage regulator (VREG) mode + 0 = VREG high power mode which is the default + 1 = VREG low power mode + [16:16] + read-only + + + USE_FAST_POWCK + selects the reference clock (clk_ref) as the source of the POWMAN clock when switched-core is powered. The POWMAN clock always switches to the slow clock (lposc) when switched-core is powered down because the fast clock stops running. + 0 always run the POWMAN clock from the slow clock (lposc) + 1 run the POWMAN clock from the fast clock when available + This setting takes effect when a power up sequence is next run + [12:12] + read-write + + + RUN_LPOSC_IN_LP + Set to 0 to stop the low power osc when the switched-core is powered down, which is unwise if using it to clock the timer + This setting takes effect when the swcore is next powered down + [8:8] + read-write + + + USE_BOD_HP + Set to 0 to prevent automatic switching to bod high power mode when switched-core is powered up + This setting takes effect when the swcore is next powered up + [7:7] + read-write + + + USE_BOD_LP + Set to 0 to prevent automatic switching to bod low power mode when switched-core is powered down + This setting takes effect when the swcore is next powered down + [6:6] + read-write + + + USE_VREG_HP + Set to 0 to prevent automatic switching to vreg high power mode when switched-core is powered up + This setting takes effect when the swcore is next powered up + [5:5] + read-write + + + USE_VREG_LP + Set to 0 to prevent automatic switching to vreg low power mode when switched-core is powered down + This setting takes effect when the swcore is next powered down + [4:4] + read-write + + + HW_PWRUP_SRAM0 + Specifies the power state of SRAM0 when powering up swcore from a low power state (P1.xxx) to a high power state (P0.0xx). + 0=power-up + 1=no change + [1:1] + read-write + + + HW_PWRUP_SRAM1 + Specifies the power state of SRAM1 when powering up swcore from a low power state (P1.xxx) to a high power state (P0.0xx). + 0=power-up + 1=no change + [0:0] + read-write + + + + + STATE + 0x00000038 + This register controls the power state of the 4 power domains. + The current power state is indicated in POWMAN_STATE_CURRENT which is read-only. + To change the state, write to POWMAN_STATE_REQ. + The coding of POWMAN_STATE_CURRENT & POWMAN_STATE_REQ corresponds to the power states + defined in the datasheet: + bit 3 = SWCORE + bit 2 = XIP cache + bit 1 = SRAM0 + bit 0 = SRAM1 + 0 = powered up + 1 = powered down + When POWMAN_STATE_REQ is written, the POWMAN_STATE_WAITING flag is set while the Power Manager determines what is required. If an invalid transition is requested the Power Manager will still register the request in POWMAN_STATE_REQ but will also set the POWMAN_BAD_REQ flag. It will then implement the power-up requests and ignore the power down requests. To do nothing would risk entering an unrecoverable lock-up state. Invalid requests are: any combination of power up and power down requests any request that results in swcore boing powered and xip unpowered If the request is to power down the switched-core domain then POWMAN_STATE_WAITING stays active until the processors halt. During this time the POWMAN_STATE_REQ field can be re-written to change or cancel the request. When the power state transition begins the POWMAN_STATE_WAITING_flag is cleared, the POWMAN_STATE_CHANGING flag is set and POWMAN register writes are ignored until the transition completes. + 0x0000000f + + + CHANGING + [13:13] + read-only + + + WAITING + [12:12] + read-only + + + BAD_HW_REQ + Bad hardware initiated state request. Went back to state 0 (i.e. everything powered up) + [11:11] + read-only + + + BAD_SW_REQ + Bad software initiated state request. No action taken. + [10:10] + read-only + + + PWRUP_WHILE_WAITING + Request ignored because of a pending pwrup request. See current_pwrup_req. Note this blocks powering up AND powering down. + [9:9] + read-write + oneToClear + + + REQ_IGNORED + [8:8] + read-write + oneToClear + + + REQ + [7:4] + read-write + + + CURRENT + [3:0] + read-only + + + + + POW_FASTDIV + 0x0000003c + 0x00000040 + + + POW_FASTDIV + divides the POWMAN clock to provide a tick for the delay module and state machines + when clk_pow is running from the slow clock it is not divided + when clk_pow is running from the fast clock it is divided by tick_div + [10:0] + read-write + + + + + POW_DELAY + 0x00000040 + power state machine delays + 0x00002011 + + + SRAM_STEP + timing between the sram0 and sram1 power state machine steps + measured in units of the powman tick period (>=1us), 0 gives a delay of 1 unit + [15:8] + read-write + + + XIP_STEP + timing between the xip power state machine steps + measured in units of the lposc period, 0 gives a delay of 1 unit + [7:4] + read-write + + + SWCORE_STEP + timing between the swcore power state machine steps + measured in units of the lposc period, 0 gives a delay of 1 unit + [3:0] + read-write + + + + + EXT_CTRL0 + 0x00000044 + Configures a gpio as a power mode aware control output + 0x0000003f + + + LP_EXIT_STATE + output level when exiting the low power state + [14:14] + read-write + + + LP_ENTRY_STATE + output level when entering the low power state + [13:13] + read-write + + + INIT_STATE + [12:12] + read-write + + + INIT + [8:8] + read-write + + + GPIO_SELECT + selects from gpio 0->30 + set to 31 to disable this feature + [5:0] + read-write + + + + + EXT_CTRL1 + 0x00000048 + Configures a gpio as a power mode aware control output + 0x0000003f + + + LP_EXIT_STATE + output level when exiting the low power state + [14:14] + read-write + + + LP_ENTRY_STATE + output level when entering the low power state + [13:13] + read-write + + + INIT_STATE + [12:12] + read-write + + + INIT + [8:8] + read-write + + + GPIO_SELECT + selects from gpio 0->30 + set to 31 to disable this feature + [5:0] + read-write + + + + + EXT_TIME_REF + 0x0000004c + Select a GPIO to use as a time reference, the source can be used to drive the low power clock at 32kHz, or to provide a 1ms tick to the timer, or provide a 1Hz tick to the timer. The tick selection is controlled by the POWMAN_TIMER register. + 0x00000000 + + + DRIVE_LPCK + Use the selected GPIO to drive the 32kHz low power clock, in place of LPOSC. This field must only be written when POWMAN_TIMER_RUN=0 + [4:4] + read-write + + + SOURCE_SEL + 0 -> gpio12 + 1 -> gpio20 + 2 -> gpio14 + 3 -> gpio22 + [1:0] + read-write + + + + + LPOSC_FREQ_KHZ_INT + 0x00000050 + Informs the AON Timer of the integer component of the clock frequency when running off the LPOSC. + 0x00000020 + + + LPOSC_FREQ_KHZ_INT + Integer component of the LPOSC or GPIO clock source frequency in kHz. Default = 32 This field must only be written when POWMAN_TIMER_RUN=0 or POWMAN_TIMER_USING_XOSC=1 + [5:0] + read-write + + + + + LPOSC_FREQ_KHZ_FRAC + 0x00000054 + Informs the AON Timer of the fractional component of the clock frequency when running off the LPOSC. + 0x0000c49c + + + LPOSC_FREQ_KHZ_FRAC + Fractional component of the LPOSC or GPIO clock source frequency in kHz. Default = 0.768 This field must only be written when POWMAN_TIMER_RUN=0 or POWMAN_TIMER_USING_XOSC=1 + [15:0] + read-write + + + + + XOSC_FREQ_KHZ_INT + 0x00000058 + Informs the AON Timer of the integer component of the clock frequency when running off the XOSC. + 0x00002ee0 + + + XOSC_FREQ_KHZ_INT + Integer component of the XOSC frequency in kHz. Default = 12000 Must be >1 This field must only be written when POWMAN_TIMER_RUN=0 or POWMAN_TIMER_USING_XOSC=0 + [15:0] + read-write + + + + + XOSC_FREQ_KHZ_FRAC + 0x0000005c + Informs the AON Timer of the fractional component of the clock frequency when running off the XOSC. + 0x00000000 + + + XOSC_FREQ_KHZ_FRAC + Fractional component of the XOSC frequency in kHz. This field must only be written when POWMAN_TIMER_RUN=0 or POWMAN_TIMER_USING_XOSC=0 + [15:0] + read-write + + + + + SET_TIME_63TO48 + 0x00000060 + 0x00000000 + + + SET_TIME_63TO48 + For setting the time, do not use for reading the time, use POWMAN_READ_TIME_UPPER and POWMAN_READ_TIME_LOWER. This field must only be written when POWMAN_TIMER_RUN=0 + [15:0] + read-write + + + + + SET_TIME_47TO32 + 0x00000064 + 0x00000000 + + + SET_TIME_47TO32 + For setting the time, do not use for reading the time, use POWMAN_READ_TIME_UPPER and POWMAN_READ_TIME_LOWER. This field must only be written when POWMAN_TIMER_RUN=0 + [15:0] + read-write + + + + + SET_TIME_31TO16 + 0x00000068 + 0x00000000 + + + SET_TIME_31TO16 + For setting the time, do not use for reading the time, use POWMAN_READ_TIME_UPPER and POWMAN_READ_TIME_LOWER. This field must only be written when POWMAN_TIMER_RUN=0 + [15:0] + read-write + + + + + SET_TIME_15TO0 + 0x0000006c + 0x00000000 + + + SET_TIME_15TO0 + For setting the time, do not use for reading the time, use POWMAN_READ_TIME_UPPER and POWMAN_READ_TIME_LOWER. This field must only be written when POWMAN_TIMER_RUN=0 + [15:0] + read-write + + + + + READ_TIME_UPPER + 0x00000070 + 0x00000000 + + + READ_TIME_UPPER + For reading bits 63:32 of the timer. When reading all 64 bits it is possible for the LOWER count to rollover during the read. It is recommended to read UPPER, then LOWER, then re-read UPPER and, if it has changed, re-read LOWER. + [31:0] + read-only + + + + + READ_TIME_LOWER + 0x00000074 + 0x00000000 + + + READ_TIME_LOWER + For reading bits 31:0 of the timer. + [31:0] + read-only + + + + + ALARM_TIME_63TO48 + 0x00000078 + 0x00000000 + + + ALARM_TIME_63TO48 + This field must only be written when POWMAN_ALARM_ENAB=0 + [15:0] + read-write + + + + + ALARM_TIME_47TO32 + 0x0000007c + 0x00000000 + + + ALARM_TIME_47TO32 + This field must only be written when POWMAN_ALARM_ENAB=0 + [15:0] + read-write + + + + + ALARM_TIME_31TO16 + 0x00000080 + 0x00000000 + + + ALARM_TIME_31TO16 + This field must only be written when POWMAN_ALARM_ENAB=0 + [15:0] + read-write + + + + + ALARM_TIME_15TO0 + 0x00000084 + 0x00000000 + + + ALARM_TIME_15TO0 + This field must only be written when POWMAN_ALARM_ENAB=0 + [15:0] + read-write + + + + + TIMER + 0x00000088 + 0x00000000 + + + USING_GPIO_1HZ + Timer is synchronised to a 1hz gpio source + [19:19] + read-only + + + USING_GPIO_1KHZ + Timer is running from a 1khz gpio source + [18:18] + read-only + + + USING_LPOSC + Timer is running from lposc + [17:17] + read-only + + + USING_XOSC + Timer is running from xosc + [16:16] + read-only + + + USE_GPIO_1HZ + Selects the gpio source as the reference for the sec counter. The msec counter will continue to use the lposc or xosc reference. + [13:13] + read-write + + + USE_GPIO_1KHZ + switch to gpio as the source of the 1kHz timer tick + [10:10] + write-only + + + USE_XOSC + switch to xosc as the source of the 1kHz timer tick + [9:9] + write-only + + + USE_LPOSC + Switch to lposc as the source of the 1kHz timer tick + [8:8] + write-only + + + ALARM + Alarm has fired. Write to 1 to clear the alarm. + [6:6] + read-write + oneToClear + + + PWRUP_ON_ALARM + Alarm wakes the chip from low power mode + [5:5] + read-write + + + ALARM_ENAB + Enables the alarm. The alarm must be disabled while writing the alarm time. + [4:4] + read-write + + + CLEAR + Clears the timer, does not disable the timer and does not affect the alarm. This control can be written at any time. + [2:2] + write-only + + + RUN + Timer enable. Setting this bit causes the timer to begin counting up from its current value. Clearing this bit stops the timer from counting. + + Before enabling the timer, set the POWMAN_LPOSC_FREQ* and POWMAN_XOSC_FREQ* registers to configure the count rate, and initialise the current time by writing to SET_TIME_63TO48 through SET_TIME_15TO0. You must not write to the SET_TIME_x registers when the timer is running. + + Once configured, start the timer by setting POWMAN_TIMER_RUN=1. This will start the timer running from the LPOSC. When the XOSC is available switch the reference clock to XOSC then select it as the timer clock by setting POWMAN_TIMER_USE_XOSC=1 + [1:1] + read-write + + + NONSEC_WRITE + Control whether Non-secure software can write to the timer registers. All other registers are hardwired to be inaccessible to Non-secure. + [0:0] + read-write + + + + + PWRUP0 + 0x0000008c + 4 GPIO powerup events can be configured to wake the chip up from a low power state. + The pwrups are level/edge sensitive and can be set to trigger on a high/rising or low/falling event + The number of gpios available depends on the package option. An invalid selection will be ignored + source = 0 selects gpio0 + . + . + source = 47 selects gpio47 + source = 48 selects qspi_ss + source = 49 selects qspi_sd0 + source = 50 selects qspi_sd1 + source = 51 selects qspi_sd2 + source = 52 selects qspi_sd3 + source = 53 selects qspi_sclk + level = 0 triggers the pwrup when the source is low + level = 1 triggers the pwrup when the source is high + 0x0000003f + + + RAW_STATUS + Value of selected gpio pin (only if enable == 1) + [10:10] + read-only + + + STATUS + Status of gpio wakeup. Write to 1 to clear a latched edge detect. + [9:9] + read-write + oneToClear + + + MODE + Edge or level detect. Edge will detect a 0 to 1 transition (or 1 to 0 transition). Level will detect a 1 or 0. Both types of event get latched into the current_pwrup_req register. + [8:8] + read-write + + + level + 0 + + + edge + 1 + + + + + DIRECTION + [7:7] + read-write + + + low_falling + 0 + + + high_rising + 1 + + + + + ENABLE + Set to 1 to enable the wakeup source. Set to 0 to disable the wakeup source and clear a pending wakeup event. + If using edge detect a latched edge needs to be cleared by writing 1 to the status register also. + [6:6] + read-write + + + SOURCE + [5:0] + read-write + + + + + PWRUP1 + 0x00000090 + 4 GPIO powerup events can be configured to wake the chip up from a low power state. + The pwrups are level/edge sensitive and can be set to trigger on a high/rising or low/falling event + The number of gpios available depends on the package option. An invalid selection will be ignored + source = 0 selects gpio0 + . + . + source = 47 selects gpio47 + source = 48 selects qspi_ss + source = 49 selects qspi_sd0 + source = 50 selects qspi_sd1 + source = 51 selects qspi_sd2 + source = 52 selects qspi_sd3 + source = 53 selects qspi_sclk + level = 0 triggers the pwrup when the source is low + level = 1 triggers the pwrup when the source is high + 0x0000003f + + + RAW_STATUS + Value of selected gpio pin (only if enable == 1) + [10:10] + read-only + + + STATUS + Status of gpio wakeup. Write to 1 to clear a latched edge detect. + [9:9] + read-write + oneToClear + + + MODE + Edge or level detect. Edge will detect a 0 to 1 transition (or 1 to 0 transition). Level will detect a 1 or 0. Both types of event get latched into the current_pwrup_req register. + [8:8] + read-write + + + level + 0 + + + edge + 1 + + + + + DIRECTION + [7:7] + read-write + + + low_falling + 0 + + + high_rising + 1 + + + + + ENABLE + Set to 1 to enable the wakeup source. Set to 0 to disable the wakeup source and clear a pending wakeup event. + If using edge detect a latched edge needs to be cleared by writing 1 to the status register also. + [6:6] + read-write + + + SOURCE + [5:0] + read-write + + + + + PWRUP2 + 0x00000094 + 4 GPIO powerup events can be configured to wake the chip up from a low power state. + The pwrups are level/edge sensitive and can be set to trigger on a high/rising or low/falling event + The number of gpios available depends on the package option. An invalid selection will be ignored + source = 0 selects gpio0 + . + . + source = 47 selects gpio47 + source = 48 selects qspi_ss + source = 49 selects qspi_sd0 + source = 50 selects qspi_sd1 + source = 51 selects qspi_sd2 + source = 52 selects qspi_sd3 + source = 53 selects qspi_sclk + level = 0 triggers the pwrup when the source is low + level = 1 triggers the pwrup when the source is high + 0x0000003f + + + RAW_STATUS + Value of selected gpio pin (only if enable == 1) + [10:10] + read-only + + + STATUS + Status of gpio wakeup. Write to 1 to clear a latched edge detect. + [9:9] + read-write + oneToClear + + + MODE + Edge or level detect. Edge will detect a 0 to 1 transition (or 1 to 0 transition). Level will detect a 1 or 0. Both types of event get latched into the current_pwrup_req register. + [8:8] + read-write + + + level + 0 + + + edge + 1 + + + + + DIRECTION + [7:7] + read-write + + + low_falling + 0 + + + high_rising + 1 + + + + + ENABLE + Set to 1 to enable the wakeup source. Set to 0 to disable the wakeup source and clear a pending wakeup event. + If using edge detect a latched edge needs to be cleared by writing 1 to the status register also. + [6:6] + read-write + + + SOURCE + [5:0] + read-write + + + + + PWRUP3 + 0x00000098 + 4 GPIO powerup events can be configured to wake the chip up from a low power state. + The pwrups are level/edge sensitive and can be set to trigger on a high/rising or low/falling event + The number of gpios available depends on the package option. An invalid selection will be ignored + source = 0 selects gpio0 + . + . + source = 47 selects gpio47 + source = 48 selects qspi_ss + source = 49 selects qspi_sd0 + source = 50 selects qspi_sd1 + source = 51 selects qspi_sd2 + source = 52 selects qspi_sd3 + source = 53 selects qspi_sclk + level = 0 triggers the pwrup when the source is low + level = 1 triggers the pwrup when the source is high + 0x0000003f + + + RAW_STATUS + Value of selected gpio pin (only if enable == 1) + [10:10] + read-only + + + STATUS + Status of gpio wakeup. Write to 1 to clear a latched edge detect. + [9:9] + read-write + oneToClear + + + MODE + Edge or level detect. Edge will detect a 0 to 1 transition (or 1 to 0 transition). Level will detect a 1 or 0. Both types of event get latched into the current_pwrup_req register. + [8:8] + read-write + + + level + 0 + + + edge + 1 + + + + + DIRECTION + [7:7] + read-write + + + low_falling + 0 + + + high_rising + 1 + + + + + ENABLE + Set to 1 to enable the wakeup source. Set to 0 to disable the wakeup source and clear a pending wakeup event. + If using edge detect a latched edge needs to be cleared by writing 1 to the status register also. + [6:6] + read-write + + + SOURCE + [5:0] + read-write + + + + + CURRENT_PWRUP_REQ + 0x0000009c + Indicates current powerup request state + pwrup events can be cleared by removing the enable from the pwrup register. The alarm pwrup req can be cleared by clearing timer.alarm_enab + 0 = chip reset, for the source of the last reset see POWMAN_CHIP_RESET + 1 = pwrup0 + 2 = pwrup1 + 3 = pwrup2 + 4 = pwrup3 + 5 = coresight_pwrup + 6 = alarm_pwrup + 0x00000000 + + + CURRENT_PWRUP_REQ + [6:0] + read-only + + + + + LAST_SWCORE_PWRUP + 0x000000a0 + Indicates which pwrup source triggered the last switched-core power up + 0 = chip reset, for the source of the last reset see POWMAN_CHIP_RESET + 1 = pwrup0 + 2 = pwrup1 + 3 = pwrup2 + 4 = pwrup3 + 5 = coresight_pwrup + 6 = alarm_pwrup + 0x00000000 + + + LAST_SWCORE_PWRUP + [6:0] + read-only + + + + + DBG_PWRCFG + 0x000000a4 + 0x00000000 + + + IGNORE + Ignore pwrup req from debugger. If pwrup req is asserted then this will prevent power down and set powerdown blocked. Set ignore to stop paying attention to pwrup_req + [0:0] + read-write + + + + + BOOTDIS + 0x000000a8 + Tell the bootrom to ignore the BOOT0..3 registers following the next RSM reset (e.g. the next core power down/up). + + If an early boot stage has soft-locked some OTP pages in order to protect their contents from later stages, there is a risk that Secure code running at a later stage can unlock the pages by powering the core up and down. + + This register can be used to ensure that the bootloader runs as normal on the next power up, preventing Secure code at a later stage from accessing OTP in its unlocked state. + + Should be used in conjunction with the OTP BOOTDIS register. + 0x00000000 + + + NEXT + This flag always ORs writes into its current contents. It can be set but not cleared by software. + + The BOOTDIS_NEXT bit is OR'd into the BOOTDIS_NOW bit when the core is powered down. Simultaneously, the BOOTDIS_NEXT bit is cleared. Setting this bit means that the BOOT0..3 registers will be ignored following the next reset of the RSM by powman. + + This flag should be set by an early boot stage that has soft-locked OTP pages, to prevent later stages from unlocking it by power cycling. + [1:1] + read-write + + + NOW + When powman resets the RSM, the current value of BOOTDIS_NEXT is OR'd into BOOTDIS_NOW, and BOOTDIS_NEXT is cleared. + + The bootrom checks this flag before reading the BOOT0..3 registers. If it is set, the bootrom clears it, and ignores the BOOT registers. This prevents Secure software from diverting the boot path before a bootloader has had the chance to soft lock OTP pages containing sensitive data. + [0:0] + read-write + oneToClear + + + + + DBGCONFIG + 0x000000ac + 0x00000000 + + + DP_INSTID + Configure DP instance ID for SWD multidrop selection. + Recommend that this is NOT changed until you require debug access in multi-chip environment + [3:0] + read-write + + + + + SCRATCH0 + 0x000000b0 + Scratch register. Information persists in low power mode + 0x00000000 + + + SCRATCH0 + [31:0] + read-write + + + + + SCRATCH1 + 0x000000b4 + Scratch register. Information persists in low power mode + 0x00000000 + + + SCRATCH1 + [31:0] + read-write + + + + + SCRATCH2 + 0x000000b8 + Scratch register. Information persists in low power mode + 0x00000000 + + + SCRATCH2 + [31:0] + read-write + + + + + SCRATCH3 + 0x000000bc + Scratch register. Information persists in low power mode + 0x00000000 + + + SCRATCH3 + [31:0] + read-write + + + + + SCRATCH4 + 0x000000c0 + Scratch register. Information persists in low power mode + 0x00000000 + + + SCRATCH4 + [31:0] + read-write + + + + + SCRATCH5 + 0x000000c4 + Scratch register. Information persists in low power mode + 0x00000000 + + + SCRATCH5 + [31:0] + read-write + + + + + SCRATCH6 + 0x000000c8 + Scratch register. Information persists in low power mode + 0x00000000 + + + SCRATCH6 + [31:0] + read-write + + + + + SCRATCH7 + 0x000000cc + Scratch register. Information persists in low power mode + 0x00000000 + + + SCRATCH7 + [31:0] + read-write + + + + + BOOT0 + 0x000000d0 + Scratch register. Information persists in low power mode + 0x00000000 + + + BOOT0 + [31:0] + read-write + + + + + BOOT1 + 0x000000d4 + Scratch register. Information persists in low power mode + 0x00000000 + + + BOOT1 + [31:0] + read-write + + + + + BOOT2 + 0x000000d8 + Scratch register. Information persists in low power mode + 0x00000000 + + + BOOT2 + [31:0] + read-write + + + + + BOOT3 + 0x000000dc + Scratch register. Information persists in low power mode + 0x00000000 + + + BOOT3 + [31:0] + read-write + + + + + INTR + 0x000000e0 + Raw Interrupts + 0x00000000 + + + PWRUP_WHILE_WAITING + Source is state.pwrup_while_waiting + [3:3] + read-only + + + STATE_REQ_IGNORED + Source is state.req_ignored + [2:2] + read-only + + + TIMER + [1:1] + read-only + + + VREG_OUTPUT_LOW + [0:0] + read-write + oneToClear + + + + + INTE + 0x000000e4 + Interrupt Enable + 0x00000000 + + + PWRUP_WHILE_WAITING + Source is state.pwrup_while_waiting + [3:3] + read-write + + + STATE_REQ_IGNORED + Source is state.req_ignored + [2:2] + read-write + + + TIMER + [1:1] + read-write + + + VREG_OUTPUT_LOW + [0:0] + read-write + + + + + INTF + 0x000000e8 + Interrupt Force + 0x00000000 + + + PWRUP_WHILE_WAITING + Source is state.pwrup_while_waiting + [3:3] + read-write + + + STATE_REQ_IGNORED + Source is state.req_ignored + [2:2] + read-write + + + TIMER + [1:1] + read-write + + + VREG_OUTPUT_LOW + [0:0] + read-write + + + + + INTS + 0x000000ec + Interrupt status after masking & forcing + 0x00000000 + + + PWRUP_WHILE_WAITING + Source is state.pwrup_while_waiting + [3:3] + read-only + + + STATE_REQ_IGNORED + Source is state.req_ignored + [2:2] + read-only + + + TIMER + [1:1] + read-only + + + VREG_OUTPUT_LOW + [0:0] + read-only + + + + + + + WATCHDOG + 0x400d8000 + + 0 + 44 + registers + + + + CTRL + 0x00000000 + Watchdog control + The rst_wdsel register determines which subsystems are reset when the watchdog is triggered. + The watchdog can be triggered in software. + 0x07000000 + + + TRIGGER + Trigger a watchdog reset + [31:31] + write-only + + + ENABLE + When not enabled the watchdog timer is paused + [30:30] + read-write + + + PAUSE_DBG1 + Pause the watchdog timer when processor 1 is in debug mode + [26:26] + read-write + + + PAUSE_DBG0 + Pause the watchdog timer when processor 0 is in debug mode + [25:25] + read-write + + + PAUSE_JTAG + Pause the watchdog timer when JTAG is accessing the bus fabric + [24:24] + read-write + + + TIME + Indicates the time in usec before a watchdog reset will be triggered + [23:0] + read-only + + + + + LOAD + 0x00000004 + Load the watchdog timer. The maximum setting is 0xffffff which corresponds to approximately 16 seconds. + 0x00000000 + + + LOAD + [23:0] + write-only + + + + + REASON + 0x00000008 + Logs the reason for the last reset. Both bits are zero for the case of a hardware reset. + + Additionally, as of RP2350, a debugger warm reset of either core (SYSRESETREQ or hartreset) will also clear the watchdog reason register, so that software loaded under the debugger following a watchdog timeout will not continue to see the timeout condition. + 0x00000000 + + + FORCE + [1:1] + read-only + + + TIMER + [0:0] + read-only + + + + + SCRATCH0 + 0x0000000c + Scratch register. Information persists through soft reset of the chip. + 0x00000000 + + + SCRATCH0 + [31:0] + read-write + + + + + SCRATCH1 + 0x00000010 + Scratch register. Information persists through soft reset of the chip. + 0x00000000 + + + SCRATCH1 + [31:0] + read-write + + + + + SCRATCH2 + 0x00000014 + Scratch register. Information persists through soft reset of the chip. + 0x00000000 + + + SCRATCH2 + [31:0] + read-write + + + + + SCRATCH3 + 0x00000018 + Scratch register. Information persists through soft reset of the chip. + 0x00000000 + + + SCRATCH3 + [31:0] + read-write + + + + + SCRATCH4 + 0x0000001c + Scratch register. Information persists through soft reset of the chip. + 0x00000000 + + + SCRATCH4 + [31:0] + read-write + + + + + SCRATCH5 + 0x00000020 + Scratch register. Information persists through soft reset of the chip. + 0x00000000 + + + SCRATCH5 + [31:0] + read-write + + + + + SCRATCH6 + 0x00000024 + Scratch register. Information persists through soft reset of the chip. + 0x00000000 + + + SCRATCH6 + [31:0] + read-write + + + + + SCRATCH7 + 0x00000028 + Scratch register. Information persists through soft reset of the chip. + 0x00000000 + + + SCRATCH7 + [31:0] + read-write + + + + + + + DMA + DMA with separate read and write masters + 0x50000000 + + 0 + 3016 + registers + + + DMA_IRQ_0 + 10 + + + DMA_IRQ_1 + 11 + + + DMA_IRQ_2 + 12 + + + DMA_IRQ_3 + 13 + + + + CH0_READ_ADDR + 0x00000000 + DMA Channel 0 Read Address pointer + 0x00000000 + + + CH0_READ_ADDR + This register updates automatically each time a read completes. The current value is the next address to be read by this channel. + [31:0] + read-write + + + + + CH0_WRITE_ADDR + 0x00000004 + DMA Channel 0 Write Address pointer + 0x00000000 + + + CH0_WRITE_ADDR + This register updates automatically each time a write completes. The current value is the next address to be written by this channel. + [31:0] + read-write + + + + + CH0_TRANS_COUNT + 0x00000008 + DMA Channel 0 Transfer Count + 0x00000000 + + + MODE + When MODE is 0x0, the transfer count decrements with each transfer until 0, and then the channel triggers the next channel indicated by CTRL_CHAIN_TO. + + When MODE is 0x1, the transfer count decrements with each transfer until 0, and then the channel re-triggers itself, in addition to the trigger indicated by CTRL_CHAIN_TO. This is useful for e.g. an endless ring-buffer DMA with periodic interrupts. + + When MODE is 0xf, the transfer count does not decrement. The DMA channel performs an endless sequence of transfers, never triggering other channels or raising interrupts, until an ABORT is raised. + + All other values are reserved. + [31:28] + read-write + + + NORMAL + 0 + + + TRIGGER_SELF + 1 + + + ENDLESS + 15 + + + + + COUNT + 28-bit transfer count (256 million transfers maximum). + + Program the number of bus transfers a channel will perform before halting. Note that, if transfers are larger than one byte in size, this is not equal to the number of bytes transferred (see CTRL_DATA_SIZE). + + When the channel is active, reading this register shows the number of transfers remaining, updating automatically each time a write transfer completes. + + Writing this register sets the RELOAD value for the transfer counter. Each time this channel is triggered, the RELOAD value is copied into the live transfer counter. The channel can be started multiple times, and will perform the same number of transfers each time, as programmed by most recent write. + + The RELOAD value can be observed at CHx_DBG_TCR. If TRANS_COUNT is used as a trigger, the written value is used immediately as the length of the new transfer sequence, as well as being written to RELOAD. + [27:0] + read-write + + + + + CH0_CTRL_TRIG + 0x0000000c + DMA Channel 0 Control and Status + 0x00000000 + + + AHB_ERROR + Logical OR of the READ_ERROR and WRITE_ERROR flags. The channel halts when it encounters any bus error, and always raises its channel IRQ flag. + [31:31] + read-only + + + READ_ERROR + If 1, the channel received a read bus error. Write one to clear. + READ_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 3 transfers later) + [30:30] + read-write + oneToClear + + + WRITE_ERROR + If 1, the channel received a write bus error. Write one to clear. + WRITE_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 5 transfers later) + [29:29] + read-write + oneToClear + + + BUSY + This flag goes high when the channel starts a new transfer sequence, and low when the last transfer of that sequence completes. Clearing EN while BUSY is high pauses the channel, and BUSY will stay high while paused. + + To terminate a sequence early (and clear the BUSY flag), see CHAN_ABORT. + [26:26] + read-only + + + SNIFF_EN + If 1, this channel's data transfers are visible to the sniff hardware, and each transfer will advance the state of the checksum. This only applies if the sniff hardware is enabled, and has this channel selected. + + This allows checksum to be enabled or disabled on a per-control- block basis. + [25:25] + read-write + + + BSWAP + Apply byte-swap transformation to DMA data. + For byte data, this has no effect. For halfword data, the two bytes of each halfword are swapped. For word data, the four bytes of each word are swapped to reverse order. + [24:24] + read-write + + + IRQ_QUIET + In QUIET mode, the channel does not generate IRQs at the end of every transfer block. Instead, an IRQ is raised when NULL is written to a trigger register, indicating the end of a control block chain. + + This reduces the number of interrupts to be serviced by the CPU when transferring a DMA chain of many small control blocks. + [23:23] + read-write + + + TREQ_SEL + Select a Transfer Request signal. + The channel uses the transfer request signal to pace its data transfer rate. Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data Request from the system). + 0x0 to 0x3a -> select DREQ n as TREQ + [22:17] + read-write + + + PIO0_TX0 + 0 + Select PIO0's TX FIFO 0 as TREQ + + + PIO0_TX1 + 1 + Select PIO0's TX FIFO 1 as TREQ + + + PIO0_TX2 + 2 + Select PIO0's TX FIFO 2 as TREQ + + + PIO0_TX3 + 3 + Select PIO0's TX FIFO 3 as TREQ + + + PIO0_RX0 + 4 + Select PIO0's RX FIFO 0 as TREQ + + + PIO0_RX1 + 5 + Select PIO0's RX FIFO 1 as TREQ + + + PIO0_RX2 + 6 + Select PIO0's RX FIFO 2 as TREQ + + + PIO0_RX3 + 7 + Select PIO0's RX FIFO 3 as TREQ + + + PIO1_TX0 + 8 + Select PIO1's TX FIFO 0 as TREQ + + + PIO1_TX1 + 9 + Select PIO1's TX FIFO 1 as TREQ + + + PIO1_TX2 + 10 + Select PIO1's TX FIFO 2 as TREQ + + + PIO1_TX3 + 11 + Select PIO1's TX FIFO 3 as TREQ + + + PIO1_RX0 + 12 + Select PIO1's RX FIFO 0 as TREQ + + + PIO1_RX1 + 13 + Select PIO1's RX FIFO 1 as TREQ + + + PIO1_RX2 + 14 + Select PIO1's RX FIFO 2 as TREQ + + + PIO1_RX3 + 15 + Select PIO1's RX FIFO 3 as TREQ + + + PIO2_TX0 + 16 + Select PIO2's TX FIFO 0 as TREQ + + + PIO2_TX1 + 17 + Select PIO2's TX FIFO 1 as TREQ + + + PIO2_TX2 + 18 + Select PIO2's TX FIFO 2 as TREQ + + + PIO2_TX3 + 19 + Select PIO2's TX FIFO 3 as TREQ + + + PIO2_RX0 + 20 + Select PIO2's RX FIFO 0 as TREQ + + + PIO2_RX1 + 21 + Select PIO2's RX FIFO 1 as TREQ + + + PIO2_RX2 + 22 + Select PIO2's RX FIFO 2 as TREQ + + + PIO2_RX3 + 23 + Select PIO2's RX FIFO 3 as TREQ + + + SPI0_TX + 24 + Select SPI0's TX FIFO as TREQ + + + SPI0_RX + 25 + Select SPI0's RX FIFO as TREQ + + + SPI1_TX + 26 + Select SPI1's TX FIFO as TREQ + + + SPI1_RX + 27 + Select SPI1's RX FIFO as TREQ + + + UART0_TX + 28 + Select UART0's TX FIFO as TREQ + + + UART0_RX + 29 + Select UART0's RX FIFO as TREQ + + + UART1_TX + 30 + Select UART1's TX FIFO as TREQ + + + UART1_RX + 31 + Select UART1's RX FIFO as TREQ + + + PWM_WRAP0 + 32 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP1 + 33 + Select PWM Counter 1's Wrap Value as TREQ + + + PWM_WRAP2 + 34 + Select PWM Counter 2's Wrap Value as TREQ + + + PWM_WRAP3 + 35 + Select PWM Counter 3's Wrap Value as TREQ + + + PWM_WRAP4 + 36 + Select PWM Counter 4's Wrap Value as TREQ + + + PWM_WRAP5 + 37 + Select PWM Counter 5's Wrap Value as TREQ + + + PWM_WRAP6 + 38 + Select PWM Counter 6's Wrap Value as TREQ + + + PWM_WRAP7 + 39 + Select PWM Counter 7's Wrap Value as TREQ + + + PWM_WRAP8 + 40 + Select PWM Counter 8's Wrap Value as TREQ + + + PWM_WRAP9 + 41 + Select PWM Counter 9's Wrap Value as TREQ + + + PWM_WRAP10 + 42 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP11 + 43 + Select PWM Counter 1's Wrap Value as TREQ + + + I2C0_TX + 44 + Select I2C0's TX FIFO as TREQ + + + I2C0_RX + 45 + Select I2C0's RX FIFO as TREQ + + + I2C1_TX + 46 + Select I2C1's TX FIFO as TREQ + + + I2C1_RX + 47 + Select I2C1's RX FIFO as TREQ + + + ADC + 48 + Select the ADC as TREQ + + + XIP_STREAM + 49 + Select the XIP Streaming FIFO as TREQ + + + XIP_QMITX + 50 + Select XIP_QMITX as TREQ + + + XIP_QMIRX + 51 + Select XIP_QMIRX as TREQ + + + HSTX + 52 + Select HSTX as TREQ + + + CORESIGHT + 53 + Select CORESIGHT as TREQ + + + SHA256 + 54 + Select SHA256 as TREQ + + + TIMER0 + 59 + Select Timer 0 as TREQ + + + TIMER1 + 60 + Select Timer 1 as TREQ + + + TIMER2 + 61 + Select Timer 2 as TREQ (Optional) + + + TIMER3 + 62 + Select Timer 3 as TREQ (Optional) + + + PERMANENT + 63 + Permanent request, for unpaced transfers. + + + + + CHAIN_TO + When this channel completes, it will trigger the channel indicated by CHAIN_TO. Disable by setting CHAIN_TO = _(this channel)_. + + Note this field resets to 0, so channels 1 and above will chain to channel 0 by default. Set this field to avoid this behaviour. + [16:13] + read-write + + + RING_SEL + Select whether RING_SIZE applies to read or write addresses. + If 0, read addresses are wrapped on a (1 << RING_SIZE) boundary. If 1, write addresses are wrapped. + [12:12] + read-write + + + RING_SIZE + Size of address wrap region. If 0, don't wrap. For values n > 0, only the lower n bits of the address will change. This wraps the address on a (1 << n) byte boundary, facilitating access to naturally-aligned ring buffers. + + Ring sizes between 2 and 32768 bytes are possible. This can apply to either read or write addresses, based on value of RING_SEL. + [11:8] + read-write + + + RING_NONE + 0 + + + + + INCR_WRITE_REV + If 1, and INCR_WRITE is 1, the write address is decremented rather than incremented with each transfer. + + If 1, and INCR_WRITE is 0, this otherwise-unused combination causes the write address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [7:7] + read-write + + + INCR_WRITE + If 1, the write address increments with each transfer. If 0, each write is directed to the same, initial address. + + Generally this should be disabled for memory-to-peripheral transfers. + [6:6] + read-write + + + INCR_READ_REV + If 1, and INCR_READ is 1, the read address is decremented rather than incremented with each transfer. + + If 1, and INCR_READ is 0, this otherwise-unused combination causes the read address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [5:5] + read-write + + + INCR_READ + If 1, the read address increments with each transfer. If 0, each read is directed to the same, initial address. + + Generally this should be disabled for peripheral-to-memory transfers. + [4:4] + read-write + + + DATA_SIZE + Set the size of each bus transfer (byte/halfword/word). READ_ADDR and WRITE_ADDR advance by this amount (1/2/4 bytes) with each transfer. + [3:2] + read-write + + + SIZE_BYTE + 0 + + + SIZE_HALFWORD + 1 + + + SIZE_WORD + 2 + + + + + HIGH_PRIORITY + HIGH_PRIORITY gives a channel preferential treatment in issue scheduling: in each scheduling round, all high priority channels are considered first, and then only a single low priority channel, before returning to the high priority channels. + + This only affects the order in which the DMA schedules channels. The DMA's bus priority is not changed. If the DMA is not saturated then a low priority channel will see no loss of throughput. + [1:1] + read-write + + + EN + DMA Channel Enable. + When 1, the channel will respond to triggering events, which will cause it to become BUSY and start transferring data. When 0, the channel will ignore triggers, stop issuing transfers, and pause the current transfer sequence (i.e. BUSY will remain high if already high) + [0:0] + read-write + + + + + CH0_AL1_CTRL + 0x00000010 + Alias for channel 0 CTRL register + 0x00000000 + + + CH0_AL1_CTRL + [31:0] + read-write + + + + + CH0_AL1_READ_ADDR + 0x00000014 + Alias for channel 0 READ_ADDR register + 0x00000000 + + + CH0_AL1_READ_ADDR + [31:0] + read-write + + + + + CH0_AL1_WRITE_ADDR + 0x00000018 + Alias for channel 0 WRITE_ADDR register + 0x00000000 + + + CH0_AL1_WRITE_ADDR + [31:0] + read-write + + + + + CH0_AL1_TRANS_COUNT_TRIG + 0x0000001c + Alias for channel 0 TRANS_COUNT register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH0_AL1_TRANS_COUNT_TRIG + [31:0] + read-write + + + + + CH0_AL2_CTRL + 0x00000020 + Alias for channel 0 CTRL register + 0x00000000 + + + CH0_AL2_CTRL + [31:0] + read-write + + + + + CH0_AL2_TRANS_COUNT + 0x00000024 + Alias for channel 0 TRANS_COUNT register + 0x00000000 + + + CH0_AL2_TRANS_COUNT + [31:0] + read-write + + + + + CH0_AL2_READ_ADDR + 0x00000028 + Alias for channel 0 READ_ADDR register + 0x00000000 + + + CH0_AL2_READ_ADDR + [31:0] + read-write + + + + + CH0_AL2_WRITE_ADDR_TRIG + 0x0000002c + Alias for channel 0 WRITE_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH0_AL2_WRITE_ADDR_TRIG + [31:0] + read-write + + + + + CH0_AL3_CTRL + 0x00000030 + Alias for channel 0 CTRL register + 0x00000000 + + + CH0_AL3_CTRL + [31:0] + read-write + + + + + CH0_AL3_WRITE_ADDR + 0x00000034 + Alias for channel 0 WRITE_ADDR register + 0x00000000 + + + CH0_AL3_WRITE_ADDR + [31:0] + read-write + + + + + CH0_AL3_TRANS_COUNT + 0x00000038 + Alias for channel 0 TRANS_COUNT register + 0x00000000 + + + CH0_AL3_TRANS_COUNT + [31:0] + read-write + + + + + CH0_AL3_READ_ADDR_TRIG + 0x0000003c + Alias for channel 0 READ_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH0_AL3_READ_ADDR_TRIG + [31:0] + read-write + + + + + CH1_READ_ADDR + 0x00000040 + DMA Channel 1 Read Address pointer + 0x00000000 + + + CH1_READ_ADDR + This register updates automatically each time a read completes. The current value is the next address to be read by this channel. + [31:0] + read-write + + + + + CH1_WRITE_ADDR + 0x00000044 + DMA Channel 1 Write Address pointer + 0x00000000 + + + CH1_WRITE_ADDR + This register updates automatically each time a write completes. The current value is the next address to be written by this channel. + [31:0] + read-write + + + + + CH1_TRANS_COUNT + 0x00000048 + DMA Channel 1 Transfer Count + 0x00000000 + + + MODE + When MODE is 0x0, the transfer count decrements with each transfer until 0, and then the channel triggers the next channel indicated by CTRL_CHAIN_TO. + + When MODE is 0x1, the transfer count decrements with each transfer until 0, and then the channel re-triggers itself, in addition to the trigger indicated by CTRL_CHAIN_TO. This is useful for e.g. an endless ring-buffer DMA with periodic interrupts. + + When MODE is 0xf, the transfer count does not decrement. The DMA channel performs an endless sequence of transfers, never triggering other channels or raising interrupts, until an ABORT is raised. + + All other values are reserved. + [31:28] + read-write + + + NORMAL + 0 + + + TRIGGER_SELF + 1 + + + ENDLESS + 15 + + + + + COUNT + 28-bit transfer count (256 million transfers maximum). + + Program the number of bus transfers a channel will perform before halting. Note that, if transfers are larger than one byte in size, this is not equal to the number of bytes transferred (see CTRL_DATA_SIZE). + + When the channel is active, reading this register shows the number of transfers remaining, updating automatically each time a write transfer completes. + + Writing this register sets the RELOAD value for the transfer counter. Each time this channel is triggered, the RELOAD value is copied into the live transfer counter. The channel can be started multiple times, and will perform the same number of transfers each time, as programmed by most recent write. + + The RELOAD value can be observed at CHx_DBG_TCR. If TRANS_COUNT is used as a trigger, the written value is used immediately as the length of the new transfer sequence, as well as being written to RELOAD. + [27:0] + read-write + + + + + CH1_CTRL_TRIG + 0x0000004c + DMA Channel 1 Control and Status + 0x00000000 + + + AHB_ERROR + Logical OR of the READ_ERROR and WRITE_ERROR flags. The channel halts when it encounters any bus error, and always raises its channel IRQ flag. + [31:31] + read-only + + + READ_ERROR + If 1, the channel received a read bus error. Write one to clear. + READ_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 3 transfers later) + [30:30] + read-write + oneToClear + + + WRITE_ERROR + If 1, the channel received a write bus error. Write one to clear. + WRITE_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 5 transfers later) + [29:29] + read-write + oneToClear + + + BUSY + This flag goes high when the channel starts a new transfer sequence, and low when the last transfer of that sequence completes. Clearing EN while BUSY is high pauses the channel, and BUSY will stay high while paused. + + To terminate a sequence early (and clear the BUSY flag), see CHAN_ABORT. + [26:26] + read-only + + + SNIFF_EN + If 1, this channel's data transfers are visible to the sniff hardware, and each transfer will advance the state of the checksum. This only applies if the sniff hardware is enabled, and has this channel selected. + + This allows checksum to be enabled or disabled on a per-control- block basis. + [25:25] + read-write + + + BSWAP + Apply byte-swap transformation to DMA data. + For byte data, this has no effect. For halfword data, the two bytes of each halfword are swapped. For word data, the four bytes of each word are swapped to reverse order. + [24:24] + read-write + + + IRQ_QUIET + In QUIET mode, the channel does not generate IRQs at the end of every transfer block. Instead, an IRQ is raised when NULL is written to a trigger register, indicating the end of a control block chain. + + This reduces the number of interrupts to be serviced by the CPU when transferring a DMA chain of many small control blocks. + [23:23] + read-write + + + TREQ_SEL + Select a Transfer Request signal. + The channel uses the transfer request signal to pace its data transfer rate. Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data Request from the system). + 0x0 to 0x3a -> select DREQ n as TREQ + [22:17] + read-write + + + PIO0_TX0 + 0 + Select PIO0's TX FIFO 0 as TREQ + + + PIO0_TX1 + 1 + Select PIO0's TX FIFO 1 as TREQ + + + PIO0_TX2 + 2 + Select PIO0's TX FIFO 2 as TREQ + + + PIO0_TX3 + 3 + Select PIO0's TX FIFO 3 as TREQ + + + PIO0_RX0 + 4 + Select PIO0's RX FIFO 0 as TREQ + + + PIO0_RX1 + 5 + Select PIO0's RX FIFO 1 as TREQ + + + PIO0_RX2 + 6 + Select PIO0's RX FIFO 2 as TREQ + + + PIO0_RX3 + 7 + Select PIO0's RX FIFO 3 as TREQ + + + PIO1_TX0 + 8 + Select PIO1's TX FIFO 0 as TREQ + + + PIO1_TX1 + 9 + Select PIO1's TX FIFO 1 as TREQ + + + PIO1_TX2 + 10 + Select PIO1's TX FIFO 2 as TREQ + + + PIO1_TX3 + 11 + Select PIO1's TX FIFO 3 as TREQ + + + PIO1_RX0 + 12 + Select PIO1's RX FIFO 0 as TREQ + + + PIO1_RX1 + 13 + Select PIO1's RX FIFO 1 as TREQ + + + PIO1_RX2 + 14 + Select PIO1's RX FIFO 2 as TREQ + + + PIO1_RX3 + 15 + Select PIO1's RX FIFO 3 as TREQ + + + PIO2_TX0 + 16 + Select PIO2's TX FIFO 0 as TREQ + + + PIO2_TX1 + 17 + Select PIO2's TX FIFO 1 as TREQ + + + PIO2_TX2 + 18 + Select PIO2's TX FIFO 2 as TREQ + + + PIO2_TX3 + 19 + Select PIO2's TX FIFO 3 as TREQ + + + PIO2_RX0 + 20 + Select PIO2's RX FIFO 0 as TREQ + + + PIO2_RX1 + 21 + Select PIO2's RX FIFO 1 as TREQ + + + PIO2_RX2 + 22 + Select PIO2's RX FIFO 2 as TREQ + + + PIO2_RX3 + 23 + Select PIO2's RX FIFO 3 as TREQ + + + SPI0_TX + 24 + Select SPI0's TX FIFO as TREQ + + + SPI0_RX + 25 + Select SPI0's RX FIFO as TREQ + + + SPI1_TX + 26 + Select SPI1's TX FIFO as TREQ + + + SPI1_RX + 27 + Select SPI1's RX FIFO as TREQ + + + UART0_TX + 28 + Select UART0's TX FIFO as TREQ + + + UART0_RX + 29 + Select UART0's RX FIFO as TREQ + + + UART1_TX + 30 + Select UART1's TX FIFO as TREQ + + + UART1_RX + 31 + Select UART1's RX FIFO as TREQ + + + PWM_WRAP0 + 32 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP1 + 33 + Select PWM Counter 1's Wrap Value as TREQ + + + PWM_WRAP2 + 34 + Select PWM Counter 2's Wrap Value as TREQ + + + PWM_WRAP3 + 35 + Select PWM Counter 3's Wrap Value as TREQ + + + PWM_WRAP4 + 36 + Select PWM Counter 4's Wrap Value as TREQ + + + PWM_WRAP5 + 37 + Select PWM Counter 5's Wrap Value as TREQ + + + PWM_WRAP6 + 38 + Select PWM Counter 6's Wrap Value as TREQ + + + PWM_WRAP7 + 39 + Select PWM Counter 7's Wrap Value as TREQ + + + PWM_WRAP8 + 40 + Select PWM Counter 8's Wrap Value as TREQ + + + PWM_WRAP9 + 41 + Select PWM Counter 9's Wrap Value as TREQ + + + PWM_WRAP10 + 42 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP11 + 43 + Select PWM Counter 1's Wrap Value as TREQ + + + I2C0_TX + 44 + Select I2C0's TX FIFO as TREQ + + + I2C0_RX + 45 + Select I2C0's RX FIFO as TREQ + + + I2C1_TX + 46 + Select I2C1's TX FIFO as TREQ + + + I2C1_RX + 47 + Select I2C1's RX FIFO as TREQ + + + ADC + 48 + Select the ADC as TREQ + + + XIP_STREAM + 49 + Select the XIP Streaming FIFO as TREQ + + + XIP_QMITX + 50 + Select XIP_QMITX as TREQ + + + XIP_QMIRX + 51 + Select XIP_QMIRX as TREQ + + + HSTX + 52 + Select HSTX as TREQ + + + CORESIGHT + 53 + Select CORESIGHT as TREQ + + + SHA256 + 54 + Select SHA256 as TREQ + + + TIMER0 + 59 + Select Timer 0 as TREQ + + + TIMER1 + 60 + Select Timer 1 as TREQ + + + TIMER2 + 61 + Select Timer 2 as TREQ (Optional) + + + TIMER3 + 62 + Select Timer 3 as TREQ (Optional) + + + PERMANENT + 63 + Permanent request, for unpaced transfers. + + + + + CHAIN_TO + When this channel completes, it will trigger the channel indicated by CHAIN_TO. Disable by setting CHAIN_TO = _(this channel)_. + + Note this field resets to 0, so channels 1 and above will chain to channel 0 by default. Set this field to avoid this behaviour. + [16:13] + read-write + + + RING_SEL + Select whether RING_SIZE applies to read or write addresses. + If 0, read addresses are wrapped on a (1 << RING_SIZE) boundary. If 1, write addresses are wrapped. + [12:12] + read-write + + + RING_SIZE + Size of address wrap region. If 0, don't wrap. For values n > 0, only the lower n bits of the address will change. This wraps the address on a (1 << n) byte boundary, facilitating access to naturally-aligned ring buffers. + + Ring sizes between 2 and 32768 bytes are possible. This can apply to either read or write addresses, based on value of RING_SEL. + [11:8] + read-write + + + RING_NONE + 0 + + + + + INCR_WRITE_REV + If 1, and INCR_WRITE is 1, the write address is decremented rather than incremented with each transfer. + + If 1, and INCR_WRITE is 0, this otherwise-unused combination causes the write address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [7:7] + read-write + + + INCR_WRITE + If 1, the write address increments with each transfer. If 0, each write is directed to the same, initial address. + + Generally this should be disabled for memory-to-peripheral transfers. + [6:6] + read-write + + + INCR_READ_REV + If 1, and INCR_READ is 1, the read address is decremented rather than incremented with each transfer. + + If 1, and INCR_READ is 0, this otherwise-unused combination causes the read address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [5:5] + read-write + + + INCR_READ + If 1, the read address increments with each transfer. If 0, each read is directed to the same, initial address. + + Generally this should be disabled for peripheral-to-memory transfers. + [4:4] + read-write + + + DATA_SIZE + Set the size of each bus transfer (byte/halfword/word). READ_ADDR and WRITE_ADDR advance by this amount (1/2/4 bytes) with each transfer. + [3:2] + read-write + + + SIZE_BYTE + 0 + + + SIZE_HALFWORD + 1 + + + SIZE_WORD + 2 + + + + + HIGH_PRIORITY + HIGH_PRIORITY gives a channel preferential treatment in issue scheduling: in each scheduling round, all high priority channels are considered first, and then only a single low priority channel, before returning to the high priority channels. + + This only affects the order in which the DMA schedules channels. The DMA's bus priority is not changed. If the DMA is not saturated then a low priority channel will see no loss of throughput. + [1:1] + read-write + + + EN + DMA Channel Enable. + When 1, the channel will respond to triggering events, which will cause it to become BUSY and start transferring data. When 0, the channel will ignore triggers, stop issuing transfers, and pause the current transfer sequence (i.e. BUSY will remain high if already high) + [0:0] + read-write + + + + + CH1_AL1_CTRL + 0x00000050 + Alias for channel 1 CTRL register + 0x00000000 + + + CH1_AL1_CTRL + [31:0] + read-write + + + + + CH1_AL1_READ_ADDR + 0x00000054 + Alias for channel 1 READ_ADDR register + 0x00000000 + + + CH1_AL1_READ_ADDR + [31:0] + read-write + + + + + CH1_AL1_WRITE_ADDR + 0x00000058 + Alias for channel 1 WRITE_ADDR register + 0x00000000 + + + CH1_AL1_WRITE_ADDR + [31:0] + read-write + + + + + CH1_AL1_TRANS_COUNT_TRIG + 0x0000005c + Alias for channel 1 TRANS_COUNT register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH1_AL1_TRANS_COUNT_TRIG + [31:0] + read-write + + + + + CH1_AL2_CTRL + 0x00000060 + Alias for channel 1 CTRL register + 0x00000000 + + + CH1_AL2_CTRL + [31:0] + read-write + + + + + CH1_AL2_TRANS_COUNT + 0x00000064 + Alias for channel 1 TRANS_COUNT register + 0x00000000 + + + CH1_AL2_TRANS_COUNT + [31:0] + read-write + + + + + CH1_AL2_READ_ADDR + 0x00000068 + Alias for channel 1 READ_ADDR register + 0x00000000 + + + CH1_AL2_READ_ADDR + [31:0] + read-write + + + + + CH1_AL2_WRITE_ADDR_TRIG + 0x0000006c + Alias for channel 1 WRITE_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH1_AL2_WRITE_ADDR_TRIG + [31:0] + read-write + + + + + CH1_AL3_CTRL + 0x00000070 + Alias for channel 1 CTRL register + 0x00000000 + + + CH1_AL3_CTRL + [31:0] + read-write + + + + + CH1_AL3_WRITE_ADDR + 0x00000074 + Alias for channel 1 WRITE_ADDR register + 0x00000000 + + + CH1_AL3_WRITE_ADDR + [31:0] + read-write + + + + + CH1_AL3_TRANS_COUNT + 0x00000078 + Alias for channel 1 TRANS_COUNT register + 0x00000000 + + + CH1_AL3_TRANS_COUNT + [31:0] + read-write + + + + + CH1_AL3_READ_ADDR_TRIG + 0x0000007c + Alias for channel 1 READ_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH1_AL3_READ_ADDR_TRIG + [31:0] + read-write + + + + + CH2_READ_ADDR + 0x00000080 + DMA Channel 2 Read Address pointer + 0x00000000 + + + CH2_READ_ADDR + This register updates automatically each time a read completes. The current value is the next address to be read by this channel. + [31:0] + read-write + + + + + CH2_WRITE_ADDR + 0x00000084 + DMA Channel 2 Write Address pointer + 0x00000000 + + + CH2_WRITE_ADDR + This register updates automatically each time a write completes. The current value is the next address to be written by this channel. + [31:0] + read-write + + + + + CH2_TRANS_COUNT + 0x00000088 + DMA Channel 2 Transfer Count + 0x00000000 + + + MODE + When MODE is 0x0, the transfer count decrements with each transfer until 0, and then the channel triggers the next channel indicated by CTRL_CHAIN_TO. + + When MODE is 0x1, the transfer count decrements with each transfer until 0, and then the channel re-triggers itself, in addition to the trigger indicated by CTRL_CHAIN_TO. This is useful for e.g. an endless ring-buffer DMA with periodic interrupts. + + When MODE is 0xf, the transfer count does not decrement. The DMA channel performs an endless sequence of transfers, never triggering other channels or raising interrupts, until an ABORT is raised. + + All other values are reserved. + [31:28] + read-write + + + NORMAL + 0 + + + TRIGGER_SELF + 1 + + + ENDLESS + 15 + + + + + COUNT + 28-bit transfer count (256 million transfers maximum). + + Program the number of bus transfers a channel will perform before halting. Note that, if transfers are larger than one byte in size, this is not equal to the number of bytes transferred (see CTRL_DATA_SIZE). + + When the channel is active, reading this register shows the number of transfers remaining, updating automatically each time a write transfer completes. + + Writing this register sets the RELOAD value for the transfer counter. Each time this channel is triggered, the RELOAD value is copied into the live transfer counter. The channel can be started multiple times, and will perform the same number of transfers each time, as programmed by most recent write. + + The RELOAD value can be observed at CHx_DBG_TCR. If TRANS_COUNT is used as a trigger, the written value is used immediately as the length of the new transfer sequence, as well as being written to RELOAD. + [27:0] + read-write + + + + + CH2_CTRL_TRIG + 0x0000008c + DMA Channel 2 Control and Status + 0x00000000 + + + AHB_ERROR + Logical OR of the READ_ERROR and WRITE_ERROR flags. The channel halts when it encounters any bus error, and always raises its channel IRQ flag. + [31:31] + read-only + + + READ_ERROR + If 1, the channel received a read bus error. Write one to clear. + READ_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 3 transfers later) + [30:30] + read-write + oneToClear + + + WRITE_ERROR + If 1, the channel received a write bus error. Write one to clear. + WRITE_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 5 transfers later) + [29:29] + read-write + oneToClear + + + BUSY + This flag goes high when the channel starts a new transfer sequence, and low when the last transfer of that sequence completes. Clearing EN while BUSY is high pauses the channel, and BUSY will stay high while paused. + + To terminate a sequence early (and clear the BUSY flag), see CHAN_ABORT. + [26:26] + read-only + + + SNIFF_EN + If 1, this channel's data transfers are visible to the sniff hardware, and each transfer will advance the state of the checksum. This only applies if the sniff hardware is enabled, and has this channel selected. + + This allows checksum to be enabled or disabled on a per-control- block basis. + [25:25] + read-write + + + BSWAP + Apply byte-swap transformation to DMA data. + For byte data, this has no effect. For halfword data, the two bytes of each halfword are swapped. For word data, the four bytes of each word are swapped to reverse order. + [24:24] + read-write + + + IRQ_QUIET + In QUIET mode, the channel does not generate IRQs at the end of every transfer block. Instead, an IRQ is raised when NULL is written to a trigger register, indicating the end of a control block chain. + + This reduces the number of interrupts to be serviced by the CPU when transferring a DMA chain of many small control blocks. + [23:23] + read-write + + + TREQ_SEL + Select a Transfer Request signal. + The channel uses the transfer request signal to pace its data transfer rate. Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data Request from the system). + 0x0 to 0x3a -> select DREQ n as TREQ + [22:17] + read-write + + + PIO0_TX0 + 0 + Select PIO0's TX FIFO 0 as TREQ + + + PIO0_TX1 + 1 + Select PIO0's TX FIFO 1 as TREQ + + + PIO0_TX2 + 2 + Select PIO0's TX FIFO 2 as TREQ + + + PIO0_TX3 + 3 + Select PIO0's TX FIFO 3 as TREQ + + + PIO0_RX0 + 4 + Select PIO0's RX FIFO 0 as TREQ + + + PIO0_RX1 + 5 + Select PIO0's RX FIFO 1 as TREQ + + + PIO0_RX2 + 6 + Select PIO0's RX FIFO 2 as TREQ + + + PIO0_RX3 + 7 + Select PIO0's RX FIFO 3 as TREQ + + + PIO1_TX0 + 8 + Select PIO1's TX FIFO 0 as TREQ + + + PIO1_TX1 + 9 + Select PIO1's TX FIFO 1 as TREQ + + + PIO1_TX2 + 10 + Select PIO1's TX FIFO 2 as TREQ + + + PIO1_TX3 + 11 + Select PIO1's TX FIFO 3 as TREQ + + + PIO1_RX0 + 12 + Select PIO1's RX FIFO 0 as TREQ + + + PIO1_RX1 + 13 + Select PIO1's RX FIFO 1 as TREQ + + + PIO1_RX2 + 14 + Select PIO1's RX FIFO 2 as TREQ + + + PIO1_RX3 + 15 + Select PIO1's RX FIFO 3 as TREQ + + + PIO2_TX0 + 16 + Select PIO2's TX FIFO 0 as TREQ + + + PIO2_TX1 + 17 + Select PIO2's TX FIFO 1 as TREQ + + + PIO2_TX2 + 18 + Select PIO2's TX FIFO 2 as TREQ + + + PIO2_TX3 + 19 + Select PIO2's TX FIFO 3 as TREQ + + + PIO2_RX0 + 20 + Select PIO2's RX FIFO 0 as TREQ + + + PIO2_RX1 + 21 + Select PIO2's RX FIFO 1 as TREQ + + + PIO2_RX2 + 22 + Select PIO2's RX FIFO 2 as TREQ + + + PIO2_RX3 + 23 + Select PIO2's RX FIFO 3 as TREQ + + + SPI0_TX + 24 + Select SPI0's TX FIFO as TREQ + + + SPI0_RX + 25 + Select SPI0's RX FIFO as TREQ + + + SPI1_TX + 26 + Select SPI1's TX FIFO as TREQ + + + SPI1_RX + 27 + Select SPI1's RX FIFO as TREQ + + + UART0_TX + 28 + Select UART0's TX FIFO as TREQ + + + UART0_RX + 29 + Select UART0's RX FIFO as TREQ + + + UART1_TX + 30 + Select UART1's TX FIFO as TREQ + + + UART1_RX + 31 + Select UART1's RX FIFO as TREQ + + + PWM_WRAP0 + 32 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP1 + 33 + Select PWM Counter 1's Wrap Value as TREQ + + + PWM_WRAP2 + 34 + Select PWM Counter 2's Wrap Value as TREQ + + + PWM_WRAP3 + 35 + Select PWM Counter 3's Wrap Value as TREQ + + + PWM_WRAP4 + 36 + Select PWM Counter 4's Wrap Value as TREQ + + + PWM_WRAP5 + 37 + Select PWM Counter 5's Wrap Value as TREQ + + + PWM_WRAP6 + 38 + Select PWM Counter 6's Wrap Value as TREQ + + + PWM_WRAP7 + 39 + Select PWM Counter 7's Wrap Value as TREQ + + + PWM_WRAP8 + 40 + Select PWM Counter 8's Wrap Value as TREQ + + + PWM_WRAP9 + 41 + Select PWM Counter 9's Wrap Value as TREQ + + + PWM_WRAP10 + 42 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP11 + 43 + Select PWM Counter 1's Wrap Value as TREQ + + + I2C0_TX + 44 + Select I2C0's TX FIFO as TREQ + + + I2C0_RX + 45 + Select I2C0's RX FIFO as TREQ + + + I2C1_TX + 46 + Select I2C1's TX FIFO as TREQ + + + I2C1_RX + 47 + Select I2C1's RX FIFO as TREQ + + + ADC + 48 + Select the ADC as TREQ + + + XIP_STREAM + 49 + Select the XIP Streaming FIFO as TREQ + + + XIP_QMITX + 50 + Select XIP_QMITX as TREQ + + + XIP_QMIRX + 51 + Select XIP_QMIRX as TREQ + + + HSTX + 52 + Select HSTX as TREQ + + + CORESIGHT + 53 + Select CORESIGHT as TREQ + + + SHA256 + 54 + Select SHA256 as TREQ + + + TIMER0 + 59 + Select Timer 0 as TREQ + + + TIMER1 + 60 + Select Timer 1 as TREQ + + + TIMER2 + 61 + Select Timer 2 as TREQ (Optional) + + + TIMER3 + 62 + Select Timer 3 as TREQ (Optional) + + + PERMANENT + 63 + Permanent request, for unpaced transfers. + + + + + CHAIN_TO + When this channel completes, it will trigger the channel indicated by CHAIN_TO. Disable by setting CHAIN_TO = _(this channel)_. + + Note this field resets to 0, so channels 1 and above will chain to channel 0 by default. Set this field to avoid this behaviour. + [16:13] + read-write + + + RING_SEL + Select whether RING_SIZE applies to read or write addresses. + If 0, read addresses are wrapped on a (1 << RING_SIZE) boundary. If 1, write addresses are wrapped. + [12:12] + read-write + + + RING_SIZE + Size of address wrap region. If 0, don't wrap. For values n > 0, only the lower n bits of the address will change. This wraps the address on a (1 << n) byte boundary, facilitating access to naturally-aligned ring buffers. + + Ring sizes between 2 and 32768 bytes are possible. This can apply to either read or write addresses, based on value of RING_SEL. + [11:8] + read-write + + + RING_NONE + 0 + + + + + INCR_WRITE_REV + If 1, and INCR_WRITE is 1, the write address is decremented rather than incremented with each transfer. + + If 1, and INCR_WRITE is 0, this otherwise-unused combination causes the write address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [7:7] + read-write + + + INCR_WRITE + If 1, the write address increments with each transfer. If 0, each write is directed to the same, initial address. + + Generally this should be disabled for memory-to-peripheral transfers. + [6:6] + read-write + + + INCR_READ_REV + If 1, and INCR_READ is 1, the read address is decremented rather than incremented with each transfer. + + If 1, and INCR_READ is 0, this otherwise-unused combination causes the read address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [5:5] + read-write + + + INCR_READ + If 1, the read address increments with each transfer. If 0, each read is directed to the same, initial address. + + Generally this should be disabled for peripheral-to-memory transfers. + [4:4] + read-write + + + DATA_SIZE + Set the size of each bus transfer (byte/halfword/word). READ_ADDR and WRITE_ADDR advance by this amount (1/2/4 bytes) with each transfer. + [3:2] + read-write + + + SIZE_BYTE + 0 + + + SIZE_HALFWORD + 1 + + + SIZE_WORD + 2 + + + + + HIGH_PRIORITY + HIGH_PRIORITY gives a channel preferential treatment in issue scheduling: in each scheduling round, all high priority channels are considered first, and then only a single low priority channel, before returning to the high priority channels. + + This only affects the order in which the DMA schedules channels. The DMA's bus priority is not changed. If the DMA is not saturated then a low priority channel will see no loss of throughput. + [1:1] + read-write + + + EN + DMA Channel Enable. + When 1, the channel will respond to triggering events, which will cause it to become BUSY and start transferring data. When 0, the channel will ignore triggers, stop issuing transfers, and pause the current transfer sequence (i.e. BUSY will remain high if already high) + [0:0] + read-write + + + + + CH2_AL1_CTRL + 0x00000090 + Alias for channel 2 CTRL register + 0x00000000 + + + CH2_AL1_CTRL + [31:0] + read-write + + + + + CH2_AL1_READ_ADDR + 0x00000094 + Alias for channel 2 READ_ADDR register + 0x00000000 + + + CH2_AL1_READ_ADDR + [31:0] + read-write + + + + + CH2_AL1_WRITE_ADDR + 0x00000098 + Alias for channel 2 WRITE_ADDR register + 0x00000000 + + + CH2_AL1_WRITE_ADDR + [31:0] + read-write + + + + + CH2_AL1_TRANS_COUNT_TRIG + 0x0000009c + Alias for channel 2 TRANS_COUNT register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH2_AL1_TRANS_COUNT_TRIG + [31:0] + read-write + + + + + CH2_AL2_CTRL + 0x000000a0 + Alias for channel 2 CTRL register + 0x00000000 + + + CH2_AL2_CTRL + [31:0] + read-write + + + + + CH2_AL2_TRANS_COUNT + 0x000000a4 + Alias for channel 2 TRANS_COUNT register + 0x00000000 + + + CH2_AL2_TRANS_COUNT + [31:0] + read-write + + + + + CH2_AL2_READ_ADDR + 0x000000a8 + Alias for channel 2 READ_ADDR register + 0x00000000 + + + CH2_AL2_READ_ADDR + [31:0] + read-write + + + + + CH2_AL2_WRITE_ADDR_TRIG + 0x000000ac + Alias for channel 2 WRITE_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH2_AL2_WRITE_ADDR_TRIG + [31:0] + read-write + + + + + CH2_AL3_CTRL + 0x000000b0 + Alias for channel 2 CTRL register + 0x00000000 + + + CH2_AL3_CTRL + [31:0] + read-write + + + + + CH2_AL3_WRITE_ADDR + 0x000000b4 + Alias for channel 2 WRITE_ADDR register + 0x00000000 + + + CH2_AL3_WRITE_ADDR + [31:0] + read-write + + + + + CH2_AL3_TRANS_COUNT + 0x000000b8 + Alias for channel 2 TRANS_COUNT register + 0x00000000 + + + CH2_AL3_TRANS_COUNT + [31:0] + read-write + + + + + CH2_AL3_READ_ADDR_TRIG + 0x000000bc + Alias for channel 2 READ_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH2_AL3_READ_ADDR_TRIG + [31:0] + read-write + + + + + CH3_READ_ADDR + 0x000000c0 + DMA Channel 3 Read Address pointer + 0x00000000 + + + CH3_READ_ADDR + This register updates automatically each time a read completes. The current value is the next address to be read by this channel. + [31:0] + read-write + + + + + CH3_WRITE_ADDR + 0x000000c4 + DMA Channel 3 Write Address pointer + 0x00000000 + + + CH3_WRITE_ADDR + This register updates automatically each time a write completes. The current value is the next address to be written by this channel. + [31:0] + read-write + + + + + CH3_TRANS_COUNT + 0x000000c8 + DMA Channel 3 Transfer Count + 0x00000000 + + + MODE + When MODE is 0x0, the transfer count decrements with each transfer until 0, and then the channel triggers the next channel indicated by CTRL_CHAIN_TO. + + When MODE is 0x1, the transfer count decrements with each transfer until 0, and then the channel re-triggers itself, in addition to the trigger indicated by CTRL_CHAIN_TO. This is useful for e.g. an endless ring-buffer DMA with periodic interrupts. + + When MODE is 0xf, the transfer count does not decrement. The DMA channel performs an endless sequence of transfers, never triggering other channels or raising interrupts, until an ABORT is raised. + + All other values are reserved. + [31:28] + read-write + + + NORMAL + 0 + + + TRIGGER_SELF + 1 + + + ENDLESS + 15 + + + + + COUNT + 28-bit transfer count (256 million transfers maximum). + + Program the number of bus transfers a channel will perform before halting. Note that, if transfers are larger than one byte in size, this is not equal to the number of bytes transferred (see CTRL_DATA_SIZE). + + When the channel is active, reading this register shows the number of transfers remaining, updating automatically each time a write transfer completes. + + Writing this register sets the RELOAD value for the transfer counter. Each time this channel is triggered, the RELOAD value is copied into the live transfer counter. The channel can be started multiple times, and will perform the same number of transfers each time, as programmed by most recent write. + + The RELOAD value can be observed at CHx_DBG_TCR. If TRANS_COUNT is used as a trigger, the written value is used immediately as the length of the new transfer sequence, as well as being written to RELOAD. + [27:0] + read-write + + + + + CH3_CTRL_TRIG + 0x000000cc + DMA Channel 3 Control and Status + 0x00000000 + + + AHB_ERROR + Logical OR of the READ_ERROR and WRITE_ERROR flags. The channel halts when it encounters any bus error, and always raises its channel IRQ flag. + [31:31] + read-only + + + READ_ERROR + If 1, the channel received a read bus error. Write one to clear. + READ_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 3 transfers later) + [30:30] + read-write + oneToClear + + + WRITE_ERROR + If 1, the channel received a write bus error. Write one to clear. + WRITE_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 5 transfers later) + [29:29] + read-write + oneToClear + + + BUSY + This flag goes high when the channel starts a new transfer sequence, and low when the last transfer of that sequence completes. Clearing EN while BUSY is high pauses the channel, and BUSY will stay high while paused. + + To terminate a sequence early (and clear the BUSY flag), see CHAN_ABORT. + [26:26] + read-only + + + SNIFF_EN + If 1, this channel's data transfers are visible to the sniff hardware, and each transfer will advance the state of the checksum. This only applies if the sniff hardware is enabled, and has this channel selected. + + This allows checksum to be enabled or disabled on a per-control- block basis. + [25:25] + read-write + + + BSWAP + Apply byte-swap transformation to DMA data. + For byte data, this has no effect. For halfword data, the two bytes of each halfword are swapped. For word data, the four bytes of each word are swapped to reverse order. + [24:24] + read-write + + + IRQ_QUIET + In QUIET mode, the channel does not generate IRQs at the end of every transfer block. Instead, an IRQ is raised when NULL is written to a trigger register, indicating the end of a control block chain. + + This reduces the number of interrupts to be serviced by the CPU when transferring a DMA chain of many small control blocks. + [23:23] + read-write + + + TREQ_SEL + Select a Transfer Request signal. + The channel uses the transfer request signal to pace its data transfer rate. Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data Request from the system). + 0x0 to 0x3a -> select DREQ n as TREQ + [22:17] + read-write + + + PIO0_TX0 + 0 + Select PIO0's TX FIFO 0 as TREQ + + + PIO0_TX1 + 1 + Select PIO0's TX FIFO 1 as TREQ + + + PIO0_TX2 + 2 + Select PIO0's TX FIFO 2 as TREQ + + + PIO0_TX3 + 3 + Select PIO0's TX FIFO 3 as TREQ + + + PIO0_RX0 + 4 + Select PIO0's RX FIFO 0 as TREQ + + + PIO0_RX1 + 5 + Select PIO0's RX FIFO 1 as TREQ + + + PIO0_RX2 + 6 + Select PIO0's RX FIFO 2 as TREQ + + + PIO0_RX3 + 7 + Select PIO0's RX FIFO 3 as TREQ + + + PIO1_TX0 + 8 + Select PIO1's TX FIFO 0 as TREQ + + + PIO1_TX1 + 9 + Select PIO1's TX FIFO 1 as TREQ + + + PIO1_TX2 + 10 + Select PIO1's TX FIFO 2 as TREQ + + + PIO1_TX3 + 11 + Select PIO1's TX FIFO 3 as TREQ + + + PIO1_RX0 + 12 + Select PIO1's RX FIFO 0 as TREQ + + + PIO1_RX1 + 13 + Select PIO1's RX FIFO 1 as TREQ + + + PIO1_RX2 + 14 + Select PIO1's RX FIFO 2 as TREQ + + + PIO1_RX3 + 15 + Select PIO1's RX FIFO 3 as TREQ + + + PIO2_TX0 + 16 + Select PIO2's TX FIFO 0 as TREQ + + + PIO2_TX1 + 17 + Select PIO2's TX FIFO 1 as TREQ + + + PIO2_TX2 + 18 + Select PIO2's TX FIFO 2 as TREQ + + + PIO2_TX3 + 19 + Select PIO2's TX FIFO 3 as TREQ + + + PIO2_RX0 + 20 + Select PIO2's RX FIFO 0 as TREQ + + + PIO2_RX1 + 21 + Select PIO2's RX FIFO 1 as TREQ + + + PIO2_RX2 + 22 + Select PIO2's RX FIFO 2 as TREQ + + + PIO2_RX3 + 23 + Select PIO2's RX FIFO 3 as TREQ + + + SPI0_TX + 24 + Select SPI0's TX FIFO as TREQ + + + SPI0_RX + 25 + Select SPI0's RX FIFO as TREQ + + + SPI1_TX + 26 + Select SPI1's TX FIFO as TREQ + + + SPI1_RX + 27 + Select SPI1's RX FIFO as TREQ + + + UART0_TX + 28 + Select UART0's TX FIFO as TREQ + + + UART0_RX + 29 + Select UART0's RX FIFO as TREQ + + + UART1_TX + 30 + Select UART1's TX FIFO as TREQ + + + UART1_RX + 31 + Select UART1's RX FIFO as TREQ + + + PWM_WRAP0 + 32 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP1 + 33 + Select PWM Counter 1's Wrap Value as TREQ + + + PWM_WRAP2 + 34 + Select PWM Counter 2's Wrap Value as TREQ + + + PWM_WRAP3 + 35 + Select PWM Counter 3's Wrap Value as TREQ + + + PWM_WRAP4 + 36 + Select PWM Counter 4's Wrap Value as TREQ + + + PWM_WRAP5 + 37 + Select PWM Counter 5's Wrap Value as TREQ + + + PWM_WRAP6 + 38 + Select PWM Counter 6's Wrap Value as TREQ + + + PWM_WRAP7 + 39 + Select PWM Counter 7's Wrap Value as TREQ + + + PWM_WRAP8 + 40 + Select PWM Counter 8's Wrap Value as TREQ + + + PWM_WRAP9 + 41 + Select PWM Counter 9's Wrap Value as TREQ + + + PWM_WRAP10 + 42 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP11 + 43 + Select PWM Counter 1's Wrap Value as TREQ + + + I2C0_TX + 44 + Select I2C0's TX FIFO as TREQ + + + I2C0_RX + 45 + Select I2C0's RX FIFO as TREQ + + + I2C1_TX + 46 + Select I2C1's TX FIFO as TREQ + + + I2C1_RX + 47 + Select I2C1's RX FIFO as TREQ + + + ADC + 48 + Select the ADC as TREQ + + + XIP_STREAM + 49 + Select the XIP Streaming FIFO as TREQ + + + XIP_QMITX + 50 + Select XIP_QMITX as TREQ + + + XIP_QMIRX + 51 + Select XIP_QMIRX as TREQ + + + HSTX + 52 + Select HSTX as TREQ + + + CORESIGHT + 53 + Select CORESIGHT as TREQ + + + SHA256 + 54 + Select SHA256 as TREQ + + + TIMER0 + 59 + Select Timer 0 as TREQ + + + TIMER1 + 60 + Select Timer 1 as TREQ + + + TIMER2 + 61 + Select Timer 2 as TREQ (Optional) + + + TIMER3 + 62 + Select Timer 3 as TREQ (Optional) + + + PERMANENT + 63 + Permanent request, for unpaced transfers. + + + + + CHAIN_TO + When this channel completes, it will trigger the channel indicated by CHAIN_TO. Disable by setting CHAIN_TO = _(this channel)_. + + Note this field resets to 0, so channels 1 and above will chain to channel 0 by default. Set this field to avoid this behaviour. + [16:13] + read-write + + + RING_SEL + Select whether RING_SIZE applies to read or write addresses. + If 0, read addresses are wrapped on a (1 << RING_SIZE) boundary. If 1, write addresses are wrapped. + [12:12] + read-write + + + RING_SIZE + Size of address wrap region. If 0, don't wrap. For values n > 0, only the lower n bits of the address will change. This wraps the address on a (1 << n) byte boundary, facilitating access to naturally-aligned ring buffers. + + Ring sizes between 2 and 32768 bytes are possible. This can apply to either read or write addresses, based on value of RING_SEL. + [11:8] + read-write + + + RING_NONE + 0 + + + + + INCR_WRITE_REV + If 1, and INCR_WRITE is 1, the write address is decremented rather than incremented with each transfer. + + If 1, and INCR_WRITE is 0, this otherwise-unused combination causes the write address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [7:7] + read-write + + + INCR_WRITE + If 1, the write address increments with each transfer. If 0, each write is directed to the same, initial address. + + Generally this should be disabled for memory-to-peripheral transfers. + [6:6] + read-write + + + INCR_READ_REV + If 1, and INCR_READ is 1, the read address is decremented rather than incremented with each transfer. + + If 1, and INCR_READ is 0, this otherwise-unused combination causes the read address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [5:5] + read-write + + + INCR_READ + If 1, the read address increments with each transfer. If 0, each read is directed to the same, initial address. + + Generally this should be disabled for peripheral-to-memory transfers. + [4:4] + read-write + + + DATA_SIZE + Set the size of each bus transfer (byte/halfword/word). READ_ADDR and WRITE_ADDR advance by this amount (1/2/4 bytes) with each transfer. + [3:2] + read-write + + + SIZE_BYTE + 0 + + + SIZE_HALFWORD + 1 + + + SIZE_WORD + 2 + + + + + HIGH_PRIORITY + HIGH_PRIORITY gives a channel preferential treatment in issue scheduling: in each scheduling round, all high priority channels are considered first, and then only a single low priority channel, before returning to the high priority channels. + + This only affects the order in which the DMA schedules channels. The DMA's bus priority is not changed. If the DMA is not saturated then a low priority channel will see no loss of throughput. + [1:1] + read-write + + + EN + DMA Channel Enable. + When 1, the channel will respond to triggering events, which will cause it to become BUSY and start transferring data. When 0, the channel will ignore triggers, stop issuing transfers, and pause the current transfer sequence (i.e. BUSY will remain high if already high) + [0:0] + read-write + + + + + CH3_AL1_CTRL + 0x000000d0 + Alias for channel 3 CTRL register + 0x00000000 + + + CH3_AL1_CTRL + [31:0] + read-write + + + + + CH3_AL1_READ_ADDR + 0x000000d4 + Alias for channel 3 READ_ADDR register + 0x00000000 + + + CH3_AL1_READ_ADDR + [31:0] + read-write + + + + + CH3_AL1_WRITE_ADDR + 0x000000d8 + Alias for channel 3 WRITE_ADDR register + 0x00000000 + + + CH3_AL1_WRITE_ADDR + [31:0] + read-write + + + + + CH3_AL1_TRANS_COUNT_TRIG + 0x000000dc + Alias for channel 3 TRANS_COUNT register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH3_AL1_TRANS_COUNT_TRIG + [31:0] + read-write + + + + + CH3_AL2_CTRL + 0x000000e0 + Alias for channel 3 CTRL register + 0x00000000 + + + CH3_AL2_CTRL + [31:0] + read-write + + + + + CH3_AL2_TRANS_COUNT + 0x000000e4 + Alias for channel 3 TRANS_COUNT register + 0x00000000 + + + CH3_AL2_TRANS_COUNT + [31:0] + read-write + + + + + CH3_AL2_READ_ADDR + 0x000000e8 + Alias for channel 3 READ_ADDR register + 0x00000000 + + + CH3_AL2_READ_ADDR + [31:0] + read-write + + + + + CH3_AL2_WRITE_ADDR_TRIG + 0x000000ec + Alias for channel 3 WRITE_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH3_AL2_WRITE_ADDR_TRIG + [31:0] + read-write + + + + + CH3_AL3_CTRL + 0x000000f0 + Alias for channel 3 CTRL register + 0x00000000 + + + CH3_AL3_CTRL + [31:0] + read-write + + + + + CH3_AL3_WRITE_ADDR + 0x000000f4 + Alias for channel 3 WRITE_ADDR register + 0x00000000 + + + CH3_AL3_WRITE_ADDR + [31:0] + read-write + + + + + CH3_AL3_TRANS_COUNT + 0x000000f8 + Alias for channel 3 TRANS_COUNT register + 0x00000000 + + + CH3_AL3_TRANS_COUNT + [31:0] + read-write + + + + + CH3_AL3_READ_ADDR_TRIG + 0x000000fc + Alias for channel 3 READ_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH3_AL3_READ_ADDR_TRIG + [31:0] + read-write + + + + + CH4_READ_ADDR + 0x00000100 + DMA Channel 4 Read Address pointer + 0x00000000 + + + CH4_READ_ADDR + This register updates automatically each time a read completes. The current value is the next address to be read by this channel. + [31:0] + read-write + + + + + CH4_WRITE_ADDR + 0x00000104 + DMA Channel 4 Write Address pointer + 0x00000000 + + + CH4_WRITE_ADDR + This register updates automatically each time a write completes. The current value is the next address to be written by this channel. + [31:0] + read-write + + + + + CH4_TRANS_COUNT + 0x00000108 + DMA Channel 4 Transfer Count + 0x00000000 + + + MODE + When MODE is 0x0, the transfer count decrements with each transfer until 0, and then the channel triggers the next channel indicated by CTRL_CHAIN_TO. + + When MODE is 0x1, the transfer count decrements with each transfer until 0, and then the channel re-triggers itself, in addition to the trigger indicated by CTRL_CHAIN_TO. This is useful for e.g. an endless ring-buffer DMA with periodic interrupts. + + When MODE is 0xf, the transfer count does not decrement. The DMA channel performs an endless sequence of transfers, never triggering other channels or raising interrupts, until an ABORT is raised. + + All other values are reserved. + [31:28] + read-write + + + NORMAL + 0 + + + TRIGGER_SELF + 1 + + + ENDLESS + 15 + + + + + COUNT + 28-bit transfer count (256 million transfers maximum). + + Program the number of bus transfers a channel will perform before halting. Note that, if transfers are larger than one byte in size, this is not equal to the number of bytes transferred (see CTRL_DATA_SIZE). + + When the channel is active, reading this register shows the number of transfers remaining, updating automatically each time a write transfer completes. + + Writing this register sets the RELOAD value for the transfer counter. Each time this channel is triggered, the RELOAD value is copied into the live transfer counter. The channel can be started multiple times, and will perform the same number of transfers each time, as programmed by most recent write. + + The RELOAD value can be observed at CHx_DBG_TCR. If TRANS_COUNT is used as a trigger, the written value is used immediately as the length of the new transfer sequence, as well as being written to RELOAD. + [27:0] + read-write + + + + + CH4_CTRL_TRIG + 0x0000010c + DMA Channel 4 Control and Status + 0x00000000 + + + AHB_ERROR + Logical OR of the READ_ERROR and WRITE_ERROR flags. The channel halts when it encounters any bus error, and always raises its channel IRQ flag. + [31:31] + read-only + + + READ_ERROR + If 1, the channel received a read bus error. Write one to clear. + READ_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 3 transfers later) + [30:30] + read-write + oneToClear + + + WRITE_ERROR + If 1, the channel received a write bus error. Write one to clear. + WRITE_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 5 transfers later) + [29:29] + read-write + oneToClear + + + BUSY + This flag goes high when the channel starts a new transfer sequence, and low when the last transfer of that sequence completes. Clearing EN while BUSY is high pauses the channel, and BUSY will stay high while paused. + + To terminate a sequence early (and clear the BUSY flag), see CHAN_ABORT. + [26:26] + read-only + + + SNIFF_EN + If 1, this channel's data transfers are visible to the sniff hardware, and each transfer will advance the state of the checksum. This only applies if the sniff hardware is enabled, and has this channel selected. + + This allows checksum to be enabled or disabled on a per-control- block basis. + [25:25] + read-write + + + BSWAP + Apply byte-swap transformation to DMA data. + For byte data, this has no effect. For halfword data, the two bytes of each halfword are swapped. For word data, the four bytes of each word are swapped to reverse order. + [24:24] + read-write + + + IRQ_QUIET + In QUIET mode, the channel does not generate IRQs at the end of every transfer block. Instead, an IRQ is raised when NULL is written to a trigger register, indicating the end of a control block chain. + + This reduces the number of interrupts to be serviced by the CPU when transferring a DMA chain of many small control blocks. + [23:23] + read-write + + + TREQ_SEL + Select a Transfer Request signal. + The channel uses the transfer request signal to pace its data transfer rate. Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data Request from the system). + 0x0 to 0x3a -> select DREQ n as TREQ + [22:17] + read-write + + + PIO0_TX0 + 0 + Select PIO0's TX FIFO 0 as TREQ + + + PIO0_TX1 + 1 + Select PIO0's TX FIFO 1 as TREQ + + + PIO0_TX2 + 2 + Select PIO0's TX FIFO 2 as TREQ + + + PIO0_TX3 + 3 + Select PIO0's TX FIFO 3 as TREQ + + + PIO0_RX0 + 4 + Select PIO0's RX FIFO 0 as TREQ + + + PIO0_RX1 + 5 + Select PIO0's RX FIFO 1 as TREQ + + + PIO0_RX2 + 6 + Select PIO0's RX FIFO 2 as TREQ + + + PIO0_RX3 + 7 + Select PIO0's RX FIFO 3 as TREQ + + + PIO1_TX0 + 8 + Select PIO1's TX FIFO 0 as TREQ + + + PIO1_TX1 + 9 + Select PIO1's TX FIFO 1 as TREQ + + + PIO1_TX2 + 10 + Select PIO1's TX FIFO 2 as TREQ + + + PIO1_TX3 + 11 + Select PIO1's TX FIFO 3 as TREQ + + + PIO1_RX0 + 12 + Select PIO1's RX FIFO 0 as TREQ + + + PIO1_RX1 + 13 + Select PIO1's RX FIFO 1 as TREQ + + + PIO1_RX2 + 14 + Select PIO1's RX FIFO 2 as TREQ + + + PIO1_RX3 + 15 + Select PIO1's RX FIFO 3 as TREQ + + + PIO2_TX0 + 16 + Select PIO2's TX FIFO 0 as TREQ + + + PIO2_TX1 + 17 + Select PIO2's TX FIFO 1 as TREQ + + + PIO2_TX2 + 18 + Select PIO2's TX FIFO 2 as TREQ + + + PIO2_TX3 + 19 + Select PIO2's TX FIFO 3 as TREQ + + + PIO2_RX0 + 20 + Select PIO2's RX FIFO 0 as TREQ + + + PIO2_RX1 + 21 + Select PIO2's RX FIFO 1 as TREQ + + + PIO2_RX2 + 22 + Select PIO2's RX FIFO 2 as TREQ + + + PIO2_RX3 + 23 + Select PIO2's RX FIFO 3 as TREQ + + + SPI0_TX + 24 + Select SPI0's TX FIFO as TREQ + + + SPI0_RX + 25 + Select SPI0's RX FIFO as TREQ + + + SPI1_TX + 26 + Select SPI1's TX FIFO as TREQ + + + SPI1_RX + 27 + Select SPI1's RX FIFO as TREQ + + + UART0_TX + 28 + Select UART0's TX FIFO as TREQ + + + UART0_RX + 29 + Select UART0's RX FIFO as TREQ + + + UART1_TX + 30 + Select UART1's TX FIFO as TREQ + + + UART1_RX + 31 + Select UART1's RX FIFO as TREQ + + + PWM_WRAP0 + 32 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP1 + 33 + Select PWM Counter 1's Wrap Value as TREQ + + + PWM_WRAP2 + 34 + Select PWM Counter 2's Wrap Value as TREQ + + + PWM_WRAP3 + 35 + Select PWM Counter 3's Wrap Value as TREQ + + + PWM_WRAP4 + 36 + Select PWM Counter 4's Wrap Value as TREQ + + + PWM_WRAP5 + 37 + Select PWM Counter 5's Wrap Value as TREQ + + + PWM_WRAP6 + 38 + Select PWM Counter 6's Wrap Value as TREQ + + + PWM_WRAP7 + 39 + Select PWM Counter 7's Wrap Value as TREQ + + + PWM_WRAP8 + 40 + Select PWM Counter 8's Wrap Value as TREQ + + + PWM_WRAP9 + 41 + Select PWM Counter 9's Wrap Value as TREQ + + + PWM_WRAP10 + 42 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP11 + 43 + Select PWM Counter 1's Wrap Value as TREQ + + + I2C0_TX + 44 + Select I2C0's TX FIFO as TREQ + + + I2C0_RX + 45 + Select I2C0's RX FIFO as TREQ + + + I2C1_TX + 46 + Select I2C1's TX FIFO as TREQ + + + I2C1_RX + 47 + Select I2C1's RX FIFO as TREQ + + + ADC + 48 + Select the ADC as TREQ + + + XIP_STREAM + 49 + Select the XIP Streaming FIFO as TREQ + + + XIP_QMITX + 50 + Select XIP_QMITX as TREQ + + + XIP_QMIRX + 51 + Select XIP_QMIRX as TREQ + + + HSTX + 52 + Select HSTX as TREQ + + + CORESIGHT + 53 + Select CORESIGHT as TREQ + + + SHA256 + 54 + Select SHA256 as TREQ + + + TIMER0 + 59 + Select Timer 0 as TREQ + + + TIMER1 + 60 + Select Timer 1 as TREQ + + + TIMER2 + 61 + Select Timer 2 as TREQ (Optional) + + + TIMER3 + 62 + Select Timer 3 as TREQ (Optional) + + + PERMANENT + 63 + Permanent request, for unpaced transfers. + + + + + CHAIN_TO + When this channel completes, it will trigger the channel indicated by CHAIN_TO. Disable by setting CHAIN_TO = _(this channel)_. + + Note this field resets to 0, so channels 1 and above will chain to channel 0 by default. Set this field to avoid this behaviour. + [16:13] + read-write + + + RING_SEL + Select whether RING_SIZE applies to read or write addresses. + If 0, read addresses are wrapped on a (1 << RING_SIZE) boundary. If 1, write addresses are wrapped. + [12:12] + read-write + + + RING_SIZE + Size of address wrap region. If 0, don't wrap. For values n > 0, only the lower n bits of the address will change. This wraps the address on a (1 << n) byte boundary, facilitating access to naturally-aligned ring buffers. + + Ring sizes between 2 and 32768 bytes are possible. This can apply to either read or write addresses, based on value of RING_SEL. + [11:8] + read-write + + + RING_NONE + 0 + + + + + INCR_WRITE_REV + If 1, and INCR_WRITE is 1, the write address is decremented rather than incremented with each transfer. + + If 1, and INCR_WRITE is 0, this otherwise-unused combination causes the write address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [7:7] + read-write + + + INCR_WRITE + If 1, the write address increments with each transfer. If 0, each write is directed to the same, initial address. + + Generally this should be disabled for memory-to-peripheral transfers. + [6:6] + read-write + + + INCR_READ_REV + If 1, and INCR_READ is 1, the read address is decremented rather than incremented with each transfer. + + If 1, and INCR_READ is 0, this otherwise-unused combination causes the read address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [5:5] + read-write + + + INCR_READ + If 1, the read address increments with each transfer. If 0, each read is directed to the same, initial address. + + Generally this should be disabled for peripheral-to-memory transfers. + [4:4] + read-write + + + DATA_SIZE + Set the size of each bus transfer (byte/halfword/word). READ_ADDR and WRITE_ADDR advance by this amount (1/2/4 bytes) with each transfer. + [3:2] + read-write + + + SIZE_BYTE + 0 + + + SIZE_HALFWORD + 1 + + + SIZE_WORD + 2 + + + + + HIGH_PRIORITY + HIGH_PRIORITY gives a channel preferential treatment in issue scheduling: in each scheduling round, all high priority channels are considered first, and then only a single low priority channel, before returning to the high priority channels. + + This only affects the order in which the DMA schedules channels. The DMA's bus priority is not changed. If the DMA is not saturated then a low priority channel will see no loss of throughput. + [1:1] + read-write + + + EN + DMA Channel Enable. + When 1, the channel will respond to triggering events, which will cause it to become BUSY and start transferring data. When 0, the channel will ignore triggers, stop issuing transfers, and pause the current transfer sequence (i.e. BUSY will remain high if already high) + [0:0] + read-write + + + + + CH4_AL1_CTRL + 0x00000110 + Alias for channel 4 CTRL register + 0x00000000 + + + CH4_AL1_CTRL + [31:0] + read-write + + + + + CH4_AL1_READ_ADDR + 0x00000114 + Alias for channel 4 READ_ADDR register + 0x00000000 + + + CH4_AL1_READ_ADDR + [31:0] + read-write + + + + + CH4_AL1_WRITE_ADDR + 0x00000118 + Alias for channel 4 WRITE_ADDR register + 0x00000000 + + + CH4_AL1_WRITE_ADDR + [31:0] + read-write + + + + + CH4_AL1_TRANS_COUNT_TRIG + 0x0000011c + Alias for channel 4 TRANS_COUNT register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH4_AL1_TRANS_COUNT_TRIG + [31:0] + read-write + + + + + CH4_AL2_CTRL + 0x00000120 + Alias for channel 4 CTRL register + 0x00000000 + + + CH4_AL2_CTRL + [31:0] + read-write + + + + + CH4_AL2_TRANS_COUNT + 0x00000124 + Alias for channel 4 TRANS_COUNT register + 0x00000000 + + + CH4_AL2_TRANS_COUNT + [31:0] + read-write + + + + + CH4_AL2_READ_ADDR + 0x00000128 + Alias for channel 4 READ_ADDR register + 0x00000000 + + + CH4_AL2_READ_ADDR + [31:0] + read-write + + + + + CH4_AL2_WRITE_ADDR_TRIG + 0x0000012c + Alias for channel 4 WRITE_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH4_AL2_WRITE_ADDR_TRIG + [31:0] + read-write + + + + + CH4_AL3_CTRL + 0x00000130 + Alias for channel 4 CTRL register + 0x00000000 + + + CH4_AL3_CTRL + [31:0] + read-write + + + + + CH4_AL3_WRITE_ADDR + 0x00000134 + Alias for channel 4 WRITE_ADDR register + 0x00000000 + + + CH4_AL3_WRITE_ADDR + [31:0] + read-write + + + + + CH4_AL3_TRANS_COUNT + 0x00000138 + Alias for channel 4 TRANS_COUNT register + 0x00000000 + + + CH4_AL3_TRANS_COUNT + [31:0] + read-write + + + + + CH4_AL3_READ_ADDR_TRIG + 0x0000013c + Alias for channel 4 READ_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH4_AL3_READ_ADDR_TRIG + [31:0] + read-write + + + + + CH5_READ_ADDR + 0x00000140 + DMA Channel 5 Read Address pointer + 0x00000000 + + + CH5_READ_ADDR + This register updates automatically each time a read completes. The current value is the next address to be read by this channel. + [31:0] + read-write + + + + + CH5_WRITE_ADDR + 0x00000144 + DMA Channel 5 Write Address pointer + 0x00000000 + + + CH5_WRITE_ADDR + This register updates automatically each time a write completes. The current value is the next address to be written by this channel. + [31:0] + read-write + + + + + CH5_TRANS_COUNT + 0x00000148 + DMA Channel 5 Transfer Count + 0x00000000 + + + MODE + When MODE is 0x0, the transfer count decrements with each transfer until 0, and then the channel triggers the next channel indicated by CTRL_CHAIN_TO. + + When MODE is 0x1, the transfer count decrements with each transfer until 0, and then the channel re-triggers itself, in addition to the trigger indicated by CTRL_CHAIN_TO. This is useful for e.g. an endless ring-buffer DMA with periodic interrupts. + + When MODE is 0xf, the transfer count does not decrement. The DMA channel performs an endless sequence of transfers, never triggering other channels or raising interrupts, until an ABORT is raised. + + All other values are reserved. + [31:28] + read-write + + + NORMAL + 0 + + + TRIGGER_SELF + 1 + + + ENDLESS + 15 + + + + + COUNT + 28-bit transfer count (256 million transfers maximum). + + Program the number of bus transfers a channel will perform before halting. Note that, if transfers are larger than one byte in size, this is not equal to the number of bytes transferred (see CTRL_DATA_SIZE). + + When the channel is active, reading this register shows the number of transfers remaining, updating automatically each time a write transfer completes. + + Writing this register sets the RELOAD value for the transfer counter. Each time this channel is triggered, the RELOAD value is copied into the live transfer counter. The channel can be started multiple times, and will perform the same number of transfers each time, as programmed by most recent write. + + The RELOAD value can be observed at CHx_DBG_TCR. If TRANS_COUNT is used as a trigger, the written value is used immediately as the length of the new transfer sequence, as well as being written to RELOAD. + [27:0] + read-write + + + + + CH5_CTRL_TRIG + 0x0000014c + DMA Channel 5 Control and Status + 0x00000000 + + + AHB_ERROR + Logical OR of the READ_ERROR and WRITE_ERROR flags. The channel halts when it encounters any bus error, and always raises its channel IRQ flag. + [31:31] + read-only + + + READ_ERROR + If 1, the channel received a read bus error. Write one to clear. + READ_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 3 transfers later) + [30:30] + read-write + oneToClear + + + WRITE_ERROR + If 1, the channel received a write bus error. Write one to clear. + WRITE_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 5 transfers later) + [29:29] + read-write + oneToClear + + + BUSY + This flag goes high when the channel starts a new transfer sequence, and low when the last transfer of that sequence completes. Clearing EN while BUSY is high pauses the channel, and BUSY will stay high while paused. + + To terminate a sequence early (and clear the BUSY flag), see CHAN_ABORT. + [26:26] + read-only + + + SNIFF_EN + If 1, this channel's data transfers are visible to the sniff hardware, and each transfer will advance the state of the checksum. This only applies if the sniff hardware is enabled, and has this channel selected. + + This allows checksum to be enabled or disabled on a per-control- block basis. + [25:25] + read-write + + + BSWAP + Apply byte-swap transformation to DMA data. + For byte data, this has no effect. For halfword data, the two bytes of each halfword are swapped. For word data, the four bytes of each word are swapped to reverse order. + [24:24] + read-write + + + IRQ_QUIET + In QUIET mode, the channel does not generate IRQs at the end of every transfer block. Instead, an IRQ is raised when NULL is written to a trigger register, indicating the end of a control block chain. + + This reduces the number of interrupts to be serviced by the CPU when transferring a DMA chain of many small control blocks. + [23:23] + read-write + + + TREQ_SEL + Select a Transfer Request signal. + The channel uses the transfer request signal to pace its data transfer rate. Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data Request from the system). + 0x0 to 0x3a -> select DREQ n as TREQ + [22:17] + read-write + + + PIO0_TX0 + 0 + Select PIO0's TX FIFO 0 as TREQ + + + PIO0_TX1 + 1 + Select PIO0's TX FIFO 1 as TREQ + + + PIO0_TX2 + 2 + Select PIO0's TX FIFO 2 as TREQ + + + PIO0_TX3 + 3 + Select PIO0's TX FIFO 3 as TREQ + + + PIO0_RX0 + 4 + Select PIO0's RX FIFO 0 as TREQ + + + PIO0_RX1 + 5 + Select PIO0's RX FIFO 1 as TREQ + + + PIO0_RX2 + 6 + Select PIO0's RX FIFO 2 as TREQ + + + PIO0_RX3 + 7 + Select PIO0's RX FIFO 3 as TREQ + + + PIO1_TX0 + 8 + Select PIO1's TX FIFO 0 as TREQ + + + PIO1_TX1 + 9 + Select PIO1's TX FIFO 1 as TREQ + + + PIO1_TX2 + 10 + Select PIO1's TX FIFO 2 as TREQ + + + PIO1_TX3 + 11 + Select PIO1's TX FIFO 3 as TREQ + + + PIO1_RX0 + 12 + Select PIO1's RX FIFO 0 as TREQ + + + PIO1_RX1 + 13 + Select PIO1's RX FIFO 1 as TREQ + + + PIO1_RX2 + 14 + Select PIO1's RX FIFO 2 as TREQ + + + PIO1_RX3 + 15 + Select PIO1's RX FIFO 3 as TREQ + + + PIO2_TX0 + 16 + Select PIO2's TX FIFO 0 as TREQ + + + PIO2_TX1 + 17 + Select PIO2's TX FIFO 1 as TREQ + + + PIO2_TX2 + 18 + Select PIO2's TX FIFO 2 as TREQ + + + PIO2_TX3 + 19 + Select PIO2's TX FIFO 3 as TREQ + + + PIO2_RX0 + 20 + Select PIO2's RX FIFO 0 as TREQ + + + PIO2_RX1 + 21 + Select PIO2's RX FIFO 1 as TREQ + + + PIO2_RX2 + 22 + Select PIO2's RX FIFO 2 as TREQ + + + PIO2_RX3 + 23 + Select PIO2's RX FIFO 3 as TREQ + + + SPI0_TX + 24 + Select SPI0's TX FIFO as TREQ + + + SPI0_RX + 25 + Select SPI0's RX FIFO as TREQ + + + SPI1_TX + 26 + Select SPI1's TX FIFO as TREQ + + + SPI1_RX + 27 + Select SPI1's RX FIFO as TREQ + + + UART0_TX + 28 + Select UART0's TX FIFO as TREQ + + + UART0_RX + 29 + Select UART0's RX FIFO as TREQ + + + UART1_TX + 30 + Select UART1's TX FIFO as TREQ + + + UART1_RX + 31 + Select UART1's RX FIFO as TREQ + + + PWM_WRAP0 + 32 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP1 + 33 + Select PWM Counter 1's Wrap Value as TREQ + + + PWM_WRAP2 + 34 + Select PWM Counter 2's Wrap Value as TREQ + + + PWM_WRAP3 + 35 + Select PWM Counter 3's Wrap Value as TREQ + + + PWM_WRAP4 + 36 + Select PWM Counter 4's Wrap Value as TREQ + + + PWM_WRAP5 + 37 + Select PWM Counter 5's Wrap Value as TREQ + + + PWM_WRAP6 + 38 + Select PWM Counter 6's Wrap Value as TREQ + + + PWM_WRAP7 + 39 + Select PWM Counter 7's Wrap Value as TREQ + + + PWM_WRAP8 + 40 + Select PWM Counter 8's Wrap Value as TREQ + + + PWM_WRAP9 + 41 + Select PWM Counter 9's Wrap Value as TREQ + + + PWM_WRAP10 + 42 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP11 + 43 + Select PWM Counter 1's Wrap Value as TREQ + + + I2C0_TX + 44 + Select I2C0's TX FIFO as TREQ + + + I2C0_RX + 45 + Select I2C0's RX FIFO as TREQ + + + I2C1_TX + 46 + Select I2C1's TX FIFO as TREQ + + + I2C1_RX + 47 + Select I2C1's RX FIFO as TREQ + + + ADC + 48 + Select the ADC as TREQ + + + XIP_STREAM + 49 + Select the XIP Streaming FIFO as TREQ + + + XIP_QMITX + 50 + Select XIP_QMITX as TREQ + + + XIP_QMIRX + 51 + Select XIP_QMIRX as TREQ + + + HSTX + 52 + Select HSTX as TREQ + + + CORESIGHT + 53 + Select CORESIGHT as TREQ + + + SHA256 + 54 + Select SHA256 as TREQ + + + TIMER0 + 59 + Select Timer 0 as TREQ + + + TIMER1 + 60 + Select Timer 1 as TREQ + + + TIMER2 + 61 + Select Timer 2 as TREQ (Optional) + + + TIMER3 + 62 + Select Timer 3 as TREQ (Optional) + + + PERMANENT + 63 + Permanent request, for unpaced transfers. + + + + + CHAIN_TO + When this channel completes, it will trigger the channel indicated by CHAIN_TO. Disable by setting CHAIN_TO = _(this channel)_. + + Note this field resets to 0, so channels 1 and above will chain to channel 0 by default. Set this field to avoid this behaviour. + [16:13] + read-write + + + RING_SEL + Select whether RING_SIZE applies to read or write addresses. + If 0, read addresses are wrapped on a (1 << RING_SIZE) boundary. If 1, write addresses are wrapped. + [12:12] + read-write + + + RING_SIZE + Size of address wrap region. If 0, don't wrap. For values n > 0, only the lower n bits of the address will change. This wraps the address on a (1 << n) byte boundary, facilitating access to naturally-aligned ring buffers. + + Ring sizes between 2 and 32768 bytes are possible. This can apply to either read or write addresses, based on value of RING_SEL. + [11:8] + read-write + + + RING_NONE + 0 + + + + + INCR_WRITE_REV + If 1, and INCR_WRITE is 1, the write address is decremented rather than incremented with each transfer. + + If 1, and INCR_WRITE is 0, this otherwise-unused combination causes the write address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [7:7] + read-write + + + INCR_WRITE + If 1, the write address increments with each transfer. If 0, each write is directed to the same, initial address. + + Generally this should be disabled for memory-to-peripheral transfers. + [6:6] + read-write + + + INCR_READ_REV + If 1, and INCR_READ is 1, the read address is decremented rather than incremented with each transfer. + + If 1, and INCR_READ is 0, this otherwise-unused combination causes the read address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [5:5] + read-write + + + INCR_READ + If 1, the read address increments with each transfer. If 0, each read is directed to the same, initial address. + + Generally this should be disabled for peripheral-to-memory transfers. + [4:4] + read-write + + + DATA_SIZE + Set the size of each bus transfer (byte/halfword/word). READ_ADDR and WRITE_ADDR advance by this amount (1/2/4 bytes) with each transfer. + [3:2] + read-write + + + SIZE_BYTE + 0 + + + SIZE_HALFWORD + 1 + + + SIZE_WORD + 2 + + + + + HIGH_PRIORITY + HIGH_PRIORITY gives a channel preferential treatment in issue scheduling: in each scheduling round, all high priority channels are considered first, and then only a single low priority channel, before returning to the high priority channels. + + This only affects the order in which the DMA schedules channels. The DMA's bus priority is not changed. If the DMA is not saturated then a low priority channel will see no loss of throughput. + [1:1] + read-write + + + EN + DMA Channel Enable. + When 1, the channel will respond to triggering events, which will cause it to become BUSY and start transferring data. When 0, the channel will ignore triggers, stop issuing transfers, and pause the current transfer sequence (i.e. BUSY will remain high if already high) + [0:0] + read-write + + + + + CH5_AL1_CTRL + 0x00000150 + Alias for channel 5 CTRL register + 0x00000000 + + + CH5_AL1_CTRL + [31:0] + read-write + + + + + CH5_AL1_READ_ADDR + 0x00000154 + Alias for channel 5 READ_ADDR register + 0x00000000 + + + CH5_AL1_READ_ADDR + [31:0] + read-write + + + + + CH5_AL1_WRITE_ADDR + 0x00000158 + Alias for channel 5 WRITE_ADDR register + 0x00000000 + + + CH5_AL1_WRITE_ADDR + [31:0] + read-write + + + + + CH5_AL1_TRANS_COUNT_TRIG + 0x0000015c + Alias for channel 5 TRANS_COUNT register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH5_AL1_TRANS_COUNT_TRIG + [31:0] + read-write + + + + + CH5_AL2_CTRL + 0x00000160 + Alias for channel 5 CTRL register + 0x00000000 + + + CH5_AL2_CTRL + [31:0] + read-write + + + + + CH5_AL2_TRANS_COUNT + 0x00000164 + Alias for channel 5 TRANS_COUNT register + 0x00000000 + + + CH5_AL2_TRANS_COUNT + [31:0] + read-write + + + + + CH5_AL2_READ_ADDR + 0x00000168 + Alias for channel 5 READ_ADDR register + 0x00000000 + + + CH5_AL2_READ_ADDR + [31:0] + read-write + + + + + CH5_AL2_WRITE_ADDR_TRIG + 0x0000016c + Alias for channel 5 WRITE_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH5_AL2_WRITE_ADDR_TRIG + [31:0] + read-write + + + + + CH5_AL3_CTRL + 0x00000170 + Alias for channel 5 CTRL register + 0x00000000 + + + CH5_AL3_CTRL + [31:0] + read-write + + + + + CH5_AL3_WRITE_ADDR + 0x00000174 + Alias for channel 5 WRITE_ADDR register + 0x00000000 + + + CH5_AL3_WRITE_ADDR + [31:0] + read-write + + + + + CH5_AL3_TRANS_COUNT + 0x00000178 + Alias for channel 5 TRANS_COUNT register + 0x00000000 + + + CH5_AL3_TRANS_COUNT + [31:0] + read-write + + + + + CH5_AL3_READ_ADDR_TRIG + 0x0000017c + Alias for channel 5 READ_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH5_AL3_READ_ADDR_TRIG + [31:0] + read-write + + + + + CH6_READ_ADDR + 0x00000180 + DMA Channel 6 Read Address pointer + 0x00000000 + + + CH6_READ_ADDR + This register updates automatically each time a read completes. The current value is the next address to be read by this channel. + [31:0] + read-write + + + + + CH6_WRITE_ADDR + 0x00000184 + DMA Channel 6 Write Address pointer + 0x00000000 + + + CH6_WRITE_ADDR + This register updates automatically each time a write completes. The current value is the next address to be written by this channel. + [31:0] + read-write + + + + + CH6_TRANS_COUNT + 0x00000188 + DMA Channel 6 Transfer Count + 0x00000000 + + + MODE + When MODE is 0x0, the transfer count decrements with each transfer until 0, and then the channel triggers the next channel indicated by CTRL_CHAIN_TO. + + When MODE is 0x1, the transfer count decrements with each transfer until 0, and then the channel re-triggers itself, in addition to the trigger indicated by CTRL_CHAIN_TO. This is useful for e.g. an endless ring-buffer DMA with periodic interrupts. + + When MODE is 0xf, the transfer count does not decrement. The DMA channel performs an endless sequence of transfers, never triggering other channels or raising interrupts, until an ABORT is raised. + + All other values are reserved. + [31:28] + read-write + + + NORMAL + 0 + + + TRIGGER_SELF + 1 + + + ENDLESS + 15 + + + + + COUNT + 28-bit transfer count (256 million transfers maximum). + + Program the number of bus transfers a channel will perform before halting. Note that, if transfers are larger than one byte in size, this is not equal to the number of bytes transferred (see CTRL_DATA_SIZE). + + When the channel is active, reading this register shows the number of transfers remaining, updating automatically each time a write transfer completes. + + Writing this register sets the RELOAD value for the transfer counter. Each time this channel is triggered, the RELOAD value is copied into the live transfer counter. The channel can be started multiple times, and will perform the same number of transfers each time, as programmed by most recent write. + + The RELOAD value can be observed at CHx_DBG_TCR. If TRANS_COUNT is used as a trigger, the written value is used immediately as the length of the new transfer sequence, as well as being written to RELOAD. + [27:0] + read-write + + + + + CH6_CTRL_TRIG + 0x0000018c + DMA Channel 6 Control and Status + 0x00000000 + + + AHB_ERROR + Logical OR of the READ_ERROR and WRITE_ERROR flags. The channel halts when it encounters any bus error, and always raises its channel IRQ flag. + [31:31] + read-only + + + READ_ERROR + If 1, the channel received a read bus error. Write one to clear. + READ_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 3 transfers later) + [30:30] + read-write + oneToClear + + + WRITE_ERROR + If 1, the channel received a write bus error. Write one to clear. + WRITE_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 5 transfers later) + [29:29] + read-write + oneToClear + + + BUSY + This flag goes high when the channel starts a new transfer sequence, and low when the last transfer of that sequence completes. Clearing EN while BUSY is high pauses the channel, and BUSY will stay high while paused. + + To terminate a sequence early (and clear the BUSY flag), see CHAN_ABORT. + [26:26] + read-only + + + SNIFF_EN + If 1, this channel's data transfers are visible to the sniff hardware, and each transfer will advance the state of the checksum. This only applies if the sniff hardware is enabled, and has this channel selected. + + This allows checksum to be enabled or disabled on a per-control- block basis. + [25:25] + read-write + + + BSWAP + Apply byte-swap transformation to DMA data. + For byte data, this has no effect. For halfword data, the two bytes of each halfword are swapped. For word data, the four bytes of each word are swapped to reverse order. + [24:24] + read-write + + + IRQ_QUIET + In QUIET mode, the channel does not generate IRQs at the end of every transfer block. Instead, an IRQ is raised when NULL is written to a trigger register, indicating the end of a control block chain. + + This reduces the number of interrupts to be serviced by the CPU when transferring a DMA chain of many small control blocks. + [23:23] + read-write + + + TREQ_SEL + Select a Transfer Request signal. + The channel uses the transfer request signal to pace its data transfer rate. Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data Request from the system). + 0x0 to 0x3a -> select DREQ n as TREQ + [22:17] + read-write + + + PIO0_TX0 + 0 + Select PIO0's TX FIFO 0 as TREQ + + + PIO0_TX1 + 1 + Select PIO0's TX FIFO 1 as TREQ + + + PIO0_TX2 + 2 + Select PIO0's TX FIFO 2 as TREQ + + + PIO0_TX3 + 3 + Select PIO0's TX FIFO 3 as TREQ + + + PIO0_RX0 + 4 + Select PIO0's RX FIFO 0 as TREQ + + + PIO0_RX1 + 5 + Select PIO0's RX FIFO 1 as TREQ + + + PIO0_RX2 + 6 + Select PIO0's RX FIFO 2 as TREQ + + + PIO0_RX3 + 7 + Select PIO0's RX FIFO 3 as TREQ + + + PIO1_TX0 + 8 + Select PIO1's TX FIFO 0 as TREQ + + + PIO1_TX1 + 9 + Select PIO1's TX FIFO 1 as TREQ + + + PIO1_TX2 + 10 + Select PIO1's TX FIFO 2 as TREQ + + + PIO1_TX3 + 11 + Select PIO1's TX FIFO 3 as TREQ + + + PIO1_RX0 + 12 + Select PIO1's RX FIFO 0 as TREQ + + + PIO1_RX1 + 13 + Select PIO1's RX FIFO 1 as TREQ + + + PIO1_RX2 + 14 + Select PIO1's RX FIFO 2 as TREQ + + + PIO1_RX3 + 15 + Select PIO1's RX FIFO 3 as TREQ + + + PIO2_TX0 + 16 + Select PIO2's TX FIFO 0 as TREQ + + + PIO2_TX1 + 17 + Select PIO2's TX FIFO 1 as TREQ + + + PIO2_TX2 + 18 + Select PIO2's TX FIFO 2 as TREQ + + + PIO2_TX3 + 19 + Select PIO2's TX FIFO 3 as TREQ + + + PIO2_RX0 + 20 + Select PIO2's RX FIFO 0 as TREQ + + + PIO2_RX1 + 21 + Select PIO2's RX FIFO 1 as TREQ + + + PIO2_RX2 + 22 + Select PIO2's RX FIFO 2 as TREQ + + + PIO2_RX3 + 23 + Select PIO2's RX FIFO 3 as TREQ + + + SPI0_TX + 24 + Select SPI0's TX FIFO as TREQ + + + SPI0_RX + 25 + Select SPI0's RX FIFO as TREQ + + + SPI1_TX + 26 + Select SPI1's TX FIFO as TREQ + + + SPI1_RX + 27 + Select SPI1's RX FIFO as TREQ + + + UART0_TX + 28 + Select UART0's TX FIFO as TREQ + + + UART0_RX + 29 + Select UART0's RX FIFO as TREQ + + + UART1_TX + 30 + Select UART1's TX FIFO as TREQ + + + UART1_RX + 31 + Select UART1's RX FIFO as TREQ + + + PWM_WRAP0 + 32 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP1 + 33 + Select PWM Counter 1's Wrap Value as TREQ + + + PWM_WRAP2 + 34 + Select PWM Counter 2's Wrap Value as TREQ + + + PWM_WRAP3 + 35 + Select PWM Counter 3's Wrap Value as TREQ + + + PWM_WRAP4 + 36 + Select PWM Counter 4's Wrap Value as TREQ + + + PWM_WRAP5 + 37 + Select PWM Counter 5's Wrap Value as TREQ + + + PWM_WRAP6 + 38 + Select PWM Counter 6's Wrap Value as TREQ + + + PWM_WRAP7 + 39 + Select PWM Counter 7's Wrap Value as TREQ + + + PWM_WRAP8 + 40 + Select PWM Counter 8's Wrap Value as TREQ + + + PWM_WRAP9 + 41 + Select PWM Counter 9's Wrap Value as TREQ + + + PWM_WRAP10 + 42 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP11 + 43 + Select PWM Counter 1's Wrap Value as TREQ + + + I2C0_TX + 44 + Select I2C0's TX FIFO as TREQ + + + I2C0_RX + 45 + Select I2C0's RX FIFO as TREQ + + + I2C1_TX + 46 + Select I2C1's TX FIFO as TREQ + + + I2C1_RX + 47 + Select I2C1's RX FIFO as TREQ + + + ADC + 48 + Select the ADC as TREQ + + + XIP_STREAM + 49 + Select the XIP Streaming FIFO as TREQ + + + XIP_QMITX + 50 + Select XIP_QMITX as TREQ + + + XIP_QMIRX + 51 + Select XIP_QMIRX as TREQ + + + HSTX + 52 + Select HSTX as TREQ + + + CORESIGHT + 53 + Select CORESIGHT as TREQ + + + SHA256 + 54 + Select SHA256 as TREQ + + + TIMER0 + 59 + Select Timer 0 as TREQ + + + TIMER1 + 60 + Select Timer 1 as TREQ + + + TIMER2 + 61 + Select Timer 2 as TREQ (Optional) + + + TIMER3 + 62 + Select Timer 3 as TREQ (Optional) + + + PERMANENT + 63 + Permanent request, for unpaced transfers. + + + + + CHAIN_TO + When this channel completes, it will trigger the channel indicated by CHAIN_TO. Disable by setting CHAIN_TO = _(this channel)_. + + Note this field resets to 0, so channels 1 and above will chain to channel 0 by default. Set this field to avoid this behaviour. + [16:13] + read-write + + + RING_SEL + Select whether RING_SIZE applies to read or write addresses. + If 0, read addresses are wrapped on a (1 << RING_SIZE) boundary. If 1, write addresses are wrapped. + [12:12] + read-write + + + RING_SIZE + Size of address wrap region. If 0, don't wrap. For values n > 0, only the lower n bits of the address will change. This wraps the address on a (1 << n) byte boundary, facilitating access to naturally-aligned ring buffers. + + Ring sizes between 2 and 32768 bytes are possible. This can apply to either read or write addresses, based on value of RING_SEL. + [11:8] + read-write + + + RING_NONE + 0 + + + + + INCR_WRITE_REV + If 1, and INCR_WRITE is 1, the write address is decremented rather than incremented with each transfer. + + If 1, and INCR_WRITE is 0, this otherwise-unused combination causes the write address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [7:7] + read-write + + + INCR_WRITE + If 1, the write address increments with each transfer. If 0, each write is directed to the same, initial address. + + Generally this should be disabled for memory-to-peripheral transfers. + [6:6] + read-write + + + INCR_READ_REV + If 1, and INCR_READ is 1, the read address is decremented rather than incremented with each transfer. + + If 1, and INCR_READ is 0, this otherwise-unused combination causes the read address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [5:5] + read-write + + + INCR_READ + If 1, the read address increments with each transfer. If 0, each read is directed to the same, initial address. + + Generally this should be disabled for peripheral-to-memory transfers. + [4:4] + read-write + + + DATA_SIZE + Set the size of each bus transfer (byte/halfword/word). READ_ADDR and WRITE_ADDR advance by this amount (1/2/4 bytes) with each transfer. + [3:2] + read-write + + + SIZE_BYTE + 0 + + + SIZE_HALFWORD + 1 + + + SIZE_WORD + 2 + + + + + HIGH_PRIORITY + HIGH_PRIORITY gives a channel preferential treatment in issue scheduling: in each scheduling round, all high priority channels are considered first, and then only a single low priority channel, before returning to the high priority channels. + + This only affects the order in which the DMA schedules channels. The DMA's bus priority is not changed. If the DMA is not saturated then a low priority channel will see no loss of throughput. + [1:1] + read-write + + + EN + DMA Channel Enable. + When 1, the channel will respond to triggering events, which will cause it to become BUSY and start transferring data. When 0, the channel will ignore triggers, stop issuing transfers, and pause the current transfer sequence (i.e. BUSY will remain high if already high) + [0:0] + read-write + + + + + CH6_AL1_CTRL + 0x00000190 + Alias for channel 6 CTRL register + 0x00000000 + + + CH6_AL1_CTRL + [31:0] + read-write + + + + + CH6_AL1_READ_ADDR + 0x00000194 + Alias for channel 6 READ_ADDR register + 0x00000000 + + + CH6_AL1_READ_ADDR + [31:0] + read-write + + + + + CH6_AL1_WRITE_ADDR + 0x00000198 + Alias for channel 6 WRITE_ADDR register + 0x00000000 + + + CH6_AL1_WRITE_ADDR + [31:0] + read-write + + + + + CH6_AL1_TRANS_COUNT_TRIG + 0x0000019c + Alias for channel 6 TRANS_COUNT register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH6_AL1_TRANS_COUNT_TRIG + [31:0] + read-write + + + + + CH6_AL2_CTRL + 0x000001a0 + Alias for channel 6 CTRL register + 0x00000000 + + + CH6_AL2_CTRL + [31:0] + read-write + + + + + CH6_AL2_TRANS_COUNT + 0x000001a4 + Alias for channel 6 TRANS_COUNT register + 0x00000000 + + + CH6_AL2_TRANS_COUNT + [31:0] + read-write + + + + + CH6_AL2_READ_ADDR + 0x000001a8 + Alias for channel 6 READ_ADDR register + 0x00000000 + + + CH6_AL2_READ_ADDR + [31:0] + read-write + + + + + CH6_AL2_WRITE_ADDR_TRIG + 0x000001ac + Alias for channel 6 WRITE_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH6_AL2_WRITE_ADDR_TRIG + [31:0] + read-write + + + + + CH6_AL3_CTRL + 0x000001b0 + Alias for channel 6 CTRL register + 0x00000000 + + + CH6_AL3_CTRL + [31:0] + read-write + + + + + CH6_AL3_WRITE_ADDR + 0x000001b4 + Alias for channel 6 WRITE_ADDR register + 0x00000000 + + + CH6_AL3_WRITE_ADDR + [31:0] + read-write + + + + + CH6_AL3_TRANS_COUNT + 0x000001b8 + Alias for channel 6 TRANS_COUNT register + 0x00000000 + + + CH6_AL3_TRANS_COUNT + [31:0] + read-write + + + + + CH6_AL3_READ_ADDR_TRIG + 0x000001bc + Alias for channel 6 READ_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH6_AL3_READ_ADDR_TRIG + [31:0] + read-write + + + + + CH7_READ_ADDR + 0x000001c0 + DMA Channel 7 Read Address pointer + 0x00000000 + + + CH7_READ_ADDR + This register updates automatically each time a read completes. The current value is the next address to be read by this channel. + [31:0] + read-write + + + + + CH7_WRITE_ADDR + 0x000001c4 + DMA Channel 7 Write Address pointer + 0x00000000 + + + CH7_WRITE_ADDR + This register updates automatically each time a write completes. The current value is the next address to be written by this channel. + [31:0] + read-write + + + + + CH7_TRANS_COUNT + 0x000001c8 + DMA Channel 7 Transfer Count + 0x00000000 + + + MODE + When MODE is 0x0, the transfer count decrements with each transfer until 0, and then the channel triggers the next channel indicated by CTRL_CHAIN_TO. + + When MODE is 0x1, the transfer count decrements with each transfer until 0, and then the channel re-triggers itself, in addition to the trigger indicated by CTRL_CHAIN_TO. This is useful for e.g. an endless ring-buffer DMA with periodic interrupts. + + When MODE is 0xf, the transfer count does not decrement. The DMA channel performs an endless sequence of transfers, never triggering other channels or raising interrupts, until an ABORT is raised. + + All other values are reserved. + [31:28] + read-write + + + NORMAL + 0 + + + TRIGGER_SELF + 1 + + + ENDLESS + 15 + + + + + COUNT + 28-bit transfer count (256 million transfers maximum). + + Program the number of bus transfers a channel will perform before halting. Note that, if transfers are larger than one byte in size, this is not equal to the number of bytes transferred (see CTRL_DATA_SIZE). + + When the channel is active, reading this register shows the number of transfers remaining, updating automatically each time a write transfer completes. + + Writing this register sets the RELOAD value for the transfer counter. Each time this channel is triggered, the RELOAD value is copied into the live transfer counter. The channel can be started multiple times, and will perform the same number of transfers each time, as programmed by most recent write. + + The RELOAD value can be observed at CHx_DBG_TCR. If TRANS_COUNT is used as a trigger, the written value is used immediately as the length of the new transfer sequence, as well as being written to RELOAD. + [27:0] + read-write + + + + + CH7_CTRL_TRIG + 0x000001cc + DMA Channel 7 Control and Status + 0x00000000 + + + AHB_ERROR + Logical OR of the READ_ERROR and WRITE_ERROR flags. The channel halts when it encounters any bus error, and always raises its channel IRQ flag. + [31:31] + read-only + + + READ_ERROR + If 1, the channel received a read bus error. Write one to clear. + READ_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 3 transfers later) + [30:30] + read-write + oneToClear + + + WRITE_ERROR + If 1, the channel received a write bus error. Write one to clear. + WRITE_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 5 transfers later) + [29:29] + read-write + oneToClear + + + BUSY + This flag goes high when the channel starts a new transfer sequence, and low when the last transfer of that sequence completes. Clearing EN while BUSY is high pauses the channel, and BUSY will stay high while paused. + + To terminate a sequence early (and clear the BUSY flag), see CHAN_ABORT. + [26:26] + read-only + + + SNIFF_EN + If 1, this channel's data transfers are visible to the sniff hardware, and each transfer will advance the state of the checksum. This only applies if the sniff hardware is enabled, and has this channel selected. + + This allows checksum to be enabled or disabled on a per-control- block basis. + [25:25] + read-write + + + BSWAP + Apply byte-swap transformation to DMA data. + For byte data, this has no effect. For halfword data, the two bytes of each halfword are swapped. For word data, the four bytes of each word are swapped to reverse order. + [24:24] + read-write + + + IRQ_QUIET + In QUIET mode, the channel does not generate IRQs at the end of every transfer block. Instead, an IRQ is raised when NULL is written to a trigger register, indicating the end of a control block chain. + + This reduces the number of interrupts to be serviced by the CPU when transferring a DMA chain of many small control blocks. + [23:23] + read-write + + + TREQ_SEL + Select a Transfer Request signal. + The channel uses the transfer request signal to pace its data transfer rate. Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data Request from the system). + 0x0 to 0x3a -> select DREQ n as TREQ + [22:17] + read-write + + + PIO0_TX0 + 0 + Select PIO0's TX FIFO 0 as TREQ + + + PIO0_TX1 + 1 + Select PIO0's TX FIFO 1 as TREQ + + + PIO0_TX2 + 2 + Select PIO0's TX FIFO 2 as TREQ + + + PIO0_TX3 + 3 + Select PIO0's TX FIFO 3 as TREQ + + + PIO0_RX0 + 4 + Select PIO0's RX FIFO 0 as TREQ + + + PIO0_RX1 + 5 + Select PIO0's RX FIFO 1 as TREQ + + + PIO0_RX2 + 6 + Select PIO0's RX FIFO 2 as TREQ + + + PIO0_RX3 + 7 + Select PIO0's RX FIFO 3 as TREQ + + + PIO1_TX0 + 8 + Select PIO1's TX FIFO 0 as TREQ + + + PIO1_TX1 + 9 + Select PIO1's TX FIFO 1 as TREQ + + + PIO1_TX2 + 10 + Select PIO1's TX FIFO 2 as TREQ + + + PIO1_TX3 + 11 + Select PIO1's TX FIFO 3 as TREQ + + + PIO1_RX0 + 12 + Select PIO1's RX FIFO 0 as TREQ + + + PIO1_RX1 + 13 + Select PIO1's RX FIFO 1 as TREQ + + + PIO1_RX2 + 14 + Select PIO1's RX FIFO 2 as TREQ + + + PIO1_RX3 + 15 + Select PIO1's RX FIFO 3 as TREQ + + + PIO2_TX0 + 16 + Select PIO2's TX FIFO 0 as TREQ + + + PIO2_TX1 + 17 + Select PIO2's TX FIFO 1 as TREQ + + + PIO2_TX2 + 18 + Select PIO2's TX FIFO 2 as TREQ + + + PIO2_TX3 + 19 + Select PIO2's TX FIFO 3 as TREQ + + + PIO2_RX0 + 20 + Select PIO2's RX FIFO 0 as TREQ + + + PIO2_RX1 + 21 + Select PIO2's RX FIFO 1 as TREQ + + + PIO2_RX2 + 22 + Select PIO2's RX FIFO 2 as TREQ + + + PIO2_RX3 + 23 + Select PIO2's RX FIFO 3 as TREQ + + + SPI0_TX + 24 + Select SPI0's TX FIFO as TREQ + + + SPI0_RX + 25 + Select SPI0's RX FIFO as TREQ + + + SPI1_TX + 26 + Select SPI1's TX FIFO as TREQ + + + SPI1_RX + 27 + Select SPI1's RX FIFO as TREQ + + + UART0_TX + 28 + Select UART0's TX FIFO as TREQ + + + UART0_RX + 29 + Select UART0's RX FIFO as TREQ + + + UART1_TX + 30 + Select UART1's TX FIFO as TREQ + + + UART1_RX + 31 + Select UART1's RX FIFO as TREQ + + + PWM_WRAP0 + 32 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP1 + 33 + Select PWM Counter 1's Wrap Value as TREQ + + + PWM_WRAP2 + 34 + Select PWM Counter 2's Wrap Value as TREQ + + + PWM_WRAP3 + 35 + Select PWM Counter 3's Wrap Value as TREQ + + + PWM_WRAP4 + 36 + Select PWM Counter 4's Wrap Value as TREQ + + + PWM_WRAP5 + 37 + Select PWM Counter 5's Wrap Value as TREQ + + + PWM_WRAP6 + 38 + Select PWM Counter 6's Wrap Value as TREQ + + + PWM_WRAP7 + 39 + Select PWM Counter 7's Wrap Value as TREQ + + + PWM_WRAP8 + 40 + Select PWM Counter 8's Wrap Value as TREQ + + + PWM_WRAP9 + 41 + Select PWM Counter 9's Wrap Value as TREQ + + + PWM_WRAP10 + 42 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP11 + 43 + Select PWM Counter 1's Wrap Value as TREQ + + + I2C0_TX + 44 + Select I2C0's TX FIFO as TREQ + + + I2C0_RX + 45 + Select I2C0's RX FIFO as TREQ + + + I2C1_TX + 46 + Select I2C1's TX FIFO as TREQ + + + I2C1_RX + 47 + Select I2C1's RX FIFO as TREQ + + + ADC + 48 + Select the ADC as TREQ + + + XIP_STREAM + 49 + Select the XIP Streaming FIFO as TREQ + + + XIP_QMITX + 50 + Select XIP_QMITX as TREQ + + + XIP_QMIRX + 51 + Select XIP_QMIRX as TREQ + + + HSTX + 52 + Select HSTX as TREQ + + + CORESIGHT + 53 + Select CORESIGHT as TREQ + + + SHA256 + 54 + Select SHA256 as TREQ + + + TIMER0 + 59 + Select Timer 0 as TREQ + + + TIMER1 + 60 + Select Timer 1 as TREQ + + + TIMER2 + 61 + Select Timer 2 as TREQ (Optional) + + + TIMER3 + 62 + Select Timer 3 as TREQ (Optional) + + + PERMANENT + 63 + Permanent request, for unpaced transfers. + + + + + CHAIN_TO + When this channel completes, it will trigger the channel indicated by CHAIN_TO. Disable by setting CHAIN_TO = _(this channel)_. + + Note this field resets to 0, so channels 1 and above will chain to channel 0 by default. Set this field to avoid this behaviour. + [16:13] + read-write + + + RING_SEL + Select whether RING_SIZE applies to read or write addresses. + If 0, read addresses are wrapped on a (1 << RING_SIZE) boundary. If 1, write addresses are wrapped. + [12:12] + read-write + + + RING_SIZE + Size of address wrap region. If 0, don't wrap. For values n > 0, only the lower n bits of the address will change. This wraps the address on a (1 << n) byte boundary, facilitating access to naturally-aligned ring buffers. + + Ring sizes between 2 and 32768 bytes are possible. This can apply to either read or write addresses, based on value of RING_SEL. + [11:8] + read-write + + + RING_NONE + 0 + + + + + INCR_WRITE_REV + If 1, and INCR_WRITE is 1, the write address is decremented rather than incremented with each transfer. + + If 1, and INCR_WRITE is 0, this otherwise-unused combination causes the write address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [7:7] + read-write + + + INCR_WRITE + If 1, the write address increments with each transfer. If 0, each write is directed to the same, initial address. + + Generally this should be disabled for memory-to-peripheral transfers. + [6:6] + read-write + + + INCR_READ_REV + If 1, and INCR_READ is 1, the read address is decremented rather than incremented with each transfer. + + If 1, and INCR_READ is 0, this otherwise-unused combination causes the read address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [5:5] + read-write + + + INCR_READ + If 1, the read address increments with each transfer. If 0, each read is directed to the same, initial address. + + Generally this should be disabled for peripheral-to-memory transfers. + [4:4] + read-write + + + DATA_SIZE + Set the size of each bus transfer (byte/halfword/word). READ_ADDR and WRITE_ADDR advance by this amount (1/2/4 bytes) with each transfer. + [3:2] + read-write + + + SIZE_BYTE + 0 + + + SIZE_HALFWORD + 1 + + + SIZE_WORD + 2 + + + + + HIGH_PRIORITY + HIGH_PRIORITY gives a channel preferential treatment in issue scheduling: in each scheduling round, all high priority channels are considered first, and then only a single low priority channel, before returning to the high priority channels. + + This only affects the order in which the DMA schedules channels. The DMA's bus priority is not changed. If the DMA is not saturated then a low priority channel will see no loss of throughput. + [1:1] + read-write + + + EN + DMA Channel Enable. + When 1, the channel will respond to triggering events, which will cause it to become BUSY and start transferring data. When 0, the channel will ignore triggers, stop issuing transfers, and pause the current transfer sequence (i.e. BUSY will remain high if already high) + [0:0] + read-write + + + + + CH7_AL1_CTRL + 0x000001d0 + Alias for channel 7 CTRL register + 0x00000000 + + + CH7_AL1_CTRL + [31:0] + read-write + + + + + CH7_AL1_READ_ADDR + 0x000001d4 + Alias for channel 7 READ_ADDR register + 0x00000000 + + + CH7_AL1_READ_ADDR + [31:0] + read-write + + + + + CH7_AL1_WRITE_ADDR + 0x000001d8 + Alias for channel 7 WRITE_ADDR register + 0x00000000 + + + CH7_AL1_WRITE_ADDR + [31:0] + read-write + + + + + CH7_AL1_TRANS_COUNT_TRIG + 0x000001dc + Alias for channel 7 TRANS_COUNT register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH7_AL1_TRANS_COUNT_TRIG + [31:0] + read-write + + + + + CH7_AL2_CTRL + 0x000001e0 + Alias for channel 7 CTRL register + 0x00000000 + + + CH7_AL2_CTRL + [31:0] + read-write + + + + + CH7_AL2_TRANS_COUNT + 0x000001e4 + Alias for channel 7 TRANS_COUNT register + 0x00000000 + + + CH7_AL2_TRANS_COUNT + [31:0] + read-write + + + + + CH7_AL2_READ_ADDR + 0x000001e8 + Alias for channel 7 READ_ADDR register + 0x00000000 + + + CH7_AL2_READ_ADDR + [31:0] + read-write + + + + + CH7_AL2_WRITE_ADDR_TRIG + 0x000001ec + Alias for channel 7 WRITE_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH7_AL2_WRITE_ADDR_TRIG + [31:0] + read-write + + + + + CH7_AL3_CTRL + 0x000001f0 + Alias for channel 7 CTRL register + 0x00000000 + + + CH7_AL3_CTRL + [31:0] + read-write + + + + + CH7_AL3_WRITE_ADDR + 0x000001f4 + Alias for channel 7 WRITE_ADDR register + 0x00000000 + + + CH7_AL3_WRITE_ADDR + [31:0] + read-write + + + + + CH7_AL3_TRANS_COUNT + 0x000001f8 + Alias for channel 7 TRANS_COUNT register + 0x00000000 + + + CH7_AL3_TRANS_COUNT + [31:0] + read-write + + + + + CH7_AL3_READ_ADDR_TRIG + 0x000001fc + Alias for channel 7 READ_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH7_AL3_READ_ADDR_TRIG + [31:0] + read-write + + + + + CH8_READ_ADDR + 0x00000200 + DMA Channel 8 Read Address pointer + 0x00000000 + + + CH8_READ_ADDR + This register updates automatically each time a read completes. The current value is the next address to be read by this channel. + [31:0] + read-write + + + + + CH8_WRITE_ADDR + 0x00000204 + DMA Channel 8 Write Address pointer + 0x00000000 + + + CH8_WRITE_ADDR + This register updates automatically each time a write completes. The current value is the next address to be written by this channel. + [31:0] + read-write + + + + + CH8_TRANS_COUNT + 0x00000208 + DMA Channel 8 Transfer Count + 0x00000000 + + + MODE + When MODE is 0x0, the transfer count decrements with each transfer until 0, and then the channel triggers the next channel indicated by CTRL_CHAIN_TO. + + When MODE is 0x1, the transfer count decrements with each transfer until 0, and then the channel re-triggers itself, in addition to the trigger indicated by CTRL_CHAIN_TO. This is useful for e.g. an endless ring-buffer DMA with periodic interrupts. + + When MODE is 0xf, the transfer count does not decrement. The DMA channel performs an endless sequence of transfers, never triggering other channels or raising interrupts, until an ABORT is raised. + + All other values are reserved. + [31:28] + read-write + + + NORMAL + 0 + + + TRIGGER_SELF + 1 + + + ENDLESS + 15 + + + + + COUNT + 28-bit transfer count (256 million transfers maximum). + + Program the number of bus transfers a channel will perform before halting. Note that, if transfers are larger than one byte in size, this is not equal to the number of bytes transferred (see CTRL_DATA_SIZE). + + When the channel is active, reading this register shows the number of transfers remaining, updating automatically each time a write transfer completes. + + Writing this register sets the RELOAD value for the transfer counter. Each time this channel is triggered, the RELOAD value is copied into the live transfer counter. The channel can be started multiple times, and will perform the same number of transfers each time, as programmed by most recent write. + + The RELOAD value can be observed at CHx_DBG_TCR. If TRANS_COUNT is used as a trigger, the written value is used immediately as the length of the new transfer sequence, as well as being written to RELOAD. + [27:0] + read-write + + + + + CH8_CTRL_TRIG + 0x0000020c + DMA Channel 8 Control and Status + 0x00000000 + + + AHB_ERROR + Logical OR of the READ_ERROR and WRITE_ERROR flags. The channel halts when it encounters any bus error, and always raises its channel IRQ flag. + [31:31] + read-only + + + READ_ERROR + If 1, the channel received a read bus error. Write one to clear. + READ_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 3 transfers later) + [30:30] + read-write + oneToClear + + + WRITE_ERROR + If 1, the channel received a write bus error. Write one to clear. + WRITE_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 5 transfers later) + [29:29] + read-write + oneToClear + + + BUSY + This flag goes high when the channel starts a new transfer sequence, and low when the last transfer of that sequence completes. Clearing EN while BUSY is high pauses the channel, and BUSY will stay high while paused. + + To terminate a sequence early (and clear the BUSY flag), see CHAN_ABORT. + [26:26] + read-only + + + SNIFF_EN + If 1, this channel's data transfers are visible to the sniff hardware, and each transfer will advance the state of the checksum. This only applies if the sniff hardware is enabled, and has this channel selected. + + This allows checksum to be enabled or disabled on a per-control- block basis. + [25:25] + read-write + + + BSWAP + Apply byte-swap transformation to DMA data. + For byte data, this has no effect. For halfword data, the two bytes of each halfword are swapped. For word data, the four bytes of each word are swapped to reverse order. + [24:24] + read-write + + + IRQ_QUIET + In QUIET mode, the channel does not generate IRQs at the end of every transfer block. Instead, an IRQ is raised when NULL is written to a trigger register, indicating the end of a control block chain. + + This reduces the number of interrupts to be serviced by the CPU when transferring a DMA chain of many small control blocks. + [23:23] + read-write + + + TREQ_SEL + Select a Transfer Request signal. + The channel uses the transfer request signal to pace its data transfer rate. Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data Request from the system). + 0x0 to 0x3a -> select DREQ n as TREQ + [22:17] + read-write + + + PIO0_TX0 + 0 + Select PIO0's TX FIFO 0 as TREQ + + + PIO0_TX1 + 1 + Select PIO0's TX FIFO 1 as TREQ + + + PIO0_TX2 + 2 + Select PIO0's TX FIFO 2 as TREQ + + + PIO0_TX3 + 3 + Select PIO0's TX FIFO 3 as TREQ + + + PIO0_RX0 + 4 + Select PIO0's RX FIFO 0 as TREQ + + + PIO0_RX1 + 5 + Select PIO0's RX FIFO 1 as TREQ + + + PIO0_RX2 + 6 + Select PIO0's RX FIFO 2 as TREQ + + + PIO0_RX3 + 7 + Select PIO0's RX FIFO 3 as TREQ + + + PIO1_TX0 + 8 + Select PIO1's TX FIFO 0 as TREQ + + + PIO1_TX1 + 9 + Select PIO1's TX FIFO 1 as TREQ + + + PIO1_TX2 + 10 + Select PIO1's TX FIFO 2 as TREQ + + + PIO1_TX3 + 11 + Select PIO1's TX FIFO 3 as TREQ + + + PIO1_RX0 + 12 + Select PIO1's RX FIFO 0 as TREQ + + + PIO1_RX1 + 13 + Select PIO1's RX FIFO 1 as TREQ + + + PIO1_RX2 + 14 + Select PIO1's RX FIFO 2 as TREQ + + + PIO1_RX3 + 15 + Select PIO1's RX FIFO 3 as TREQ + + + PIO2_TX0 + 16 + Select PIO2's TX FIFO 0 as TREQ + + + PIO2_TX1 + 17 + Select PIO2's TX FIFO 1 as TREQ + + + PIO2_TX2 + 18 + Select PIO2's TX FIFO 2 as TREQ + + + PIO2_TX3 + 19 + Select PIO2's TX FIFO 3 as TREQ + + + PIO2_RX0 + 20 + Select PIO2's RX FIFO 0 as TREQ + + + PIO2_RX1 + 21 + Select PIO2's RX FIFO 1 as TREQ + + + PIO2_RX2 + 22 + Select PIO2's RX FIFO 2 as TREQ + + + PIO2_RX3 + 23 + Select PIO2's RX FIFO 3 as TREQ + + + SPI0_TX + 24 + Select SPI0's TX FIFO as TREQ + + + SPI0_RX + 25 + Select SPI0's RX FIFO as TREQ + + + SPI1_TX + 26 + Select SPI1's TX FIFO as TREQ + + + SPI1_RX + 27 + Select SPI1's RX FIFO as TREQ + + + UART0_TX + 28 + Select UART0's TX FIFO as TREQ + + + UART0_RX + 29 + Select UART0's RX FIFO as TREQ + + + UART1_TX + 30 + Select UART1's TX FIFO as TREQ + + + UART1_RX + 31 + Select UART1's RX FIFO as TREQ + + + PWM_WRAP0 + 32 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP1 + 33 + Select PWM Counter 1's Wrap Value as TREQ + + + PWM_WRAP2 + 34 + Select PWM Counter 2's Wrap Value as TREQ + + + PWM_WRAP3 + 35 + Select PWM Counter 3's Wrap Value as TREQ + + + PWM_WRAP4 + 36 + Select PWM Counter 4's Wrap Value as TREQ + + + PWM_WRAP5 + 37 + Select PWM Counter 5's Wrap Value as TREQ + + + PWM_WRAP6 + 38 + Select PWM Counter 6's Wrap Value as TREQ + + + PWM_WRAP7 + 39 + Select PWM Counter 7's Wrap Value as TREQ + + + PWM_WRAP8 + 40 + Select PWM Counter 8's Wrap Value as TREQ + + + PWM_WRAP9 + 41 + Select PWM Counter 9's Wrap Value as TREQ + + + PWM_WRAP10 + 42 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP11 + 43 + Select PWM Counter 1's Wrap Value as TREQ + + + I2C0_TX + 44 + Select I2C0's TX FIFO as TREQ + + + I2C0_RX + 45 + Select I2C0's RX FIFO as TREQ + + + I2C1_TX + 46 + Select I2C1's TX FIFO as TREQ + + + I2C1_RX + 47 + Select I2C1's RX FIFO as TREQ + + + ADC + 48 + Select the ADC as TREQ + + + XIP_STREAM + 49 + Select the XIP Streaming FIFO as TREQ + + + XIP_QMITX + 50 + Select XIP_QMITX as TREQ + + + XIP_QMIRX + 51 + Select XIP_QMIRX as TREQ + + + HSTX + 52 + Select HSTX as TREQ + + + CORESIGHT + 53 + Select CORESIGHT as TREQ + + + SHA256 + 54 + Select SHA256 as TREQ + + + TIMER0 + 59 + Select Timer 0 as TREQ + + + TIMER1 + 60 + Select Timer 1 as TREQ + + + TIMER2 + 61 + Select Timer 2 as TREQ (Optional) + + + TIMER3 + 62 + Select Timer 3 as TREQ (Optional) + + + PERMANENT + 63 + Permanent request, for unpaced transfers. + + + + + CHAIN_TO + When this channel completes, it will trigger the channel indicated by CHAIN_TO. Disable by setting CHAIN_TO = _(this channel)_. + + Note this field resets to 0, so channels 1 and above will chain to channel 0 by default. Set this field to avoid this behaviour. + [16:13] + read-write + + + RING_SEL + Select whether RING_SIZE applies to read or write addresses. + If 0, read addresses are wrapped on a (1 << RING_SIZE) boundary. If 1, write addresses are wrapped. + [12:12] + read-write + + + RING_SIZE + Size of address wrap region. If 0, don't wrap. For values n > 0, only the lower n bits of the address will change. This wraps the address on a (1 << n) byte boundary, facilitating access to naturally-aligned ring buffers. + + Ring sizes between 2 and 32768 bytes are possible. This can apply to either read or write addresses, based on value of RING_SEL. + [11:8] + read-write + + + RING_NONE + 0 + + + + + INCR_WRITE_REV + If 1, and INCR_WRITE is 1, the write address is decremented rather than incremented with each transfer. + + If 1, and INCR_WRITE is 0, this otherwise-unused combination causes the write address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [7:7] + read-write + + + INCR_WRITE + If 1, the write address increments with each transfer. If 0, each write is directed to the same, initial address. + + Generally this should be disabled for memory-to-peripheral transfers. + [6:6] + read-write + + + INCR_READ_REV + If 1, and INCR_READ is 1, the read address is decremented rather than incremented with each transfer. + + If 1, and INCR_READ is 0, this otherwise-unused combination causes the read address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [5:5] + read-write + + + INCR_READ + If 1, the read address increments with each transfer. If 0, each read is directed to the same, initial address. + + Generally this should be disabled for peripheral-to-memory transfers. + [4:4] + read-write + + + DATA_SIZE + Set the size of each bus transfer (byte/halfword/word). READ_ADDR and WRITE_ADDR advance by this amount (1/2/4 bytes) with each transfer. + [3:2] + read-write + + + SIZE_BYTE + 0 + + + SIZE_HALFWORD + 1 + + + SIZE_WORD + 2 + + + + + HIGH_PRIORITY + HIGH_PRIORITY gives a channel preferential treatment in issue scheduling: in each scheduling round, all high priority channels are considered first, and then only a single low priority channel, before returning to the high priority channels. + + This only affects the order in which the DMA schedules channels. The DMA's bus priority is not changed. If the DMA is not saturated then a low priority channel will see no loss of throughput. + [1:1] + read-write + + + EN + DMA Channel Enable. + When 1, the channel will respond to triggering events, which will cause it to become BUSY and start transferring data. When 0, the channel will ignore triggers, stop issuing transfers, and pause the current transfer sequence (i.e. BUSY will remain high if already high) + [0:0] + read-write + + + + + CH8_AL1_CTRL + 0x00000210 + Alias for channel 8 CTRL register + 0x00000000 + + + CH8_AL1_CTRL + [31:0] + read-write + + + + + CH8_AL1_READ_ADDR + 0x00000214 + Alias for channel 8 READ_ADDR register + 0x00000000 + + + CH8_AL1_READ_ADDR + [31:0] + read-write + + + + + CH8_AL1_WRITE_ADDR + 0x00000218 + Alias for channel 8 WRITE_ADDR register + 0x00000000 + + + CH8_AL1_WRITE_ADDR + [31:0] + read-write + + + + + CH8_AL1_TRANS_COUNT_TRIG + 0x0000021c + Alias for channel 8 TRANS_COUNT register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH8_AL1_TRANS_COUNT_TRIG + [31:0] + read-write + + + + + CH8_AL2_CTRL + 0x00000220 + Alias for channel 8 CTRL register + 0x00000000 + + + CH8_AL2_CTRL + [31:0] + read-write + + + + + CH8_AL2_TRANS_COUNT + 0x00000224 + Alias for channel 8 TRANS_COUNT register + 0x00000000 + + + CH8_AL2_TRANS_COUNT + [31:0] + read-write + + + + + CH8_AL2_READ_ADDR + 0x00000228 + Alias for channel 8 READ_ADDR register + 0x00000000 + + + CH8_AL2_READ_ADDR + [31:0] + read-write + + + + + CH8_AL2_WRITE_ADDR_TRIG + 0x0000022c + Alias for channel 8 WRITE_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH8_AL2_WRITE_ADDR_TRIG + [31:0] + read-write + + + + + CH8_AL3_CTRL + 0x00000230 + Alias for channel 8 CTRL register + 0x00000000 + + + CH8_AL3_CTRL + [31:0] + read-write + + + + + CH8_AL3_WRITE_ADDR + 0x00000234 + Alias for channel 8 WRITE_ADDR register + 0x00000000 + + + CH8_AL3_WRITE_ADDR + [31:0] + read-write + + + + + CH8_AL3_TRANS_COUNT + 0x00000238 + Alias for channel 8 TRANS_COUNT register + 0x00000000 + + + CH8_AL3_TRANS_COUNT + [31:0] + read-write + + + + + CH8_AL3_READ_ADDR_TRIG + 0x0000023c + Alias for channel 8 READ_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH8_AL3_READ_ADDR_TRIG + [31:0] + read-write + + + + + CH9_READ_ADDR + 0x00000240 + DMA Channel 9 Read Address pointer + 0x00000000 + + + CH9_READ_ADDR + This register updates automatically each time a read completes. The current value is the next address to be read by this channel. + [31:0] + read-write + + + + + CH9_WRITE_ADDR + 0x00000244 + DMA Channel 9 Write Address pointer + 0x00000000 + + + CH9_WRITE_ADDR + This register updates automatically each time a write completes. The current value is the next address to be written by this channel. + [31:0] + read-write + + + + + CH9_TRANS_COUNT + 0x00000248 + DMA Channel 9 Transfer Count + 0x00000000 + + + MODE + When MODE is 0x0, the transfer count decrements with each transfer until 0, and then the channel triggers the next channel indicated by CTRL_CHAIN_TO. + + When MODE is 0x1, the transfer count decrements with each transfer until 0, and then the channel re-triggers itself, in addition to the trigger indicated by CTRL_CHAIN_TO. This is useful for e.g. an endless ring-buffer DMA with periodic interrupts. + + When MODE is 0xf, the transfer count does not decrement. The DMA channel performs an endless sequence of transfers, never triggering other channels or raising interrupts, until an ABORT is raised. + + All other values are reserved. + [31:28] + read-write + + + NORMAL + 0 + + + TRIGGER_SELF + 1 + + + ENDLESS + 15 + + + + + COUNT + 28-bit transfer count (256 million transfers maximum). + + Program the number of bus transfers a channel will perform before halting. Note that, if transfers are larger than one byte in size, this is not equal to the number of bytes transferred (see CTRL_DATA_SIZE). + + When the channel is active, reading this register shows the number of transfers remaining, updating automatically each time a write transfer completes. + + Writing this register sets the RELOAD value for the transfer counter. Each time this channel is triggered, the RELOAD value is copied into the live transfer counter. The channel can be started multiple times, and will perform the same number of transfers each time, as programmed by most recent write. + + The RELOAD value can be observed at CHx_DBG_TCR. If TRANS_COUNT is used as a trigger, the written value is used immediately as the length of the new transfer sequence, as well as being written to RELOAD. + [27:0] + read-write + + + + + CH9_CTRL_TRIG + 0x0000024c + DMA Channel 9 Control and Status + 0x00000000 + + + AHB_ERROR + Logical OR of the READ_ERROR and WRITE_ERROR flags. The channel halts when it encounters any bus error, and always raises its channel IRQ flag. + [31:31] + read-only + + + READ_ERROR + If 1, the channel received a read bus error. Write one to clear. + READ_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 3 transfers later) + [30:30] + read-write + oneToClear + + + WRITE_ERROR + If 1, the channel received a write bus error. Write one to clear. + WRITE_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 5 transfers later) + [29:29] + read-write + oneToClear + + + BUSY + This flag goes high when the channel starts a new transfer sequence, and low when the last transfer of that sequence completes. Clearing EN while BUSY is high pauses the channel, and BUSY will stay high while paused. + + To terminate a sequence early (and clear the BUSY flag), see CHAN_ABORT. + [26:26] + read-only + + + SNIFF_EN + If 1, this channel's data transfers are visible to the sniff hardware, and each transfer will advance the state of the checksum. This only applies if the sniff hardware is enabled, and has this channel selected. + + This allows checksum to be enabled or disabled on a per-control- block basis. + [25:25] + read-write + + + BSWAP + Apply byte-swap transformation to DMA data. + For byte data, this has no effect. For halfword data, the two bytes of each halfword are swapped. For word data, the four bytes of each word are swapped to reverse order. + [24:24] + read-write + + + IRQ_QUIET + In QUIET mode, the channel does not generate IRQs at the end of every transfer block. Instead, an IRQ is raised when NULL is written to a trigger register, indicating the end of a control block chain. + + This reduces the number of interrupts to be serviced by the CPU when transferring a DMA chain of many small control blocks. + [23:23] + read-write + + + TREQ_SEL + Select a Transfer Request signal. + The channel uses the transfer request signal to pace its data transfer rate. Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data Request from the system). + 0x0 to 0x3a -> select DREQ n as TREQ + [22:17] + read-write + + + PIO0_TX0 + 0 + Select PIO0's TX FIFO 0 as TREQ + + + PIO0_TX1 + 1 + Select PIO0's TX FIFO 1 as TREQ + + + PIO0_TX2 + 2 + Select PIO0's TX FIFO 2 as TREQ + + + PIO0_TX3 + 3 + Select PIO0's TX FIFO 3 as TREQ + + + PIO0_RX0 + 4 + Select PIO0's RX FIFO 0 as TREQ + + + PIO0_RX1 + 5 + Select PIO0's RX FIFO 1 as TREQ + + + PIO0_RX2 + 6 + Select PIO0's RX FIFO 2 as TREQ + + + PIO0_RX3 + 7 + Select PIO0's RX FIFO 3 as TREQ + + + PIO1_TX0 + 8 + Select PIO1's TX FIFO 0 as TREQ + + + PIO1_TX1 + 9 + Select PIO1's TX FIFO 1 as TREQ + + + PIO1_TX2 + 10 + Select PIO1's TX FIFO 2 as TREQ + + + PIO1_TX3 + 11 + Select PIO1's TX FIFO 3 as TREQ + + + PIO1_RX0 + 12 + Select PIO1's RX FIFO 0 as TREQ + + + PIO1_RX1 + 13 + Select PIO1's RX FIFO 1 as TREQ + + + PIO1_RX2 + 14 + Select PIO1's RX FIFO 2 as TREQ + + + PIO1_RX3 + 15 + Select PIO1's RX FIFO 3 as TREQ + + + PIO2_TX0 + 16 + Select PIO2's TX FIFO 0 as TREQ + + + PIO2_TX1 + 17 + Select PIO2's TX FIFO 1 as TREQ + + + PIO2_TX2 + 18 + Select PIO2's TX FIFO 2 as TREQ + + + PIO2_TX3 + 19 + Select PIO2's TX FIFO 3 as TREQ + + + PIO2_RX0 + 20 + Select PIO2's RX FIFO 0 as TREQ + + + PIO2_RX1 + 21 + Select PIO2's RX FIFO 1 as TREQ + + + PIO2_RX2 + 22 + Select PIO2's RX FIFO 2 as TREQ + + + PIO2_RX3 + 23 + Select PIO2's RX FIFO 3 as TREQ + + + SPI0_TX + 24 + Select SPI0's TX FIFO as TREQ + + + SPI0_RX + 25 + Select SPI0's RX FIFO as TREQ + + + SPI1_TX + 26 + Select SPI1's TX FIFO as TREQ + + + SPI1_RX + 27 + Select SPI1's RX FIFO as TREQ + + + UART0_TX + 28 + Select UART0's TX FIFO as TREQ + + + UART0_RX + 29 + Select UART0's RX FIFO as TREQ + + + UART1_TX + 30 + Select UART1's TX FIFO as TREQ + + + UART1_RX + 31 + Select UART1's RX FIFO as TREQ + + + PWM_WRAP0 + 32 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP1 + 33 + Select PWM Counter 1's Wrap Value as TREQ + + + PWM_WRAP2 + 34 + Select PWM Counter 2's Wrap Value as TREQ + + + PWM_WRAP3 + 35 + Select PWM Counter 3's Wrap Value as TREQ + + + PWM_WRAP4 + 36 + Select PWM Counter 4's Wrap Value as TREQ + + + PWM_WRAP5 + 37 + Select PWM Counter 5's Wrap Value as TREQ + + + PWM_WRAP6 + 38 + Select PWM Counter 6's Wrap Value as TREQ + + + PWM_WRAP7 + 39 + Select PWM Counter 7's Wrap Value as TREQ + + + PWM_WRAP8 + 40 + Select PWM Counter 8's Wrap Value as TREQ + + + PWM_WRAP9 + 41 + Select PWM Counter 9's Wrap Value as TREQ + + + PWM_WRAP10 + 42 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP11 + 43 + Select PWM Counter 1's Wrap Value as TREQ + + + I2C0_TX + 44 + Select I2C0's TX FIFO as TREQ + + + I2C0_RX + 45 + Select I2C0's RX FIFO as TREQ + + + I2C1_TX + 46 + Select I2C1's TX FIFO as TREQ + + + I2C1_RX + 47 + Select I2C1's RX FIFO as TREQ + + + ADC + 48 + Select the ADC as TREQ + + + XIP_STREAM + 49 + Select the XIP Streaming FIFO as TREQ + + + XIP_QMITX + 50 + Select XIP_QMITX as TREQ + + + XIP_QMIRX + 51 + Select XIP_QMIRX as TREQ + + + HSTX + 52 + Select HSTX as TREQ + + + CORESIGHT + 53 + Select CORESIGHT as TREQ + + + SHA256 + 54 + Select SHA256 as TREQ + + + TIMER0 + 59 + Select Timer 0 as TREQ + + + TIMER1 + 60 + Select Timer 1 as TREQ + + + TIMER2 + 61 + Select Timer 2 as TREQ (Optional) + + + TIMER3 + 62 + Select Timer 3 as TREQ (Optional) + + + PERMANENT + 63 + Permanent request, for unpaced transfers. + + + + + CHAIN_TO + When this channel completes, it will trigger the channel indicated by CHAIN_TO. Disable by setting CHAIN_TO = _(this channel)_. + + Note this field resets to 0, so channels 1 and above will chain to channel 0 by default. Set this field to avoid this behaviour. + [16:13] + read-write + + + RING_SEL + Select whether RING_SIZE applies to read or write addresses. + If 0, read addresses are wrapped on a (1 << RING_SIZE) boundary. If 1, write addresses are wrapped. + [12:12] + read-write + + + RING_SIZE + Size of address wrap region. If 0, don't wrap. For values n > 0, only the lower n bits of the address will change. This wraps the address on a (1 << n) byte boundary, facilitating access to naturally-aligned ring buffers. + + Ring sizes between 2 and 32768 bytes are possible. This can apply to either read or write addresses, based on value of RING_SEL. + [11:8] + read-write + + + RING_NONE + 0 + + + + + INCR_WRITE_REV + If 1, and INCR_WRITE is 1, the write address is decremented rather than incremented with each transfer. + + If 1, and INCR_WRITE is 0, this otherwise-unused combination causes the write address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [7:7] + read-write + + + INCR_WRITE + If 1, the write address increments with each transfer. If 0, each write is directed to the same, initial address. + + Generally this should be disabled for memory-to-peripheral transfers. + [6:6] + read-write + + + INCR_READ_REV + If 1, and INCR_READ is 1, the read address is decremented rather than incremented with each transfer. + + If 1, and INCR_READ is 0, this otherwise-unused combination causes the read address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [5:5] + read-write + + + INCR_READ + If 1, the read address increments with each transfer. If 0, each read is directed to the same, initial address. + + Generally this should be disabled for peripheral-to-memory transfers. + [4:4] + read-write + + + DATA_SIZE + Set the size of each bus transfer (byte/halfword/word). READ_ADDR and WRITE_ADDR advance by this amount (1/2/4 bytes) with each transfer. + [3:2] + read-write + + + SIZE_BYTE + 0 + + + SIZE_HALFWORD + 1 + + + SIZE_WORD + 2 + + + + + HIGH_PRIORITY + HIGH_PRIORITY gives a channel preferential treatment in issue scheduling: in each scheduling round, all high priority channels are considered first, and then only a single low priority channel, before returning to the high priority channels. + + This only affects the order in which the DMA schedules channels. The DMA's bus priority is not changed. If the DMA is not saturated then a low priority channel will see no loss of throughput. + [1:1] + read-write + + + EN + DMA Channel Enable. + When 1, the channel will respond to triggering events, which will cause it to become BUSY and start transferring data. When 0, the channel will ignore triggers, stop issuing transfers, and pause the current transfer sequence (i.e. BUSY will remain high if already high) + [0:0] + read-write + + + + + CH9_AL1_CTRL + 0x00000250 + Alias for channel 9 CTRL register + 0x00000000 + + + CH9_AL1_CTRL + [31:0] + read-write + + + + + CH9_AL1_READ_ADDR + 0x00000254 + Alias for channel 9 READ_ADDR register + 0x00000000 + + + CH9_AL1_READ_ADDR + [31:0] + read-write + + + + + CH9_AL1_WRITE_ADDR + 0x00000258 + Alias for channel 9 WRITE_ADDR register + 0x00000000 + + + CH9_AL1_WRITE_ADDR + [31:0] + read-write + + + + + CH9_AL1_TRANS_COUNT_TRIG + 0x0000025c + Alias for channel 9 TRANS_COUNT register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH9_AL1_TRANS_COUNT_TRIG + [31:0] + read-write + + + + + CH9_AL2_CTRL + 0x00000260 + Alias for channel 9 CTRL register + 0x00000000 + + + CH9_AL2_CTRL + [31:0] + read-write + + + + + CH9_AL2_TRANS_COUNT + 0x00000264 + Alias for channel 9 TRANS_COUNT register + 0x00000000 + + + CH9_AL2_TRANS_COUNT + [31:0] + read-write + + + + + CH9_AL2_READ_ADDR + 0x00000268 + Alias for channel 9 READ_ADDR register + 0x00000000 + + + CH9_AL2_READ_ADDR + [31:0] + read-write + + + + + CH9_AL2_WRITE_ADDR_TRIG + 0x0000026c + Alias for channel 9 WRITE_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH9_AL2_WRITE_ADDR_TRIG + [31:0] + read-write + + + + + CH9_AL3_CTRL + 0x00000270 + Alias for channel 9 CTRL register + 0x00000000 + + + CH9_AL3_CTRL + [31:0] + read-write + + + + + CH9_AL3_WRITE_ADDR + 0x00000274 + Alias for channel 9 WRITE_ADDR register + 0x00000000 + + + CH9_AL3_WRITE_ADDR + [31:0] + read-write + + + + + CH9_AL3_TRANS_COUNT + 0x00000278 + Alias for channel 9 TRANS_COUNT register + 0x00000000 + + + CH9_AL3_TRANS_COUNT + [31:0] + read-write + + + + + CH9_AL3_READ_ADDR_TRIG + 0x0000027c + Alias for channel 9 READ_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH9_AL3_READ_ADDR_TRIG + [31:0] + read-write + + + + + CH10_READ_ADDR + 0x00000280 + DMA Channel 10 Read Address pointer + 0x00000000 + + + CH10_READ_ADDR + This register updates automatically each time a read completes. The current value is the next address to be read by this channel. + [31:0] + read-write + + + + + CH10_WRITE_ADDR + 0x00000284 + DMA Channel 10 Write Address pointer + 0x00000000 + + + CH10_WRITE_ADDR + This register updates automatically each time a write completes. The current value is the next address to be written by this channel. + [31:0] + read-write + + + + + CH10_TRANS_COUNT + 0x00000288 + DMA Channel 10 Transfer Count + 0x00000000 + + + MODE + When MODE is 0x0, the transfer count decrements with each transfer until 0, and then the channel triggers the next channel indicated by CTRL_CHAIN_TO. + + When MODE is 0x1, the transfer count decrements with each transfer until 0, and then the channel re-triggers itself, in addition to the trigger indicated by CTRL_CHAIN_TO. This is useful for e.g. an endless ring-buffer DMA with periodic interrupts. + + When MODE is 0xf, the transfer count does not decrement. The DMA channel performs an endless sequence of transfers, never triggering other channels or raising interrupts, until an ABORT is raised. + + All other values are reserved. + [31:28] + read-write + + + NORMAL + 0 + + + TRIGGER_SELF + 1 + + + ENDLESS + 15 + + + + + COUNT + 28-bit transfer count (256 million transfers maximum). + + Program the number of bus transfers a channel will perform before halting. Note that, if transfers are larger than one byte in size, this is not equal to the number of bytes transferred (see CTRL_DATA_SIZE). + + When the channel is active, reading this register shows the number of transfers remaining, updating automatically each time a write transfer completes. + + Writing this register sets the RELOAD value for the transfer counter. Each time this channel is triggered, the RELOAD value is copied into the live transfer counter. The channel can be started multiple times, and will perform the same number of transfers each time, as programmed by most recent write. + + The RELOAD value can be observed at CHx_DBG_TCR. If TRANS_COUNT is used as a trigger, the written value is used immediately as the length of the new transfer sequence, as well as being written to RELOAD. + [27:0] + read-write + + + + + CH10_CTRL_TRIG + 0x0000028c + DMA Channel 10 Control and Status + 0x00000000 + + + AHB_ERROR + Logical OR of the READ_ERROR and WRITE_ERROR flags. The channel halts when it encounters any bus error, and always raises its channel IRQ flag. + [31:31] + read-only + + + READ_ERROR + If 1, the channel received a read bus error. Write one to clear. + READ_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 3 transfers later) + [30:30] + read-write + oneToClear + + + WRITE_ERROR + If 1, the channel received a write bus error. Write one to clear. + WRITE_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 5 transfers later) + [29:29] + read-write + oneToClear + + + BUSY + This flag goes high when the channel starts a new transfer sequence, and low when the last transfer of that sequence completes. Clearing EN while BUSY is high pauses the channel, and BUSY will stay high while paused. + + To terminate a sequence early (and clear the BUSY flag), see CHAN_ABORT. + [26:26] + read-only + + + SNIFF_EN + If 1, this channel's data transfers are visible to the sniff hardware, and each transfer will advance the state of the checksum. This only applies if the sniff hardware is enabled, and has this channel selected. + + This allows checksum to be enabled or disabled on a per-control- block basis. + [25:25] + read-write + + + BSWAP + Apply byte-swap transformation to DMA data. + For byte data, this has no effect. For halfword data, the two bytes of each halfword are swapped. For word data, the four bytes of each word are swapped to reverse order. + [24:24] + read-write + + + IRQ_QUIET + In QUIET mode, the channel does not generate IRQs at the end of every transfer block. Instead, an IRQ is raised when NULL is written to a trigger register, indicating the end of a control block chain. + + This reduces the number of interrupts to be serviced by the CPU when transferring a DMA chain of many small control blocks. + [23:23] + read-write + + + TREQ_SEL + Select a Transfer Request signal. + The channel uses the transfer request signal to pace its data transfer rate. Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data Request from the system). + 0x0 to 0x3a -> select DREQ n as TREQ + [22:17] + read-write + + + PIO0_TX0 + 0 + Select PIO0's TX FIFO 0 as TREQ + + + PIO0_TX1 + 1 + Select PIO0's TX FIFO 1 as TREQ + + + PIO0_TX2 + 2 + Select PIO0's TX FIFO 2 as TREQ + + + PIO0_TX3 + 3 + Select PIO0's TX FIFO 3 as TREQ + + + PIO0_RX0 + 4 + Select PIO0's RX FIFO 0 as TREQ + + + PIO0_RX1 + 5 + Select PIO0's RX FIFO 1 as TREQ + + + PIO0_RX2 + 6 + Select PIO0's RX FIFO 2 as TREQ + + + PIO0_RX3 + 7 + Select PIO0's RX FIFO 3 as TREQ + + + PIO1_TX0 + 8 + Select PIO1's TX FIFO 0 as TREQ + + + PIO1_TX1 + 9 + Select PIO1's TX FIFO 1 as TREQ + + + PIO1_TX2 + 10 + Select PIO1's TX FIFO 2 as TREQ + + + PIO1_TX3 + 11 + Select PIO1's TX FIFO 3 as TREQ + + + PIO1_RX0 + 12 + Select PIO1's RX FIFO 0 as TREQ + + + PIO1_RX1 + 13 + Select PIO1's RX FIFO 1 as TREQ + + + PIO1_RX2 + 14 + Select PIO1's RX FIFO 2 as TREQ + + + PIO1_RX3 + 15 + Select PIO1's RX FIFO 3 as TREQ + + + PIO2_TX0 + 16 + Select PIO2's TX FIFO 0 as TREQ + + + PIO2_TX1 + 17 + Select PIO2's TX FIFO 1 as TREQ + + + PIO2_TX2 + 18 + Select PIO2's TX FIFO 2 as TREQ + + + PIO2_TX3 + 19 + Select PIO2's TX FIFO 3 as TREQ + + + PIO2_RX0 + 20 + Select PIO2's RX FIFO 0 as TREQ + + + PIO2_RX1 + 21 + Select PIO2's RX FIFO 1 as TREQ + + + PIO2_RX2 + 22 + Select PIO2's RX FIFO 2 as TREQ + + + PIO2_RX3 + 23 + Select PIO2's RX FIFO 3 as TREQ + + + SPI0_TX + 24 + Select SPI0's TX FIFO as TREQ + + + SPI0_RX + 25 + Select SPI0's RX FIFO as TREQ + + + SPI1_TX + 26 + Select SPI1's TX FIFO as TREQ + + + SPI1_RX + 27 + Select SPI1's RX FIFO as TREQ + + + UART0_TX + 28 + Select UART0's TX FIFO as TREQ + + + UART0_RX + 29 + Select UART0's RX FIFO as TREQ + + + UART1_TX + 30 + Select UART1's TX FIFO as TREQ + + + UART1_RX + 31 + Select UART1's RX FIFO as TREQ + + + PWM_WRAP0 + 32 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP1 + 33 + Select PWM Counter 1's Wrap Value as TREQ + + + PWM_WRAP2 + 34 + Select PWM Counter 2's Wrap Value as TREQ + + + PWM_WRAP3 + 35 + Select PWM Counter 3's Wrap Value as TREQ + + + PWM_WRAP4 + 36 + Select PWM Counter 4's Wrap Value as TREQ + + + PWM_WRAP5 + 37 + Select PWM Counter 5's Wrap Value as TREQ + + + PWM_WRAP6 + 38 + Select PWM Counter 6's Wrap Value as TREQ + + + PWM_WRAP7 + 39 + Select PWM Counter 7's Wrap Value as TREQ + + + PWM_WRAP8 + 40 + Select PWM Counter 8's Wrap Value as TREQ + + + PWM_WRAP9 + 41 + Select PWM Counter 9's Wrap Value as TREQ + + + PWM_WRAP10 + 42 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP11 + 43 + Select PWM Counter 1's Wrap Value as TREQ + + + I2C0_TX + 44 + Select I2C0's TX FIFO as TREQ + + + I2C0_RX + 45 + Select I2C0's RX FIFO as TREQ + + + I2C1_TX + 46 + Select I2C1's TX FIFO as TREQ + + + I2C1_RX + 47 + Select I2C1's RX FIFO as TREQ + + + ADC + 48 + Select the ADC as TREQ + + + XIP_STREAM + 49 + Select the XIP Streaming FIFO as TREQ + + + XIP_QMITX + 50 + Select XIP_QMITX as TREQ + + + XIP_QMIRX + 51 + Select XIP_QMIRX as TREQ + + + HSTX + 52 + Select HSTX as TREQ + + + CORESIGHT + 53 + Select CORESIGHT as TREQ + + + SHA256 + 54 + Select SHA256 as TREQ + + + TIMER0 + 59 + Select Timer 0 as TREQ + + + TIMER1 + 60 + Select Timer 1 as TREQ + + + TIMER2 + 61 + Select Timer 2 as TREQ (Optional) + + + TIMER3 + 62 + Select Timer 3 as TREQ (Optional) + + + PERMANENT + 63 + Permanent request, for unpaced transfers. + + + + + CHAIN_TO + When this channel completes, it will trigger the channel indicated by CHAIN_TO. Disable by setting CHAIN_TO = _(this channel)_. + + Note this field resets to 0, so channels 1 and above will chain to channel 0 by default. Set this field to avoid this behaviour. + [16:13] + read-write + + + RING_SEL + Select whether RING_SIZE applies to read or write addresses. + If 0, read addresses are wrapped on a (1 << RING_SIZE) boundary. If 1, write addresses are wrapped. + [12:12] + read-write + + + RING_SIZE + Size of address wrap region. If 0, don't wrap. For values n > 0, only the lower n bits of the address will change. This wraps the address on a (1 << n) byte boundary, facilitating access to naturally-aligned ring buffers. + + Ring sizes between 2 and 32768 bytes are possible. This can apply to either read or write addresses, based on value of RING_SEL. + [11:8] + read-write + + + RING_NONE + 0 + + + + + INCR_WRITE_REV + If 1, and INCR_WRITE is 1, the write address is decremented rather than incremented with each transfer. + + If 1, and INCR_WRITE is 0, this otherwise-unused combination causes the write address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [7:7] + read-write + + + INCR_WRITE + If 1, the write address increments with each transfer. If 0, each write is directed to the same, initial address. + + Generally this should be disabled for memory-to-peripheral transfers. + [6:6] + read-write + + + INCR_READ_REV + If 1, and INCR_READ is 1, the read address is decremented rather than incremented with each transfer. + + If 1, and INCR_READ is 0, this otherwise-unused combination causes the read address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [5:5] + read-write + + + INCR_READ + If 1, the read address increments with each transfer. If 0, each read is directed to the same, initial address. + + Generally this should be disabled for peripheral-to-memory transfers. + [4:4] + read-write + + + DATA_SIZE + Set the size of each bus transfer (byte/halfword/word). READ_ADDR and WRITE_ADDR advance by this amount (1/2/4 bytes) with each transfer. + [3:2] + read-write + + + SIZE_BYTE + 0 + + + SIZE_HALFWORD + 1 + + + SIZE_WORD + 2 + + + + + HIGH_PRIORITY + HIGH_PRIORITY gives a channel preferential treatment in issue scheduling: in each scheduling round, all high priority channels are considered first, and then only a single low priority channel, before returning to the high priority channels. + + This only affects the order in which the DMA schedules channels. The DMA's bus priority is not changed. If the DMA is not saturated then a low priority channel will see no loss of throughput. + [1:1] + read-write + + + EN + DMA Channel Enable. + When 1, the channel will respond to triggering events, which will cause it to become BUSY and start transferring data. When 0, the channel will ignore triggers, stop issuing transfers, and pause the current transfer sequence (i.e. BUSY will remain high if already high) + [0:0] + read-write + + + + + CH10_AL1_CTRL + 0x00000290 + Alias for channel 10 CTRL register + 0x00000000 + + + CH10_AL1_CTRL + [31:0] + read-write + + + + + CH10_AL1_READ_ADDR + 0x00000294 + Alias for channel 10 READ_ADDR register + 0x00000000 + + + CH10_AL1_READ_ADDR + [31:0] + read-write + + + + + CH10_AL1_WRITE_ADDR + 0x00000298 + Alias for channel 10 WRITE_ADDR register + 0x00000000 + + + CH10_AL1_WRITE_ADDR + [31:0] + read-write + + + + + CH10_AL1_TRANS_COUNT_TRIG + 0x0000029c + Alias for channel 10 TRANS_COUNT register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH10_AL1_TRANS_COUNT_TRIG + [31:0] + read-write + + + + + CH10_AL2_CTRL + 0x000002a0 + Alias for channel 10 CTRL register + 0x00000000 + + + CH10_AL2_CTRL + [31:0] + read-write + + + + + CH10_AL2_TRANS_COUNT + 0x000002a4 + Alias for channel 10 TRANS_COUNT register + 0x00000000 + + + CH10_AL2_TRANS_COUNT + [31:0] + read-write + + + + + CH10_AL2_READ_ADDR + 0x000002a8 + Alias for channel 10 READ_ADDR register + 0x00000000 + + + CH10_AL2_READ_ADDR + [31:0] + read-write + + + + + CH10_AL2_WRITE_ADDR_TRIG + 0x000002ac + Alias for channel 10 WRITE_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH10_AL2_WRITE_ADDR_TRIG + [31:0] + read-write + + + + + CH10_AL3_CTRL + 0x000002b0 + Alias for channel 10 CTRL register + 0x00000000 + + + CH10_AL3_CTRL + [31:0] + read-write + + + + + CH10_AL3_WRITE_ADDR + 0x000002b4 + Alias for channel 10 WRITE_ADDR register + 0x00000000 + + + CH10_AL3_WRITE_ADDR + [31:0] + read-write + + + + + CH10_AL3_TRANS_COUNT + 0x000002b8 + Alias for channel 10 TRANS_COUNT register + 0x00000000 + + + CH10_AL3_TRANS_COUNT + [31:0] + read-write + + + + + CH10_AL3_READ_ADDR_TRIG + 0x000002bc + Alias for channel 10 READ_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH10_AL3_READ_ADDR_TRIG + [31:0] + read-write + + + + + CH11_READ_ADDR + 0x000002c0 + DMA Channel 11 Read Address pointer + 0x00000000 + + + CH11_READ_ADDR + This register updates automatically each time a read completes. The current value is the next address to be read by this channel. + [31:0] + read-write + + + + + CH11_WRITE_ADDR + 0x000002c4 + DMA Channel 11 Write Address pointer + 0x00000000 + + + CH11_WRITE_ADDR + This register updates automatically each time a write completes. The current value is the next address to be written by this channel. + [31:0] + read-write + + + + + CH11_TRANS_COUNT + 0x000002c8 + DMA Channel 11 Transfer Count + 0x00000000 + + + MODE + When MODE is 0x0, the transfer count decrements with each transfer until 0, and then the channel triggers the next channel indicated by CTRL_CHAIN_TO. + + When MODE is 0x1, the transfer count decrements with each transfer until 0, and then the channel re-triggers itself, in addition to the trigger indicated by CTRL_CHAIN_TO. This is useful for e.g. an endless ring-buffer DMA with periodic interrupts. + + When MODE is 0xf, the transfer count does not decrement. The DMA channel performs an endless sequence of transfers, never triggering other channels or raising interrupts, until an ABORT is raised. + + All other values are reserved. + [31:28] + read-write + + + NORMAL + 0 + + + TRIGGER_SELF + 1 + + + ENDLESS + 15 + + + + + COUNT + 28-bit transfer count (256 million transfers maximum). + + Program the number of bus transfers a channel will perform before halting. Note that, if transfers are larger than one byte in size, this is not equal to the number of bytes transferred (see CTRL_DATA_SIZE). + + When the channel is active, reading this register shows the number of transfers remaining, updating automatically each time a write transfer completes. + + Writing this register sets the RELOAD value for the transfer counter. Each time this channel is triggered, the RELOAD value is copied into the live transfer counter. The channel can be started multiple times, and will perform the same number of transfers each time, as programmed by most recent write. + + The RELOAD value can be observed at CHx_DBG_TCR. If TRANS_COUNT is used as a trigger, the written value is used immediately as the length of the new transfer sequence, as well as being written to RELOAD. + [27:0] + read-write + + + + + CH11_CTRL_TRIG + 0x000002cc + DMA Channel 11 Control and Status + 0x00000000 + + + AHB_ERROR + Logical OR of the READ_ERROR and WRITE_ERROR flags. The channel halts when it encounters any bus error, and always raises its channel IRQ flag. + [31:31] + read-only + + + READ_ERROR + If 1, the channel received a read bus error. Write one to clear. + READ_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 3 transfers later) + [30:30] + read-write + oneToClear + + + WRITE_ERROR + If 1, the channel received a write bus error. Write one to clear. + WRITE_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 5 transfers later) + [29:29] + read-write + oneToClear + + + BUSY + This flag goes high when the channel starts a new transfer sequence, and low when the last transfer of that sequence completes. Clearing EN while BUSY is high pauses the channel, and BUSY will stay high while paused. + + To terminate a sequence early (and clear the BUSY flag), see CHAN_ABORT. + [26:26] + read-only + + + SNIFF_EN + If 1, this channel's data transfers are visible to the sniff hardware, and each transfer will advance the state of the checksum. This only applies if the sniff hardware is enabled, and has this channel selected. + + This allows checksum to be enabled or disabled on a per-control- block basis. + [25:25] + read-write + + + BSWAP + Apply byte-swap transformation to DMA data. + For byte data, this has no effect. For halfword data, the two bytes of each halfword are swapped. For word data, the four bytes of each word are swapped to reverse order. + [24:24] + read-write + + + IRQ_QUIET + In QUIET mode, the channel does not generate IRQs at the end of every transfer block. Instead, an IRQ is raised when NULL is written to a trigger register, indicating the end of a control block chain. + + This reduces the number of interrupts to be serviced by the CPU when transferring a DMA chain of many small control blocks. + [23:23] + read-write + + + TREQ_SEL + Select a Transfer Request signal. + The channel uses the transfer request signal to pace its data transfer rate. Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data Request from the system). + 0x0 to 0x3a -> select DREQ n as TREQ + [22:17] + read-write + + + PIO0_TX0 + 0 + Select PIO0's TX FIFO 0 as TREQ + + + PIO0_TX1 + 1 + Select PIO0's TX FIFO 1 as TREQ + + + PIO0_TX2 + 2 + Select PIO0's TX FIFO 2 as TREQ + + + PIO0_TX3 + 3 + Select PIO0's TX FIFO 3 as TREQ + + + PIO0_RX0 + 4 + Select PIO0's RX FIFO 0 as TREQ + + + PIO0_RX1 + 5 + Select PIO0's RX FIFO 1 as TREQ + + + PIO0_RX2 + 6 + Select PIO0's RX FIFO 2 as TREQ + + + PIO0_RX3 + 7 + Select PIO0's RX FIFO 3 as TREQ + + + PIO1_TX0 + 8 + Select PIO1's TX FIFO 0 as TREQ + + + PIO1_TX1 + 9 + Select PIO1's TX FIFO 1 as TREQ + + + PIO1_TX2 + 10 + Select PIO1's TX FIFO 2 as TREQ + + + PIO1_TX3 + 11 + Select PIO1's TX FIFO 3 as TREQ + + + PIO1_RX0 + 12 + Select PIO1's RX FIFO 0 as TREQ + + + PIO1_RX1 + 13 + Select PIO1's RX FIFO 1 as TREQ + + + PIO1_RX2 + 14 + Select PIO1's RX FIFO 2 as TREQ + + + PIO1_RX3 + 15 + Select PIO1's RX FIFO 3 as TREQ + + + PIO2_TX0 + 16 + Select PIO2's TX FIFO 0 as TREQ + + + PIO2_TX1 + 17 + Select PIO2's TX FIFO 1 as TREQ + + + PIO2_TX2 + 18 + Select PIO2's TX FIFO 2 as TREQ + + + PIO2_TX3 + 19 + Select PIO2's TX FIFO 3 as TREQ + + + PIO2_RX0 + 20 + Select PIO2's RX FIFO 0 as TREQ + + + PIO2_RX1 + 21 + Select PIO2's RX FIFO 1 as TREQ + + + PIO2_RX2 + 22 + Select PIO2's RX FIFO 2 as TREQ + + + PIO2_RX3 + 23 + Select PIO2's RX FIFO 3 as TREQ + + + SPI0_TX + 24 + Select SPI0's TX FIFO as TREQ + + + SPI0_RX + 25 + Select SPI0's RX FIFO as TREQ + + + SPI1_TX + 26 + Select SPI1's TX FIFO as TREQ + + + SPI1_RX + 27 + Select SPI1's RX FIFO as TREQ + + + UART0_TX + 28 + Select UART0's TX FIFO as TREQ + + + UART0_RX + 29 + Select UART0's RX FIFO as TREQ + + + UART1_TX + 30 + Select UART1's TX FIFO as TREQ + + + UART1_RX + 31 + Select UART1's RX FIFO as TREQ + + + PWM_WRAP0 + 32 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP1 + 33 + Select PWM Counter 1's Wrap Value as TREQ + + + PWM_WRAP2 + 34 + Select PWM Counter 2's Wrap Value as TREQ + + + PWM_WRAP3 + 35 + Select PWM Counter 3's Wrap Value as TREQ + + + PWM_WRAP4 + 36 + Select PWM Counter 4's Wrap Value as TREQ + + + PWM_WRAP5 + 37 + Select PWM Counter 5's Wrap Value as TREQ + + + PWM_WRAP6 + 38 + Select PWM Counter 6's Wrap Value as TREQ + + + PWM_WRAP7 + 39 + Select PWM Counter 7's Wrap Value as TREQ + + + PWM_WRAP8 + 40 + Select PWM Counter 8's Wrap Value as TREQ + + + PWM_WRAP9 + 41 + Select PWM Counter 9's Wrap Value as TREQ + + + PWM_WRAP10 + 42 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP11 + 43 + Select PWM Counter 1's Wrap Value as TREQ + + + I2C0_TX + 44 + Select I2C0's TX FIFO as TREQ + + + I2C0_RX + 45 + Select I2C0's RX FIFO as TREQ + + + I2C1_TX + 46 + Select I2C1's TX FIFO as TREQ + + + I2C1_RX + 47 + Select I2C1's RX FIFO as TREQ + + + ADC + 48 + Select the ADC as TREQ + + + XIP_STREAM + 49 + Select the XIP Streaming FIFO as TREQ + + + XIP_QMITX + 50 + Select XIP_QMITX as TREQ + + + XIP_QMIRX + 51 + Select XIP_QMIRX as TREQ + + + HSTX + 52 + Select HSTX as TREQ + + + CORESIGHT + 53 + Select CORESIGHT as TREQ + + + SHA256 + 54 + Select SHA256 as TREQ + + + TIMER0 + 59 + Select Timer 0 as TREQ + + + TIMER1 + 60 + Select Timer 1 as TREQ + + + TIMER2 + 61 + Select Timer 2 as TREQ (Optional) + + + TIMER3 + 62 + Select Timer 3 as TREQ (Optional) + + + PERMANENT + 63 + Permanent request, for unpaced transfers. + + + + + CHAIN_TO + When this channel completes, it will trigger the channel indicated by CHAIN_TO. Disable by setting CHAIN_TO = _(this channel)_. + + Note this field resets to 0, so channels 1 and above will chain to channel 0 by default. Set this field to avoid this behaviour. + [16:13] + read-write + + + RING_SEL + Select whether RING_SIZE applies to read or write addresses. + If 0, read addresses are wrapped on a (1 << RING_SIZE) boundary. If 1, write addresses are wrapped. + [12:12] + read-write + + + RING_SIZE + Size of address wrap region. If 0, don't wrap. For values n > 0, only the lower n bits of the address will change. This wraps the address on a (1 << n) byte boundary, facilitating access to naturally-aligned ring buffers. + + Ring sizes between 2 and 32768 bytes are possible. This can apply to either read or write addresses, based on value of RING_SEL. + [11:8] + read-write + + + RING_NONE + 0 + + + + + INCR_WRITE_REV + If 1, and INCR_WRITE is 1, the write address is decremented rather than incremented with each transfer. + + If 1, and INCR_WRITE is 0, this otherwise-unused combination causes the write address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [7:7] + read-write + + + INCR_WRITE + If 1, the write address increments with each transfer. If 0, each write is directed to the same, initial address. + + Generally this should be disabled for memory-to-peripheral transfers. + [6:6] + read-write + + + INCR_READ_REV + If 1, and INCR_READ is 1, the read address is decremented rather than incremented with each transfer. + + If 1, and INCR_READ is 0, this otherwise-unused combination causes the read address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [5:5] + read-write + + + INCR_READ + If 1, the read address increments with each transfer. If 0, each read is directed to the same, initial address. + + Generally this should be disabled for peripheral-to-memory transfers. + [4:4] + read-write + + + DATA_SIZE + Set the size of each bus transfer (byte/halfword/word). READ_ADDR and WRITE_ADDR advance by this amount (1/2/4 bytes) with each transfer. + [3:2] + read-write + + + SIZE_BYTE + 0 + + + SIZE_HALFWORD + 1 + + + SIZE_WORD + 2 + + + + + HIGH_PRIORITY + HIGH_PRIORITY gives a channel preferential treatment in issue scheduling: in each scheduling round, all high priority channels are considered first, and then only a single low priority channel, before returning to the high priority channels. + + This only affects the order in which the DMA schedules channels. The DMA's bus priority is not changed. If the DMA is not saturated then a low priority channel will see no loss of throughput. + [1:1] + read-write + + + EN + DMA Channel Enable. + When 1, the channel will respond to triggering events, which will cause it to become BUSY and start transferring data. When 0, the channel will ignore triggers, stop issuing transfers, and pause the current transfer sequence (i.e. BUSY will remain high if already high) + [0:0] + read-write + + + + + CH11_AL1_CTRL + 0x000002d0 + Alias for channel 11 CTRL register + 0x00000000 + + + CH11_AL1_CTRL + [31:0] + read-write + + + + + CH11_AL1_READ_ADDR + 0x000002d4 + Alias for channel 11 READ_ADDR register + 0x00000000 + + + CH11_AL1_READ_ADDR + [31:0] + read-write + + + + + CH11_AL1_WRITE_ADDR + 0x000002d8 + Alias for channel 11 WRITE_ADDR register + 0x00000000 + + + CH11_AL1_WRITE_ADDR + [31:0] + read-write + + + + + CH11_AL1_TRANS_COUNT_TRIG + 0x000002dc + Alias for channel 11 TRANS_COUNT register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH11_AL1_TRANS_COUNT_TRIG + [31:0] + read-write + + + + + CH11_AL2_CTRL + 0x000002e0 + Alias for channel 11 CTRL register + 0x00000000 + + + CH11_AL2_CTRL + [31:0] + read-write + + + + + CH11_AL2_TRANS_COUNT + 0x000002e4 + Alias for channel 11 TRANS_COUNT register + 0x00000000 + + + CH11_AL2_TRANS_COUNT + [31:0] + read-write + + + + + CH11_AL2_READ_ADDR + 0x000002e8 + Alias for channel 11 READ_ADDR register + 0x00000000 + + + CH11_AL2_READ_ADDR + [31:0] + read-write + + + + + CH11_AL2_WRITE_ADDR_TRIG + 0x000002ec + Alias for channel 11 WRITE_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH11_AL2_WRITE_ADDR_TRIG + [31:0] + read-write + + + + + CH11_AL3_CTRL + 0x000002f0 + Alias for channel 11 CTRL register + 0x00000000 + + + CH11_AL3_CTRL + [31:0] + read-write + + + + + CH11_AL3_WRITE_ADDR + 0x000002f4 + Alias for channel 11 WRITE_ADDR register + 0x00000000 + + + CH11_AL3_WRITE_ADDR + [31:0] + read-write + + + + + CH11_AL3_TRANS_COUNT + 0x000002f8 + Alias for channel 11 TRANS_COUNT register + 0x00000000 + + + CH11_AL3_TRANS_COUNT + [31:0] + read-write + + + + + CH11_AL3_READ_ADDR_TRIG + 0x000002fc + Alias for channel 11 READ_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH11_AL3_READ_ADDR_TRIG + [31:0] + read-write + + + + + CH12_READ_ADDR + 0x00000300 + DMA Channel 12 Read Address pointer + 0x00000000 + + + CH12_READ_ADDR + This register updates automatically each time a read completes. The current value is the next address to be read by this channel. + [31:0] + read-write + + + + + CH12_WRITE_ADDR + 0x00000304 + DMA Channel 12 Write Address pointer + 0x00000000 + + + CH12_WRITE_ADDR + This register updates automatically each time a write completes. The current value is the next address to be written by this channel. + [31:0] + read-write + + + + + CH12_TRANS_COUNT + 0x00000308 + DMA Channel 12 Transfer Count + 0x00000000 + + + MODE + When MODE is 0x0, the transfer count decrements with each transfer until 0, and then the channel triggers the next channel indicated by CTRL_CHAIN_TO. + + When MODE is 0x1, the transfer count decrements with each transfer until 0, and then the channel re-triggers itself, in addition to the trigger indicated by CTRL_CHAIN_TO. This is useful for e.g. an endless ring-buffer DMA with periodic interrupts. + + When MODE is 0xf, the transfer count does not decrement. The DMA channel performs an endless sequence of transfers, never triggering other channels or raising interrupts, until an ABORT is raised. + + All other values are reserved. + [31:28] + read-write + + + NORMAL + 0 + + + TRIGGER_SELF + 1 + + + ENDLESS + 15 + + + + + COUNT + 28-bit transfer count (256 million transfers maximum). + + Program the number of bus transfers a channel will perform before halting. Note that, if transfers are larger than one byte in size, this is not equal to the number of bytes transferred (see CTRL_DATA_SIZE). + + When the channel is active, reading this register shows the number of transfers remaining, updating automatically each time a write transfer completes. + + Writing this register sets the RELOAD value for the transfer counter. Each time this channel is triggered, the RELOAD value is copied into the live transfer counter. The channel can be started multiple times, and will perform the same number of transfers each time, as programmed by most recent write. + + The RELOAD value can be observed at CHx_DBG_TCR. If TRANS_COUNT is used as a trigger, the written value is used immediately as the length of the new transfer sequence, as well as being written to RELOAD. + [27:0] + read-write + + + + + CH12_CTRL_TRIG + 0x0000030c + DMA Channel 12 Control and Status + 0x00000000 + + + AHB_ERROR + Logical OR of the READ_ERROR and WRITE_ERROR flags. The channel halts when it encounters any bus error, and always raises its channel IRQ flag. + [31:31] + read-only + + + READ_ERROR + If 1, the channel received a read bus error. Write one to clear. + READ_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 3 transfers later) + [30:30] + read-write + oneToClear + + + WRITE_ERROR + If 1, the channel received a write bus error. Write one to clear. + WRITE_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 5 transfers later) + [29:29] + read-write + oneToClear + + + BUSY + This flag goes high when the channel starts a new transfer sequence, and low when the last transfer of that sequence completes. Clearing EN while BUSY is high pauses the channel, and BUSY will stay high while paused. + + To terminate a sequence early (and clear the BUSY flag), see CHAN_ABORT. + [26:26] + read-only + + + SNIFF_EN + If 1, this channel's data transfers are visible to the sniff hardware, and each transfer will advance the state of the checksum. This only applies if the sniff hardware is enabled, and has this channel selected. + + This allows checksum to be enabled or disabled on a per-control- block basis. + [25:25] + read-write + + + BSWAP + Apply byte-swap transformation to DMA data. + For byte data, this has no effect. For halfword data, the two bytes of each halfword are swapped. For word data, the four bytes of each word are swapped to reverse order. + [24:24] + read-write + + + IRQ_QUIET + In QUIET mode, the channel does not generate IRQs at the end of every transfer block. Instead, an IRQ is raised when NULL is written to a trigger register, indicating the end of a control block chain. + + This reduces the number of interrupts to be serviced by the CPU when transferring a DMA chain of many small control blocks. + [23:23] + read-write + + + TREQ_SEL + Select a Transfer Request signal. + The channel uses the transfer request signal to pace its data transfer rate. Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data Request from the system). + 0x0 to 0x3a -> select DREQ n as TREQ + [22:17] + read-write + + + PIO0_TX0 + 0 + Select PIO0's TX FIFO 0 as TREQ + + + PIO0_TX1 + 1 + Select PIO0's TX FIFO 1 as TREQ + + + PIO0_TX2 + 2 + Select PIO0's TX FIFO 2 as TREQ + + + PIO0_TX3 + 3 + Select PIO0's TX FIFO 3 as TREQ + + + PIO0_RX0 + 4 + Select PIO0's RX FIFO 0 as TREQ + + + PIO0_RX1 + 5 + Select PIO0's RX FIFO 1 as TREQ + + + PIO0_RX2 + 6 + Select PIO0's RX FIFO 2 as TREQ + + + PIO0_RX3 + 7 + Select PIO0's RX FIFO 3 as TREQ + + + PIO1_TX0 + 8 + Select PIO1's TX FIFO 0 as TREQ + + + PIO1_TX1 + 9 + Select PIO1's TX FIFO 1 as TREQ + + + PIO1_TX2 + 10 + Select PIO1's TX FIFO 2 as TREQ + + + PIO1_TX3 + 11 + Select PIO1's TX FIFO 3 as TREQ + + + PIO1_RX0 + 12 + Select PIO1's RX FIFO 0 as TREQ + + + PIO1_RX1 + 13 + Select PIO1's RX FIFO 1 as TREQ + + + PIO1_RX2 + 14 + Select PIO1's RX FIFO 2 as TREQ + + + PIO1_RX3 + 15 + Select PIO1's RX FIFO 3 as TREQ + + + PIO2_TX0 + 16 + Select PIO2's TX FIFO 0 as TREQ + + + PIO2_TX1 + 17 + Select PIO2's TX FIFO 1 as TREQ + + + PIO2_TX2 + 18 + Select PIO2's TX FIFO 2 as TREQ + + + PIO2_TX3 + 19 + Select PIO2's TX FIFO 3 as TREQ + + + PIO2_RX0 + 20 + Select PIO2's RX FIFO 0 as TREQ + + + PIO2_RX1 + 21 + Select PIO2's RX FIFO 1 as TREQ + + + PIO2_RX2 + 22 + Select PIO2's RX FIFO 2 as TREQ + + + PIO2_RX3 + 23 + Select PIO2's RX FIFO 3 as TREQ + + + SPI0_TX + 24 + Select SPI0's TX FIFO as TREQ + + + SPI0_RX + 25 + Select SPI0's RX FIFO as TREQ + + + SPI1_TX + 26 + Select SPI1's TX FIFO as TREQ + + + SPI1_RX + 27 + Select SPI1's RX FIFO as TREQ + + + UART0_TX + 28 + Select UART0's TX FIFO as TREQ + + + UART0_RX + 29 + Select UART0's RX FIFO as TREQ + + + UART1_TX + 30 + Select UART1's TX FIFO as TREQ + + + UART1_RX + 31 + Select UART1's RX FIFO as TREQ + + + PWM_WRAP0 + 32 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP1 + 33 + Select PWM Counter 1's Wrap Value as TREQ + + + PWM_WRAP2 + 34 + Select PWM Counter 2's Wrap Value as TREQ + + + PWM_WRAP3 + 35 + Select PWM Counter 3's Wrap Value as TREQ + + + PWM_WRAP4 + 36 + Select PWM Counter 4's Wrap Value as TREQ + + + PWM_WRAP5 + 37 + Select PWM Counter 5's Wrap Value as TREQ + + + PWM_WRAP6 + 38 + Select PWM Counter 6's Wrap Value as TREQ + + + PWM_WRAP7 + 39 + Select PWM Counter 7's Wrap Value as TREQ + + + PWM_WRAP8 + 40 + Select PWM Counter 8's Wrap Value as TREQ + + + PWM_WRAP9 + 41 + Select PWM Counter 9's Wrap Value as TREQ + + + PWM_WRAP10 + 42 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP11 + 43 + Select PWM Counter 1's Wrap Value as TREQ + + + I2C0_TX + 44 + Select I2C0's TX FIFO as TREQ + + + I2C0_RX + 45 + Select I2C0's RX FIFO as TREQ + + + I2C1_TX + 46 + Select I2C1's TX FIFO as TREQ + + + I2C1_RX + 47 + Select I2C1's RX FIFO as TREQ + + + ADC + 48 + Select the ADC as TREQ + + + XIP_STREAM + 49 + Select the XIP Streaming FIFO as TREQ + + + XIP_QMITX + 50 + Select XIP_QMITX as TREQ + + + XIP_QMIRX + 51 + Select XIP_QMIRX as TREQ + + + HSTX + 52 + Select HSTX as TREQ + + + CORESIGHT + 53 + Select CORESIGHT as TREQ + + + SHA256 + 54 + Select SHA256 as TREQ + + + TIMER0 + 59 + Select Timer 0 as TREQ + + + TIMER1 + 60 + Select Timer 1 as TREQ + + + TIMER2 + 61 + Select Timer 2 as TREQ (Optional) + + + TIMER3 + 62 + Select Timer 3 as TREQ (Optional) + + + PERMANENT + 63 + Permanent request, for unpaced transfers. + + + + + CHAIN_TO + When this channel completes, it will trigger the channel indicated by CHAIN_TO. Disable by setting CHAIN_TO = _(this channel)_. + + Note this field resets to 0, so channels 1 and above will chain to channel 0 by default. Set this field to avoid this behaviour. + [16:13] + read-write + + + RING_SEL + Select whether RING_SIZE applies to read or write addresses. + If 0, read addresses are wrapped on a (1 << RING_SIZE) boundary. If 1, write addresses are wrapped. + [12:12] + read-write + + + RING_SIZE + Size of address wrap region. If 0, don't wrap. For values n > 0, only the lower n bits of the address will change. This wraps the address on a (1 << n) byte boundary, facilitating access to naturally-aligned ring buffers. + + Ring sizes between 2 and 32768 bytes are possible. This can apply to either read or write addresses, based on value of RING_SEL. + [11:8] + read-write + + + RING_NONE + 0 + + + + + INCR_WRITE_REV + If 1, and INCR_WRITE is 1, the write address is decremented rather than incremented with each transfer. + + If 1, and INCR_WRITE is 0, this otherwise-unused combination causes the write address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [7:7] + read-write + + + INCR_WRITE + If 1, the write address increments with each transfer. If 0, each write is directed to the same, initial address. + + Generally this should be disabled for memory-to-peripheral transfers. + [6:6] + read-write + + + INCR_READ_REV + If 1, and INCR_READ is 1, the read address is decremented rather than incremented with each transfer. + + If 1, and INCR_READ is 0, this otherwise-unused combination causes the read address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [5:5] + read-write + + + INCR_READ + If 1, the read address increments with each transfer. If 0, each read is directed to the same, initial address. + + Generally this should be disabled for peripheral-to-memory transfers. + [4:4] + read-write + + + DATA_SIZE + Set the size of each bus transfer (byte/halfword/word). READ_ADDR and WRITE_ADDR advance by this amount (1/2/4 bytes) with each transfer. + [3:2] + read-write + + + SIZE_BYTE + 0 + + + SIZE_HALFWORD + 1 + + + SIZE_WORD + 2 + + + + + HIGH_PRIORITY + HIGH_PRIORITY gives a channel preferential treatment in issue scheduling: in each scheduling round, all high priority channels are considered first, and then only a single low priority channel, before returning to the high priority channels. + + This only affects the order in which the DMA schedules channels. The DMA's bus priority is not changed. If the DMA is not saturated then a low priority channel will see no loss of throughput. + [1:1] + read-write + + + EN + DMA Channel Enable. + When 1, the channel will respond to triggering events, which will cause it to become BUSY and start transferring data. When 0, the channel will ignore triggers, stop issuing transfers, and pause the current transfer sequence (i.e. BUSY will remain high if already high) + [0:0] + read-write + + + + + CH12_AL1_CTRL + 0x00000310 + Alias for channel 12 CTRL register + 0x00000000 + + + CH12_AL1_CTRL + [31:0] + read-write + + + + + CH12_AL1_READ_ADDR + 0x00000314 + Alias for channel 12 READ_ADDR register + 0x00000000 + + + CH12_AL1_READ_ADDR + [31:0] + read-write + + + + + CH12_AL1_WRITE_ADDR + 0x00000318 + Alias for channel 12 WRITE_ADDR register + 0x00000000 + + + CH12_AL1_WRITE_ADDR + [31:0] + read-write + + + + + CH12_AL1_TRANS_COUNT_TRIG + 0x0000031c + Alias for channel 12 TRANS_COUNT register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH12_AL1_TRANS_COUNT_TRIG + [31:0] + read-write + + + + + CH12_AL2_CTRL + 0x00000320 + Alias for channel 12 CTRL register + 0x00000000 + + + CH12_AL2_CTRL + [31:0] + read-write + + + + + CH12_AL2_TRANS_COUNT + 0x00000324 + Alias for channel 12 TRANS_COUNT register + 0x00000000 + + + CH12_AL2_TRANS_COUNT + [31:0] + read-write + + + + + CH12_AL2_READ_ADDR + 0x00000328 + Alias for channel 12 READ_ADDR register + 0x00000000 + + + CH12_AL2_READ_ADDR + [31:0] + read-write + + + + + CH12_AL2_WRITE_ADDR_TRIG + 0x0000032c + Alias for channel 12 WRITE_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH12_AL2_WRITE_ADDR_TRIG + [31:0] + read-write + + + + + CH12_AL3_CTRL + 0x00000330 + Alias for channel 12 CTRL register + 0x00000000 + + + CH12_AL3_CTRL + [31:0] + read-write + + + + + CH12_AL3_WRITE_ADDR + 0x00000334 + Alias for channel 12 WRITE_ADDR register + 0x00000000 + + + CH12_AL3_WRITE_ADDR + [31:0] + read-write + + + + + CH12_AL3_TRANS_COUNT + 0x00000338 + Alias for channel 12 TRANS_COUNT register + 0x00000000 + + + CH12_AL3_TRANS_COUNT + [31:0] + read-write + + + + + CH12_AL3_READ_ADDR_TRIG + 0x0000033c + Alias for channel 12 READ_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH12_AL3_READ_ADDR_TRIG + [31:0] + read-write + + + + + CH13_READ_ADDR + 0x00000340 + DMA Channel 13 Read Address pointer + 0x00000000 + + + CH13_READ_ADDR + This register updates automatically each time a read completes. The current value is the next address to be read by this channel. + [31:0] + read-write + + + + + CH13_WRITE_ADDR + 0x00000344 + DMA Channel 13 Write Address pointer + 0x00000000 + + + CH13_WRITE_ADDR + This register updates automatically each time a write completes. The current value is the next address to be written by this channel. + [31:0] + read-write + + + + + CH13_TRANS_COUNT + 0x00000348 + DMA Channel 13 Transfer Count + 0x00000000 + + + MODE + When MODE is 0x0, the transfer count decrements with each transfer until 0, and then the channel triggers the next channel indicated by CTRL_CHAIN_TO. + + When MODE is 0x1, the transfer count decrements with each transfer until 0, and then the channel re-triggers itself, in addition to the trigger indicated by CTRL_CHAIN_TO. This is useful for e.g. an endless ring-buffer DMA with periodic interrupts. + + When MODE is 0xf, the transfer count does not decrement. The DMA channel performs an endless sequence of transfers, never triggering other channels or raising interrupts, until an ABORT is raised. + + All other values are reserved. + [31:28] + read-write + + + NORMAL + 0 + + + TRIGGER_SELF + 1 + + + ENDLESS + 15 + + + + + COUNT + 28-bit transfer count (256 million transfers maximum). + + Program the number of bus transfers a channel will perform before halting. Note that, if transfers are larger than one byte in size, this is not equal to the number of bytes transferred (see CTRL_DATA_SIZE). + + When the channel is active, reading this register shows the number of transfers remaining, updating automatically each time a write transfer completes. + + Writing this register sets the RELOAD value for the transfer counter. Each time this channel is triggered, the RELOAD value is copied into the live transfer counter. The channel can be started multiple times, and will perform the same number of transfers each time, as programmed by most recent write. + + The RELOAD value can be observed at CHx_DBG_TCR. If TRANS_COUNT is used as a trigger, the written value is used immediately as the length of the new transfer sequence, as well as being written to RELOAD. + [27:0] + read-write + + + + + CH13_CTRL_TRIG + 0x0000034c + DMA Channel 13 Control and Status + 0x00000000 + + + AHB_ERROR + Logical OR of the READ_ERROR and WRITE_ERROR flags. The channel halts when it encounters any bus error, and always raises its channel IRQ flag. + [31:31] + read-only + + + READ_ERROR + If 1, the channel received a read bus error. Write one to clear. + READ_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 3 transfers later) + [30:30] + read-write + oneToClear + + + WRITE_ERROR + If 1, the channel received a write bus error. Write one to clear. + WRITE_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 5 transfers later) + [29:29] + read-write + oneToClear + + + BUSY + This flag goes high when the channel starts a new transfer sequence, and low when the last transfer of that sequence completes. Clearing EN while BUSY is high pauses the channel, and BUSY will stay high while paused. + + To terminate a sequence early (and clear the BUSY flag), see CHAN_ABORT. + [26:26] + read-only + + + SNIFF_EN + If 1, this channel's data transfers are visible to the sniff hardware, and each transfer will advance the state of the checksum. This only applies if the sniff hardware is enabled, and has this channel selected. + + This allows checksum to be enabled or disabled on a per-control- block basis. + [25:25] + read-write + + + BSWAP + Apply byte-swap transformation to DMA data. + For byte data, this has no effect. For halfword data, the two bytes of each halfword are swapped. For word data, the four bytes of each word are swapped to reverse order. + [24:24] + read-write + + + IRQ_QUIET + In QUIET mode, the channel does not generate IRQs at the end of every transfer block. Instead, an IRQ is raised when NULL is written to a trigger register, indicating the end of a control block chain. + + This reduces the number of interrupts to be serviced by the CPU when transferring a DMA chain of many small control blocks. + [23:23] + read-write + + + TREQ_SEL + Select a Transfer Request signal. + The channel uses the transfer request signal to pace its data transfer rate. Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data Request from the system). + 0x0 to 0x3a -> select DREQ n as TREQ + [22:17] + read-write + + + PIO0_TX0 + 0 + Select PIO0's TX FIFO 0 as TREQ + + + PIO0_TX1 + 1 + Select PIO0's TX FIFO 1 as TREQ + + + PIO0_TX2 + 2 + Select PIO0's TX FIFO 2 as TREQ + + + PIO0_TX3 + 3 + Select PIO0's TX FIFO 3 as TREQ + + + PIO0_RX0 + 4 + Select PIO0's RX FIFO 0 as TREQ + + + PIO0_RX1 + 5 + Select PIO0's RX FIFO 1 as TREQ + + + PIO0_RX2 + 6 + Select PIO0's RX FIFO 2 as TREQ + + + PIO0_RX3 + 7 + Select PIO0's RX FIFO 3 as TREQ + + + PIO1_TX0 + 8 + Select PIO1's TX FIFO 0 as TREQ + + + PIO1_TX1 + 9 + Select PIO1's TX FIFO 1 as TREQ + + + PIO1_TX2 + 10 + Select PIO1's TX FIFO 2 as TREQ + + + PIO1_TX3 + 11 + Select PIO1's TX FIFO 3 as TREQ + + + PIO1_RX0 + 12 + Select PIO1's RX FIFO 0 as TREQ + + + PIO1_RX1 + 13 + Select PIO1's RX FIFO 1 as TREQ + + + PIO1_RX2 + 14 + Select PIO1's RX FIFO 2 as TREQ + + + PIO1_RX3 + 15 + Select PIO1's RX FIFO 3 as TREQ + + + PIO2_TX0 + 16 + Select PIO2's TX FIFO 0 as TREQ + + + PIO2_TX1 + 17 + Select PIO2's TX FIFO 1 as TREQ + + + PIO2_TX2 + 18 + Select PIO2's TX FIFO 2 as TREQ + + + PIO2_TX3 + 19 + Select PIO2's TX FIFO 3 as TREQ + + + PIO2_RX0 + 20 + Select PIO2's RX FIFO 0 as TREQ + + + PIO2_RX1 + 21 + Select PIO2's RX FIFO 1 as TREQ + + + PIO2_RX2 + 22 + Select PIO2's RX FIFO 2 as TREQ + + + PIO2_RX3 + 23 + Select PIO2's RX FIFO 3 as TREQ + + + SPI0_TX + 24 + Select SPI0's TX FIFO as TREQ + + + SPI0_RX + 25 + Select SPI0's RX FIFO as TREQ + + + SPI1_TX + 26 + Select SPI1's TX FIFO as TREQ + + + SPI1_RX + 27 + Select SPI1's RX FIFO as TREQ + + + UART0_TX + 28 + Select UART0's TX FIFO as TREQ + + + UART0_RX + 29 + Select UART0's RX FIFO as TREQ + + + UART1_TX + 30 + Select UART1's TX FIFO as TREQ + + + UART1_RX + 31 + Select UART1's RX FIFO as TREQ + + + PWM_WRAP0 + 32 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP1 + 33 + Select PWM Counter 1's Wrap Value as TREQ + + + PWM_WRAP2 + 34 + Select PWM Counter 2's Wrap Value as TREQ + + + PWM_WRAP3 + 35 + Select PWM Counter 3's Wrap Value as TREQ + + + PWM_WRAP4 + 36 + Select PWM Counter 4's Wrap Value as TREQ + + + PWM_WRAP5 + 37 + Select PWM Counter 5's Wrap Value as TREQ + + + PWM_WRAP6 + 38 + Select PWM Counter 6's Wrap Value as TREQ + + + PWM_WRAP7 + 39 + Select PWM Counter 7's Wrap Value as TREQ + + + PWM_WRAP8 + 40 + Select PWM Counter 8's Wrap Value as TREQ + + + PWM_WRAP9 + 41 + Select PWM Counter 9's Wrap Value as TREQ + + + PWM_WRAP10 + 42 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP11 + 43 + Select PWM Counter 1's Wrap Value as TREQ + + + I2C0_TX + 44 + Select I2C0's TX FIFO as TREQ + + + I2C0_RX + 45 + Select I2C0's RX FIFO as TREQ + + + I2C1_TX + 46 + Select I2C1's TX FIFO as TREQ + + + I2C1_RX + 47 + Select I2C1's RX FIFO as TREQ + + + ADC + 48 + Select the ADC as TREQ + + + XIP_STREAM + 49 + Select the XIP Streaming FIFO as TREQ + + + XIP_QMITX + 50 + Select XIP_QMITX as TREQ + + + XIP_QMIRX + 51 + Select XIP_QMIRX as TREQ + + + HSTX + 52 + Select HSTX as TREQ + + + CORESIGHT + 53 + Select CORESIGHT as TREQ + + + SHA256 + 54 + Select SHA256 as TREQ + + + TIMER0 + 59 + Select Timer 0 as TREQ + + + TIMER1 + 60 + Select Timer 1 as TREQ + + + TIMER2 + 61 + Select Timer 2 as TREQ (Optional) + + + TIMER3 + 62 + Select Timer 3 as TREQ (Optional) + + + PERMANENT + 63 + Permanent request, for unpaced transfers. + + + + + CHAIN_TO + When this channel completes, it will trigger the channel indicated by CHAIN_TO. Disable by setting CHAIN_TO = _(this channel)_. + + Note this field resets to 0, so channels 1 and above will chain to channel 0 by default. Set this field to avoid this behaviour. + [16:13] + read-write + + + RING_SEL + Select whether RING_SIZE applies to read or write addresses. + If 0, read addresses are wrapped on a (1 << RING_SIZE) boundary. If 1, write addresses are wrapped. + [12:12] + read-write + + + RING_SIZE + Size of address wrap region. If 0, don't wrap. For values n > 0, only the lower n bits of the address will change. This wraps the address on a (1 << n) byte boundary, facilitating access to naturally-aligned ring buffers. + + Ring sizes between 2 and 32768 bytes are possible. This can apply to either read or write addresses, based on value of RING_SEL. + [11:8] + read-write + + + RING_NONE + 0 + + + + + INCR_WRITE_REV + If 1, and INCR_WRITE is 1, the write address is decremented rather than incremented with each transfer. + + If 1, and INCR_WRITE is 0, this otherwise-unused combination causes the write address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [7:7] + read-write + + + INCR_WRITE + If 1, the write address increments with each transfer. If 0, each write is directed to the same, initial address. + + Generally this should be disabled for memory-to-peripheral transfers. + [6:6] + read-write + + + INCR_READ_REV + If 1, and INCR_READ is 1, the read address is decremented rather than incremented with each transfer. + + If 1, and INCR_READ is 0, this otherwise-unused combination causes the read address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [5:5] + read-write + + + INCR_READ + If 1, the read address increments with each transfer. If 0, each read is directed to the same, initial address. + + Generally this should be disabled for peripheral-to-memory transfers. + [4:4] + read-write + + + DATA_SIZE + Set the size of each bus transfer (byte/halfword/word). READ_ADDR and WRITE_ADDR advance by this amount (1/2/4 bytes) with each transfer. + [3:2] + read-write + + + SIZE_BYTE + 0 + + + SIZE_HALFWORD + 1 + + + SIZE_WORD + 2 + + + + + HIGH_PRIORITY + HIGH_PRIORITY gives a channel preferential treatment in issue scheduling: in each scheduling round, all high priority channels are considered first, and then only a single low priority channel, before returning to the high priority channels. + + This only affects the order in which the DMA schedules channels. The DMA's bus priority is not changed. If the DMA is not saturated then a low priority channel will see no loss of throughput. + [1:1] + read-write + + + EN + DMA Channel Enable. + When 1, the channel will respond to triggering events, which will cause it to become BUSY and start transferring data. When 0, the channel will ignore triggers, stop issuing transfers, and pause the current transfer sequence (i.e. BUSY will remain high if already high) + [0:0] + read-write + + + + + CH13_AL1_CTRL + 0x00000350 + Alias for channel 13 CTRL register + 0x00000000 + + + CH13_AL1_CTRL + [31:0] + read-write + + + + + CH13_AL1_READ_ADDR + 0x00000354 + Alias for channel 13 READ_ADDR register + 0x00000000 + + + CH13_AL1_READ_ADDR + [31:0] + read-write + + + + + CH13_AL1_WRITE_ADDR + 0x00000358 + Alias for channel 13 WRITE_ADDR register + 0x00000000 + + + CH13_AL1_WRITE_ADDR + [31:0] + read-write + + + + + CH13_AL1_TRANS_COUNT_TRIG + 0x0000035c + Alias for channel 13 TRANS_COUNT register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH13_AL1_TRANS_COUNT_TRIG + [31:0] + read-write + + + + + CH13_AL2_CTRL + 0x00000360 + Alias for channel 13 CTRL register + 0x00000000 + + + CH13_AL2_CTRL + [31:0] + read-write + + + + + CH13_AL2_TRANS_COUNT + 0x00000364 + Alias for channel 13 TRANS_COUNT register + 0x00000000 + + + CH13_AL2_TRANS_COUNT + [31:0] + read-write + + + + + CH13_AL2_READ_ADDR + 0x00000368 + Alias for channel 13 READ_ADDR register + 0x00000000 + + + CH13_AL2_READ_ADDR + [31:0] + read-write + + + + + CH13_AL2_WRITE_ADDR_TRIG + 0x0000036c + Alias for channel 13 WRITE_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH13_AL2_WRITE_ADDR_TRIG + [31:0] + read-write + + + + + CH13_AL3_CTRL + 0x00000370 + Alias for channel 13 CTRL register + 0x00000000 + + + CH13_AL3_CTRL + [31:0] + read-write + + + + + CH13_AL3_WRITE_ADDR + 0x00000374 + Alias for channel 13 WRITE_ADDR register + 0x00000000 + + + CH13_AL3_WRITE_ADDR + [31:0] + read-write + + + + + CH13_AL3_TRANS_COUNT + 0x00000378 + Alias for channel 13 TRANS_COUNT register + 0x00000000 + + + CH13_AL3_TRANS_COUNT + [31:0] + read-write + + + + + CH13_AL3_READ_ADDR_TRIG + 0x0000037c + Alias for channel 13 READ_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH13_AL3_READ_ADDR_TRIG + [31:0] + read-write + + + + + CH14_READ_ADDR + 0x00000380 + DMA Channel 14 Read Address pointer + 0x00000000 + + + CH14_READ_ADDR + This register updates automatically each time a read completes. The current value is the next address to be read by this channel. + [31:0] + read-write + + + + + CH14_WRITE_ADDR + 0x00000384 + DMA Channel 14 Write Address pointer + 0x00000000 + + + CH14_WRITE_ADDR + This register updates automatically each time a write completes. The current value is the next address to be written by this channel. + [31:0] + read-write + + + + + CH14_TRANS_COUNT + 0x00000388 + DMA Channel 14 Transfer Count + 0x00000000 + + + MODE + When MODE is 0x0, the transfer count decrements with each transfer until 0, and then the channel triggers the next channel indicated by CTRL_CHAIN_TO. + + When MODE is 0x1, the transfer count decrements with each transfer until 0, and then the channel re-triggers itself, in addition to the trigger indicated by CTRL_CHAIN_TO. This is useful for e.g. an endless ring-buffer DMA with periodic interrupts. + + When MODE is 0xf, the transfer count does not decrement. The DMA channel performs an endless sequence of transfers, never triggering other channels or raising interrupts, until an ABORT is raised. + + All other values are reserved. + [31:28] + read-write + + + NORMAL + 0 + + + TRIGGER_SELF + 1 + + + ENDLESS + 15 + + + + + COUNT + 28-bit transfer count (256 million transfers maximum). + + Program the number of bus transfers a channel will perform before halting. Note that, if transfers are larger than one byte in size, this is not equal to the number of bytes transferred (see CTRL_DATA_SIZE). + + When the channel is active, reading this register shows the number of transfers remaining, updating automatically each time a write transfer completes. + + Writing this register sets the RELOAD value for the transfer counter. Each time this channel is triggered, the RELOAD value is copied into the live transfer counter. The channel can be started multiple times, and will perform the same number of transfers each time, as programmed by most recent write. + + The RELOAD value can be observed at CHx_DBG_TCR. If TRANS_COUNT is used as a trigger, the written value is used immediately as the length of the new transfer sequence, as well as being written to RELOAD. + [27:0] + read-write + + + + + CH14_CTRL_TRIG + 0x0000038c + DMA Channel 14 Control and Status + 0x00000000 + + + AHB_ERROR + Logical OR of the READ_ERROR and WRITE_ERROR flags. The channel halts when it encounters any bus error, and always raises its channel IRQ flag. + [31:31] + read-only + + + READ_ERROR + If 1, the channel received a read bus error. Write one to clear. + READ_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 3 transfers later) + [30:30] + read-write + oneToClear + + + WRITE_ERROR + If 1, the channel received a write bus error. Write one to clear. + WRITE_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 5 transfers later) + [29:29] + read-write + oneToClear + + + BUSY + This flag goes high when the channel starts a new transfer sequence, and low when the last transfer of that sequence completes. Clearing EN while BUSY is high pauses the channel, and BUSY will stay high while paused. + + To terminate a sequence early (and clear the BUSY flag), see CHAN_ABORT. + [26:26] + read-only + + + SNIFF_EN + If 1, this channel's data transfers are visible to the sniff hardware, and each transfer will advance the state of the checksum. This only applies if the sniff hardware is enabled, and has this channel selected. + + This allows checksum to be enabled or disabled on a per-control- block basis. + [25:25] + read-write + + + BSWAP + Apply byte-swap transformation to DMA data. + For byte data, this has no effect. For halfword data, the two bytes of each halfword are swapped. For word data, the four bytes of each word are swapped to reverse order. + [24:24] + read-write + + + IRQ_QUIET + In QUIET mode, the channel does not generate IRQs at the end of every transfer block. Instead, an IRQ is raised when NULL is written to a trigger register, indicating the end of a control block chain. + + This reduces the number of interrupts to be serviced by the CPU when transferring a DMA chain of many small control blocks. + [23:23] + read-write + + + TREQ_SEL + Select a Transfer Request signal. + The channel uses the transfer request signal to pace its data transfer rate. Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data Request from the system). + 0x0 to 0x3a -> select DREQ n as TREQ + [22:17] + read-write + + + PIO0_TX0 + 0 + Select PIO0's TX FIFO 0 as TREQ + + + PIO0_TX1 + 1 + Select PIO0's TX FIFO 1 as TREQ + + + PIO0_TX2 + 2 + Select PIO0's TX FIFO 2 as TREQ + + + PIO0_TX3 + 3 + Select PIO0's TX FIFO 3 as TREQ + + + PIO0_RX0 + 4 + Select PIO0's RX FIFO 0 as TREQ + + + PIO0_RX1 + 5 + Select PIO0's RX FIFO 1 as TREQ + + + PIO0_RX2 + 6 + Select PIO0's RX FIFO 2 as TREQ + + + PIO0_RX3 + 7 + Select PIO0's RX FIFO 3 as TREQ + + + PIO1_TX0 + 8 + Select PIO1's TX FIFO 0 as TREQ + + + PIO1_TX1 + 9 + Select PIO1's TX FIFO 1 as TREQ + + + PIO1_TX2 + 10 + Select PIO1's TX FIFO 2 as TREQ + + + PIO1_TX3 + 11 + Select PIO1's TX FIFO 3 as TREQ + + + PIO1_RX0 + 12 + Select PIO1's RX FIFO 0 as TREQ + + + PIO1_RX1 + 13 + Select PIO1's RX FIFO 1 as TREQ + + + PIO1_RX2 + 14 + Select PIO1's RX FIFO 2 as TREQ + + + PIO1_RX3 + 15 + Select PIO1's RX FIFO 3 as TREQ + + + PIO2_TX0 + 16 + Select PIO2's TX FIFO 0 as TREQ + + + PIO2_TX1 + 17 + Select PIO2's TX FIFO 1 as TREQ + + + PIO2_TX2 + 18 + Select PIO2's TX FIFO 2 as TREQ + + + PIO2_TX3 + 19 + Select PIO2's TX FIFO 3 as TREQ + + + PIO2_RX0 + 20 + Select PIO2's RX FIFO 0 as TREQ + + + PIO2_RX1 + 21 + Select PIO2's RX FIFO 1 as TREQ + + + PIO2_RX2 + 22 + Select PIO2's RX FIFO 2 as TREQ + + + PIO2_RX3 + 23 + Select PIO2's RX FIFO 3 as TREQ + + + SPI0_TX + 24 + Select SPI0's TX FIFO as TREQ + + + SPI0_RX + 25 + Select SPI0's RX FIFO as TREQ + + + SPI1_TX + 26 + Select SPI1's TX FIFO as TREQ + + + SPI1_RX + 27 + Select SPI1's RX FIFO as TREQ + + + UART0_TX + 28 + Select UART0's TX FIFO as TREQ + + + UART0_RX + 29 + Select UART0's RX FIFO as TREQ + + + UART1_TX + 30 + Select UART1's TX FIFO as TREQ + + + UART1_RX + 31 + Select UART1's RX FIFO as TREQ + + + PWM_WRAP0 + 32 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP1 + 33 + Select PWM Counter 1's Wrap Value as TREQ + + + PWM_WRAP2 + 34 + Select PWM Counter 2's Wrap Value as TREQ + + + PWM_WRAP3 + 35 + Select PWM Counter 3's Wrap Value as TREQ + + + PWM_WRAP4 + 36 + Select PWM Counter 4's Wrap Value as TREQ + + + PWM_WRAP5 + 37 + Select PWM Counter 5's Wrap Value as TREQ + + + PWM_WRAP6 + 38 + Select PWM Counter 6's Wrap Value as TREQ + + + PWM_WRAP7 + 39 + Select PWM Counter 7's Wrap Value as TREQ + + + PWM_WRAP8 + 40 + Select PWM Counter 8's Wrap Value as TREQ + + + PWM_WRAP9 + 41 + Select PWM Counter 9's Wrap Value as TREQ + + + PWM_WRAP10 + 42 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP11 + 43 + Select PWM Counter 1's Wrap Value as TREQ + + + I2C0_TX + 44 + Select I2C0's TX FIFO as TREQ + + + I2C0_RX + 45 + Select I2C0's RX FIFO as TREQ + + + I2C1_TX + 46 + Select I2C1's TX FIFO as TREQ + + + I2C1_RX + 47 + Select I2C1's RX FIFO as TREQ + + + ADC + 48 + Select the ADC as TREQ + + + XIP_STREAM + 49 + Select the XIP Streaming FIFO as TREQ + + + XIP_QMITX + 50 + Select XIP_QMITX as TREQ + + + XIP_QMIRX + 51 + Select XIP_QMIRX as TREQ + + + HSTX + 52 + Select HSTX as TREQ + + + CORESIGHT + 53 + Select CORESIGHT as TREQ + + + SHA256 + 54 + Select SHA256 as TREQ + + + TIMER0 + 59 + Select Timer 0 as TREQ + + + TIMER1 + 60 + Select Timer 1 as TREQ + + + TIMER2 + 61 + Select Timer 2 as TREQ (Optional) + + + TIMER3 + 62 + Select Timer 3 as TREQ (Optional) + + + PERMANENT + 63 + Permanent request, for unpaced transfers. + + + + + CHAIN_TO + When this channel completes, it will trigger the channel indicated by CHAIN_TO. Disable by setting CHAIN_TO = _(this channel)_. + + Note this field resets to 0, so channels 1 and above will chain to channel 0 by default. Set this field to avoid this behaviour. + [16:13] + read-write + + + RING_SEL + Select whether RING_SIZE applies to read or write addresses. + If 0, read addresses are wrapped on a (1 << RING_SIZE) boundary. If 1, write addresses are wrapped. + [12:12] + read-write + + + RING_SIZE + Size of address wrap region. If 0, don't wrap. For values n > 0, only the lower n bits of the address will change. This wraps the address on a (1 << n) byte boundary, facilitating access to naturally-aligned ring buffers. + + Ring sizes between 2 and 32768 bytes are possible. This can apply to either read or write addresses, based on value of RING_SEL. + [11:8] + read-write + + + RING_NONE + 0 + + + + + INCR_WRITE_REV + If 1, and INCR_WRITE is 1, the write address is decremented rather than incremented with each transfer. + + If 1, and INCR_WRITE is 0, this otherwise-unused combination causes the write address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [7:7] + read-write + + + INCR_WRITE + If 1, the write address increments with each transfer. If 0, each write is directed to the same, initial address. + + Generally this should be disabled for memory-to-peripheral transfers. + [6:6] + read-write + + + INCR_READ_REV + If 1, and INCR_READ is 1, the read address is decremented rather than incremented with each transfer. + + If 1, and INCR_READ is 0, this otherwise-unused combination causes the read address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [5:5] + read-write + + + INCR_READ + If 1, the read address increments with each transfer. If 0, each read is directed to the same, initial address. + + Generally this should be disabled for peripheral-to-memory transfers. + [4:4] + read-write + + + DATA_SIZE + Set the size of each bus transfer (byte/halfword/word). READ_ADDR and WRITE_ADDR advance by this amount (1/2/4 bytes) with each transfer. + [3:2] + read-write + + + SIZE_BYTE + 0 + + + SIZE_HALFWORD + 1 + + + SIZE_WORD + 2 + + + + + HIGH_PRIORITY + HIGH_PRIORITY gives a channel preferential treatment in issue scheduling: in each scheduling round, all high priority channels are considered first, and then only a single low priority channel, before returning to the high priority channels. + + This only affects the order in which the DMA schedules channels. The DMA's bus priority is not changed. If the DMA is not saturated then a low priority channel will see no loss of throughput. + [1:1] + read-write + + + EN + DMA Channel Enable. + When 1, the channel will respond to triggering events, which will cause it to become BUSY and start transferring data. When 0, the channel will ignore triggers, stop issuing transfers, and pause the current transfer sequence (i.e. BUSY will remain high if already high) + [0:0] + read-write + + + + + CH14_AL1_CTRL + 0x00000390 + Alias for channel 14 CTRL register + 0x00000000 + + + CH14_AL1_CTRL + [31:0] + read-write + + + + + CH14_AL1_READ_ADDR + 0x00000394 + Alias for channel 14 READ_ADDR register + 0x00000000 + + + CH14_AL1_READ_ADDR + [31:0] + read-write + + + + + CH14_AL1_WRITE_ADDR + 0x00000398 + Alias for channel 14 WRITE_ADDR register + 0x00000000 + + + CH14_AL1_WRITE_ADDR + [31:0] + read-write + + + + + CH14_AL1_TRANS_COUNT_TRIG + 0x0000039c + Alias for channel 14 TRANS_COUNT register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH14_AL1_TRANS_COUNT_TRIG + [31:0] + read-write + + + + + CH14_AL2_CTRL + 0x000003a0 + Alias for channel 14 CTRL register + 0x00000000 + + + CH14_AL2_CTRL + [31:0] + read-write + + + + + CH14_AL2_TRANS_COUNT + 0x000003a4 + Alias for channel 14 TRANS_COUNT register + 0x00000000 + + + CH14_AL2_TRANS_COUNT + [31:0] + read-write + + + + + CH14_AL2_READ_ADDR + 0x000003a8 + Alias for channel 14 READ_ADDR register + 0x00000000 + + + CH14_AL2_READ_ADDR + [31:0] + read-write + + + + + CH14_AL2_WRITE_ADDR_TRIG + 0x000003ac + Alias for channel 14 WRITE_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH14_AL2_WRITE_ADDR_TRIG + [31:0] + read-write + + + + + CH14_AL3_CTRL + 0x000003b0 + Alias for channel 14 CTRL register + 0x00000000 + + + CH14_AL3_CTRL + [31:0] + read-write + + + + + CH14_AL3_WRITE_ADDR + 0x000003b4 + Alias for channel 14 WRITE_ADDR register + 0x00000000 + + + CH14_AL3_WRITE_ADDR + [31:0] + read-write + + + + + CH14_AL3_TRANS_COUNT + 0x000003b8 + Alias for channel 14 TRANS_COUNT register + 0x00000000 + + + CH14_AL3_TRANS_COUNT + [31:0] + read-write + + + + + CH14_AL3_READ_ADDR_TRIG + 0x000003bc + Alias for channel 14 READ_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH14_AL3_READ_ADDR_TRIG + [31:0] + read-write + + + + + CH15_READ_ADDR + 0x000003c0 + DMA Channel 15 Read Address pointer + 0x00000000 + + + CH15_READ_ADDR + This register updates automatically each time a read completes. The current value is the next address to be read by this channel. + [31:0] + read-write + + + + + CH15_WRITE_ADDR + 0x000003c4 + DMA Channel 15 Write Address pointer + 0x00000000 + + + CH15_WRITE_ADDR + This register updates automatically each time a write completes. The current value is the next address to be written by this channel. + [31:0] + read-write + + + + + CH15_TRANS_COUNT + 0x000003c8 + DMA Channel 15 Transfer Count + 0x00000000 + + + MODE + When MODE is 0x0, the transfer count decrements with each transfer until 0, and then the channel triggers the next channel indicated by CTRL_CHAIN_TO. + + When MODE is 0x1, the transfer count decrements with each transfer until 0, and then the channel re-triggers itself, in addition to the trigger indicated by CTRL_CHAIN_TO. This is useful for e.g. an endless ring-buffer DMA with periodic interrupts. + + When MODE is 0xf, the transfer count does not decrement. The DMA channel performs an endless sequence of transfers, never triggering other channels or raising interrupts, until an ABORT is raised. + + All other values are reserved. + [31:28] + read-write + + + NORMAL + 0 + + + TRIGGER_SELF + 1 + + + ENDLESS + 15 + + + + + COUNT + 28-bit transfer count (256 million transfers maximum). + + Program the number of bus transfers a channel will perform before halting. Note that, if transfers are larger than one byte in size, this is not equal to the number of bytes transferred (see CTRL_DATA_SIZE). + + When the channel is active, reading this register shows the number of transfers remaining, updating automatically each time a write transfer completes. + + Writing this register sets the RELOAD value for the transfer counter. Each time this channel is triggered, the RELOAD value is copied into the live transfer counter. The channel can be started multiple times, and will perform the same number of transfers each time, as programmed by most recent write. + + The RELOAD value can be observed at CHx_DBG_TCR. If TRANS_COUNT is used as a trigger, the written value is used immediately as the length of the new transfer sequence, as well as being written to RELOAD. + [27:0] + read-write + + + + + CH15_CTRL_TRIG + 0x000003cc + DMA Channel 15 Control and Status + 0x00000000 + + + AHB_ERROR + Logical OR of the READ_ERROR and WRITE_ERROR flags. The channel halts when it encounters any bus error, and always raises its channel IRQ flag. + [31:31] + read-only + + + READ_ERROR + If 1, the channel received a read bus error. Write one to clear. + READ_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 3 transfers later) + [30:30] + read-write + oneToClear + + + WRITE_ERROR + If 1, the channel received a write bus error. Write one to clear. + WRITE_ADDR shows the approximate address where the bus error was encountered (will not be earlier, or more than 5 transfers later) + [29:29] + read-write + oneToClear + + + BUSY + This flag goes high when the channel starts a new transfer sequence, and low when the last transfer of that sequence completes. Clearing EN while BUSY is high pauses the channel, and BUSY will stay high while paused. + + To terminate a sequence early (and clear the BUSY flag), see CHAN_ABORT. + [26:26] + read-only + + + SNIFF_EN + If 1, this channel's data transfers are visible to the sniff hardware, and each transfer will advance the state of the checksum. This only applies if the sniff hardware is enabled, and has this channel selected. + + This allows checksum to be enabled or disabled on a per-control- block basis. + [25:25] + read-write + + + BSWAP + Apply byte-swap transformation to DMA data. + For byte data, this has no effect. For halfword data, the two bytes of each halfword are swapped. For word data, the four bytes of each word are swapped to reverse order. + [24:24] + read-write + + + IRQ_QUIET + In QUIET mode, the channel does not generate IRQs at the end of every transfer block. Instead, an IRQ is raised when NULL is written to a trigger register, indicating the end of a control block chain. + + This reduces the number of interrupts to be serviced by the CPU when transferring a DMA chain of many small control blocks. + [23:23] + read-write + + + TREQ_SEL + Select a Transfer Request signal. + The channel uses the transfer request signal to pace its data transfer rate. Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data Request from the system). + 0x0 to 0x3a -> select DREQ n as TREQ + [22:17] + read-write + + + PIO0_TX0 + 0 + Select PIO0's TX FIFO 0 as TREQ + + + PIO0_TX1 + 1 + Select PIO0's TX FIFO 1 as TREQ + + + PIO0_TX2 + 2 + Select PIO0's TX FIFO 2 as TREQ + + + PIO0_TX3 + 3 + Select PIO0's TX FIFO 3 as TREQ + + + PIO0_RX0 + 4 + Select PIO0's RX FIFO 0 as TREQ + + + PIO0_RX1 + 5 + Select PIO0's RX FIFO 1 as TREQ + + + PIO0_RX2 + 6 + Select PIO0's RX FIFO 2 as TREQ + + + PIO0_RX3 + 7 + Select PIO0's RX FIFO 3 as TREQ + + + PIO1_TX0 + 8 + Select PIO1's TX FIFO 0 as TREQ + + + PIO1_TX1 + 9 + Select PIO1's TX FIFO 1 as TREQ + + + PIO1_TX2 + 10 + Select PIO1's TX FIFO 2 as TREQ + + + PIO1_TX3 + 11 + Select PIO1's TX FIFO 3 as TREQ + + + PIO1_RX0 + 12 + Select PIO1's RX FIFO 0 as TREQ + + + PIO1_RX1 + 13 + Select PIO1's RX FIFO 1 as TREQ + + + PIO1_RX2 + 14 + Select PIO1's RX FIFO 2 as TREQ + + + PIO1_RX3 + 15 + Select PIO1's RX FIFO 3 as TREQ + + + PIO2_TX0 + 16 + Select PIO2's TX FIFO 0 as TREQ + + + PIO2_TX1 + 17 + Select PIO2's TX FIFO 1 as TREQ + + + PIO2_TX2 + 18 + Select PIO2's TX FIFO 2 as TREQ + + + PIO2_TX3 + 19 + Select PIO2's TX FIFO 3 as TREQ + + + PIO2_RX0 + 20 + Select PIO2's RX FIFO 0 as TREQ + + + PIO2_RX1 + 21 + Select PIO2's RX FIFO 1 as TREQ + + + PIO2_RX2 + 22 + Select PIO2's RX FIFO 2 as TREQ + + + PIO2_RX3 + 23 + Select PIO2's RX FIFO 3 as TREQ + + + SPI0_TX + 24 + Select SPI0's TX FIFO as TREQ + + + SPI0_RX + 25 + Select SPI0's RX FIFO as TREQ + + + SPI1_TX + 26 + Select SPI1's TX FIFO as TREQ + + + SPI1_RX + 27 + Select SPI1's RX FIFO as TREQ + + + UART0_TX + 28 + Select UART0's TX FIFO as TREQ + + + UART0_RX + 29 + Select UART0's RX FIFO as TREQ + + + UART1_TX + 30 + Select UART1's TX FIFO as TREQ + + + UART1_RX + 31 + Select UART1's RX FIFO as TREQ + + + PWM_WRAP0 + 32 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP1 + 33 + Select PWM Counter 1's Wrap Value as TREQ + + + PWM_WRAP2 + 34 + Select PWM Counter 2's Wrap Value as TREQ + + + PWM_WRAP3 + 35 + Select PWM Counter 3's Wrap Value as TREQ + + + PWM_WRAP4 + 36 + Select PWM Counter 4's Wrap Value as TREQ + + + PWM_WRAP5 + 37 + Select PWM Counter 5's Wrap Value as TREQ + + + PWM_WRAP6 + 38 + Select PWM Counter 6's Wrap Value as TREQ + + + PWM_WRAP7 + 39 + Select PWM Counter 7's Wrap Value as TREQ + + + PWM_WRAP8 + 40 + Select PWM Counter 8's Wrap Value as TREQ + + + PWM_WRAP9 + 41 + Select PWM Counter 9's Wrap Value as TREQ + + + PWM_WRAP10 + 42 + Select PWM Counter 0's Wrap Value as TREQ + + + PWM_WRAP11 + 43 + Select PWM Counter 1's Wrap Value as TREQ + + + I2C0_TX + 44 + Select I2C0's TX FIFO as TREQ + + + I2C0_RX + 45 + Select I2C0's RX FIFO as TREQ + + + I2C1_TX + 46 + Select I2C1's TX FIFO as TREQ + + + I2C1_RX + 47 + Select I2C1's RX FIFO as TREQ + + + ADC + 48 + Select the ADC as TREQ + + + XIP_STREAM + 49 + Select the XIP Streaming FIFO as TREQ + + + XIP_QMITX + 50 + Select XIP_QMITX as TREQ + + + XIP_QMIRX + 51 + Select XIP_QMIRX as TREQ + + + HSTX + 52 + Select HSTX as TREQ + + + CORESIGHT + 53 + Select CORESIGHT as TREQ + + + SHA256 + 54 + Select SHA256 as TREQ + + + TIMER0 + 59 + Select Timer 0 as TREQ + + + TIMER1 + 60 + Select Timer 1 as TREQ + + + TIMER2 + 61 + Select Timer 2 as TREQ (Optional) + + + TIMER3 + 62 + Select Timer 3 as TREQ (Optional) + + + PERMANENT + 63 + Permanent request, for unpaced transfers. + + + + + CHAIN_TO + When this channel completes, it will trigger the channel indicated by CHAIN_TO. Disable by setting CHAIN_TO = _(this channel)_. + + Note this field resets to 0, so channels 1 and above will chain to channel 0 by default. Set this field to avoid this behaviour. + [16:13] + read-write + + + RING_SEL + Select whether RING_SIZE applies to read or write addresses. + If 0, read addresses are wrapped on a (1 << RING_SIZE) boundary. If 1, write addresses are wrapped. + [12:12] + read-write + + + RING_SIZE + Size of address wrap region. If 0, don't wrap. For values n > 0, only the lower n bits of the address will change. This wraps the address on a (1 << n) byte boundary, facilitating access to naturally-aligned ring buffers. + + Ring sizes between 2 and 32768 bytes are possible. This can apply to either read or write addresses, based on value of RING_SEL. + [11:8] + read-write + + + RING_NONE + 0 + + + + + INCR_WRITE_REV + If 1, and INCR_WRITE is 1, the write address is decremented rather than incremented with each transfer. + + If 1, and INCR_WRITE is 0, this otherwise-unused combination causes the write address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [7:7] + read-write + + + INCR_WRITE + If 1, the write address increments with each transfer. If 0, each write is directed to the same, initial address. + + Generally this should be disabled for memory-to-peripheral transfers. + [6:6] + read-write + + + INCR_READ_REV + If 1, and INCR_READ is 1, the read address is decremented rather than incremented with each transfer. + + If 1, and INCR_READ is 0, this otherwise-unused combination causes the read address to be incremented by twice the transfer size, i.e. skipping over alternate addresses. + [5:5] + read-write + + + INCR_READ + If 1, the read address increments with each transfer. If 0, each read is directed to the same, initial address. + + Generally this should be disabled for peripheral-to-memory transfers. + [4:4] + read-write + + + DATA_SIZE + Set the size of each bus transfer (byte/halfword/word). READ_ADDR and WRITE_ADDR advance by this amount (1/2/4 bytes) with each transfer. + [3:2] + read-write + + + SIZE_BYTE + 0 + + + SIZE_HALFWORD + 1 + + + SIZE_WORD + 2 + + + + + HIGH_PRIORITY + HIGH_PRIORITY gives a channel preferential treatment in issue scheduling: in each scheduling round, all high priority channels are considered first, and then only a single low priority channel, before returning to the high priority channels. + + This only affects the order in which the DMA schedules channels. The DMA's bus priority is not changed. If the DMA is not saturated then a low priority channel will see no loss of throughput. + [1:1] + read-write + + + EN + DMA Channel Enable. + When 1, the channel will respond to triggering events, which will cause it to become BUSY and start transferring data. When 0, the channel will ignore triggers, stop issuing transfers, and pause the current transfer sequence (i.e. BUSY will remain high if already high) + [0:0] + read-write + + + + + CH15_AL1_CTRL + 0x000003d0 + Alias for channel 15 CTRL register + 0x00000000 + + + CH15_AL1_CTRL + [31:0] + read-write + + + + + CH15_AL1_READ_ADDR + 0x000003d4 + Alias for channel 15 READ_ADDR register + 0x00000000 + + + CH15_AL1_READ_ADDR + [31:0] + read-write + + + + + CH15_AL1_WRITE_ADDR + 0x000003d8 + Alias for channel 15 WRITE_ADDR register + 0x00000000 + + + CH15_AL1_WRITE_ADDR + [31:0] + read-write + + + + + CH15_AL1_TRANS_COUNT_TRIG + 0x000003dc + Alias for channel 15 TRANS_COUNT register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH15_AL1_TRANS_COUNT_TRIG + [31:0] + read-write + + + + + CH15_AL2_CTRL + 0x000003e0 + Alias for channel 15 CTRL register + 0x00000000 + + + CH15_AL2_CTRL + [31:0] + read-write + + + + + CH15_AL2_TRANS_COUNT + 0x000003e4 + Alias for channel 15 TRANS_COUNT register + 0x00000000 + + + CH15_AL2_TRANS_COUNT + [31:0] + read-write + + + + + CH15_AL2_READ_ADDR + 0x000003e8 + Alias for channel 15 READ_ADDR register + 0x00000000 + + + CH15_AL2_READ_ADDR + [31:0] + read-write + + + + + CH15_AL2_WRITE_ADDR_TRIG + 0x000003ec + Alias for channel 15 WRITE_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH15_AL2_WRITE_ADDR_TRIG + [31:0] + read-write + + + + + CH15_AL3_CTRL + 0x000003f0 + Alias for channel 15 CTRL register + 0x00000000 + + + CH15_AL3_CTRL + [31:0] + read-write + + + + + CH15_AL3_WRITE_ADDR + 0x000003f4 + Alias for channel 15 WRITE_ADDR register + 0x00000000 + + + CH15_AL3_WRITE_ADDR + [31:0] + read-write + + + + + CH15_AL3_TRANS_COUNT + 0x000003f8 + Alias for channel 15 TRANS_COUNT register + 0x00000000 + + + CH15_AL3_TRANS_COUNT + [31:0] + read-write + + + + + CH15_AL3_READ_ADDR_TRIG + 0x000003fc + Alias for channel 15 READ_ADDR register + This is a trigger register (0xc). Writing a nonzero value will + reload the channel counter and start the channel. + 0x00000000 + + + CH15_AL3_READ_ADDR_TRIG + [31:0] + read-write + + + + + INTR + 0x00000400 + Interrupt Status (raw) + 0x00000000 + + + INTR + Raw interrupt status for DMA Channels 0..15. Bit n corresponds to channel n. Ignores any masking or forcing. Channel interrupts can be cleared by writing a bit mask to INTR or INTS0/1/2/3. + + Channel interrupts can be routed to either of four system-level IRQs based on INTE0, INTE1, INTE2 and INTE3. + + The multiple system-level interrupts might be used to allow NVIC IRQ preemption for more time-critical channels, to spread IRQ load across different cores, or to target IRQs to different security domains. + + It is also valid to ignore the multiple IRQs, and just use INTE0/INTS0/IRQ 0. + + If this register is accessed at a security/privilege level less than that of a given channel (as defined by that channel's SECCFG_CHx register), then that channel's interrupt status will read as 0, ignore writes. + [15:0] + read-write + oneToClear + + + + + INTE0 + 0x00000404 + Interrupt Enables for IRQ 0 + 0x00000000 + + + INTE0 + Set bit n to pass interrupts from channel n to DMA IRQ 0. + + Note this bit has no effect if the channel security/privilege level, defined by SECCFG_CHx, is greater than the IRQ security/privilege defined by SECCFG_IRQ0. + [15:0] + read-write + + + + + INTF0 + 0x00000408 + Force Interrupts + 0x00000000 + + + INTF0 + Write 1s to force the corresponding bits in INTS0. The interrupt remains asserted until INTF0 is cleared. + [15:0] + read-write + + + + + INTS0 + 0x0000040c + Interrupt Status for IRQ 0 + 0x00000000 + + + INTS0 + Indicates active channel interrupt requests which are currently causing IRQ 0 to be asserted. + Channel interrupts can be cleared by writing a bit mask here. + + Channels with a security/privilege (SECCFG_CHx) greater SECCFG_IRQ0) read as 0 in this register, and ignore writes. + [15:0] + read-write + oneToClear + + + + + INTR1 + 0x00000410 + Interrupt Status (raw) + 0x00000000 + + + INTR1 + Raw interrupt status for DMA Channels 0..15. Bit n corresponds to channel n. Ignores any masking or forcing. Channel interrupts can be cleared by writing a bit mask to INTR or INTS0/1/2/3. + + Channel interrupts can be routed to either of four system-level IRQs based on INTE0, INTE1, INTE2 and INTE3. + + The multiple system-level interrupts might be used to allow NVIC IRQ preemption for more time-critical channels, to spread IRQ load across different cores, or to target IRQs to different security domains. + + It is also valid to ignore the multiple IRQs, and just use INTE0/INTS0/IRQ 0. + + If this register is accessed at a security/privilege level less than that of a given channel (as defined by that channel's SECCFG_CHx register), then that channel's interrupt status will read as 0, ignore writes. + [15:0] + read-write + oneToClear + + + + + INTE1 + 0x00000414 + Interrupt Enables for IRQ 1 + 0x00000000 + + + INTE1 + Set bit n to pass interrupts from channel n to DMA IRQ 1. + + Note this bit has no effect if the channel security/privilege level, defined by SECCFG_CHx, is greater than the IRQ security/privilege defined by SECCFG_IRQ1. + [15:0] + read-write + + + + + INTF1 + 0x00000418 + Force Interrupts + 0x00000000 + + + INTF1 + Write 1s to force the corresponding bits in INTS1. The interrupt remains asserted until INTF1 is cleared. + [15:0] + read-write + + + + + INTS1 + 0x0000041c + Interrupt Status for IRQ 1 + 0x00000000 + + + INTS1 + Indicates active channel interrupt requests which are currently causing IRQ 1 to be asserted. + Channel interrupts can be cleared by writing a bit mask here. + + Channels with a security/privilege (SECCFG_CHx) greater SECCFG_IRQ1) read as 0 in this register, and ignore writes. + [15:0] + read-write + oneToClear + + + + + INTR2 + 0x00000420 + Interrupt Status (raw) + 0x00000000 + + + INTR2 + Raw interrupt status for DMA Channels 0..15. Bit n corresponds to channel n. Ignores any masking or forcing. Channel interrupts can be cleared by writing a bit mask to INTR or INTS0/1/2/3. + + Channel interrupts can be routed to either of four system-level IRQs based on INTE0, INTE1, INTE2 and INTE3. + + The multiple system-level interrupts might be used to allow NVIC IRQ preemption for more time-critical channels, to spread IRQ load across different cores, or to target IRQs to different security domains. + + It is also valid to ignore the multiple IRQs, and just use INTE0/INTS0/IRQ 0. + + If this register is accessed at a security/privilege level less than that of a given channel (as defined by that channel's SECCFG_CHx register), then that channel's interrupt status will read as 0, ignore writes. + [15:0] + read-write + oneToClear + + + + + INTE2 + 0x00000424 + Interrupt Enables for IRQ 2 + 0x00000000 + + + INTE2 + Set bit n to pass interrupts from channel n to DMA IRQ 2. + + Note this bit has no effect if the channel security/privilege level, defined by SECCFG_CHx, is greater than the IRQ security/privilege defined by SECCFG_IRQ2. + [15:0] + read-write + + + + + INTF2 + 0x00000428 + Force Interrupts + 0x00000000 + + + INTF2 + Write 1s to force the corresponding bits in INTS2. The interrupt remains asserted until INTF2 is cleared. + [15:0] + read-write + + + + + INTS2 + 0x0000042c + Interrupt Status for IRQ 2 + 0x00000000 + + + INTS2 + Indicates active channel interrupt requests which are currently causing IRQ 2 to be asserted. + Channel interrupts can be cleared by writing a bit mask here. + + Channels with a security/privilege (SECCFG_CHx) greater SECCFG_IRQ2) read as 0 in this register, and ignore writes. + [15:0] + read-write + oneToClear + + + + + INTR3 + 0x00000430 + Interrupt Status (raw) + 0x00000000 + + + INTR3 + Raw interrupt status for DMA Channels 0..15. Bit n corresponds to channel n. Ignores any masking or forcing. Channel interrupts can be cleared by writing a bit mask to INTR or INTS0/1/2/3. + + Channel interrupts can be routed to either of four system-level IRQs based on INTE0, INTE1, INTE2 and INTE3. + + The multiple system-level interrupts might be used to allow NVIC IRQ preemption for more time-critical channels, to spread IRQ load across different cores, or to target IRQs to different security domains. + + It is also valid to ignore the multiple IRQs, and just use INTE0/INTS0/IRQ 0. + + If this register is accessed at a security/privilege level less than that of a given channel (as defined by that channel's SECCFG_CHx register), then that channel's interrupt status will read as 0, ignore writes. + [15:0] + read-write + oneToClear + + + + + INTE3 + 0x00000434 + Interrupt Enables for IRQ 3 + 0x00000000 + + + INTE3 + Set bit n to pass interrupts from channel n to DMA IRQ 3. + + Note this bit has no effect if the channel security/privilege level, defined by SECCFG_CHx, is greater than the IRQ security/privilege defined by SECCFG_IRQ3. + [15:0] + read-write + + + + + INTF3 + 0x00000438 + Force Interrupts + 0x00000000 + + + INTF3 + Write 1s to force the corresponding bits in INTS3. The interrupt remains asserted until INTF3 is cleared. + [15:0] + read-write + + + + + INTS3 + 0x0000043c + Interrupt Status for IRQ 3 + 0x00000000 + + + INTS3 + Indicates active channel interrupt requests which are currently causing IRQ 3 to be asserted. + Channel interrupts can be cleared by writing a bit mask here. + + Channels with a security/privilege (SECCFG_CHx) greater SECCFG_IRQ3) read as 0 in this register, and ignore writes. + [15:0] + read-write + oneToClear + + + + + TIMER0 + 0x00000440 + Pacing (X/Y) fractional timer + The pacing timer produces TREQ assertions at a rate set by ((X/Y) * sys_clk). This equation is evaluated every sys_clk cycles and therefore can only generate TREQs at a rate of 1 per sys_clk (i.e. permanent TREQ) or less. + 0x00000000 + + + X + Pacing Timer Dividend. Specifies the X value for the (X/Y) fractional timer. + [31:16] + read-write + + + Y + Pacing Timer Divisor. Specifies the Y value for the (X/Y) fractional timer. + [15:0] + read-write + + + + + TIMER1 + 0x00000444 + Pacing (X/Y) fractional timer + The pacing timer produces TREQ assertions at a rate set by ((X/Y) * sys_clk). This equation is evaluated every sys_clk cycles and therefore can only generate TREQs at a rate of 1 per sys_clk (i.e. permanent TREQ) or less. + 0x00000000 + + + X + Pacing Timer Dividend. Specifies the X value for the (X/Y) fractional timer. + [31:16] + read-write + + + Y + Pacing Timer Divisor. Specifies the Y value for the (X/Y) fractional timer. + [15:0] + read-write + + + + + TIMER2 + 0x00000448 + Pacing (X/Y) fractional timer + The pacing timer produces TREQ assertions at a rate set by ((X/Y) * sys_clk). This equation is evaluated every sys_clk cycles and therefore can only generate TREQs at a rate of 1 per sys_clk (i.e. permanent TREQ) or less. + 0x00000000 + + + X + Pacing Timer Dividend. Specifies the X value for the (X/Y) fractional timer. + [31:16] + read-write + + + Y + Pacing Timer Divisor. Specifies the Y value for the (X/Y) fractional timer. + [15:0] + read-write + + + + + TIMER3 + 0x0000044c + Pacing (X/Y) fractional timer + The pacing timer produces TREQ assertions at a rate set by ((X/Y) * sys_clk). This equation is evaluated every sys_clk cycles and therefore can only generate TREQs at a rate of 1 per sys_clk (i.e. permanent TREQ) or less. + 0x00000000 + + + X + Pacing Timer Dividend. Specifies the X value for the (X/Y) fractional timer. + [31:16] + read-write + + + Y + Pacing Timer Divisor. Specifies the Y value for the (X/Y) fractional timer. + [15:0] + read-write + + + + + MULTI_CHAN_TRIGGER + 0x00000450 + Trigger one or more channels simultaneously + 0x00000000 + + + MULTI_CHAN_TRIGGER + Each bit in this register corresponds to a DMA channel. Writing a 1 to the relevant bit is the same as writing to that channel's trigger register; the channel will start if it is currently enabled and not already busy. + [15:0] + write-only + + + + + SNIFF_CTRL + 0x00000454 + Sniffer Control + 0x00000000 + + + OUT_INV + If set, the result appears inverted (bitwise complement) when read. This does not affect the way the checksum is calculated; the result is transformed on-the-fly between the result register and the bus. + [11:11] + read-write + + + OUT_REV + If set, the result appears bit-reversed when read. This does not affect the way the checksum is calculated; the result is transformed on-the-fly between the result register and the bus. + [10:10] + read-write + + + BSWAP + Locally perform a byte reverse on the sniffed data, before feeding into checksum. + + Note that the sniff hardware is downstream of the DMA channel byteswap performed in the read master: if channel CTRL_BSWAP and SNIFF_CTRL_BSWAP are both enabled, their effects cancel from the sniffer's point of view. + [9:9] + read-write + + + CALC + [8:5] + read-write + + + CRC32 + 0 + Calculate a CRC-32 (IEEE802.3 polynomial) + + + CRC32R + 1 + Calculate a CRC-32 (IEEE802.3 polynomial) with bit reversed data + + + CRC16 + 2 + Calculate a CRC-16-CCITT + + + CRC16R + 3 + Calculate a CRC-16-CCITT with bit reversed data + + + EVEN + 14 + XOR reduction over all data. == 1 if the total 1 population count is odd. + + + SUM + 15 + Calculate a simple 32-bit checksum (addition with a 32 bit accumulator) + + + + + DMACH + DMA channel for Sniffer to observe + [4:1] + read-write + + + EN + Enable sniffer + [0:0] + read-write + + + + + SNIFF_DATA + 0x00000458 + Data accumulator for sniff hardware + 0x00000000 + + + SNIFF_DATA + Write an initial seed value here before starting a DMA transfer on the channel indicated by SNIFF_CTRL_DMACH. The hardware will update this register each time it observes a read from the indicated channel. Once the channel completes, the final result can be read from this register. + [31:0] + read-write + + + + + FIFO_LEVELS + 0x00000460 + Debug RAF, WAF, TDF levels + 0x00000000 + + + RAF_LVL + Current Read-Address-FIFO fill level + [23:16] + read-only + + + WAF_LVL + Current Write-Address-FIFO fill level + [15:8] + read-only + + + TDF_LVL + Current Transfer-Data-FIFO fill level + [7:0] + read-only + + + + + CHAN_ABORT + 0x00000464 + Abort an in-progress transfer sequence on one or more channels + 0x00000000 + + + CHAN_ABORT + Each bit corresponds to a channel. Writing a 1 aborts whatever transfer sequence is in progress on that channel. The bit will remain high until any in-flight transfers have been flushed through the address and data FIFOs. + + After writing, this register must be polled until it returns all-zero. Until this point, it is unsafe to restart the channel. + [15:0] + write-only + + + + + N_CHANNELS + 0x00000468 + The number of channels this DMA instance is equipped with. This DMA supports up to 16 hardware channels, but can be configured with as few as one, to minimise silicon area. + 0x00000000 + + + N_CHANNELS + [4:0] + read-only + + + + + SECCFG_CH0 + 0x00000480 + Security configuration for channel 0. Control whether this channel performs Secure/Non-secure and Privileged/Unprivileged bus accesses. + + If this channel generates bus accesses of some security level, an access of at least that level (in the order S+P > S+U > NS+P > NS+U) is required to program, trigger, abort, check the status of, interrupt on or acknowledge the interrupt of this channel. + + This register automatically locks down (becomes read-only) once software starts to configure the channel. + + This register is world-readable, but is writable only from a Secure, Privileged context. + 0x00000003 + + + LOCK + LOCK is 0 at reset, and is set to 1 automatically upon a successful write to this channel's control registers. That is, a write to CTRL, READ_ADDR, WRITE_ADDR, TRANS_COUNT and their aliases. + + Once its LOCK bit is set, this register becomes read-only. + + A failed write, for example due to the write's privilege being lower than that specified in the channel's SECCFG register, will not set the LOCK bit. + [2:2] + read-write + + + S + Secure channel. If 1, this channel performs Secure bus accesses. If 0, it performs Non-secure bus accesses. + + If 1, this channel is controllable only from a Secure context. + [1:1] + read-write + + + P + Privileged channel. If 1, this channel performs Privileged bus accesses. If 0, it performs Unprivileged bus accesses. + + If 1, this channel is controllable only from a Privileged context of the same Secure/Non-secure level, or any context of a higher Secure/Non-secure level. + [0:0] + read-write + + + + + SECCFG_CH1 + 0x00000484 + Security configuration for channel 1. Control whether this channel performs Secure/Non-secure and Privileged/Unprivileged bus accesses. + + If this channel generates bus accesses of some security level, an access of at least that level (in the order S+P > S+U > NS+P > NS+U) is required to program, trigger, abort, check the status of, interrupt on or acknowledge the interrupt of this channel. + + This register automatically locks down (becomes read-only) once software starts to configure the channel. + + This register is world-readable, but is writable only from a Secure, Privileged context. + 0x00000003 + + + LOCK + LOCK is 0 at reset, and is set to 1 automatically upon a successful write to this channel's control registers. That is, a write to CTRL, READ_ADDR, WRITE_ADDR, TRANS_COUNT and their aliases. + + Once its LOCK bit is set, this register becomes read-only. + + A failed write, for example due to the write's privilege being lower than that specified in the channel's SECCFG register, will not set the LOCK bit. + [2:2] + read-write + + + S + Secure channel. If 1, this channel performs Secure bus accesses. If 0, it performs Non-secure bus accesses. + + If 1, this channel is controllable only from a Secure context. + [1:1] + read-write + + + P + Privileged channel. If 1, this channel performs Privileged bus accesses. If 0, it performs Unprivileged bus accesses. + + If 1, this channel is controllable only from a Privileged context of the same Secure/Non-secure level, or any context of a higher Secure/Non-secure level. + [0:0] + read-write + + + + + SECCFG_CH2 + 0x00000488 + Security configuration for channel 2. Control whether this channel performs Secure/Non-secure and Privileged/Unprivileged bus accesses. + + If this channel generates bus accesses of some security level, an access of at least that level (in the order S+P > S+U > NS+P > NS+U) is required to program, trigger, abort, check the status of, interrupt on or acknowledge the interrupt of this channel. + + This register automatically locks down (becomes read-only) once software starts to configure the channel. + + This register is world-readable, but is writable only from a Secure, Privileged context. + 0x00000003 + + + LOCK + LOCK is 0 at reset, and is set to 1 automatically upon a successful write to this channel's control registers. That is, a write to CTRL, READ_ADDR, WRITE_ADDR, TRANS_COUNT and their aliases. + + Once its LOCK bit is set, this register becomes read-only. + + A failed write, for example due to the write's privilege being lower than that specified in the channel's SECCFG register, will not set the LOCK bit. + [2:2] + read-write + + + S + Secure channel. If 1, this channel performs Secure bus accesses. If 0, it performs Non-secure bus accesses. + + If 1, this channel is controllable only from a Secure context. + [1:1] + read-write + + + P + Privileged channel. If 1, this channel performs Privileged bus accesses. If 0, it performs Unprivileged bus accesses. + + If 1, this channel is controllable only from a Privileged context of the same Secure/Non-secure level, or any context of a higher Secure/Non-secure level. + [0:0] + read-write + + + + + SECCFG_CH3 + 0x0000048c + Security configuration for channel 3. Control whether this channel performs Secure/Non-secure and Privileged/Unprivileged bus accesses. + + If this channel generates bus accesses of some security level, an access of at least that level (in the order S+P > S+U > NS+P > NS+U) is required to program, trigger, abort, check the status of, interrupt on or acknowledge the interrupt of this channel. + + This register automatically locks down (becomes read-only) once software starts to configure the channel. + + This register is world-readable, but is writable only from a Secure, Privileged context. + 0x00000003 + + + LOCK + LOCK is 0 at reset, and is set to 1 automatically upon a successful write to this channel's control registers. That is, a write to CTRL, READ_ADDR, WRITE_ADDR, TRANS_COUNT and their aliases. + + Once its LOCK bit is set, this register becomes read-only. + + A failed write, for example due to the write's privilege being lower than that specified in the channel's SECCFG register, will not set the LOCK bit. + [2:2] + read-write + + + S + Secure channel. If 1, this channel performs Secure bus accesses. If 0, it performs Non-secure bus accesses. + + If 1, this channel is controllable only from a Secure context. + [1:1] + read-write + + + P + Privileged channel. If 1, this channel performs Privileged bus accesses. If 0, it performs Unprivileged bus accesses. + + If 1, this channel is controllable only from a Privileged context of the same Secure/Non-secure level, or any context of a higher Secure/Non-secure level. + [0:0] + read-write + + + + + SECCFG_CH4 + 0x00000490 + Security configuration for channel 4. Control whether this channel performs Secure/Non-secure and Privileged/Unprivileged bus accesses. + + If this channel generates bus accesses of some security level, an access of at least that level (in the order S+P > S+U > NS+P > NS+U) is required to program, trigger, abort, check the status of, interrupt on or acknowledge the interrupt of this channel. + + This register automatically locks down (becomes read-only) once software starts to configure the channel. + + This register is world-readable, but is writable only from a Secure, Privileged context. + 0x00000003 + + + LOCK + LOCK is 0 at reset, and is set to 1 automatically upon a successful write to this channel's control registers. That is, a write to CTRL, READ_ADDR, WRITE_ADDR, TRANS_COUNT and their aliases. + + Once its LOCK bit is set, this register becomes read-only. + + A failed write, for example due to the write's privilege being lower than that specified in the channel's SECCFG register, will not set the LOCK bit. + [2:2] + read-write + + + S + Secure channel. If 1, this channel performs Secure bus accesses. If 0, it performs Non-secure bus accesses. + + If 1, this channel is controllable only from a Secure context. + [1:1] + read-write + + + P + Privileged channel. If 1, this channel performs Privileged bus accesses. If 0, it performs Unprivileged bus accesses. + + If 1, this channel is controllable only from a Privileged context of the same Secure/Non-secure level, or any context of a higher Secure/Non-secure level. + [0:0] + read-write + + + + + SECCFG_CH5 + 0x00000494 + Security configuration for channel 5. Control whether this channel performs Secure/Non-secure and Privileged/Unprivileged bus accesses. + + If this channel generates bus accesses of some security level, an access of at least that level (in the order S+P > S+U > NS+P > NS+U) is required to program, trigger, abort, check the status of, interrupt on or acknowledge the interrupt of this channel. + + This register automatically locks down (becomes read-only) once software starts to configure the channel. + + This register is world-readable, but is writable only from a Secure, Privileged context. + 0x00000003 + + + LOCK + LOCK is 0 at reset, and is set to 1 automatically upon a successful write to this channel's control registers. That is, a write to CTRL, READ_ADDR, WRITE_ADDR, TRANS_COUNT and their aliases. + + Once its LOCK bit is set, this register becomes read-only. + + A failed write, for example due to the write's privilege being lower than that specified in the channel's SECCFG register, will not set the LOCK bit. + [2:2] + read-write + + + S + Secure channel. If 1, this channel performs Secure bus accesses. If 0, it performs Non-secure bus accesses. + + If 1, this channel is controllable only from a Secure context. + [1:1] + read-write + + + P + Privileged channel. If 1, this channel performs Privileged bus accesses. If 0, it performs Unprivileged bus accesses. + + If 1, this channel is controllable only from a Privileged context of the same Secure/Non-secure level, or any context of a higher Secure/Non-secure level. + [0:0] + read-write + + + + + SECCFG_CH6 + 0x00000498 + Security configuration for channel 6. Control whether this channel performs Secure/Non-secure and Privileged/Unprivileged bus accesses. + + If this channel generates bus accesses of some security level, an access of at least that level (in the order S+P > S+U > NS+P > NS+U) is required to program, trigger, abort, check the status of, interrupt on or acknowledge the interrupt of this channel. + + This register automatically locks down (becomes read-only) once software starts to configure the channel. + + This register is world-readable, but is writable only from a Secure, Privileged context. + 0x00000003 + + + LOCK + LOCK is 0 at reset, and is set to 1 automatically upon a successful write to this channel's control registers. That is, a write to CTRL, READ_ADDR, WRITE_ADDR, TRANS_COUNT and their aliases. + + Once its LOCK bit is set, this register becomes read-only. + + A failed write, for example due to the write's privilege being lower than that specified in the channel's SECCFG register, will not set the LOCK bit. + [2:2] + read-write + + + S + Secure channel. If 1, this channel performs Secure bus accesses. If 0, it performs Non-secure bus accesses. + + If 1, this channel is controllable only from a Secure context. + [1:1] + read-write + + + P + Privileged channel. If 1, this channel performs Privileged bus accesses. If 0, it performs Unprivileged bus accesses. + + If 1, this channel is controllable only from a Privileged context of the same Secure/Non-secure level, or any context of a higher Secure/Non-secure level. + [0:0] + read-write + + + + + SECCFG_CH7 + 0x0000049c + Security configuration for channel 7. Control whether this channel performs Secure/Non-secure and Privileged/Unprivileged bus accesses. + + If this channel generates bus accesses of some security level, an access of at least that level (in the order S+P > S+U > NS+P > NS+U) is required to program, trigger, abort, check the status of, interrupt on or acknowledge the interrupt of this channel. + + This register automatically locks down (becomes read-only) once software starts to configure the channel. + + This register is world-readable, but is writable only from a Secure, Privileged context. + 0x00000003 + + + LOCK + LOCK is 0 at reset, and is set to 1 automatically upon a successful write to this channel's control registers. That is, a write to CTRL, READ_ADDR, WRITE_ADDR, TRANS_COUNT and their aliases. + + Once its LOCK bit is set, this register becomes read-only. + + A failed write, for example due to the write's privilege being lower than that specified in the channel's SECCFG register, will not set the LOCK bit. + [2:2] + read-write + + + S + Secure channel. If 1, this channel performs Secure bus accesses. If 0, it performs Non-secure bus accesses. + + If 1, this channel is controllable only from a Secure context. + [1:1] + read-write + + + P + Privileged channel. If 1, this channel performs Privileged bus accesses. If 0, it performs Unprivileged bus accesses. + + If 1, this channel is controllable only from a Privileged context of the same Secure/Non-secure level, or any context of a higher Secure/Non-secure level. + [0:0] + read-write + + + + + SECCFG_CH8 + 0x000004a0 + Security configuration for channel 8. Control whether this channel performs Secure/Non-secure and Privileged/Unprivileged bus accesses. + + If this channel generates bus accesses of some security level, an access of at least that level (in the order S+P > S+U > NS+P > NS+U) is required to program, trigger, abort, check the status of, interrupt on or acknowledge the interrupt of this channel. + + This register automatically locks down (becomes read-only) once software starts to configure the channel. + + This register is world-readable, but is writable only from a Secure, Privileged context. + 0x00000003 + + + LOCK + LOCK is 0 at reset, and is set to 1 automatically upon a successful write to this channel's control registers. That is, a write to CTRL, READ_ADDR, WRITE_ADDR, TRANS_COUNT and their aliases. + + Once its LOCK bit is set, this register becomes read-only. + + A failed write, for example due to the write's privilege being lower than that specified in the channel's SECCFG register, will not set the LOCK bit. + [2:2] + read-write + + + S + Secure channel. If 1, this channel performs Secure bus accesses. If 0, it performs Non-secure bus accesses. + + If 1, this channel is controllable only from a Secure context. + [1:1] + read-write + + + P + Privileged channel. If 1, this channel performs Privileged bus accesses. If 0, it performs Unprivileged bus accesses. + + If 1, this channel is controllable only from a Privileged context of the same Secure/Non-secure level, or any context of a higher Secure/Non-secure level. + [0:0] + read-write + + + + + SECCFG_CH9 + 0x000004a4 + Security configuration for channel 9. Control whether this channel performs Secure/Non-secure and Privileged/Unprivileged bus accesses. + + If this channel generates bus accesses of some security level, an access of at least that level (in the order S+P > S+U > NS+P > NS+U) is required to program, trigger, abort, check the status of, interrupt on or acknowledge the interrupt of this channel. + + This register automatically locks down (becomes read-only) once software starts to configure the channel. + + This register is world-readable, but is writable only from a Secure, Privileged context. + 0x00000003 + + + LOCK + LOCK is 0 at reset, and is set to 1 automatically upon a successful write to this channel's control registers. That is, a write to CTRL, READ_ADDR, WRITE_ADDR, TRANS_COUNT and their aliases. + + Once its LOCK bit is set, this register becomes read-only. + + A failed write, for example due to the write's privilege being lower than that specified in the channel's SECCFG register, will not set the LOCK bit. + [2:2] + read-write + + + S + Secure channel. If 1, this channel performs Secure bus accesses. If 0, it performs Non-secure bus accesses. + + If 1, this channel is controllable only from a Secure context. + [1:1] + read-write + + + P + Privileged channel. If 1, this channel performs Privileged bus accesses. If 0, it performs Unprivileged bus accesses. + + If 1, this channel is controllable only from a Privileged context of the same Secure/Non-secure level, or any context of a higher Secure/Non-secure level. + [0:0] + read-write + + + + + SECCFG_CH10 + 0x000004a8 + Security configuration for channel 10. Control whether this channel performs Secure/Non-secure and Privileged/Unprivileged bus accesses. + + If this channel generates bus accesses of some security level, an access of at least that level (in the order S+P > S+U > NS+P > NS+U) is required to program, trigger, abort, check the status of, interrupt on or acknowledge the interrupt of this channel. + + This register automatically locks down (becomes read-only) once software starts to configure the channel. + + This register is world-readable, but is writable only from a Secure, Privileged context. + 0x00000003 + + + LOCK + LOCK is 0 at reset, and is set to 1 automatically upon a successful write to this channel's control registers. That is, a write to CTRL, READ_ADDR, WRITE_ADDR, TRANS_COUNT and their aliases. + + Once its LOCK bit is set, this register becomes read-only. + + A failed write, for example due to the write's privilege being lower than that specified in the channel's SECCFG register, will not set the LOCK bit. + [2:2] + read-write + + + S + Secure channel. If 1, this channel performs Secure bus accesses. If 0, it performs Non-secure bus accesses. + + If 1, this channel is controllable only from a Secure context. + [1:1] + read-write + + + P + Privileged channel. If 1, this channel performs Privileged bus accesses. If 0, it performs Unprivileged bus accesses. + + If 1, this channel is controllable only from a Privileged context of the same Secure/Non-secure level, or any context of a higher Secure/Non-secure level. + [0:0] + read-write + + + + + SECCFG_CH11 + 0x000004ac + Security configuration for channel 11. Control whether this channel performs Secure/Non-secure and Privileged/Unprivileged bus accesses. + + If this channel generates bus accesses of some security level, an access of at least that level (in the order S+P > S+U > NS+P > NS+U) is required to program, trigger, abort, check the status of, interrupt on or acknowledge the interrupt of this channel. + + This register automatically locks down (becomes read-only) once software starts to configure the channel. + + This register is world-readable, but is writable only from a Secure, Privileged context. + 0x00000003 + + + LOCK + LOCK is 0 at reset, and is set to 1 automatically upon a successful write to this channel's control registers. That is, a write to CTRL, READ_ADDR, WRITE_ADDR, TRANS_COUNT and their aliases. + + Once its LOCK bit is set, this register becomes read-only. + + A failed write, for example due to the write's privilege being lower than that specified in the channel's SECCFG register, will not set the LOCK bit. + [2:2] + read-write + + + S + Secure channel. If 1, this channel performs Secure bus accesses. If 0, it performs Non-secure bus accesses. + + If 1, this channel is controllable only from a Secure context. + [1:1] + read-write + + + P + Privileged channel. If 1, this channel performs Privileged bus accesses. If 0, it performs Unprivileged bus accesses. + + If 1, this channel is controllable only from a Privileged context of the same Secure/Non-secure level, or any context of a higher Secure/Non-secure level. + [0:0] + read-write + + + + + SECCFG_CH12 + 0x000004b0 + Security configuration for channel 12. Control whether this channel performs Secure/Non-secure and Privileged/Unprivileged bus accesses. + + If this channel generates bus accesses of some security level, an access of at least that level (in the order S+P > S+U > NS+P > NS+U) is required to program, trigger, abort, check the status of, interrupt on or acknowledge the interrupt of this channel. + + This register automatically locks down (becomes read-only) once software starts to configure the channel. + + This register is world-readable, but is writable only from a Secure, Privileged context. + 0x00000003 + + + LOCK + LOCK is 0 at reset, and is set to 1 automatically upon a successful write to this channel's control registers. That is, a write to CTRL, READ_ADDR, WRITE_ADDR, TRANS_COUNT and their aliases. + + Once its LOCK bit is set, this register becomes read-only. + + A failed write, for example due to the write's privilege being lower than that specified in the channel's SECCFG register, will not set the LOCK bit. + [2:2] + read-write + + + S + Secure channel. If 1, this channel performs Secure bus accesses. If 0, it performs Non-secure bus accesses. + + If 1, this channel is controllable only from a Secure context. + [1:1] + read-write + + + P + Privileged channel. If 1, this channel performs Privileged bus accesses. If 0, it performs Unprivileged bus accesses. + + If 1, this channel is controllable only from a Privileged context of the same Secure/Non-secure level, or any context of a higher Secure/Non-secure level. + [0:0] + read-write + + + + + SECCFG_CH13 + 0x000004b4 + Security configuration for channel 13. Control whether this channel performs Secure/Non-secure and Privileged/Unprivileged bus accesses. + + If this channel generates bus accesses of some security level, an access of at least that level (in the order S+P > S+U > NS+P > NS+U) is required to program, trigger, abort, check the status of, interrupt on or acknowledge the interrupt of this channel. + + This register automatically locks down (becomes read-only) once software starts to configure the channel. + + This register is world-readable, but is writable only from a Secure, Privileged context. + 0x00000003 + + + LOCK + LOCK is 0 at reset, and is set to 1 automatically upon a successful write to this channel's control registers. That is, a write to CTRL, READ_ADDR, WRITE_ADDR, TRANS_COUNT and their aliases. + + Once its LOCK bit is set, this register becomes read-only. + + A failed write, for example due to the write's privilege being lower than that specified in the channel's SECCFG register, will not set the LOCK bit. + [2:2] + read-write + + + S + Secure channel. If 1, this channel performs Secure bus accesses. If 0, it performs Non-secure bus accesses. + + If 1, this channel is controllable only from a Secure context. + [1:1] + read-write + + + P + Privileged channel. If 1, this channel performs Privileged bus accesses. If 0, it performs Unprivileged bus accesses. + + If 1, this channel is controllable only from a Privileged context of the same Secure/Non-secure level, or any context of a higher Secure/Non-secure level. + [0:0] + read-write + + + + + SECCFG_CH14 + 0x000004b8 + Security configuration for channel 14. Control whether this channel performs Secure/Non-secure and Privileged/Unprivileged bus accesses. + + If this channel generates bus accesses of some security level, an access of at least that level (in the order S+P > S+U > NS+P > NS+U) is required to program, trigger, abort, check the status of, interrupt on or acknowledge the interrupt of this channel. + + This register automatically locks down (becomes read-only) once software starts to configure the channel. + + This register is world-readable, but is writable only from a Secure, Privileged context. + 0x00000003 + + + LOCK + LOCK is 0 at reset, and is set to 1 automatically upon a successful write to this channel's control registers. That is, a write to CTRL, READ_ADDR, WRITE_ADDR, TRANS_COUNT and their aliases. + + Once its LOCK bit is set, this register becomes read-only. + + A failed write, for example due to the write's privilege being lower than that specified in the channel's SECCFG register, will not set the LOCK bit. + [2:2] + read-write + + + S + Secure channel. If 1, this channel performs Secure bus accesses. If 0, it performs Non-secure bus accesses. + + If 1, this channel is controllable only from a Secure context. + [1:1] + read-write + + + P + Privileged channel. If 1, this channel performs Privileged bus accesses. If 0, it performs Unprivileged bus accesses. + + If 1, this channel is controllable only from a Privileged context of the same Secure/Non-secure level, or any context of a higher Secure/Non-secure level. + [0:0] + read-write + + + + + SECCFG_CH15 + 0x000004bc + Security configuration for channel 15. Control whether this channel performs Secure/Non-secure and Privileged/Unprivileged bus accesses. + + If this channel generates bus accesses of some security level, an access of at least that level (in the order S+P > S+U > NS+P > NS+U) is required to program, trigger, abort, check the status of, interrupt on or acknowledge the interrupt of this channel. + + This register automatically locks down (becomes read-only) once software starts to configure the channel. + + This register is world-readable, but is writable only from a Secure, Privileged context. + 0x00000003 + + + LOCK + LOCK is 0 at reset, and is set to 1 automatically upon a successful write to this channel's control registers. That is, a write to CTRL, READ_ADDR, WRITE_ADDR, TRANS_COUNT and their aliases. + + Once its LOCK bit is set, this register becomes read-only. + + A failed write, for example due to the write's privilege being lower than that specified in the channel's SECCFG register, will not set the LOCK bit. + [2:2] + read-write + + + S + Secure channel. If 1, this channel performs Secure bus accesses. If 0, it performs Non-secure bus accesses. + + If 1, this channel is controllable only from a Secure context. + [1:1] + read-write + + + P + Privileged channel. If 1, this channel performs Privileged bus accesses. If 0, it performs Unprivileged bus accesses. + + If 1, this channel is controllable only from a Privileged context of the same Secure/Non-secure level, or any context of a higher Secure/Non-secure level. + [0:0] + read-write + + + + + SECCFG_IRQ0 + 0x000004c0 + Security configuration for IRQ 0. Control whether the IRQ permits configuration by Non-secure/Unprivileged contexts, and whether it can observe Secure/Privileged channel interrupt flags. + 0x00000003 + + + S + Secure IRQ. If 1, this IRQ's control registers can only be accessed from a Secure context. + + If 0, this IRQ's control registers can be accessed from a Non-secure context, but Secure channels (as per SECCFG_CHx) are masked from the IRQ status, and this IRQ's registers can not be used to acknowledge the channel interrupts of Secure channels. + [1:1] + read-write + + + P + Privileged IRQ. If 1, this IRQ's control registers can only be accessed from a Privileged context. + + If 0, this IRQ's control registers can be accessed from an Unprivileged context, but Privileged channels (as per SECCFG_CHx) are masked from the IRQ status, and this IRQ's registers can not be used to acknowledge the channel interrupts of Privileged channels. + [0:0] + read-write + + + + + SECCFG_IRQ1 + 0x000004c4 + Security configuration for IRQ 1. Control whether the IRQ permits configuration by Non-secure/Unprivileged contexts, and whether it can observe Secure/Privileged channel interrupt flags. + 0x00000003 + + + S + Secure IRQ. If 1, this IRQ's control registers can only be accessed from a Secure context. + + If 0, this IRQ's control registers can be accessed from a Non-secure context, but Secure channels (as per SECCFG_CHx) are masked from the IRQ status, and this IRQ's registers can not be used to acknowledge the channel interrupts of Secure channels. + [1:1] + read-write + + + P + Privileged IRQ. If 1, this IRQ's control registers can only be accessed from a Privileged context. + + If 0, this IRQ's control registers can be accessed from an Unprivileged context, but Privileged channels (as per SECCFG_CHx) are masked from the IRQ status, and this IRQ's registers can not be used to acknowledge the channel interrupts of Privileged channels. + [0:0] + read-write + + + + + SECCFG_IRQ2 + 0x000004c8 + Security configuration for IRQ 2. Control whether the IRQ permits configuration by Non-secure/Unprivileged contexts, and whether it can observe Secure/Privileged channel interrupt flags. + 0x00000003 + + + S + Secure IRQ. If 1, this IRQ's control registers can only be accessed from a Secure context. + + If 0, this IRQ's control registers can be accessed from a Non-secure context, but Secure channels (as per SECCFG_CHx) are masked from the IRQ status, and this IRQ's registers can not be used to acknowledge the channel interrupts of Secure channels. + [1:1] + read-write + + + P + Privileged IRQ. If 1, this IRQ's control registers can only be accessed from a Privileged context. + + If 0, this IRQ's control registers can be accessed from an Unprivileged context, but Privileged channels (as per SECCFG_CHx) are masked from the IRQ status, and this IRQ's registers can not be used to acknowledge the channel interrupts of Privileged channels. + [0:0] + read-write + + + + + SECCFG_IRQ3 + 0x000004cc + Security configuration for IRQ 3. Control whether the IRQ permits configuration by Non-secure/Unprivileged contexts, and whether it can observe Secure/Privileged channel interrupt flags. + 0x00000003 + + + S + Secure IRQ. If 1, this IRQ's control registers can only be accessed from a Secure context. + + If 0, this IRQ's control registers can be accessed from a Non-secure context, but Secure channels (as per SECCFG_CHx) are masked from the IRQ status, and this IRQ's registers can not be used to acknowledge the channel interrupts of Secure channels. + [1:1] + read-write + + + P + Privileged IRQ. If 1, this IRQ's control registers can only be accessed from a Privileged context. + + If 0, this IRQ's control registers can be accessed from an Unprivileged context, but Privileged channels (as per SECCFG_CHx) are masked from the IRQ status, and this IRQ's registers can not be used to acknowledge the channel interrupts of Privileged channels. + [0:0] + read-write + + + + + SECCFG_MISC + 0x000004d0 + Miscellaneous security configuration + 0x000003ff + + + TIMER3_S + If 1, the TIMER3 register is only accessible from a Secure context, and timer DREQ 3 is only visible to Secure channels. + [9:9] + read-write + + + TIMER3_P + If 1, the TIMER3 register is only accessible from a Privileged (or more Secure) context, and timer DREQ 3 is only visible to Privileged (or more Secure) channels. + [8:8] + read-write + + + TIMER2_S + If 1, the TIMER2 register is only accessible from a Secure context, and timer DREQ 2 is only visible to Secure channels. + [7:7] + read-write + + + TIMER2_P + If 1, the TIMER2 register is only accessible from a Privileged (or more Secure) context, and timer DREQ 2 is only visible to Privileged (or more Secure) channels. + [6:6] + read-write + + + TIMER1_S + If 1, the TIMER1 register is only accessible from a Secure context, and timer DREQ 1 is only visible to Secure channels. + [5:5] + read-write + + + TIMER1_P + If 1, the TIMER1 register is only accessible from a Privileged (or more Secure) context, and timer DREQ 1 is only visible to Privileged (or more Secure) channels. + [4:4] + read-write + + + TIMER0_S + If 1, the TIMER0 register is only accessible from a Secure context, and timer DREQ 0 is only visible to Secure channels. + [3:3] + read-write + + + TIMER0_P + If 1, the TIMER0 register is only accessible from a Privileged (or more Secure) context, and timer DREQ 0 is only visible to Privileged (or more Secure) channels. + [2:2] + read-write + + + SNIFF_S + If 1, the sniffer can see data transfers from Secure channels, and can itself only be accessed from a Secure context. + + If 0, the sniffer can be accessed from either a Secure or Non-secure context, but can not see data transfers of Secure channels. + [1:1] + read-write + + + SNIFF_P + If 1, the sniffer can see data transfers from Privileged channels, and can itself only be accessed from a privileged context, or from a Secure context when SNIFF_S is 0. + + If 0, the sniffer can be accessed from either a Privileged or Unprivileged context (with sufficient security level) but can not see transfers from Privileged channels. + [0:0] + read-write + + + + + MPU_CTRL + 0x00000500 + Control register for DMA MPU. Accessible only from a Privileged context. + 0x00000000 + + + NS_HIDE_ADDR + By default, when a region's S bit is clear, Non-secure-Privileged reads can see the region's base address and limit address. Set this bit to make the addresses appear as 0 to Non-secure reads, even when the region is Non-secure, to avoid leaking information about the processor SAU map. + [3:3] + read-write + + + S + Determine whether an address not covered by an active MPU region is Secure (1) or Non-secure (0) + [2:2] + read-write + + + P + Determine whether an address not covered by an active MPU region is Privileged (1) or Unprivileged (0) + [1:1] + read-write + + + + + MPU_BAR0 + 0x00000504 + Base address register for MPU region 0. Writable only from a Secure, Privileged context. + 0x00000000 + + + ADDR + This MPU region matches addresses where addr[31:5] (the 27 most significant bits) are greater than or equal to BAR_ADDR, and less than or equal to LAR_ADDR. + + Readable from any Privileged context, if and only if this region's S bit is clear, and MPU_CTRL_NS_HIDE_ADDR is clear. Otherwise readable only from a Secure, Privileged context. + [31:5] + read-write + + + + + MPU_LAR0 + 0x00000508 + Limit address register for MPU region 0. Writable only from a Secure, Privileged context, with the exception of the P bit. + 0x00000000 + + + ADDR + Limit address bits 31:5. Readable from any Privileged context, if and only if this region's S bit is clear, and MPU_CTRL_NS_HIDE_ADDR is clear. Otherwise readable only from a Secure, Privileged context. + [31:5] + read-write + + + S + Determines the Secure/Non-secure (=1/0) status of addresses matching this region, if this region is enabled. + [2:2] + read-write + + + P + Determines the Privileged/Unprivileged (=1/0) status of addresses matching this region, if this region is enabled. Writable from any Privileged context, if and only if the S bit is clear. Otherwise, writable only from a Secure, Privileged context. + [1:1] + read-write + + + EN + Region enable. If 1, any address within range specified by the base address (BAR_ADDR) and limit address (LAR_ADDR) has the attributes specified by S and P. + [0:0] + read-write + + + + + MPU_BAR1 + 0x0000050c + Base address register for MPU region 1. Writable only from a Secure, Privileged context. + 0x00000000 + + + ADDR + This MPU region matches addresses where addr[31:5] (the 27 most significant bits) are greater than or equal to BAR_ADDR, and less than or equal to LAR_ADDR. + + Readable from any Privileged context, if and only if this region's S bit is clear, and MPU_CTRL_NS_HIDE_ADDR is clear. Otherwise readable only from a Secure, Privileged context. + [31:5] + read-write + + + + + MPU_LAR1 + 0x00000510 + Limit address register for MPU region 1. Writable only from a Secure, Privileged context, with the exception of the P bit. + 0x00000000 + + + ADDR + Limit address bits 31:5. Readable from any Privileged context, if and only if this region's S bit is clear, and MPU_CTRL_NS_HIDE_ADDR is clear. Otherwise readable only from a Secure, Privileged context. + [31:5] + read-write + + + S + Determines the Secure/Non-secure (=1/0) status of addresses matching this region, if this region is enabled. + [2:2] + read-write + + + P + Determines the Privileged/Unprivileged (=1/0) status of addresses matching this region, if this region is enabled. Writable from any Privileged context, if and only if the S bit is clear. Otherwise, writable only from a Secure, Privileged context. + [1:1] + read-write + + + EN + Region enable. If 1, any address within range specified by the base address (BAR_ADDR) and limit address (LAR_ADDR) has the attributes specified by S and P. + [0:0] + read-write + + + + + MPU_BAR2 + 0x00000514 + Base address register for MPU region 2. Writable only from a Secure, Privileged context. + 0x00000000 + + + ADDR + This MPU region matches addresses where addr[31:5] (the 27 most significant bits) are greater than or equal to BAR_ADDR, and less than or equal to LAR_ADDR. + + Readable from any Privileged context, if and only if this region's S bit is clear, and MPU_CTRL_NS_HIDE_ADDR is clear. Otherwise readable only from a Secure, Privileged context. + [31:5] + read-write + + + + + MPU_LAR2 + 0x00000518 + Limit address register for MPU region 2. Writable only from a Secure, Privileged context, with the exception of the P bit. + 0x00000000 + + + ADDR + Limit address bits 31:5. Readable from any Privileged context, if and only if this region's S bit is clear, and MPU_CTRL_NS_HIDE_ADDR is clear. Otherwise readable only from a Secure, Privileged context. + [31:5] + read-write + + + S + Determines the Secure/Non-secure (=1/0) status of addresses matching this region, if this region is enabled. + [2:2] + read-write + + + P + Determines the Privileged/Unprivileged (=1/0) status of addresses matching this region, if this region is enabled. Writable from any Privileged context, if and only if the S bit is clear. Otherwise, writable only from a Secure, Privileged context. + [1:1] + read-write + + + EN + Region enable. If 1, any address within range specified by the base address (BAR_ADDR) and limit address (LAR_ADDR) has the attributes specified by S and P. + [0:0] + read-write + + + + + MPU_BAR3 + 0x0000051c + Base address register for MPU region 3. Writable only from a Secure, Privileged context. + 0x00000000 + + + ADDR + This MPU region matches addresses where addr[31:5] (the 27 most significant bits) are greater than or equal to BAR_ADDR, and less than or equal to LAR_ADDR. + + Readable from any Privileged context, if and only if this region's S bit is clear, and MPU_CTRL_NS_HIDE_ADDR is clear. Otherwise readable only from a Secure, Privileged context. + [31:5] + read-write + + + + + MPU_LAR3 + 0x00000520 + Limit address register for MPU region 3. Writable only from a Secure, Privileged context, with the exception of the P bit. + 0x00000000 + + + ADDR + Limit address bits 31:5. Readable from any Privileged context, if and only if this region's S bit is clear, and MPU_CTRL_NS_HIDE_ADDR is clear. Otherwise readable only from a Secure, Privileged context. + [31:5] + read-write + + + S + Determines the Secure/Non-secure (=1/0) status of addresses matching this region, if this region is enabled. + [2:2] + read-write + + + P + Determines the Privileged/Unprivileged (=1/0) status of addresses matching this region, if this region is enabled. Writable from any Privileged context, if and only if the S bit is clear. Otherwise, writable only from a Secure, Privileged context. + [1:1] + read-write + + + EN + Region enable. If 1, any address within range specified by the base address (BAR_ADDR) and limit address (LAR_ADDR) has the attributes specified by S and P. + [0:0] + read-write + + + + + MPU_BAR4 + 0x00000524 + Base address register for MPU region 4. Writable only from a Secure, Privileged context. + 0x00000000 + + + ADDR + This MPU region matches addresses where addr[31:5] (the 27 most significant bits) are greater than or equal to BAR_ADDR, and less than or equal to LAR_ADDR. + + Readable from any Privileged context, if and only if this region's S bit is clear, and MPU_CTRL_NS_HIDE_ADDR is clear. Otherwise readable only from a Secure, Privileged context. + [31:5] + read-write + + + + + MPU_LAR4 + 0x00000528 + Limit address register for MPU region 4. Writable only from a Secure, Privileged context, with the exception of the P bit. + 0x00000000 + + + ADDR + Limit address bits 31:5. Readable from any Privileged context, if and only if this region's S bit is clear, and MPU_CTRL_NS_HIDE_ADDR is clear. Otherwise readable only from a Secure, Privileged context. + [31:5] + read-write + + + S + Determines the Secure/Non-secure (=1/0) status of addresses matching this region, if this region is enabled. + [2:2] + read-write + + + P + Determines the Privileged/Unprivileged (=1/0) status of addresses matching this region, if this region is enabled. Writable from any Privileged context, if and only if the S bit is clear. Otherwise, writable only from a Secure, Privileged context. + [1:1] + read-write + + + EN + Region enable. If 1, any address within range specified by the base address (BAR_ADDR) and limit address (LAR_ADDR) has the attributes specified by S and P. + [0:0] + read-write + + + + + MPU_BAR5 + 0x0000052c + Base address register for MPU region 5. Writable only from a Secure, Privileged context. + 0x00000000 + + + ADDR + This MPU region matches addresses where addr[31:5] (the 27 most significant bits) are greater than or equal to BAR_ADDR, and less than or equal to LAR_ADDR. + + Readable from any Privileged context, if and only if this region's S bit is clear, and MPU_CTRL_NS_HIDE_ADDR is clear. Otherwise readable only from a Secure, Privileged context. + [31:5] + read-write + + + + + MPU_LAR5 + 0x00000530 + Limit address register for MPU region 5. Writable only from a Secure, Privileged context, with the exception of the P bit. + 0x00000000 + + + ADDR + Limit address bits 31:5. Readable from any Privileged context, if and only if this region's S bit is clear, and MPU_CTRL_NS_HIDE_ADDR is clear. Otherwise readable only from a Secure, Privileged context. + [31:5] + read-write + + + S + Determines the Secure/Non-secure (=1/0) status of addresses matching this region, if this region is enabled. + [2:2] + read-write + + + P + Determines the Privileged/Unprivileged (=1/0) status of addresses matching this region, if this region is enabled. Writable from any Privileged context, if and only if the S bit is clear. Otherwise, writable only from a Secure, Privileged context. + [1:1] + read-write + + + EN + Region enable. If 1, any address within range specified by the base address (BAR_ADDR) and limit address (LAR_ADDR) has the attributes specified by S and P. + [0:0] + read-write + + + + + MPU_BAR6 + 0x00000534 + Base address register for MPU region 6. Writable only from a Secure, Privileged context. + 0x00000000 + + + ADDR + This MPU region matches addresses where addr[31:5] (the 27 most significant bits) are greater than or equal to BAR_ADDR, and less than or equal to LAR_ADDR. + + Readable from any Privileged context, if and only if this region's S bit is clear, and MPU_CTRL_NS_HIDE_ADDR is clear. Otherwise readable only from a Secure, Privileged context. + [31:5] + read-write + + + + + MPU_LAR6 + 0x00000538 + Limit address register for MPU region 6. Writable only from a Secure, Privileged context, with the exception of the P bit. + 0x00000000 + + + ADDR + Limit address bits 31:5. Readable from any Privileged context, if and only if this region's S bit is clear, and MPU_CTRL_NS_HIDE_ADDR is clear. Otherwise readable only from a Secure, Privileged context. + [31:5] + read-write + + + S + Determines the Secure/Non-secure (=1/0) status of addresses matching this region, if this region is enabled. + [2:2] + read-write + + + P + Determines the Privileged/Unprivileged (=1/0) status of addresses matching this region, if this region is enabled. Writable from any Privileged context, if and only if the S bit is clear. Otherwise, writable only from a Secure, Privileged context. + [1:1] + read-write + + + EN + Region enable. If 1, any address within range specified by the base address (BAR_ADDR) and limit address (LAR_ADDR) has the attributes specified by S and P. + [0:0] + read-write + + + + + MPU_BAR7 + 0x0000053c + Base address register for MPU region 7. Writable only from a Secure, Privileged context. + 0x00000000 + + + ADDR + This MPU region matches addresses where addr[31:5] (the 27 most significant bits) are greater than or equal to BAR_ADDR, and less than or equal to LAR_ADDR. + + Readable from any Privileged context, if and only if this region's S bit is clear, and MPU_CTRL_NS_HIDE_ADDR is clear. Otherwise readable only from a Secure, Privileged context. + [31:5] + read-write + + + + + MPU_LAR7 + 0x00000540 + Limit address register for MPU region 7. Writable only from a Secure, Privileged context, with the exception of the P bit. + 0x00000000 + + + ADDR + Limit address bits 31:5. Readable from any Privileged context, if and only if this region's S bit is clear, and MPU_CTRL_NS_HIDE_ADDR is clear. Otherwise readable only from a Secure, Privileged context. + [31:5] + read-write + + + S + Determines the Secure/Non-secure (=1/0) status of addresses matching this region, if this region is enabled. + [2:2] + read-write + + + P + Determines the Privileged/Unprivileged (=1/0) status of addresses matching this region, if this region is enabled. Writable from any Privileged context, if and only if the S bit is clear. Otherwise, writable only from a Secure, Privileged context. + [1:1] + read-write + + + EN + Region enable. If 1, any address within range specified by the base address (BAR_ADDR) and limit address (LAR_ADDR) has the attributes specified by S and P. + [0:0] + read-write + + + + + CH0_DBG_CTDREQ + 0x00000800 + Read: get channel DREQ counter (i.e. how many accesses the DMA expects it can perform on the peripheral without overflow/underflow. Write any value: clears the counter, and cause channel to re-initiate DREQ handshake. + 0x00000000 + + + CH0_DBG_CTDREQ + [5:0] + read-write + oneToClear + + + + + CH0_DBG_TCR + 0x00000804 + Read to get channel TRANS_COUNT reload value, i.e. the length of the next transfer + 0x00000000 + + + CH0_DBG_TCR + [31:0] + read-only + + + + + CH1_DBG_CTDREQ + 0x00000840 + Read: get channel DREQ counter (i.e. how many accesses the DMA expects it can perform on the peripheral without overflow/underflow. Write any value: clears the counter, and cause channel to re-initiate DREQ handshake. + 0x00000000 + + + CH1_DBG_CTDREQ + [5:0] + read-write + oneToClear + + + + + CH1_DBG_TCR + 0x00000844 + Read to get channel TRANS_COUNT reload value, i.e. the length of the next transfer + 0x00000000 + + + CH1_DBG_TCR + [31:0] + read-only + + + + + CH2_DBG_CTDREQ + 0x00000880 + Read: get channel DREQ counter (i.e. how many accesses the DMA expects it can perform on the peripheral without overflow/underflow. Write any value: clears the counter, and cause channel to re-initiate DREQ handshake. + 0x00000000 + + + CH2_DBG_CTDREQ + [5:0] + read-write + oneToClear + + + + + CH2_DBG_TCR + 0x00000884 + Read to get channel TRANS_COUNT reload value, i.e. the length of the next transfer + 0x00000000 + + + CH2_DBG_TCR + [31:0] + read-only + + + + + CH3_DBG_CTDREQ + 0x000008c0 + Read: get channel DREQ counter (i.e. how many accesses the DMA expects it can perform on the peripheral without overflow/underflow. Write any value: clears the counter, and cause channel to re-initiate DREQ handshake. + 0x00000000 + + + CH3_DBG_CTDREQ + [5:0] + read-write + oneToClear + + + + + CH3_DBG_TCR + 0x000008c4 + Read to get channel TRANS_COUNT reload value, i.e. the length of the next transfer + 0x00000000 + + + CH3_DBG_TCR + [31:0] + read-only + + + + + CH4_DBG_CTDREQ + 0x00000900 + Read: get channel DREQ counter (i.e. how many accesses the DMA expects it can perform on the peripheral without overflow/underflow. Write any value: clears the counter, and cause channel to re-initiate DREQ handshake. + 0x00000000 + + + CH4_DBG_CTDREQ + [5:0] + read-write + oneToClear + + + + + CH4_DBG_TCR + 0x00000904 + Read to get channel TRANS_COUNT reload value, i.e. the length of the next transfer + 0x00000000 + + + CH4_DBG_TCR + [31:0] + read-only + + + + + CH5_DBG_CTDREQ + 0x00000940 + Read: get channel DREQ counter (i.e. how many accesses the DMA expects it can perform on the peripheral without overflow/underflow. Write any value: clears the counter, and cause channel to re-initiate DREQ handshake. + 0x00000000 + + + CH5_DBG_CTDREQ + [5:0] + read-write + oneToClear + + + + + CH5_DBG_TCR + 0x00000944 + Read to get channel TRANS_COUNT reload value, i.e. the length of the next transfer + 0x00000000 + + + CH5_DBG_TCR + [31:0] + read-only + + + + + CH6_DBG_CTDREQ + 0x00000980 + Read: get channel DREQ counter (i.e. how many accesses the DMA expects it can perform on the peripheral without overflow/underflow. Write any value: clears the counter, and cause channel to re-initiate DREQ handshake. + 0x00000000 + + + CH6_DBG_CTDREQ + [5:0] + read-write + oneToClear + + + + + CH6_DBG_TCR + 0x00000984 + Read to get channel TRANS_COUNT reload value, i.e. the length of the next transfer + 0x00000000 + + + CH6_DBG_TCR + [31:0] + read-only + + + + + CH7_DBG_CTDREQ + 0x000009c0 + Read: get channel DREQ counter (i.e. how many accesses the DMA expects it can perform on the peripheral without overflow/underflow. Write any value: clears the counter, and cause channel to re-initiate DREQ handshake. + 0x00000000 + + + CH7_DBG_CTDREQ + [5:0] + read-write + oneToClear + + + + + CH7_DBG_TCR + 0x000009c4 + Read to get channel TRANS_COUNT reload value, i.e. the length of the next transfer + 0x00000000 + + + CH7_DBG_TCR + [31:0] + read-only + + + + + CH8_DBG_CTDREQ + 0x00000a00 + Read: get channel DREQ counter (i.e. how many accesses the DMA expects it can perform on the peripheral without overflow/underflow. Write any value: clears the counter, and cause channel to re-initiate DREQ handshake. + 0x00000000 + + + CH8_DBG_CTDREQ + [5:0] + read-write + oneToClear + + + + + CH8_DBG_TCR + 0x00000a04 + Read to get channel TRANS_COUNT reload value, i.e. the length of the next transfer + 0x00000000 + + + CH8_DBG_TCR + [31:0] + read-only + + + + + CH9_DBG_CTDREQ + 0x00000a40 + Read: get channel DREQ counter (i.e. how many accesses the DMA expects it can perform on the peripheral without overflow/underflow. Write any value: clears the counter, and cause channel to re-initiate DREQ handshake. + 0x00000000 + + + CH9_DBG_CTDREQ + [5:0] + read-write + oneToClear + + + + + CH9_DBG_TCR + 0x00000a44 + Read to get channel TRANS_COUNT reload value, i.e. the length of the next transfer + 0x00000000 + + + CH9_DBG_TCR + [31:0] + read-only + + + + + CH10_DBG_CTDREQ + 0x00000a80 + Read: get channel DREQ counter (i.e. how many accesses the DMA expects it can perform on the peripheral without overflow/underflow. Write any value: clears the counter, and cause channel to re-initiate DREQ handshake. + 0x00000000 + + + CH10_DBG_CTDREQ + [5:0] + read-write + oneToClear + + + + + CH10_DBG_TCR + 0x00000a84 + Read to get channel TRANS_COUNT reload value, i.e. the length of the next transfer + 0x00000000 + + + CH10_DBG_TCR + [31:0] + read-only + + + + + CH11_DBG_CTDREQ + 0x00000ac0 + Read: get channel DREQ counter (i.e. how many accesses the DMA expects it can perform on the peripheral without overflow/underflow. Write any value: clears the counter, and cause channel to re-initiate DREQ handshake. + 0x00000000 + + + CH11_DBG_CTDREQ + [5:0] + read-write + oneToClear + + + + + CH11_DBG_TCR + 0x00000ac4 + Read to get channel TRANS_COUNT reload value, i.e. the length of the next transfer + 0x00000000 + + + CH11_DBG_TCR + [31:0] + read-only + + + + + CH12_DBG_CTDREQ + 0x00000b00 + Read: get channel DREQ counter (i.e. how many accesses the DMA expects it can perform on the peripheral without overflow/underflow. Write any value: clears the counter, and cause channel to re-initiate DREQ handshake. + 0x00000000 + + + CH12_DBG_CTDREQ + [5:0] + read-write + oneToClear + + + + + CH12_DBG_TCR + 0x00000b04 + Read to get channel TRANS_COUNT reload value, i.e. the length of the next transfer + 0x00000000 + + + CH12_DBG_TCR + [31:0] + read-only + + + + + CH13_DBG_CTDREQ + 0x00000b40 + Read: get channel DREQ counter (i.e. how many accesses the DMA expects it can perform on the peripheral without overflow/underflow. Write any value: clears the counter, and cause channel to re-initiate DREQ handshake. + 0x00000000 + + + CH13_DBG_CTDREQ + [5:0] + read-write + oneToClear + + + + + CH13_DBG_TCR + 0x00000b44 + Read to get channel TRANS_COUNT reload value, i.e. the length of the next transfer + 0x00000000 + + + CH13_DBG_TCR + [31:0] + read-only + + + + + CH14_DBG_CTDREQ + 0x00000b80 + Read: get channel DREQ counter (i.e. how many accesses the DMA expects it can perform on the peripheral without overflow/underflow. Write any value: clears the counter, and cause channel to re-initiate DREQ handshake. + 0x00000000 + + + CH14_DBG_CTDREQ + [5:0] + read-write + oneToClear + + + + + CH14_DBG_TCR + 0x00000b84 + Read to get channel TRANS_COUNT reload value, i.e. the length of the next transfer + 0x00000000 + + + CH14_DBG_TCR + [31:0] + read-only + + + + + CH15_DBG_CTDREQ + 0x00000bc0 + Read: get channel DREQ counter (i.e. how many accesses the DMA expects it can perform on the peripheral without overflow/underflow. Write any value: clears the counter, and cause channel to re-initiate DREQ handshake. + 0x00000000 + + + CH15_DBG_CTDREQ + [5:0] + read-write + oneToClear + + + + + CH15_DBG_TCR + 0x00000bc4 + Read to get channel TRANS_COUNT reload value, i.e. the length of the next transfer + 0x00000000 + + + CH15_DBG_TCR + [31:0] + read-only + + + + + + + TIMER0 + Controls time and alarms + + time is a 64 bit value indicating the time since power-on + + timeh is the top 32 bits of time & timel is the bottom 32 bits to change time write to timelw before timehw to read time read from timelr before timehr + + An alarm is set by setting alarm_enable and writing to the corresponding alarm register When an alarm is pending, the corresponding alarm_running signal will be high An alarm can be cancelled before it has finished by clearing the alarm_enable When an alarm fires, the corresponding alarm_irq is set and alarm_running is cleared To clear the interrupt write a 1 to the corresponding alarm_irq The timer can be locked to prevent writing + 0x400b0000 + + 0 + 76 + registers + + + TIMER0_IRQ_0 + 0 + + + TIMER0_IRQ_1 + 1 + + + TIMER0_IRQ_2 + 2 + + + TIMER0_IRQ_3 + 3 + + + + TIMEHW + 0x00000000 + Write to bits 63:32 of time always write timelw before timehw + 0x00000000 + + + TIMEHW + [31:0] + write-only + + + + + TIMELW + 0x00000004 + Write to bits 31:0 of time writes do not get copied to time until timehw is written + 0x00000000 + + + TIMELW + [31:0] + write-only + + + + + TIMEHR + 0x00000008 + Read from bits 63:32 of time always read timelr before timehr + 0x00000000 + + + TIMEHR + [31:0] + read-only + + + + + TIMELR + 0x0000000c + Read from bits 31:0 of time + 0x00000000 + + + TIMELR + [31:0] + read-only + modify + + + + + ALARM0 + 0x00000010 + Arm alarm 0, and configure the time it will fire. Once armed, the alarm fires when TIMER_ALARM0 == TIMELR. The alarm will disarm itself once it fires, and can be disarmed early using the ARMED status register. + 0x00000000 + + + ALARM0 + [31:0] + read-write + + + + + ALARM1 + 0x00000014 + Arm alarm 1, and configure the time it will fire. Once armed, the alarm fires when TIMER_ALARM1 == TIMELR. The alarm will disarm itself once it fires, and can be disarmed early using the ARMED status register. + 0x00000000 + + + ALARM1 + [31:0] + read-write + + + + + ALARM2 + 0x00000018 + Arm alarm 2, and configure the time it will fire. Once armed, the alarm fires when TIMER_ALARM2 == TIMELR. The alarm will disarm itself once it fires, and can be disarmed early using the ARMED status register. + 0x00000000 + + + ALARM2 + [31:0] + read-write + + + + + ALARM3 + 0x0000001c + Arm alarm 3, and configure the time it will fire. Once armed, the alarm fires when TIMER_ALARM3 == TIMELR. The alarm will disarm itself once it fires, and can be disarmed early using the ARMED status register. + 0x00000000 + + + ALARM3 + [31:0] + read-write + + + + + ARMED + 0x00000020 + Indicates the armed/disarmed status of each alarm. A write to the corresponding ALARMx register arms the alarm. Alarms automatically disarm upon firing, but writing ones here will disarm immediately without waiting to fire. + 0x00000000 + + + ARMED + [3:0] + read-write + oneToClear + + + + + TIMERAWH + 0x00000024 + Raw read from bits 63:32 of time (no side effects) + 0x00000000 + + + TIMERAWH + [31:0] + read-only + + + + + TIMERAWL + 0x00000028 + Raw read from bits 31:0 of time (no side effects) + 0x00000000 + + + TIMERAWL + [31:0] + read-only + + + + + DBGPAUSE + 0x0000002c + Set bits high to enable pause when the corresponding debug ports are active + 0x00000007 + + + DBG1 + Pause when processor 1 is in debug mode + [2:2] + read-write + + + DBG0 + Pause when processor 0 is in debug mode + [1:1] + read-write + + + + + PAUSE + 0x00000030 + Set high to pause the timer + 0x00000000 + + + PAUSE + [0:0] + read-write + + + + + LOCKED + 0x00000034 + Set locked bit to disable write access to timer Once set, cannot be cleared (without a reset) + 0x00000000 + + + LOCKED + [0:0] + read-write + + + + + SOURCE + 0x00000038 + Selects the source for the timer. Defaults to the normal tick configured in the ticks block (typically configured to 1 microsecond). Writing to 1 will ignore the tick and count clk_sys cycles instead. + 0x00000000 + + + CLK_SYS + [0:0] + read-write + + + TICK + 0 + + + CLK_SYS + 1 + + + + + + + INTR + 0x0000003c + Raw Interrupts + 0x00000000 + + + ALARM_3 + [3:3] + read-write + oneToClear + + + ALARM_2 + [2:2] + read-write + oneToClear + + + ALARM_1 + [1:1] + read-write + oneToClear + + + ALARM_0 + [0:0] + read-write + oneToClear + + + + + INTE + 0x00000040 + Interrupt Enable + 0x00000000 + + + ALARM_3 + [3:3] + read-write + + + ALARM_2 + [2:2] + read-write + + + ALARM_1 + [1:1] + read-write + + + ALARM_0 + [0:0] + read-write + + + + + INTF + 0x00000044 + Interrupt Force + 0x00000000 + + + ALARM_3 + [3:3] + read-write + + + ALARM_2 + [2:2] + read-write + + + ALARM_1 + [1:1] + read-write + + + ALARM_0 + [0:0] + read-write + + + + + INTS + 0x00000048 + Interrupt status after masking & forcing + 0x00000000 + + + ALARM_3 + [3:3] + read-only + + + ALARM_2 + [2:2] + read-only + + + ALARM_1 + [1:1] + read-only + + + ALARM_0 + [0:0] + read-only + + + + + + + TIMER1 + 0x400b8000 + + TIMER1_IRQ_0 + 4 + + + TIMER1_IRQ_1 + 5 + + + TIMER1_IRQ_2 + 6 + + + TIMER1_IRQ_3 + 7 + + + + PWM + Simple PWM + 0x400a8000 + + 0 + 272 + registers + + + PWM_IRQ_WRAP_0 + 8 + + + PWM_IRQ_WRAP_1 + 9 + + + + CH0_CSR + 0x00000000 + Control and status register + 0x00000000 + + + PH_ADV + Advance the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running + at less than full speed (div_int + div_frac / 16 > 1) + [7:7] + write-only + + + PH_RET + Retard the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running. + [6:6] + write-only + + + DIVMODE + [5:4] + read-write + + + div + 0 + Free-running counting at rate dictated by fractional divider + + + level + 1 + Fractional divider operation is gated by the PWM B pin. + + + rise + 2 + Counter advances with each rising edge of the PWM B pin. + + + fall + 3 + Counter advances with each falling edge of the PWM B pin. + + + + + B_INV + Invert output B + [3:3] + read-write + + + A_INV + Invert output A + [2:2] + read-write + + + PH_CORRECT + 1: Enable phase-correct modulation. 0: Trailing-edge + [1:1] + read-write + + + EN + Enable the PWM channel. + [0:0] + read-write + + + + + CH0_DIV + 0x00000004 + INT and FRAC form a fixed-point fractional number. + Counting rate is system clock frequency divided by this number. + Fractional division uses simple 1st-order sigma-delta. + 0x00000010 + + + INT + [11:4] + read-write + + + FRAC + [3:0] + read-write + + + + + CH0_CTR + 0x00000008 + Direct access to the PWM counter + 0x00000000 + + + CH0_CTR + [15:0] + read-write + + + + + CH0_CC + 0x0000000c + Counter compare values + 0x00000000 + + + B + [31:16] + read-write + + + A + [15:0] + read-write + + + + + CH0_TOP + 0x00000010 + Counter wrap value + 0x0000ffff + + + CH0_TOP + [15:0] + read-write + + + + + CH1_CSR + 0x00000014 + Control and status register + 0x00000000 + + + PH_ADV + Advance the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running + at less than full speed (div_int + div_frac / 16 > 1) + [7:7] + write-only + + + PH_RET + Retard the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running. + [6:6] + write-only + + + DIVMODE + [5:4] + read-write + + + div + 0 + Free-running counting at rate dictated by fractional divider + + + level + 1 + Fractional divider operation is gated by the PWM B pin. + + + rise + 2 + Counter advances with each rising edge of the PWM B pin. + + + fall + 3 + Counter advances with each falling edge of the PWM B pin. + + + + + B_INV + Invert output B + [3:3] + read-write + + + A_INV + Invert output A + [2:2] + read-write + + + PH_CORRECT + 1: Enable phase-correct modulation. 0: Trailing-edge + [1:1] + read-write + + + EN + Enable the PWM channel. + [0:0] + read-write + + + + + CH1_DIV + 0x00000018 + INT and FRAC form a fixed-point fractional number. + Counting rate is system clock frequency divided by this number. + Fractional division uses simple 1st-order sigma-delta. + 0x00000010 + + + INT + [11:4] + read-write + + + FRAC + [3:0] + read-write + + + + + CH1_CTR + 0x0000001c + Direct access to the PWM counter + 0x00000000 + + + CH1_CTR + [15:0] + read-write + + + + + CH1_CC + 0x00000020 + Counter compare values + 0x00000000 + + + B + [31:16] + read-write + + + A + [15:0] + read-write + + + + + CH1_TOP + 0x00000024 + Counter wrap value + 0x0000ffff + + + CH1_TOP + [15:0] + read-write + + + + + CH2_CSR + 0x00000028 + Control and status register + 0x00000000 + + + PH_ADV + Advance the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running + at less than full speed (div_int + div_frac / 16 > 1) + [7:7] + write-only + + + PH_RET + Retard the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running. + [6:6] + write-only + + + DIVMODE + [5:4] + read-write + + + div + 0 + Free-running counting at rate dictated by fractional divider + + + level + 1 + Fractional divider operation is gated by the PWM B pin. + + + rise + 2 + Counter advances with each rising edge of the PWM B pin. + + + fall + 3 + Counter advances with each falling edge of the PWM B pin. + + + + + B_INV + Invert output B + [3:3] + read-write + + + A_INV + Invert output A + [2:2] + read-write + + + PH_CORRECT + 1: Enable phase-correct modulation. 0: Trailing-edge + [1:1] + read-write + + + EN + Enable the PWM channel. + [0:0] + read-write + + + + + CH2_DIV + 0x0000002c + INT and FRAC form a fixed-point fractional number. + Counting rate is system clock frequency divided by this number. + Fractional division uses simple 1st-order sigma-delta. + 0x00000010 + + + INT + [11:4] + read-write + + + FRAC + [3:0] + read-write + + + + + CH2_CTR + 0x00000030 + Direct access to the PWM counter + 0x00000000 + + + CH2_CTR + [15:0] + read-write + + + + + CH2_CC + 0x00000034 + Counter compare values + 0x00000000 + + + B + [31:16] + read-write + + + A + [15:0] + read-write + + + + + CH2_TOP + 0x00000038 + Counter wrap value + 0x0000ffff + + + CH2_TOP + [15:0] + read-write + + + + + CH3_CSR + 0x0000003c + Control and status register + 0x00000000 + + + PH_ADV + Advance the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running + at less than full speed (div_int + div_frac / 16 > 1) + [7:7] + write-only + + + PH_RET + Retard the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running. + [6:6] + write-only + + + DIVMODE + [5:4] + read-write + + + div + 0 + Free-running counting at rate dictated by fractional divider + + + level + 1 + Fractional divider operation is gated by the PWM B pin. + + + rise + 2 + Counter advances with each rising edge of the PWM B pin. + + + fall + 3 + Counter advances with each falling edge of the PWM B pin. + + + + + B_INV + Invert output B + [3:3] + read-write + + + A_INV + Invert output A + [2:2] + read-write + + + PH_CORRECT + 1: Enable phase-correct modulation. 0: Trailing-edge + [1:1] + read-write + + + EN + Enable the PWM channel. + [0:0] + read-write + + + + + CH3_DIV + 0x00000040 + INT and FRAC form a fixed-point fractional number. + Counting rate is system clock frequency divided by this number. + Fractional division uses simple 1st-order sigma-delta. + 0x00000010 + + + INT + [11:4] + read-write + + + FRAC + [3:0] + read-write + + + + + CH3_CTR + 0x00000044 + Direct access to the PWM counter + 0x00000000 + + + CH3_CTR + [15:0] + read-write + + + + + CH3_CC + 0x00000048 + Counter compare values + 0x00000000 + + + B + [31:16] + read-write + + + A + [15:0] + read-write + + + + + CH3_TOP + 0x0000004c + Counter wrap value + 0x0000ffff + + + CH3_TOP + [15:0] + read-write + + + + + CH4_CSR + 0x00000050 + Control and status register + 0x00000000 + + + PH_ADV + Advance the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running + at less than full speed (div_int + div_frac / 16 > 1) + [7:7] + write-only + + + PH_RET + Retard the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running. + [6:6] + write-only + + + DIVMODE + [5:4] + read-write + + + div + 0 + Free-running counting at rate dictated by fractional divider + + + level + 1 + Fractional divider operation is gated by the PWM B pin. + + + rise + 2 + Counter advances with each rising edge of the PWM B pin. + + + fall + 3 + Counter advances with each falling edge of the PWM B pin. + + + + + B_INV + Invert output B + [3:3] + read-write + + + A_INV + Invert output A + [2:2] + read-write + + + PH_CORRECT + 1: Enable phase-correct modulation. 0: Trailing-edge + [1:1] + read-write + + + EN + Enable the PWM channel. + [0:0] + read-write + + + + + CH4_DIV + 0x00000054 + INT and FRAC form a fixed-point fractional number. + Counting rate is system clock frequency divided by this number. + Fractional division uses simple 1st-order sigma-delta. + 0x00000010 + + + INT + [11:4] + read-write + + + FRAC + [3:0] + read-write + + + + + CH4_CTR + 0x00000058 + Direct access to the PWM counter + 0x00000000 + + + CH4_CTR + [15:0] + read-write + + + + + CH4_CC + 0x0000005c + Counter compare values + 0x00000000 + + + B + [31:16] + read-write + + + A + [15:0] + read-write + + + + + CH4_TOP + 0x00000060 + Counter wrap value + 0x0000ffff + + + CH4_TOP + [15:0] + read-write + + + + + CH5_CSR + 0x00000064 + Control and status register + 0x00000000 + + + PH_ADV + Advance the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running + at less than full speed (div_int + div_frac / 16 > 1) + [7:7] + write-only + + + PH_RET + Retard the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running. + [6:6] + write-only + + + DIVMODE + [5:4] + read-write + + + div + 0 + Free-running counting at rate dictated by fractional divider + + + level + 1 + Fractional divider operation is gated by the PWM B pin. + + + rise + 2 + Counter advances with each rising edge of the PWM B pin. + + + fall + 3 + Counter advances with each falling edge of the PWM B pin. + + + + + B_INV + Invert output B + [3:3] + read-write + + + A_INV + Invert output A + [2:2] + read-write + + + PH_CORRECT + 1: Enable phase-correct modulation. 0: Trailing-edge + [1:1] + read-write + + + EN + Enable the PWM channel. + [0:0] + read-write + + + + + CH5_DIV + 0x00000068 + INT and FRAC form a fixed-point fractional number. + Counting rate is system clock frequency divided by this number. + Fractional division uses simple 1st-order sigma-delta. + 0x00000010 + + + INT + [11:4] + read-write + + + FRAC + [3:0] + read-write + + + + + CH5_CTR + 0x0000006c + Direct access to the PWM counter + 0x00000000 + + + CH5_CTR + [15:0] + read-write + + + + + CH5_CC + 0x00000070 + Counter compare values + 0x00000000 + + + B + [31:16] + read-write + + + A + [15:0] + read-write + + + + + CH5_TOP + 0x00000074 + Counter wrap value + 0x0000ffff + + + CH5_TOP + [15:0] + read-write + + + + + CH6_CSR + 0x00000078 + Control and status register + 0x00000000 + + + PH_ADV + Advance the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running + at less than full speed (div_int + div_frac / 16 > 1) + [7:7] + write-only + + + PH_RET + Retard the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running. + [6:6] + write-only + + + DIVMODE + [5:4] + read-write + + + div + 0 + Free-running counting at rate dictated by fractional divider + + + level + 1 + Fractional divider operation is gated by the PWM B pin. + + + rise + 2 + Counter advances with each rising edge of the PWM B pin. + + + fall + 3 + Counter advances with each falling edge of the PWM B pin. + + + + + B_INV + Invert output B + [3:3] + read-write + + + A_INV + Invert output A + [2:2] + read-write + + + PH_CORRECT + 1: Enable phase-correct modulation. 0: Trailing-edge + [1:1] + read-write + + + EN + Enable the PWM channel. + [0:0] + read-write + + + + + CH6_DIV + 0x0000007c + INT and FRAC form a fixed-point fractional number. + Counting rate is system clock frequency divided by this number. + Fractional division uses simple 1st-order sigma-delta. + 0x00000010 + + + INT + [11:4] + read-write + + + FRAC + [3:0] + read-write + + + + + CH6_CTR + 0x00000080 + Direct access to the PWM counter + 0x00000000 + + + CH6_CTR + [15:0] + read-write + + + + + CH6_CC + 0x00000084 + Counter compare values + 0x00000000 + + + B + [31:16] + read-write + + + A + [15:0] + read-write + + + + + CH6_TOP + 0x00000088 + Counter wrap value + 0x0000ffff + + + CH6_TOP + [15:0] + read-write + + + + + CH7_CSR + 0x0000008c + Control and status register + 0x00000000 + + + PH_ADV + Advance the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running + at less than full speed (div_int + div_frac / 16 > 1) + [7:7] + write-only + + + PH_RET + Retard the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running. + [6:6] + write-only + + + DIVMODE + [5:4] + read-write + + + div + 0 + Free-running counting at rate dictated by fractional divider + + + level + 1 + Fractional divider operation is gated by the PWM B pin. + + + rise + 2 + Counter advances with each rising edge of the PWM B pin. + + + fall + 3 + Counter advances with each falling edge of the PWM B pin. + + + + + B_INV + Invert output B + [3:3] + read-write + + + A_INV + Invert output A + [2:2] + read-write + + + PH_CORRECT + 1: Enable phase-correct modulation. 0: Trailing-edge + [1:1] + read-write + + + EN + Enable the PWM channel. + [0:0] + read-write + + + + + CH7_DIV + 0x00000090 + INT and FRAC form a fixed-point fractional number. + Counting rate is system clock frequency divided by this number. + Fractional division uses simple 1st-order sigma-delta. + 0x00000010 + + + INT + [11:4] + read-write + + + FRAC + [3:0] + read-write + + + + + CH7_CTR + 0x00000094 + Direct access to the PWM counter + 0x00000000 + + + CH7_CTR + [15:0] + read-write + + + + + CH7_CC + 0x00000098 + Counter compare values + 0x00000000 + + + B + [31:16] + read-write + + + A + [15:0] + read-write + + + + + CH7_TOP + 0x0000009c + Counter wrap value + 0x0000ffff + + + CH7_TOP + [15:0] + read-write + + + + + CH8_CSR + 0x000000a0 + Control and status register + 0x00000000 + + + PH_ADV + Advance the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running + at less than full speed (div_int + div_frac / 16 > 1) + [7:7] + write-only + + + PH_RET + Retard the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running. + [6:6] + write-only + + + DIVMODE + [5:4] + read-write + + + div + 0 + Free-running counting at rate dictated by fractional divider + + + level + 1 + Fractional divider operation is gated by the PWM B pin. + + + rise + 2 + Counter advances with each rising edge of the PWM B pin. + + + fall + 3 + Counter advances with each falling edge of the PWM B pin. + + + + + B_INV + Invert output B + [3:3] + read-write + + + A_INV + Invert output A + [2:2] + read-write + + + PH_CORRECT + 1: Enable phase-correct modulation. 0: Trailing-edge + [1:1] + read-write + + + EN + Enable the PWM channel. + [0:0] + read-write + + + + + CH8_DIV + 0x000000a4 + INT and FRAC form a fixed-point fractional number. + Counting rate is system clock frequency divided by this number. + Fractional division uses simple 1st-order sigma-delta. + 0x00000010 + + + INT + [11:4] + read-write + + + FRAC + [3:0] + read-write + + + + + CH8_CTR + 0x000000a8 + Direct access to the PWM counter + 0x00000000 + + + CH8_CTR + [15:0] + read-write + + + + + CH8_CC + 0x000000ac + Counter compare values + 0x00000000 + + + B + [31:16] + read-write + + + A + [15:0] + read-write + + + + + CH8_TOP + 0x000000b0 + Counter wrap value + 0x0000ffff + + + CH8_TOP + [15:0] + read-write + + + + + CH9_CSR + 0x000000b4 + Control and status register + 0x00000000 + + + PH_ADV + Advance the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running + at less than full speed (div_int + div_frac / 16 > 1) + [7:7] + write-only + + + PH_RET + Retard the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running. + [6:6] + write-only + + + DIVMODE + [5:4] + read-write + + + div + 0 + Free-running counting at rate dictated by fractional divider + + + level + 1 + Fractional divider operation is gated by the PWM B pin. + + + rise + 2 + Counter advances with each rising edge of the PWM B pin. + + + fall + 3 + Counter advances with each falling edge of the PWM B pin. + + + + + B_INV + Invert output B + [3:3] + read-write + + + A_INV + Invert output A + [2:2] + read-write + + + PH_CORRECT + 1: Enable phase-correct modulation. 0: Trailing-edge + [1:1] + read-write + + + EN + Enable the PWM channel. + [0:0] + read-write + + + + + CH9_DIV + 0x000000b8 + INT and FRAC form a fixed-point fractional number. + Counting rate is system clock frequency divided by this number. + Fractional division uses simple 1st-order sigma-delta. + 0x00000010 + + + INT + [11:4] + read-write + + + FRAC + [3:0] + read-write + + + + + CH9_CTR + 0x000000bc + Direct access to the PWM counter + 0x00000000 + + + CH9_CTR + [15:0] + read-write + + + + + CH9_CC + 0x000000c0 + Counter compare values + 0x00000000 + + + B + [31:16] + read-write + + + A + [15:0] + read-write + + + + + CH9_TOP + 0x000000c4 + Counter wrap value + 0x0000ffff + + + CH9_TOP + [15:0] + read-write + + + + + CH10_CSR + 0x000000c8 + Control and status register + 0x00000000 + + + PH_ADV + Advance the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running + at less than full speed (div_int + div_frac / 16 > 1) + [7:7] + write-only + + + PH_RET + Retard the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running. + [6:6] + write-only + + + DIVMODE + [5:4] + read-write + + + div + 0 + Free-running counting at rate dictated by fractional divider + + + level + 1 + Fractional divider operation is gated by the PWM B pin. + + + rise + 2 + Counter advances with each rising edge of the PWM B pin. + + + fall + 3 + Counter advances with each falling edge of the PWM B pin. + + + + + B_INV + Invert output B + [3:3] + read-write + + + A_INV + Invert output A + [2:2] + read-write + + + PH_CORRECT + 1: Enable phase-correct modulation. 0: Trailing-edge + [1:1] + read-write + + + EN + Enable the PWM channel. + [0:0] + read-write + + + + + CH10_DIV + 0x000000cc + INT and FRAC form a fixed-point fractional number. + Counting rate is system clock frequency divided by this number. + Fractional division uses simple 1st-order sigma-delta. + 0x00000010 + + + INT + [11:4] + read-write + + + FRAC + [3:0] + read-write + + + + + CH10_CTR + 0x000000d0 + Direct access to the PWM counter + 0x00000000 + + + CH10_CTR + [15:0] + read-write + + + + + CH10_CC + 0x000000d4 + Counter compare values + 0x00000000 + + + B + [31:16] + read-write + + + A + [15:0] + read-write + + + + + CH10_TOP + 0x000000d8 + Counter wrap value + 0x0000ffff + + + CH10_TOP + [15:0] + read-write + + + + + CH11_CSR + 0x000000dc + Control and status register + 0x00000000 + + + PH_ADV + Advance the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running + at less than full speed (div_int + div_frac / 16 > 1) + [7:7] + write-only + + + PH_RET + Retard the phase of the counter by 1 count, while it is running. + Self-clearing. Write a 1, and poll until low. Counter must be running. + [6:6] + write-only + + + DIVMODE + [5:4] + read-write + + + div + 0 + Free-running counting at rate dictated by fractional divider + + + level + 1 + Fractional divider operation is gated by the PWM B pin. + + + rise + 2 + Counter advances with each rising edge of the PWM B pin. + + + fall + 3 + Counter advances with each falling edge of the PWM B pin. + + + + + B_INV + Invert output B + [3:3] + read-write + + + A_INV + Invert output A + [2:2] + read-write + + + PH_CORRECT + 1: Enable phase-correct modulation. 0: Trailing-edge + [1:1] + read-write + + + EN + Enable the PWM channel. + [0:0] + read-write + + + + + CH11_DIV + 0x000000e0 + INT and FRAC form a fixed-point fractional number. + Counting rate is system clock frequency divided by this number. + Fractional division uses simple 1st-order sigma-delta. + 0x00000010 + + + INT + [11:4] + read-write + + + FRAC + [3:0] + read-write + + + + + CH11_CTR + 0x000000e4 + Direct access to the PWM counter + 0x00000000 + + + CH11_CTR + [15:0] + read-write + + + + + CH11_CC + 0x000000e8 + Counter compare values + 0x00000000 + + + B + [31:16] + read-write + + + A + [15:0] + read-write + + + + + CH11_TOP + 0x000000ec + Counter wrap value + 0x0000ffff + + + CH11_TOP + [15:0] + read-write + + + + + EN + 0x000000f0 + This register aliases the CSR_EN bits for all channels. + Writing to this register allows multiple channels to be enabled + or disabled simultaneously, so they can run in perfect sync. + For each channel, there is only one physical EN register bit, + which can be accessed through here or CHx_CSR. + 0x00000000 + + + CH11 + [11:11] + read-write + + + CH10 + [10:10] + read-write + + + CH9 + [9:9] + read-write + + + CH8 + [8:8] + read-write + + + CH7 + [7:7] + read-write + + + CH6 + [6:6] + read-write + + + CH5 + [5:5] + read-write + + + CH4 + [4:4] + read-write + + + CH3 + [3:3] + read-write + + + CH2 + [2:2] + read-write + + + CH1 + [1:1] + read-write + + + CH0 + [0:0] + read-write + + + + + INTR + 0x000000f4 + Raw Interrupts + 0x00000000 + + + CH11 + [11:11] + read-write + oneToClear + + + CH10 + [10:10] + read-write + oneToClear + + + CH9 + [9:9] + read-write + oneToClear + + + CH8 + [8:8] + read-write + oneToClear + + + CH7 + [7:7] + read-write + oneToClear + + + CH6 + [6:6] + read-write + oneToClear + + + CH5 + [5:5] + read-write + oneToClear + + + CH4 + [4:4] + read-write + oneToClear + + + CH3 + [3:3] + read-write + oneToClear + + + CH2 + [2:2] + read-write + oneToClear + + + CH1 + [1:1] + read-write + oneToClear + + + CH0 + [0:0] + read-write + oneToClear + + + + + IRQ0_INTE + 0x000000f8 + Interrupt Enable for irq0 + 0x00000000 + + + CH11 + [11:11] + read-write + + + CH10 + [10:10] + read-write + + + CH9 + [9:9] + read-write + + + CH8 + [8:8] + read-write + + + CH7 + [7:7] + read-write + + + CH6 + [6:6] + read-write + + + CH5 + [5:5] + read-write + + + CH4 + [4:4] + read-write + + + CH3 + [3:3] + read-write + + + CH2 + [2:2] + read-write + + + CH1 + [1:1] + read-write + + + CH0 + [0:0] + read-write + + + + + IRQ0_INTF + 0x000000fc + Interrupt Force for irq0 + 0x00000000 + + + CH11 + [11:11] + read-write + + + CH10 + [10:10] + read-write + + + CH9 + [9:9] + read-write + + + CH8 + [8:8] + read-write + + + CH7 + [7:7] + read-write + + + CH6 + [6:6] + read-write + + + CH5 + [5:5] + read-write + + + CH4 + [4:4] + read-write + + + CH3 + [3:3] + read-write + + + CH2 + [2:2] + read-write + + + CH1 + [1:1] + read-write + + + CH0 + [0:0] + read-write + + + + + IRQ0_INTS + 0x00000100 + Interrupt status after masking & forcing for irq0 + 0x00000000 + + + CH11 + [11:11] + read-only + + + CH10 + [10:10] + read-only + + + CH9 + [9:9] + read-only + + + CH8 + [8:8] + read-only + + + CH7 + [7:7] + read-only + + + CH6 + [6:6] + read-only + + + CH5 + [5:5] + read-only + + + CH4 + [4:4] + read-only + + + CH3 + [3:3] + read-only + + + CH2 + [2:2] + read-only + + + CH1 + [1:1] + read-only + + + CH0 + [0:0] + read-only + + + + + IRQ1_INTE + 0x00000104 + Interrupt Enable for irq1 + 0x00000000 + + + CH11 + [11:11] + read-write + + + CH10 + [10:10] + read-write + + + CH9 + [9:9] + read-write + + + CH8 + [8:8] + read-write + + + CH7 + [7:7] + read-write + + + CH6 + [6:6] + read-write + + + CH5 + [5:5] + read-write + + + CH4 + [4:4] + read-write + + + CH3 + [3:3] + read-write + + + CH2 + [2:2] + read-write + + + CH1 + [1:1] + read-write + + + CH0 + [0:0] + read-write + + + + + IRQ1_INTF + 0x00000108 + Interrupt Force for irq1 + 0x00000000 + + + CH11 + [11:11] + read-write + + + CH10 + [10:10] + read-write + + + CH9 + [9:9] + read-write + + + CH8 + [8:8] + read-write + + + CH7 + [7:7] + read-write + + + CH6 + [6:6] + read-write + + + CH5 + [5:5] + read-write + + + CH4 + [4:4] + read-write + + + CH3 + [3:3] + read-write + + + CH2 + [2:2] + read-write + + + CH1 + [1:1] + read-write + + + CH0 + [0:0] + read-write + + + + + IRQ1_INTS + 0x0000010c + Interrupt status after masking & forcing for irq1 + 0x00000000 + + + CH11 + [11:11] + read-only + + + CH10 + [10:10] + read-only + + + CH9 + [9:9] + read-only + + + CH8 + [8:8] + read-only + + + CH7 + [7:7] + read-only + + + CH6 + [6:6] + read-only + + + CH5 + [5:5] + read-only + + + CH4 + [4:4] + read-only + + + CH3 + [3:3] + read-only + + + CH2 + [2:2] + read-only + + + CH1 + [1:1] + read-only + + + CH0 + [0:0] + read-only + + + + + + + ADC + Control and data interface to SAR ADC + 0x400a0000 + + 0 + 36 + registers + + + ADC_IRQ_FIFO + 35 + + + + CS + 0x00000000 + ADC Control and Status + 0x00000000 + + + RROBIN + Round-robin sampling. 1 bit per channel. Set all bits to 0 to disable. + Otherwise, the ADC will cycle through each enabled channel in a round-robin fashion. + The first channel to be sampled will be the one currently indicated by AINSEL. + AINSEL will be updated after each conversion with the newly-selected channel. + [24:16] + read-write + + + AINSEL + Select analog mux input. Updated automatically in round-robin mode. + This is corrected for the package option so only ADC channels which are bonded are available, and in the correct order + [15:12] + read-write + + + ERR_STICKY + Some past ADC conversion encountered an error. Write 1 to clear. + [10:10] + read-write + oneToClear + + + ERR + The most recent ADC conversion encountered an error; result is undefined or noisy. + [9:9] + read-only + + + READY + 1 if the ADC is ready to start a new conversion. Implies any previous conversion has completed. + 0 whilst conversion in progress. + [8:8] + read-only + + + START_MANY + Continuously perform conversions whilst this bit is 1. A new conversion will start immediately after the previous finishes. + [3:3] + read-write + + + START_ONCE + Start a single conversion. Self-clearing. Ignored if start_many is asserted. + [2:2] + write-only + + + TS_EN + Power on temperature sensor. 1 - enabled. 0 - disabled. + [1:1] + read-write + + + EN + Power on ADC and enable its clock. + 1 - enabled. 0 - disabled. + [0:0] + read-write + + + + + RESULT + 0x00000004 + Result of most recent ADC conversion + 0x00000000 + + + RESULT + [11:0] + read-only + + + + + FCS + 0x00000008 + FIFO control and status + 0x00000000 + + + THRESH + DREQ/IRQ asserted when level >= threshold + [27:24] + read-write + + + LEVEL + The number of conversion results currently waiting in the FIFO + [19:16] + read-only + + + OVER + 1 if the FIFO has been overflowed. Write 1 to clear. + [11:11] + read-write + oneToClear + + + UNDER + 1 if the FIFO has been underflowed. Write 1 to clear. + [10:10] + read-write + oneToClear + + + FULL + [9:9] + read-only + + + EMPTY + [8:8] + read-only + + + DREQ_EN + If 1: assert DMA requests when FIFO contains data + [3:3] + read-write + + + ERR + If 1: conversion error bit appears in the FIFO alongside the result + [2:2] + read-write + + + SHIFT + If 1: FIFO results are right-shifted to be one byte in size. Enables DMA to byte buffers. + [1:1] + read-write + + + EN + If 1: write result to the FIFO after each conversion. + [0:0] + read-write + + + + + FIFO + 0x0000000c + Conversion result FIFO + 0x00000000 + + + ERR + 1 if this particular sample experienced a conversion error. Remains in the same location if the sample is shifted. + [15:15] + read-only + modify + + + VAL + [11:0] + read-only + modify + + + + + DIV + 0x00000010 + Clock divider. If non-zero, CS_START_MANY will start conversions + at regular intervals rather than back-to-back. + The divider is reset when either of these fields are written. + Total period is 1 + INT + FRAC / 256 + 0x00000000 + + + INT + Integer part of clock divisor. + [23:8] + read-write + + + FRAC + Fractional part of clock divisor. First-order delta-sigma. + [7:0] + read-write + + + + + INTR + 0x00000014 + Raw Interrupts + 0x00000000 + + + FIFO + Triggered when the sample FIFO reaches a certain level. + This level can be programmed via the FCS_THRESH field. + [0:0] + read-only + + + + + INTE + 0x00000018 + Interrupt Enable + 0x00000000 + + + FIFO + Triggered when the sample FIFO reaches a certain level. + This level can be programmed via the FCS_THRESH field. + [0:0] + read-write + + + + + INTF + 0x0000001c + Interrupt Force + 0x00000000 + + + FIFO + Triggered when the sample FIFO reaches a certain level. + This level can be programmed via the FCS_THRESH field. + [0:0] + read-write + + + + + INTS + 0x00000020 + Interrupt status after masking & forcing + 0x00000000 + + + FIFO + Triggered when the sample FIFO reaches a certain level. + This level can be programmed via the FCS_THRESH field. + [0:0] + read-only + + + + + + + I2C0 + DW_apb_i2c address block + + List of configuration constants for the Synopsys I2C hardware (you may see references to these in I2C register header; these are *fixed* values, set at hardware design time): + + IC_ULTRA_FAST_MODE ................ 0x0 + IC_UFM_TBUF_CNT_DEFAULT ........... 0x8 + IC_UFM_SCL_LOW_COUNT .............. 0x0008 + IC_UFM_SCL_HIGH_COUNT ............. 0x0006 + IC_TX_TL .......................... 0x0 + IC_TX_CMD_BLOCK ................... 0x1 + IC_HAS_DMA ........................ 0x1 + IC_HAS_ASYNC_FIFO ................. 0x0 + IC_SMBUS_ARP ...................... 0x0 + IC_FIRST_DATA_BYTE_STATUS ......... 0x1 + IC_INTR_IO ........................ 0x1 + IC_MASTER_MODE .................... 0x1 + IC_DEFAULT_ACK_GENERAL_CALL ....... 0x1 + IC_INTR_POL ....................... 0x1 + IC_OPTIONAL_SAR ................... 0x0 + IC_DEFAULT_TAR_SLAVE_ADDR ......... 0x055 + IC_DEFAULT_SLAVE_ADDR ............. 0x055 + IC_DEFAULT_HS_SPKLEN .............. 0x1 + IC_FS_SCL_HIGH_COUNT .............. 0x0006 + IC_HS_SCL_LOW_COUNT ............... 0x0008 + IC_DEVICE_ID_VALUE ................ 0x0 + IC_10BITADDR_MASTER ............... 0x0 + IC_CLK_FREQ_OPTIMIZATION .......... 0x0 + IC_DEFAULT_FS_SPKLEN .............. 0x7 + IC_ADD_ENCODED_PARAMS ............. 0x0 + IC_DEFAULT_SDA_HOLD ............... 0x000001 + IC_DEFAULT_SDA_SETUP .............. 0x64 + IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT . 0x0 + IC_CLOCK_PERIOD ................... 100 + IC_EMPTYFIFO_HOLD_MASTER_EN ....... 1 + IC_RESTART_EN ..................... 0x1 + IC_TX_CMD_BLOCK_DEFAULT ........... 0x0 + IC_BUS_CLEAR_FEATURE .............. 0x0 + IC_CAP_LOADING .................... 100 + IC_FS_SCL_LOW_COUNT ............... 0x000d + APB_DATA_WIDTH .................... 32 + IC_SDA_STUCK_TIMEOUT_DEFAULT ...... 0xffffffff + IC_SLV_DATA_NACK_ONLY ............. 0x1 + IC_10BITADDR_SLAVE ................ 0x0 + IC_CLK_TYPE ....................... 0x0 + IC_SMBUS_UDID_MSB ................. 0x0 + IC_SMBUS_SUSPEND_ALERT ............ 0x0 + IC_HS_SCL_HIGH_COUNT .............. 0x0006 + IC_SLV_RESTART_DET_EN ............. 0x1 + IC_SMBUS .......................... 0x0 + IC_OPTIONAL_SAR_DEFAULT ........... 0x0 + IC_PERSISTANT_SLV_ADDR_DEFAULT .... 0x0 + IC_USE_COUNTS ..................... 0x0 + IC_RX_BUFFER_DEPTH ................ 16 + IC_SCL_STUCK_TIMEOUT_DEFAULT ...... 0xffffffff + IC_RX_FULL_HLD_BUS_EN ............. 0x1 + IC_SLAVE_DISABLE .................. 0x1 + IC_RX_TL .......................... 0x0 + IC_DEVICE_ID ...................... 0x0 + IC_HC_COUNT_VALUES ................ 0x0 + I2C_DYNAMIC_TAR_UPDATE ............ 0 + IC_SMBUS_CLK_LOW_MEXT_DEFAULT ..... 0xffffffff + IC_SMBUS_CLK_LOW_SEXT_DEFAULT ..... 0xffffffff + IC_HS_MASTER_CODE ................. 0x1 + IC_SMBUS_RST_IDLE_CNT_DEFAULT ..... 0xffff + IC_SMBUS_UDID_LSB_DEFAULT ......... 0xffffffff + IC_SS_SCL_HIGH_COUNT .............. 0x0028 + IC_SS_SCL_LOW_COUNT ............... 0x002f + IC_MAX_SPEED_MODE ................. 0x2 + IC_STAT_FOR_CLK_STRETCH ........... 0x0 + IC_STOP_DET_IF_MASTER_ACTIVE ...... 0x0 + IC_DEFAULT_UFM_SPKLEN ............. 0x1 + IC_TX_BUFFER_DEPTH ................ 16 + 0x40090000 + + 0 + 256 + registers + + + I2C0_IRQ + 36 + + + + IC_CON + 0x00000000 + I2C Control Register. This register can be written only when the DW_apb_i2c is disabled, which corresponds to the IC_ENABLE[0] register being set to 0. Writes at other times have no effect. + + Read/Write Access: - bit 10 is read only. - bit 11 is read only - bit 16 is read only - bit 17 is read only - bits 18 and 19 are read only. + 0x00000065 + + + STOP_DET_IF_MASTER_ACTIVE + Master issues the STOP_DET interrupt irrespective of whether master is active or not + [10:10] + read-only + + + RX_FIFO_FULL_HLD_CTRL + This bit controls whether DW_apb_i2c should hold the bus when the Rx FIFO is physically full to its RX_BUFFER_DEPTH, as described in the IC_RX_FULL_HLD_BUS_EN parameter. + + Reset value: 0x0. + [9:9] + read-write + + + DISABLED + 0 + Overflow when RX_FIFO is full + + + ENABLED + 1 + Hold bus when RX_FIFO is full + + + + + TX_EMPTY_CTRL + This bit controls the generation of the TX_EMPTY interrupt, as described in the IC_RAW_INTR_STAT register. + + Reset value: 0x0. + [8:8] + read-write + + + DISABLED + 0 + Default behaviour of TX_EMPTY interrupt + + + ENABLED + 1 + Controlled generation of TX_EMPTY interrupt + + + + + STOP_DET_IFADDRESSED + In slave mode: - 1'b1: issues the STOP_DET interrupt only when it is addressed. - 1'b0: issues the STOP_DET irrespective of whether it's addressed or not. Reset value: 0x0 + + NOTE: During a general call address, this slave does not issue the STOP_DET interrupt if STOP_DET_IF_ADDRESSED = 1'b1, even if the slave responds to the general call address by generating ACK. The STOP_DET interrupt is generated only when the transmitted address matches the slave address (SAR). + [7:7] + read-write + + + DISABLED + 0 + slave issues STOP_DET intr always + + + ENABLED + 1 + slave issues STOP_DET intr only if addressed + + + + + IC_SLAVE_DISABLE + This bit controls whether I2C has its slave disabled, which means once the presetn signal is applied, then this bit is set and the slave is disabled. + + If this bit is set (slave is disabled), DW_apb_i2c functions only as a master and does not perform any action that requires a slave. + + NOTE: Software should ensure that if this bit is written with 0, then bit 0 should also be written with a 0. + [6:6] + read-write + + + SLAVE_ENABLED + 0 + Slave mode is enabled + + + SLAVE_DISABLED + 1 + Slave mode is disabled + + + + + IC_RESTART_EN + Determines whether RESTART conditions may be sent when acting as a master. Some older slaves do not support handling RESTART conditions; however, RESTART conditions are used in several DW_apb_i2c operations. When RESTART is disabled, the master is prohibited from performing the following functions: - Sending a START BYTE - Performing any high-speed mode operation - High-speed mode operation - Performing direction changes in combined format mode - Performing a read operation with a 10-bit address By replacing RESTART condition followed by a STOP and a subsequent START condition, split operations are broken down into multiple DW_apb_i2c transfers. If the above operations are performed, it will result in setting bit 6 (TX_ABRT) of the IC_RAW_INTR_STAT register. + + Reset value: ENABLED + [5:5] + read-write + + + DISABLED + 0 + Master restart disabled + + + ENABLED + 1 + Master restart enabled + + + + + IC_10BITADDR_MASTER + Controls whether the DW_apb_i2c starts its transfers in 7- or 10-bit addressing mode when acting as a master. - 0: 7-bit addressing - 1: 10-bit addressing + [4:4] + read-write + + + ADDR_7BITS + 0 + Master 7Bit addressing mode + + + ADDR_10BITS + 1 + Master 10Bit addressing mode + + + + + IC_10BITADDR_SLAVE + When acting as a slave, this bit controls whether the DW_apb_i2c responds to 7- or 10-bit addresses. - 0: 7-bit addressing. The DW_apb_i2c ignores transactions that involve 10-bit addressing; for 7-bit addressing, only the lower 7 bits of the IC_SAR register are compared. - 1: 10-bit addressing. The DW_apb_i2c responds to only 10-bit addressing transfers that match the full 10 bits of the IC_SAR register. + [3:3] + read-write + + + ADDR_7BITS + 0 + Slave 7Bit addressing + + + ADDR_10BITS + 1 + Slave 10Bit addressing + + + + + SPEED + These bits control at which speed the DW_apb_i2c operates; its setting is relevant only if one is operating the DW_apb_i2c in master mode. Hardware protects against illegal values being programmed by software. These bits must be programmed appropriately for slave mode also, as it is used to capture correct value of spike filter as per the speed mode. + + This register should be programmed only with a value in the range of 1 to IC_MAX_SPEED_MODE; otherwise, hardware updates this register with the value of IC_MAX_SPEED_MODE. + + 1: standard mode (100 kbit/s) + + 2: fast mode (<=400 kbit/s) or fast mode plus (<=1000Kbit/s) + + 3: high speed mode (3.4 Mbit/s) + + Note: This field is not applicable when IC_ULTRA_FAST_MODE=1 + [2:1] + read-write + + + STANDARD + 1 + Standard Speed mode of operation + + + FAST + 2 + Fast or Fast Plus mode of operation + + + HIGH + 3 + High Speed mode of operation + + + + + MASTER_MODE + This bit controls whether the DW_apb_i2c master is enabled. + + NOTE: Software should ensure that if this bit is written with '1' then bit 6 should also be written with a '1'. + [0:0] + read-write + + + DISABLED + 0 + Master mode is disabled + + + ENABLED + 1 + Master mode is enabled + + + + + + + IC_TAR + 0x00000004 + I2C Target Address Register + + This register is 12 bits wide, and bits 31:12 are reserved. This register can be written to only when IC_ENABLE[0] is set to 0. + + Note: If the software or application is aware that the DW_apb_i2c is not using the TAR address for the pending commands in the Tx FIFO, then it is possible to update the TAR address even while the Tx FIFO has entries (IC_STATUS[2]= 0). - It is not necessary to perform any write to this register if DW_apb_i2c is enabled as an I2C slave only. + 0x00000055 + + + SPECIAL + This bit indicates whether software performs a Device-ID or General Call or START BYTE command. - 0: ignore bit 10 GC_OR_START and use IC_TAR normally - 1: perform special I2C command as specified in Device_ID or GC_OR_START bit Reset value: 0x0 + [11:11] + read-write + + + DISABLED + 0 + Disables programming of GENERAL_CALL or START_BYTE transmission + + + ENABLED + 1 + Enables programming of GENERAL_CALL or START_BYTE transmission + + + + + GC_OR_START + If bit 11 (SPECIAL) is set to 1 and bit 13(Device-ID) is set to 0, then this bit indicates whether a General Call or START byte command is to be performed by the DW_apb_i2c. - 0: General Call Address - after issuing a General Call, only writes may be performed. Attempting to issue a read command results in setting bit 6 (TX_ABRT) of the IC_RAW_INTR_STAT register. The DW_apb_i2c remains in General Call mode until the SPECIAL bit value (bit 11) is cleared. - 1: START BYTE Reset value: 0x0 + [10:10] + read-write + + + GENERAL_CALL + 0 + GENERAL_CALL byte transmission + + + START_BYTE + 1 + START byte transmission + + + + + IC_TAR + This is the target address for any master transaction. When transmitting a General Call, these bits are ignored. To generate a START BYTE, the CPU needs to write only once into these bits. + + If the IC_TAR and IC_SAR are the same, loopback exists but the FIFOs are shared between master and slave, so full loopback is not feasible. Only one direction loopback mode is supported (simplex), not duplex. A master cannot transmit to itself; it can transmit to only a slave. + [9:0] + read-write + + + + + IC_SAR + 0x00000008 + I2C Slave Address Register + 0x00000055 + + + IC_SAR + The IC_SAR holds the slave address when the I2C is operating as a slave. For 7-bit addressing, only IC_SAR[6:0] is used. + + This register can be written only when the I2C interface is disabled, which corresponds to the IC_ENABLE[0] register being set to 0. Writes at other times have no effect. + + Note: The default values cannot be any of the reserved address locations: that is, 0x00 to 0x07, or 0x78 to 0x7f. The correct operation of the device is not guaranteed if you program the IC_SAR or IC_TAR to a reserved value. Refer to <<table_I2C_firstbyte_bit_defs>> for a complete list of these reserved values. + [9:0] + read-write + + + + + IC_DATA_CMD + 0x00000010 + I2C Rx/Tx Data Buffer and Command Register; this is the register the CPU writes to when filling the TX FIFO and the CPU reads from when retrieving bytes from RX FIFO. + + The size of the register changes as follows: + + Write: - 11 bits when IC_EMPTYFIFO_HOLD_MASTER_EN=1 - 9 bits when IC_EMPTYFIFO_HOLD_MASTER_EN=0 Read: - 12 bits when IC_FIRST_DATA_BYTE_STATUS = 1 - 8 bits when IC_FIRST_DATA_BYTE_STATUS = 0 Note: In order for the DW_apb_i2c to continue acknowledging reads, a read command should be written for every byte that is to be received; otherwise the DW_apb_i2c will stop acknowledging. + 0x00000000 + + + FIRST_DATA_BYTE + Indicates the first data byte received after the address phase for receive transfer in Master receiver or Slave receiver mode. + + Reset value : 0x0 + + NOTE: In case of APB_DATA_WIDTH=8, + + 1. The user has to perform two APB Reads to IC_DATA_CMD in order to get status on 11 bit. + + 2. In order to read the 11 bit, the user has to perform the first data byte read [7:0] (offset 0x10) and then perform the second read [15:8] (offset 0x11) in order to know the status of 11 bit (whether the data received in previous read is a first data byte or not). + + 3. The 11th bit is an optional read field, user can ignore 2nd byte read [15:8] (offset 0x11) if not interested in FIRST_DATA_BYTE status. + [11:11] + read-only + + + INACTIVE + 0 + Sequential data byte received + + + ACTIVE + 1 + Non sequential data byte received + + + + + RESTART + This bit controls whether a RESTART is issued before the byte is sent or received. + + 1 - If IC_RESTART_EN is 1, a RESTART is issued before the data is sent/received (according to the value of CMD), regardless of whether or not the transfer direction is changing from the previous command; if IC_RESTART_EN is 0, a STOP followed by a START is issued instead. + + 0 - If IC_RESTART_EN is 1, a RESTART is issued only if the transfer direction is changing from the previous command; if IC_RESTART_EN is 0, a STOP followed by a START is issued instead. + + Reset value: 0x0 + [10:10] + write-only + + + DISABLE + 0 + Don't Issue RESTART before this command + + + ENABLE + 1 + Issue RESTART before this command + + + + + STOP + This bit controls whether a STOP is issued after the byte is sent or received. + + - 1 - STOP is issued after this byte, regardless of whether or not the Tx FIFO is empty. If the Tx FIFO is not empty, the master immediately tries to start a new transfer by issuing a START and arbitrating for the bus. - 0 - STOP is not issued after this byte, regardless of whether or not the Tx FIFO is empty. If the Tx FIFO is not empty, the master continues the current transfer by sending/receiving data bytes according to the value of the CMD bit. If the Tx FIFO is empty, the master holds the SCL line low and stalls the bus until a new command is available in the Tx FIFO. Reset value: 0x0 + [9:9] + write-only + + + DISABLE + 0 + Don't Issue STOP after this command + + + ENABLE + 1 + Issue STOP after this command + + + + + CMD + This bit controls whether a read or a write is performed. This bit does not control the direction when the DW_apb_i2con acts as a slave. It controls only the direction when it acts as a master. + + When a command is entered in the TX FIFO, this bit distinguishes the write and read commands. In slave-receiver mode, this bit is a 'don't care' because writes to this register are not required. In slave-transmitter mode, a '0' indicates that the data in IC_DATA_CMD is to be transmitted. + + When programming this bit, you should remember the following: attempting to perform a read operation after a General Call command has been sent results in a TX_ABRT interrupt (bit 6 of the IC_RAW_INTR_STAT register), unless bit 11 (SPECIAL) in the IC_TAR register has been cleared. If a '1' is written to this bit after receiving a RD_REQ interrupt, then a TX_ABRT interrupt occurs. + + Reset value: 0x0 + [8:8] + write-only + + + WRITE + 0 + Master Write Command + + + READ + 1 + Master Read Command + + + + + DAT + This register contains the data to be transmitted or received on the I2C bus. If you are writing to this register and want to perform a read, bits 7:0 (DAT) are ignored by the DW_apb_i2c. However, when you read this register, these bits return the value of data received on the DW_apb_i2c interface. + + Reset value: 0x0 + [7:0] + read-write + + + + + IC_SS_SCL_HCNT + 0x00000014 + Standard Speed I2C Clock SCL High Count Register + 0x00000028 + + + IC_SS_SCL_HCNT + This register must be set before any I2C bus transaction can take place to ensure proper I/O timing. This register sets the SCL clock high-period count for standard speed. For more information, refer to 'IC_CLK Frequency Configuration'. + + This register can be written only when the I2C interface is disabled which corresponds to the IC_ENABLE[0] register being set to 0. Writes at other times have no effect. + + The minimum valid value is 6; hardware prevents values less than this being written, and if attempted results in 6 being set. For designs with APB_DATA_WIDTH = 8, the order of programming is important to ensure the correct operation of the DW_apb_i2c. The lower byte must be programmed first. Then the upper byte is programmed. + + NOTE: This register must not be programmed to a value higher than 65525, because DW_apb_i2c uses a 16-bit counter to flag an I2C bus idle condition when this counter reaches a value of IC_SS_SCL_HCNT + 10. + [15:0] + read-write + + + + + IC_SS_SCL_LCNT + 0x00000018 + Standard Speed I2C Clock SCL Low Count Register + 0x0000002f + + + IC_SS_SCL_LCNT + This register must be set before any I2C bus transaction can take place to ensure proper I/O timing. This register sets the SCL clock low period count for standard speed. For more information, refer to 'IC_CLK Frequency Configuration' + + This register can be written only when the I2C interface is disabled which corresponds to the IC_ENABLE[0] register being set to 0. Writes at other times have no effect. + + The minimum valid value is 8; hardware prevents values less than this being written, and if attempted, results in 8 being set. For designs with APB_DATA_WIDTH = 8, the order of programming is important to ensure the correct operation of DW_apb_i2c. The lower byte must be programmed first, and then the upper byte is programmed. + [15:0] + read-write + + + + + IC_FS_SCL_HCNT + 0x0000001c + Fast Mode or Fast Mode Plus I2C Clock SCL High Count Register + 0x00000006 + + + IC_FS_SCL_HCNT + This register must be set before any I2C bus transaction can take place to ensure proper I/O timing. This register sets the SCL clock high-period count for fast mode or fast mode plus. It is used in high-speed mode to send the Master Code and START BYTE or General CALL. For more information, refer to 'IC_CLK Frequency Configuration'. + + This register goes away and becomes read-only returning 0s if IC_MAX_SPEED_MODE = standard. This register can be written only when the I2C interface is disabled, which corresponds to the IC_ENABLE[0] register being set to 0. Writes at other times have no effect. + + The minimum valid value is 6; hardware prevents values less than this being written, and if attempted results in 6 being set. For designs with APB_DATA_WIDTH == 8 the order of programming is important to ensure the correct operation of the DW_apb_i2c. The lower byte must be programmed first. Then the upper byte is programmed. + [15:0] + read-write + + + + + IC_FS_SCL_LCNT + 0x00000020 + Fast Mode or Fast Mode Plus I2C Clock SCL Low Count Register + 0x0000000d + + + IC_FS_SCL_LCNT + This register must be set before any I2C bus transaction can take place to ensure proper I/O timing. This register sets the SCL clock low period count for fast speed. It is used in high-speed mode to send the Master Code and START BYTE or General CALL. For more information, refer to 'IC_CLK Frequency Configuration'. + + This register goes away and becomes read-only returning 0s if IC_MAX_SPEED_MODE = standard. + + This register can be written only when the I2C interface is disabled, which corresponds to the IC_ENABLE[0] register being set to 0. Writes at other times have no effect. + + The minimum valid value is 8; hardware prevents values less than this being written, and if attempted results in 8 being set. For designs with APB_DATA_WIDTH = 8 the order of programming is important to ensure the correct operation of the DW_apb_i2c. The lower byte must be programmed first. Then the upper byte is programmed. If the value is less than 8 then the count value gets changed to 8. + [15:0] + read-write + + + + + IC_INTR_STAT + 0x0000002c + I2C Interrupt Status Register + + Each bit in this register has a corresponding mask bit in the IC_INTR_MASK register. These bits are cleared by reading the matching interrupt clear register. The unmasked raw versions of these bits are available in the IC_RAW_INTR_STAT register. + 0x00000000 + + + R_RESTART_DET + See IC_RAW_INTR_STAT for a detailed description of R_RESTART_DET bit. + + Reset value: 0x0 + [12:12] + read-only + + + INACTIVE + 0 + R_RESTART_DET interrupt is inactive + + + ACTIVE + 1 + R_RESTART_DET interrupt is active + + + + + R_GEN_CALL + See IC_RAW_INTR_STAT for a detailed description of R_GEN_CALL bit. + + Reset value: 0x0 + [11:11] + read-only + + + INACTIVE + 0 + R_GEN_CALL interrupt is inactive + + + ACTIVE + 1 + R_GEN_CALL interrupt is active + + + + + R_START_DET + See IC_RAW_INTR_STAT for a detailed description of R_START_DET bit. + + Reset value: 0x0 + [10:10] + read-only + + + INACTIVE + 0 + R_START_DET interrupt is inactive + + + ACTIVE + 1 + R_START_DET interrupt is active + + + + + R_STOP_DET + See IC_RAW_INTR_STAT for a detailed description of R_STOP_DET bit. + + Reset value: 0x0 + [9:9] + read-only + + + INACTIVE + 0 + R_STOP_DET interrupt is inactive + + + ACTIVE + 1 + R_STOP_DET interrupt is active + + + + + R_ACTIVITY + See IC_RAW_INTR_STAT for a detailed description of R_ACTIVITY bit. + + Reset value: 0x0 + [8:8] + read-only + + + INACTIVE + 0 + R_ACTIVITY interrupt is inactive + + + ACTIVE + 1 + R_ACTIVITY interrupt is active + + + + + R_RX_DONE + See IC_RAW_INTR_STAT for a detailed description of R_RX_DONE bit. + + Reset value: 0x0 + [7:7] + read-only + + + INACTIVE + 0 + R_RX_DONE interrupt is inactive + + + ACTIVE + 1 + R_RX_DONE interrupt is active + + + + + R_TX_ABRT + See IC_RAW_INTR_STAT for a detailed description of R_TX_ABRT bit. + + Reset value: 0x0 + [6:6] + read-only + + + INACTIVE + 0 + R_TX_ABRT interrupt is inactive + + + ACTIVE + 1 + R_TX_ABRT interrupt is active + + + + + R_RD_REQ + See IC_RAW_INTR_STAT for a detailed description of R_RD_REQ bit. + + Reset value: 0x0 + [5:5] + read-only + + + INACTIVE + 0 + R_RD_REQ interrupt is inactive + + + ACTIVE + 1 + R_RD_REQ interrupt is active + + + + + R_TX_EMPTY + See IC_RAW_INTR_STAT for a detailed description of R_TX_EMPTY bit. + + Reset value: 0x0 + [4:4] + read-only + + + INACTIVE + 0 + R_TX_EMPTY interrupt is inactive + + + ACTIVE + 1 + R_TX_EMPTY interrupt is active + + + + + R_TX_OVER + See IC_RAW_INTR_STAT for a detailed description of R_TX_OVER bit. + + Reset value: 0x0 + [3:3] + read-only + + + INACTIVE + 0 + R_TX_OVER interrupt is inactive + + + ACTIVE + 1 + R_TX_OVER interrupt is active + + + + + R_RX_FULL + See IC_RAW_INTR_STAT for a detailed description of R_RX_FULL bit. + + Reset value: 0x0 + [2:2] + read-only + + + INACTIVE + 0 + R_RX_FULL interrupt is inactive + + + ACTIVE + 1 + R_RX_FULL interrupt is active + + + + + R_RX_OVER + See IC_RAW_INTR_STAT for a detailed description of R_RX_OVER bit. + + Reset value: 0x0 + [1:1] + read-only + + + INACTIVE + 0 + R_RX_OVER interrupt is inactive + + + ACTIVE + 1 + R_RX_OVER interrupt is active + + + + + R_RX_UNDER + See IC_RAW_INTR_STAT for a detailed description of R_RX_UNDER bit. + + Reset value: 0x0 + [0:0] + read-only + + + INACTIVE + 0 + RX_UNDER interrupt is inactive + + + ACTIVE + 1 + RX_UNDER interrupt is active + + + + + + + IC_INTR_MASK + 0x00000030 + I2C Interrupt Mask Register. + + These bits mask their corresponding interrupt status bits. This register is active low; a value of 0 masks the interrupt, whereas a value of 1 unmasks the interrupt. + 0x000008ff + + + M_RESTART_DET + This bit masks the R_RESTART_DET interrupt in IC_INTR_STAT register. + + Reset value: 0x0 + [12:12] + read-write + + + ENABLED + 0 + RESTART_DET interrupt is masked + + + DISABLED + 1 + RESTART_DET interrupt is unmasked + + + + + M_GEN_CALL + This bit masks the R_GEN_CALL interrupt in IC_INTR_STAT register. + + Reset value: 0x1 + [11:11] + read-write + + + ENABLED + 0 + GEN_CALL interrupt is masked + + + DISABLED + 1 + GEN_CALL interrupt is unmasked + + + + + M_START_DET + This bit masks the R_START_DET interrupt in IC_INTR_STAT register. + + Reset value: 0x0 + [10:10] + read-write + + + ENABLED + 0 + START_DET interrupt is masked + + + DISABLED + 1 + START_DET interrupt is unmasked + + + + + M_STOP_DET + This bit masks the R_STOP_DET interrupt in IC_INTR_STAT register. + + Reset value: 0x0 + [9:9] + read-write + + + ENABLED + 0 + STOP_DET interrupt is masked + + + DISABLED + 1 + STOP_DET interrupt is unmasked + + + + + M_ACTIVITY + This bit masks the R_ACTIVITY interrupt in IC_INTR_STAT register. + + Reset value: 0x0 + [8:8] + read-write + + + ENABLED + 0 + ACTIVITY interrupt is masked + + + DISABLED + 1 + ACTIVITY interrupt is unmasked + + + + + M_RX_DONE + This bit masks the R_RX_DONE interrupt in IC_INTR_STAT register. + + Reset value: 0x1 + [7:7] + read-write + + + ENABLED + 0 + RX_DONE interrupt is masked + + + DISABLED + 1 + RX_DONE interrupt is unmasked + + + + + M_TX_ABRT + This bit masks the R_TX_ABRT interrupt in IC_INTR_STAT register. + + Reset value: 0x1 + [6:6] + read-write + + + ENABLED + 0 + TX_ABORT interrupt is masked + + + DISABLED + 1 + TX_ABORT interrupt is unmasked + + + + + M_RD_REQ + This bit masks the R_RD_REQ interrupt in IC_INTR_STAT register. + + Reset value: 0x1 + [5:5] + read-write + + + ENABLED + 0 + RD_REQ interrupt is masked + + + DISABLED + 1 + RD_REQ interrupt is unmasked + + + + + M_TX_EMPTY + This bit masks the R_TX_EMPTY interrupt in IC_INTR_STAT register. + + Reset value: 0x1 + [4:4] + read-write + + + ENABLED + 0 + TX_EMPTY interrupt is masked + + + DISABLED + 1 + TX_EMPTY interrupt is unmasked + + + + + M_TX_OVER + This bit masks the R_TX_OVER interrupt in IC_INTR_STAT register. + + Reset value: 0x1 + [3:3] + read-write + + + ENABLED + 0 + TX_OVER interrupt is masked + + + DISABLED + 1 + TX_OVER interrupt is unmasked + + + + + M_RX_FULL + This bit masks the R_RX_FULL interrupt in IC_INTR_STAT register. + + Reset value: 0x1 + [2:2] + read-write + + + ENABLED + 0 + RX_FULL interrupt is masked + + + DISABLED + 1 + RX_FULL interrupt is unmasked + + + + + M_RX_OVER + This bit masks the R_RX_OVER interrupt in IC_INTR_STAT register. + + Reset value: 0x1 + [1:1] + read-write + + + ENABLED + 0 + RX_OVER interrupt is masked + + + DISABLED + 1 + RX_OVER interrupt is unmasked + + + + + M_RX_UNDER + This bit masks the R_RX_UNDER interrupt in IC_INTR_STAT register. + + Reset value: 0x1 + [0:0] + read-write + + + ENABLED + 0 + RX_UNDER interrupt is masked + + + DISABLED + 1 + RX_UNDER interrupt is unmasked + + + + + + + IC_RAW_INTR_STAT + 0x00000034 + I2C Raw Interrupt Status Register + + Unlike the IC_INTR_STAT register, these bits are not masked so they always show the true status of the DW_apb_i2c. + 0x00000000 + + + RESTART_DET + Indicates whether a RESTART condition has occurred on the I2C interface when DW_apb_i2c is operating in Slave mode and the slave is being addressed. Enabled only when IC_SLV_RESTART_DET_EN=1. + + Note: However, in high-speed mode or during a START BYTE transfer, the RESTART comes before the address field as per the I2C protocol. In this case, the slave is not the addressed slave when the RESTART is issued, therefore DW_apb_i2c does not generate the RESTART_DET interrupt. + + Reset value: 0x0 + [12:12] + read-only + + + INACTIVE + 0 + RESTART_DET interrupt is inactive + + + ACTIVE + 1 + RESTART_DET interrupt is active + + + + + GEN_CALL + Set only when a General Call address is received and it is acknowledged. It stays set until it is cleared either by disabling DW_apb_i2c or when the CPU reads bit 0 of the IC_CLR_GEN_CALL register. DW_apb_i2c stores the received data in the Rx buffer. + + Reset value: 0x0 + [11:11] + read-only + + + INACTIVE + 0 + GEN_CALL interrupt is inactive + + + ACTIVE + 1 + GEN_CALL interrupt is active + + + + + START_DET + Indicates whether a START or RESTART condition has occurred on the I2C interface regardless of whether DW_apb_i2c is operating in slave or master mode. + + Reset value: 0x0 + [10:10] + read-only + + + INACTIVE + 0 + START_DET interrupt is inactive + + + ACTIVE + 1 + START_DET interrupt is active + + + + + STOP_DET + Indicates whether a STOP condition has occurred on the I2C interface regardless of whether DW_apb_i2c is operating in slave or master mode. + + In Slave Mode: - If IC_CON[7]=1'b1 (STOP_DET_IFADDRESSED), the STOP_DET interrupt will be issued only if slave is addressed. Note: During a general call address, this slave does not issue a STOP_DET interrupt if STOP_DET_IF_ADDRESSED=1'b1, even if the slave responds to the general call address by generating ACK. The STOP_DET interrupt is generated only when the transmitted address matches the slave address (SAR). - If IC_CON[7]=1'b0 (STOP_DET_IFADDRESSED), the STOP_DET interrupt is issued irrespective of whether it is being addressed. In Master Mode: - If IC_CON[10]=1'b1 (STOP_DET_IF_MASTER_ACTIVE),the STOP_DET interrupt will be issued only if Master is active. - If IC_CON[10]=1'b0 (STOP_DET_IFADDRESSED),the STOP_DET interrupt will be issued irrespective of whether master is active or not. Reset value: 0x0 + [9:9] + read-only + + + INACTIVE + 0 + STOP_DET interrupt is inactive + + + ACTIVE + 1 + STOP_DET interrupt is active + + + + + ACTIVITY + This bit captures DW_apb_i2c activity and stays set until it is cleared. There are four ways to clear it: - Disabling the DW_apb_i2c - Reading the IC_CLR_ACTIVITY register - Reading the IC_CLR_INTR register - System reset Once this bit is set, it stays set unless one of the four methods is used to clear it. Even if the DW_apb_i2c module is idle, this bit remains set until cleared, indicating that there was activity on the bus. + + Reset value: 0x0 + [8:8] + read-only + + + INACTIVE + 0 + RAW_INTR_ACTIVITY interrupt is inactive + + + ACTIVE + 1 + RAW_INTR_ACTIVITY interrupt is active + + + + + RX_DONE + When the DW_apb_i2c is acting as a slave-transmitter, this bit is set to 1 if the master does not acknowledge a transmitted byte. This occurs on the last byte of the transmission, indicating that the transmission is done. + + Reset value: 0x0 + [7:7] + read-only + + + INACTIVE + 0 + RX_DONE interrupt is inactive + + + ACTIVE + 1 + RX_DONE interrupt is active + + + + + TX_ABRT + This bit indicates if DW_apb_i2c, as an I2C transmitter, is unable to complete the intended actions on the contents of the transmit FIFO. This situation can occur both as an I2C master or an I2C slave, and is referred to as a 'transmit abort'. When this bit is set to 1, the IC_TX_ABRT_SOURCE register indicates the reason why the transmit abort takes places. + + Note: The DW_apb_i2c flushes/resets/empties the TX_FIFO and RX_FIFO whenever there is a transmit abort caused by any of the events tracked by the IC_TX_ABRT_SOURCE register. The FIFOs remains in this flushed state until the register IC_CLR_TX_ABRT is read. Once this read is performed, the Tx FIFO is then ready to accept more data bytes from the APB interface. + + Reset value: 0x0 + [6:6] + read-only + + + INACTIVE + 0 + TX_ABRT interrupt is inactive + + + ACTIVE + 1 + TX_ABRT interrupt is active + + + + + RD_REQ + This bit is set to 1 when DW_apb_i2c is acting as a slave and another I2C master is attempting to read data from DW_apb_i2c. The DW_apb_i2c holds the I2C bus in a wait state (SCL=0) until this interrupt is serviced, which means that the slave has been addressed by a remote master that is asking for data to be transferred. The processor must respond to this interrupt and then write the requested data to the IC_DATA_CMD register. This bit is set to 0 just after the processor reads the IC_CLR_RD_REQ register. + + Reset value: 0x0 + [5:5] + read-only + + + INACTIVE + 0 + RD_REQ interrupt is inactive + + + ACTIVE + 1 + RD_REQ interrupt is active + + + + + TX_EMPTY + The behavior of the TX_EMPTY interrupt status differs based on the TX_EMPTY_CTRL selection in the IC_CON register. - When TX_EMPTY_CTRL = 0: This bit is set to 1 when the transmit buffer is at or below the threshold value set in the IC_TX_TL register. - When TX_EMPTY_CTRL = 1: This bit is set to 1 when the transmit buffer is at or below the threshold value set in the IC_TX_TL register and the transmission of the address/data from the internal shift register for the most recently popped command is completed. It is automatically cleared by hardware when the buffer level goes above the threshold. When IC_ENABLE[0] is set to 0, the TX FIFO is flushed and held in reset. There the TX FIFO looks like it has no data within it, so this bit is set to 1, provided there is activity in the master or slave state machines. When there is no longer any activity, then with ic_en=0, this bit is set to 0. + + Reset value: 0x0. + [4:4] + read-only + + + INACTIVE + 0 + TX_EMPTY interrupt is inactive + + + ACTIVE + 1 + TX_EMPTY interrupt is active + + + + + TX_OVER + Set during transmit if the transmit buffer is filled to IC_TX_BUFFER_DEPTH and the processor attempts to issue another I2C command by writing to the IC_DATA_CMD register. When the module is disabled, this bit keeps its level until the master or slave state machines go into idle, and when ic_en goes to 0, this interrupt is cleared. + + Reset value: 0x0 + [3:3] + read-only + + + INACTIVE + 0 + TX_OVER interrupt is inactive + + + ACTIVE + 1 + TX_OVER interrupt is active + + + + + RX_FULL + Set when the receive buffer reaches or goes above the RX_TL threshold in the IC_RX_TL register. It is automatically cleared by hardware when buffer level goes below the threshold. If the module is disabled (IC_ENABLE[0]=0), the RX FIFO is flushed and held in reset; therefore the RX FIFO is not full. So this bit is cleared once the IC_ENABLE bit 0 is programmed with a 0, regardless of the activity that continues. + + Reset value: 0x0 + [2:2] + read-only + + + INACTIVE + 0 + RX_FULL interrupt is inactive + + + ACTIVE + 1 + RX_FULL interrupt is active + + + + + RX_OVER + Set if the receive buffer is completely filled to IC_RX_BUFFER_DEPTH and an additional byte is received from an external I2C device. The DW_apb_i2c acknowledges this, but any data bytes received after the FIFO is full are lost. If the module is disabled (IC_ENABLE[0]=0), this bit keeps its level until the master or slave state machines go into idle, and when ic_en goes to 0, this interrupt is cleared. + + Note: If bit 9 of the IC_CON register (RX_FIFO_FULL_HLD_CTRL) is programmed to HIGH, then the RX_OVER interrupt never occurs, because the Rx FIFO never overflows. + + Reset value: 0x0 + [1:1] + read-only + + + INACTIVE + 0 + RX_OVER interrupt is inactive + + + ACTIVE + 1 + RX_OVER interrupt is active + + + + + RX_UNDER + Set if the processor attempts to read the receive buffer when it is empty by reading from the IC_DATA_CMD register. If the module is disabled (IC_ENABLE[0]=0), this bit keeps its level until the master or slave state machines go into idle, and when ic_en goes to 0, this interrupt is cleared. + + Reset value: 0x0 + [0:0] + read-only + + + INACTIVE + 0 + RX_UNDER interrupt is inactive + + + ACTIVE + 1 + RX_UNDER interrupt is active + + + + + + + IC_RX_TL + 0x00000038 + I2C Receive FIFO Threshold Register + 0x00000000 + + + RX_TL + Receive FIFO Threshold Level. + + Controls the level of entries (or above) that triggers the RX_FULL interrupt (bit 2 in IC_RAW_INTR_STAT register). The valid range is 0-255, with the additional restriction that hardware does not allow this value to be set to a value larger than the depth of the buffer. If an attempt is made to do that, the actual value set will be the maximum depth of the buffer. A value of 0 sets the threshold for 1 entry, and a value of 255 sets the threshold for 256 entries. + [7:0] + read-write + + + + + IC_TX_TL + 0x0000003c + I2C Transmit FIFO Threshold Register + 0x00000000 + + + TX_TL + Transmit FIFO Threshold Level. + + Controls the level of entries (or below) that trigger the TX_EMPTY interrupt (bit 4 in IC_RAW_INTR_STAT register). The valid range is 0-255, with the additional restriction that it may not be set to value larger than the depth of the buffer. If an attempt is made to do that, the actual value set will be the maximum depth of the buffer. A value of 0 sets the threshold for 0 entries, and a value of 255 sets the threshold for 255 entries. + [7:0] + read-write + + + + + IC_CLR_INTR + 0x00000040 + Clear Combined and Individual Interrupt Register + 0x00000000 + + + CLR_INTR + Read this register to clear the combined interrupt, all individual interrupts, and the IC_TX_ABRT_SOURCE register. This bit does not clear hardware clearable interrupts but software clearable interrupts. Refer to Bit 9 of the IC_TX_ABRT_SOURCE register for an exception to clearing IC_TX_ABRT_SOURCE. + + Reset value: 0x0 + [0:0] + read-only + + + + + IC_CLR_RX_UNDER + 0x00000044 + Clear RX_UNDER Interrupt Register + 0x00000000 + + + CLR_RX_UNDER + Read this register to clear the RX_UNDER interrupt (bit 0) of the IC_RAW_INTR_STAT register. + + Reset value: 0x0 + [0:0] + read-only + + + + + IC_CLR_RX_OVER + 0x00000048 + Clear RX_OVER Interrupt Register + 0x00000000 + + + CLR_RX_OVER + Read this register to clear the RX_OVER interrupt (bit 1) of the IC_RAW_INTR_STAT register. + + Reset value: 0x0 + [0:0] + read-only + + + + + IC_CLR_TX_OVER + 0x0000004c + Clear TX_OVER Interrupt Register + 0x00000000 + + + CLR_TX_OVER + Read this register to clear the TX_OVER interrupt (bit 3) of the IC_RAW_INTR_STAT register. + + Reset value: 0x0 + [0:0] + read-only + + + + + IC_CLR_RD_REQ + 0x00000050 + Clear RD_REQ Interrupt Register + 0x00000000 + + + CLR_RD_REQ + Read this register to clear the RD_REQ interrupt (bit 5) of the IC_RAW_INTR_STAT register. + + Reset value: 0x0 + [0:0] + read-only + + + + + IC_CLR_TX_ABRT + 0x00000054 + Clear TX_ABRT Interrupt Register + 0x00000000 + + + CLR_TX_ABRT + Read this register to clear the TX_ABRT interrupt (bit 6) of the IC_RAW_INTR_STAT register, and the IC_TX_ABRT_SOURCE register. This also releases the TX FIFO from the flushed/reset state, allowing more writes to the TX FIFO. Refer to Bit 9 of the IC_TX_ABRT_SOURCE register for an exception to clearing IC_TX_ABRT_SOURCE. + + Reset value: 0x0 + [0:0] + read-only + + + + + IC_CLR_RX_DONE + 0x00000058 + Clear RX_DONE Interrupt Register + 0x00000000 + + + CLR_RX_DONE + Read this register to clear the RX_DONE interrupt (bit 7) of the IC_RAW_INTR_STAT register. + + Reset value: 0x0 + [0:0] + read-only + + + + + IC_CLR_ACTIVITY + 0x0000005c + Clear ACTIVITY Interrupt Register + 0x00000000 + + + CLR_ACTIVITY + Reading this register clears the ACTIVITY interrupt if the I2C is not active anymore. If the I2C module is still active on the bus, the ACTIVITY interrupt bit continues to be set. It is automatically cleared by hardware if the module is disabled and if there is no further activity on the bus. The value read from this register to get status of the ACTIVITY interrupt (bit 8) of the IC_RAW_INTR_STAT register. + + Reset value: 0x0 + [0:0] + read-only + + + + + IC_CLR_STOP_DET + 0x00000060 + Clear STOP_DET Interrupt Register + 0x00000000 + + + CLR_STOP_DET + Read this register to clear the STOP_DET interrupt (bit 9) of the IC_RAW_INTR_STAT register. + + Reset value: 0x0 + [0:0] + read-only + + + + + IC_CLR_START_DET + 0x00000064 + Clear START_DET Interrupt Register + 0x00000000 + + + CLR_START_DET + Read this register to clear the START_DET interrupt (bit 10) of the IC_RAW_INTR_STAT register. + + Reset value: 0x0 + [0:0] + read-only + + + + + IC_CLR_GEN_CALL + 0x00000068 + Clear GEN_CALL Interrupt Register + 0x00000000 + + + CLR_GEN_CALL + Read this register to clear the GEN_CALL interrupt (bit 11) of IC_RAW_INTR_STAT register. + + Reset value: 0x0 + [0:0] + read-only + + + + + IC_ENABLE + 0x0000006c + I2C Enable Register + 0x00000000 + + + TX_CMD_BLOCK + In Master mode: - 1'b1: Blocks the transmission of data on I2C bus even if Tx FIFO has data to transmit. - 1'b0: The transmission of data starts on I2C bus automatically, as soon as the first data is available in the Tx FIFO. Note: To block the execution of Master commands, set the TX_CMD_BLOCK bit only when Tx FIFO is empty (IC_STATUS[2]==1) and Master is in Idle state (IC_STATUS[5] == 0). Any further commands put in the Tx FIFO are not executed until TX_CMD_BLOCK bit is unset. Reset value: IC_TX_CMD_BLOCK_DEFAULT + [2:2] + read-write + + + NOT_BLOCKED + 0 + Tx Command execution not blocked + + + BLOCKED + 1 + Tx Command execution blocked + + + + + ABORT + When set, the controller initiates the transfer abort. - 0: ABORT not initiated or ABORT done - 1: ABORT operation in progress The software can abort the I2C transfer in master mode by setting this bit. The software can set this bit only when ENABLE is already set; otherwise, the controller ignores any write to ABORT bit. The software cannot clear the ABORT bit once set. In response to an ABORT, the controller issues a STOP and flushes the Tx FIFO after completing the current transfer, then sets the TX_ABORT interrupt after the abort operation. The ABORT bit is cleared automatically after the abort operation. + + For a detailed description on how to abort I2C transfers, refer to 'Aborting I2C Transfers'. + + Reset value: 0x0 + [1:1] + read-write + + + DISABLE + 0 + ABORT operation not in progress + + + ENABLED + 1 + ABORT operation in progress + + + + + ENABLE + Controls whether the DW_apb_i2c is enabled. - 0: Disables DW_apb_i2c (TX and RX FIFOs are held in an erased state) - 1: Enables DW_apb_i2c Software can disable DW_apb_i2c while it is active. However, it is important that care be taken to ensure that DW_apb_i2c is disabled properly. A recommended procedure is described in 'Disabling DW_apb_i2c'. + + When DW_apb_i2c is disabled, the following occurs: - The TX FIFO and RX FIFO get flushed. - Status bits in the IC_INTR_STAT register are still active until DW_apb_i2c goes into IDLE state. If the module is transmitting, it stops as well as deletes the contents of the transmit buffer after the current transfer is complete. If the module is receiving, the DW_apb_i2c stops the current transfer at the end of the current byte and does not acknowledge the transfer. + + In systems with asynchronous pclk and ic_clk when IC_CLK_TYPE parameter set to asynchronous (1), there is a two ic_clk delay when enabling or disabling the DW_apb_i2c. For a detailed description on how to disable DW_apb_i2c, refer to 'Disabling DW_apb_i2c' + + Reset value: 0x0 + [0:0] + read-write + + + DISABLED + 0 + I2C is disabled + + + ENABLED + 1 + I2C is enabled + + + + + + + IC_STATUS + 0x00000070 + I2C Status Register + + This is a read-only register used to indicate the current transfer status and FIFO status. The status register may be read at any time. None of the bits in this register request an interrupt. + + When the I2C is disabled by writing 0 in bit 0 of the IC_ENABLE register: - Bits 1 and 2 are set to 1 - Bits 3 and 10 are set to 0 When the master or slave state machines goes to idle and ic_en=0: - Bits 5 and 6 are set to 0 + 0x00000006 + + + SLV_ACTIVITY + Slave FSM Activity Status. When the Slave Finite State Machine (FSM) is not in the IDLE state, this bit is set. - 0: Slave FSM is in IDLE state so the Slave part of DW_apb_i2c is not Active - 1: Slave FSM is not in IDLE state so the Slave part of DW_apb_i2c is Active Reset value: 0x0 + [6:6] + read-only + + + IDLE + 0 + Slave is idle + + + ACTIVE + 1 + Slave not idle + + + + + MST_ACTIVITY + Master FSM Activity Status. When the Master Finite State Machine (FSM) is not in the IDLE state, this bit is set. - 0: Master FSM is in IDLE state so the Master part of DW_apb_i2c is not Active - 1: Master FSM is not in IDLE state so the Master part of DW_apb_i2c is Active Note: IC_STATUS[0]-that is, ACTIVITY bit-is the OR of SLV_ACTIVITY and MST_ACTIVITY bits. + + Reset value: 0x0 + [5:5] + read-only + + + IDLE + 0 + Master is idle + + + ACTIVE + 1 + Master not idle + + + + + RFF + Receive FIFO Completely Full. When the receive FIFO is completely full, this bit is set. When the receive FIFO contains one or more empty location, this bit is cleared. - 0: Receive FIFO is not full - 1: Receive FIFO is full Reset value: 0x0 + [4:4] + read-only + + + NOT_FULL + 0 + Rx FIFO not full + + + FULL + 1 + Rx FIFO is full + + + + + RFNE + Receive FIFO Not Empty. This bit is set when the receive FIFO contains one or more entries; it is cleared when the receive FIFO is empty. - 0: Receive FIFO is empty - 1: Receive FIFO is not empty Reset value: 0x0 + [3:3] + read-only + + + EMPTY + 0 + Rx FIFO is empty + + + NOT_EMPTY + 1 + Rx FIFO not empty + + + + + TFE + Transmit FIFO Completely Empty. When the transmit FIFO is completely empty, this bit is set. When it contains one or more valid entries, this bit is cleared. This bit field does not request an interrupt. - 0: Transmit FIFO is not empty - 1: Transmit FIFO is empty Reset value: 0x1 + [2:2] + read-only + + + NON_EMPTY + 0 + Tx FIFO not empty + + + EMPTY + 1 + Tx FIFO is empty + + + + + TFNF + Transmit FIFO Not Full. Set when the transmit FIFO contains one or more empty locations, and is cleared when the FIFO is full. - 0: Transmit FIFO is full - 1: Transmit FIFO is not full Reset value: 0x1 + [1:1] + read-only + + + FULL + 0 + Tx FIFO is full + + + NOT_FULL + 1 + Tx FIFO not full + + + + + ACTIVITY + I2C Activity Status. Reset value: 0x0 + [0:0] + read-only + + + INACTIVE + 0 + I2C is idle + + + ACTIVE + 1 + I2C is active + + + + + + + IC_TXFLR + 0x00000074 + I2C Transmit FIFO Level Register This register contains the number of valid data entries in the transmit FIFO buffer. It is cleared whenever: - The I2C is disabled - There is a transmit abort - that is, TX_ABRT bit is set in the IC_RAW_INTR_STAT register - The slave bulk transmit mode is aborted The register increments whenever data is placed into the transmit FIFO and decrements when data is taken from the transmit FIFO. + 0x00000000 + + + TXFLR + Transmit FIFO Level. Contains the number of valid data entries in the transmit FIFO. + + Reset value: 0x0 + [4:0] + read-only + + + + + IC_RXFLR + 0x00000078 + I2C Receive FIFO Level Register This register contains the number of valid data entries in the receive FIFO buffer. It is cleared whenever: - The I2C is disabled - Whenever there is a transmit abort caused by any of the events tracked in IC_TX_ABRT_SOURCE The register increments whenever data is placed into the receive FIFO and decrements when data is taken from the receive FIFO. + 0x00000000 + + + RXFLR + Receive FIFO Level. Contains the number of valid data entries in the receive FIFO. + + Reset value: 0x0 + [4:0] + read-only + + + + + IC_SDA_HOLD + 0x0000007c + I2C SDA Hold Time Length Register + + The bits [15:0] of this register are used to control the hold time of SDA during transmit in both slave and master mode (after SCL goes from HIGH to LOW). + + The bits [23:16] of this register are used to extend the SDA transition (if any) whenever SCL is HIGH in the receiver in either master or slave mode. + + Writes to this register succeed only when IC_ENABLE[0]=0. + + The values in this register are in units of ic_clk period. The value programmed in IC_SDA_TX_HOLD must be greater than the minimum hold time in each mode (one cycle in master mode, seven cycles in slave mode) for the value to be implemented. + + The programmed SDA hold time during transmit (IC_SDA_TX_HOLD) cannot exceed at any time the duration of the low part of scl. Therefore the programmed value cannot be larger than N_SCL_LOW-2, where N_SCL_LOW is the duration of the low part of the scl period measured in ic_clk cycles. + 0x00000001 + + + IC_SDA_RX_HOLD + Sets the required SDA hold time in units of ic_clk period, when DW_apb_i2c acts as a receiver. + + Reset value: IC_DEFAULT_SDA_HOLD[23:16]. + [23:16] + read-write + + + IC_SDA_TX_HOLD + Sets the required SDA hold time in units of ic_clk period, when DW_apb_i2c acts as a transmitter. + + Reset value: IC_DEFAULT_SDA_HOLD[15:0]. + [15:0] + read-write + + + + + IC_TX_ABRT_SOURCE + 0x00000080 + I2C Transmit Abort Source Register + + This register has 32 bits that indicate the source of the TX_ABRT bit. Except for Bit 9, this register is cleared whenever the IC_CLR_TX_ABRT register or the IC_CLR_INTR register is read. To clear Bit 9, the source of the ABRT_SBYTE_NORSTRT must be fixed first; RESTART must be enabled (IC_CON[5]=1), the SPECIAL bit must be cleared (IC_TAR[11]), or the GC_OR_START bit must be cleared (IC_TAR[10]). + + Once the source of the ABRT_SBYTE_NORSTRT is fixed, then this bit can be cleared in the same manner as other bits in this register. If the source of the ABRT_SBYTE_NORSTRT is not fixed before attempting to clear this bit, Bit 9 clears for one cycle and is then re-asserted. + 0x00000000 + + + TX_FLUSH_CNT + This field indicates the number of Tx FIFO Data Commands which are flushed due to TX_ABRT interrupt. It is cleared whenever I2C is disabled. + + Reset value: 0x0 + + Role of DW_apb_i2c: Master-Transmitter or Slave-Transmitter + [31:23] + read-only + + + ABRT_USER_ABRT + This is a master-mode-only bit. Master has detected the transfer abort (IC_ENABLE[1]) + + Reset value: 0x0 + + Role of DW_apb_i2c: Master-Transmitter + [16:16] + read-only + + + ABRT_USER_ABRT_VOID + 0 + Transfer abort detected by master- scenario not present + + + ABRT_USER_ABRT_GENERATED + 1 + Transfer abort detected by master + + + + + ABRT_SLVRD_INTX + 1: When the processor side responds to a slave mode request for data to be transmitted to a remote master and user writes a 1 in CMD (bit 8) of IC_DATA_CMD register. + + Reset value: 0x0 + + Role of DW_apb_i2c: Slave-Transmitter + [15:15] + read-only + + + ABRT_SLVRD_INTX_VOID + 0 + Slave trying to transmit to remote master in read mode- scenario not present + + + ABRT_SLVRD_INTX_GENERATED + 1 + Slave trying to transmit to remote master in read mode + + + + + ABRT_SLV_ARBLOST + This field indicates that a Slave has lost the bus while transmitting data to a remote master. IC_TX_ABRT_SOURCE[12] is set at the same time. Note: Even though the slave never 'owns' the bus, something could go wrong on the bus. This is a fail safe check. For instance, during a data transmission at the low-to-high transition of SCL, if what is on the data bus is not what is supposed to be transmitted, then DW_apb_i2c no longer own the bus. + + Reset value: 0x0 + + Role of DW_apb_i2c: Slave-Transmitter + [14:14] + read-only + + + ABRT_SLV_ARBLOST_VOID + 0 + Slave lost arbitration to remote master- scenario not present + + + ABRT_SLV_ARBLOST_GENERATED + 1 + Slave lost arbitration to remote master + + + + + ABRT_SLVFLUSH_TXFIFO + This field specifies that the Slave has received a read command and some data exists in the TX FIFO, so the slave issues a TX_ABRT interrupt to flush old data in TX FIFO. + + Reset value: 0x0 + + Role of DW_apb_i2c: Slave-Transmitter + [13:13] + read-only + + + ABRT_SLVFLUSH_TXFIFO_VOID + 0 + Slave flushes existing data in TX-FIFO upon getting read command- scenario not present + + + ABRT_SLVFLUSH_TXFIFO_GENERATED + 1 + Slave flushes existing data in TX-FIFO upon getting read command + + + + + ARB_LOST + This field specifies that the Master has lost arbitration, or if IC_TX_ABRT_SOURCE[14] is also set, then the slave transmitter has lost arbitration. + + Reset value: 0x0 + + Role of DW_apb_i2c: Master-Transmitter or Slave-Transmitter + [12:12] + read-only + + + ABRT_LOST_VOID + 0 + Master or Slave-Transmitter lost arbitration- scenario not present + + + ABRT_LOST_GENERATED + 1 + Master or Slave-Transmitter lost arbitration + + + + + ABRT_MASTER_DIS + This field indicates that the User tries to initiate a Master operation with the Master mode disabled. + + Reset value: 0x0 + + Role of DW_apb_i2c: Master-Transmitter or Master-Receiver + [11:11] + read-only + + + ABRT_MASTER_DIS_VOID + 0 + User initiating master operation when MASTER disabled- scenario not present + + + ABRT_MASTER_DIS_GENERATED + 1 + User initiating master operation when MASTER disabled + + + + + ABRT_10B_RD_NORSTRT + This field indicates that the restart is disabled (IC_RESTART_EN bit (IC_CON[5]) =0) and the master sends a read command in 10-bit addressing mode. + + Reset value: 0x0 + + Role of DW_apb_i2c: Master-Receiver + [10:10] + read-only + + + ABRT_10B_RD_VOID + 0 + Master not trying to read in 10Bit addressing mode when RESTART disabled + + + ABRT_10B_RD_GENERATED + 1 + Master trying to read in 10Bit addressing mode when RESTART disabled + + + + + ABRT_SBYTE_NORSTRT + To clear Bit 9, the source of the ABRT_SBYTE_NORSTRT must be fixed first; restart must be enabled (IC_CON[5]=1), the SPECIAL bit must be cleared (IC_TAR[11]), or the GC_OR_START bit must be cleared (IC_TAR[10]). Once the source of the ABRT_SBYTE_NORSTRT is fixed, then this bit can be cleared in the same manner as other bits in this register. If the source of the ABRT_SBYTE_NORSTRT is not fixed before attempting to clear this bit, bit 9 clears for one cycle and then gets reasserted. When this field is set to 1, the restart is disabled (IC_RESTART_EN bit (IC_CON[5]) =0) and the user is trying to send a START Byte. + + Reset value: 0x0 + + Role of DW_apb_i2c: Master + [9:9] + read-only + + + ABRT_SBYTE_NORSTRT_VOID + 0 + User trying to send START byte when RESTART disabled- scenario not present + + + ABRT_SBYTE_NORSTRT_GENERATED + 1 + User trying to send START byte when RESTART disabled + + + + + ABRT_HS_NORSTRT + This field indicates that the restart is disabled (IC_RESTART_EN bit (IC_CON[5]) =0) and the user is trying to use the master to transfer data in High Speed mode. + + Reset value: 0x0 + + Role of DW_apb_i2c: Master-Transmitter or Master-Receiver + [8:8] + read-only + + + ABRT_HS_NORSTRT_VOID + 0 + User trying to switch Master to HS mode when RESTART disabled- scenario not present + + + ABRT_HS_NORSTRT_GENERATED + 1 + User trying to switch Master to HS mode when RESTART disabled + + + + + ABRT_SBYTE_ACKDET + This field indicates that the Master has sent a START Byte and the START Byte was acknowledged (wrong behavior). + + Reset value: 0x0 + + Role of DW_apb_i2c: Master + [7:7] + read-only + + + ABRT_SBYTE_ACKDET_VOID + 0 + ACK detected for START byte- scenario not present + + + ABRT_SBYTE_ACKDET_GENERATED + 1 + ACK detected for START byte + + + + + ABRT_HS_ACKDET + This field indicates that the Master is in High Speed mode and the High Speed Master code was acknowledged (wrong behavior). + + Reset value: 0x0 + + Role of DW_apb_i2c: Master + [6:6] + read-only + + + ABRT_HS_ACK_VOID + 0 + HS Master code ACKed in HS Mode- scenario not present + + + ABRT_HS_ACK_GENERATED + 1 + HS Master code ACKed in HS Mode + + + + + ABRT_GCALL_READ + This field indicates that DW_apb_i2c in the master mode has sent a General Call but the user programmed the byte following the General Call to be a read from the bus (IC_DATA_CMD[9] is set to 1). + + Reset value: 0x0 + + Role of DW_apb_i2c: Master-Transmitter + [5:5] + read-only + + + ABRT_GCALL_READ_VOID + 0 + GCALL is followed by read from bus-scenario not present + + + ABRT_GCALL_READ_GENERATED + 1 + GCALL is followed by read from bus + + + + + ABRT_GCALL_NOACK + This field indicates that DW_apb_i2c in master mode has sent a General Call and no slave on the bus acknowledged the General Call. + + Reset value: 0x0 + + Role of DW_apb_i2c: Master-Transmitter + [4:4] + read-only + + + ABRT_GCALL_NOACK_VOID + 0 + GCALL not ACKed by any slave-scenario not present + + + ABRT_GCALL_NOACK_GENERATED + 1 + GCALL not ACKed by any slave + + + + + ABRT_TXDATA_NOACK + This field indicates the master-mode only bit. When the master receives an acknowledgement for the address, but when it sends data byte(s) following the address, it did not receive an acknowledge from the remote slave(s). + + Reset value: 0x0 + + Role of DW_apb_i2c: Master-Transmitter + [3:3] + read-only + + + ABRT_TXDATA_NOACK_VOID + 0 + Transmitted data non-ACKed by addressed slave-scenario not present + + + ABRT_TXDATA_NOACK_GENERATED + 1 + Transmitted data not ACKed by addressed slave + + + + + ABRT_10ADDR2_NOACK + This field indicates that the Master is in 10-bit address mode and that the second address byte of the 10-bit address was not acknowledged by any slave. + + Reset value: 0x0 + + Role of DW_apb_i2c: Master-Transmitter or Master-Receiver + [2:2] + read-only + + + INACTIVE + 0 + This abort is not generated + + + ACTIVE + 1 + Byte 2 of 10Bit Address not ACKed by any slave + + + + + ABRT_10ADDR1_NOACK + This field indicates that the Master is in 10-bit address mode and the first 10-bit address byte was not acknowledged by any slave. + + Reset value: 0x0 + + Role of DW_apb_i2c: Master-Transmitter or Master-Receiver + [1:1] + read-only + + + INACTIVE + 0 + This abort is not generated + + + ACTIVE + 1 + Byte 1 of 10Bit Address not ACKed by any slave + + + + + ABRT_7B_ADDR_NOACK + This field indicates that the Master is in 7-bit addressing mode and the address sent was not acknowledged by any slave. + + Reset value: 0x0 + + Role of DW_apb_i2c: Master-Transmitter or Master-Receiver + [0:0] + read-only + + + INACTIVE + 0 + This abort is not generated + + + ACTIVE + 1 + This abort is generated because of NOACK for 7-bit address + + + + + + + IC_SLV_DATA_NACK_ONLY + 0x00000084 + Generate Slave Data NACK Register + + The register is used to generate a NACK for the data part of a transfer when DW_apb_i2c is acting as a slave-receiver. This register only exists when the IC_SLV_DATA_NACK_ONLY parameter is set to 1. When this parameter disabled, this register does not exist and writing to the register's address has no effect. + + A write can occur on this register if both of the following conditions are met: - DW_apb_i2c is disabled (IC_ENABLE[0] = 0) - Slave part is inactive (IC_STATUS[6] = 0) Note: The IC_STATUS[6] is a register read-back location for the internal slv_activity signal; the user should poll this before writing the ic_slv_data_nack_only bit. + 0x00000000 + + + NACK + Generate NACK. This NACK generation only occurs when DW_apb_i2c is a slave-receiver. If this register is set to a value of 1, it can only generate a NACK after a data byte is received; hence, the data transfer is aborted and the data received is not pushed to the receive buffer. + + When the register is set to a value of 0, it generates NACK/ACK, depending on normal criteria. - 1: generate NACK after data byte received - 0: generate NACK/ACK normally Reset value: 0x0 + [0:0] + read-write + + + DISABLED + 0 + Slave receiver generates NACK normally + + + ENABLED + 1 + Slave receiver generates NACK upon data reception only + + + + + + + IC_DMA_CR + 0x00000088 + DMA Control Register + + The register is used to enable the DMA Controller interface operation. There is a separate bit for transmit and receive. This can be programmed regardless of the state of IC_ENABLE. + 0x00000000 + + + TDMAE + Transmit DMA Enable. This bit enables/disables the transmit FIFO DMA channel. Reset value: 0x0 + [1:1] + read-write + + + DISABLED + 0 + transmit FIFO DMA channel disabled + + + ENABLED + 1 + Transmit FIFO DMA channel enabled + + + + + RDMAE + Receive DMA Enable. This bit enables/disables the receive FIFO DMA channel. Reset value: 0x0 + [0:0] + read-write + + + DISABLED + 0 + Receive FIFO DMA channel disabled + + + ENABLED + 1 + Receive FIFO DMA channel enabled + + + + + + + IC_DMA_TDLR + 0x0000008c + DMA Transmit Data Level Register + 0x00000000 + + + DMATDL + Transmit Data Level. This bit field controls the level at which a DMA request is made by the transmit logic. It is equal to the watermark level; that is, the dma_tx_req signal is generated when the number of valid data entries in the transmit FIFO is equal to or below this field value, and TDMAE = 1. + + Reset value: 0x0 + [3:0] + read-write + + + + + IC_DMA_RDLR + 0x00000090 + I2C Receive Data Level Register + 0x00000000 + + + DMARDL + Receive Data Level. This bit field controls the level at which a DMA request is made by the receive logic. The watermark level = DMARDL+1; that is, dma_rx_req is generated when the number of valid data entries in the receive FIFO is equal to or more than this field value + 1, and RDMAE =1. For instance, when DMARDL is 0, then dma_rx_req is asserted when 1 or more data entries are present in the receive FIFO. + + Reset value: 0x0 + [3:0] + read-write + + + + + IC_SDA_SETUP + 0x00000094 + I2C SDA Setup Register + + This register controls the amount of time delay (in terms of number of ic_clk clock periods) introduced in the rising edge of SCL - relative to SDA changing - when DW_apb_i2c services a read request in a slave-transmitter operation. The relevant I2C requirement is tSU:DAT (note 4) as detailed in the I2C Bus Specification. This register must be programmed with a value equal to or greater than 2. + + Writes to this register succeed only when IC_ENABLE[0] = 0. + + Note: The length of setup time is calculated using [(IC_SDA_SETUP - 1) * (ic_clk_period)], so if the user requires 10 ic_clk periods of setup time, they should program a value of 11. The IC_SDA_SETUP register is only used by the DW_apb_i2c when operating as a slave transmitter. + 0x00000064 + + + SDA_SETUP + SDA Setup. It is recommended that if the required delay is 1000ns, then for an ic_clk frequency of 10 MHz, IC_SDA_SETUP should be programmed to a value of 11. IC_SDA_SETUP must be programmed with a minimum value of 2. + [7:0] + read-write + + + + + IC_ACK_GENERAL_CALL + 0x00000098 + I2C ACK General Call Register + + The register controls whether DW_apb_i2c responds with a ACK or NACK when it receives an I2C General Call address. + + This register is applicable only when the DW_apb_i2c is in slave mode. + 0x00000001 + + + ACK_GEN_CALL + ACK General Call. When set to 1, DW_apb_i2c responds with a ACK (by asserting ic_data_oe) when it receives a General Call. Otherwise, DW_apb_i2c responds with a NACK (by negating ic_data_oe). + [0:0] + read-write + + + DISABLED + 0 + Generate NACK for a General Call + + + ENABLED + 1 + Generate ACK for a General Call + + + + + + + IC_ENABLE_STATUS + 0x0000009c + I2C Enable Status Register + + The register is used to report the DW_apb_i2c hardware status when the IC_ENABLE[0] register is set from 1 to 0; that is, when DW_apb_i2c is disabled. + + If IC_ENABLE[0] has been set to 1, bits 2:1 are forced to 0, and bit 0 is forced to 1. + + If IC_ENABLE[0] has been set to 0, bits 2:1 is only be valid as soon as bit 0 is read as '0'. + + Note: When IC_ENABLE[0] has been set to 0, a delay occurs for bit 0 to be read as 0 because disabling the DW_apb_i2c depends on I2C bus activities. + 0x00000000 + + + SLV_RX_DATA_LOST + Slave Received Data Lost. This bit indicates if a Slave-Receiver operation has been aborted with at least one data byte received from an I2C transfer due to the setting bit 0 of IC_ENABLE from 1 to 0. When read as 1, DW_apb_i2c is deemed to have been actively engaged in an aborted I2C transfer (with matching address) and the data phase of the I2C transfer has been entered, even though a data byte has been responded with a NACK. + + Note: If the remote I2C master terminates the transfer with a STOP condition before the DW_apb_i2c has a chance to NACK a transfer, and IC_ENABLE[0] has been set to 0, then this bit is also set to 1. + + When read as 0, DW_apb_i2c is deemed to have been disabled without being actively involved in the data phase of a Slave-Receiver transfer. + + Note: The CPU can safely read this bit when IC_EN (bit 0) is read as 0. + + Reset value: 0x0 + [2:2] + read-only + + + INACTIVE + 0 + Slave RX Data is not lost + + + ACTIVE + 1 + Slave RX Data is lost + + + + + SLV_DISABLED_WHILE_BUSY + Slave Disabled While Busy (Transmit, Receive). This bit indicates if a potential or active Slave operation has been aborted due to the setting bit 0 of the IC_ENABLE register from 1 to 0. This bit is set when the CPU writes a 0 to the IC_ENABLE register while: + + (a) DW_apb_i2c is receiving the address byte of the Slave-Transmitter operation from a remote master; + + OR, + + (b) address and data bytes of the Slave-Receiver operation from a remote master. + + When read as 1, DW_apb_i2c is deemed to have forced a NACK during any part of an I2C transfer, irrespective of whether the I2C address matches the slave address set in DW_apb_i2c (IC_SAR register) OR if the transfer is completed before IC_ENABLE is set to 0 but has not taken effect. + + Note: If the remote I2C master terminates the transfer with a STOP condition before the DW_apb_i2c has a chance to NACK a transfer, and IC_ENABLE[0] has been set to 0, then this bit will also be set to 1. + + When read as 0, DW_apb_i2c is deemed to have been disabled when there is master activity, or when the I2C bus is idle. + + Note: The CPU can safely read this bit when IC_EN (bit 0) is read as 0. + + Reset value: 0x0 + [1:1] + read-only + + + INACTIVE + 0 + Slave is disabled when it is idle + + + ACTIVE + 1 + Slave is disabled when it is active + + + + + IC_EN + ic_en Status. This bit always reflects the value driven on the output port ic_en. - When read as 1, DW_apb_i2c is deemed to be in an enabled state. - When read as 0, DW_apb_i2c is deemed completely inactive. Note: The CPU can safely read this bit anytime. When this bit is read as 0, the CPU can safely read SLV_RX_DATA_LOST (bit 2) and SLV_DISABLED_WHILE_BUSY (bit 1). + + Reset value: 0x0 + [0:0] + read-only + + + DISABLED + 0 + I2C disabled + + + ENABLED + 1 + I2C enabled + + + + + + + IC_FS_SPKLEN + 0x000000a0 + I2C SS, FS or FM+ spike suppression limit + + This register is used to store the duration, measured in ic_clk cycles, of the longest spike that is filtered out by the spike suppression logic when the component is operating in SS, FS or FM+ modes. The relevant I2C requirement is tSP (table 4) as detailed in the I2C Bus Specification. This register must be programmed with a minimum value of 1. + 0x00000007 + + + IC_FS_SPKLEN + This register must be set before any I2C bus transaction can take place to ensure stable operation. This register sets the duration, measured in ic_clk cycles, of the longest spike in the SCL or SDA lines that will be filtered out by the spike suppression logic. This register can be written only when the I2C interface is disabled which corresponds to the IC_ENABLE[0] register being set to 0. Writes at other times have no effect. The minimum valid value is 1; hardware prevents values less than this being written, and if attempted results in 1 being set. or more information, refer to 'Spike Suppression'. + [7:0] + read-write + + + + + IC_CLR_RESTART_DET + 0x000000a8 + Clear RESTART_DET Interrupt Register + 0x00000000 + + + CLR_RESTART_DET + Read this register to clear the RESTART_DET interrupt (bit 12) of IC_RAW_INTR_STAT register. + + Reset value: 0x0 + [0:0] + read-only + + + + + IC_COMP_PARAM_1 + 0x000000f4 + Component Parameter Register 1 + + Note This register is not implemented and therefore reads as 0. If it was implemented it would be a constant read-only register that contains encoded information about the component's parameter settings. Fields shown below are the settings for those parameters + 0x00000000 + + + TX_BUFFER_DEPTH + TX Buffer Depth = 16 + [23:16] + read-only + + + RX_BUFFER_DEPTH + RX Buffer Depth = 16 + [15:8] + read-only + + + ADD_ENCODED_PARAMS + Encoded parameters not visible + [7:7] + read-only + + + HAS_DMA + DMA handshaking signals are enabled + [6:6] + read-only + + + INTR_IO + COMBINED Interrupt outputs + [5:5] + read-only + + + HC_COUNT_VALUES + Programmable count values for each mode. + [4:4] + read-only + + + MAX_SPEED_MODE + MAX SPEED MODE = FAST MODE + [3:2] + read-only + + + APB_DATA_WIDTH + APB data bus width is 32 bits + [1:0] + read-only + + + + + IC_COMP_VERSION + 0x000000f8 + I2C Component Version Register + 0x3230312a + + + IC_COMP_VERSION + [31:0] + read-only + + + + + IC_COMP_TYPE + 0x000000fc + I2C Component Type Register + 0x44570140 + + + IC_COMP_TYPE + Designware Component Type number = 0x44_57_01_40. This assigned unique hex value is constant and is derived from the two ASCII letters 'DW' followed by a 16-bit unsigned number. + [31:0] + read-only + + + + + + + I2C1 + 0x40098000 + + I2C1_IRQ + 37 + + + + SPI0 + 0x40080000 + + 0 + 4096 + registers + + + SPI0_IRQ + 31 + + + + SSPCR0 + 0x00000000 + Control register 0, SSPCR0 on page 3-4 + 0x00000000 + + + SCR + Serial clock rate. The value SCR is used to generate the transmit and receive bit rate of the PrimeCell SSP. The bit rate is: F SSPCLK CPSDVSR x (1+SCR) where CPSDVSR is an even value from 2-254, programmed through the SSPCPSR register and SCR is a value from 0-255. + [15:8] + read-write + + + SPH + SSPCLKOUT phase, applicable to Motorola SPI frame format only. See Motorola SPI frame format on page 2-10. + [7:7] + read-write + + + SPO + SSPCLKOUT polarity, applicable to Motorola SPI frame format only. See Motorola SPI frame format on page 2-10. + [6:6] + read-write + + + FRF + Frame format: 00 Motorola SPI frame format. 01 TI synchronous serial frame format. 10 National Microwire frame format. 11 Reserved, undefined operation. + [5:4] + read-write + + + DSS + Data Size Select: 0000 Reserved, undefined operation. 0001 Reserved, undefined operation. 0010 Reserved, undefined operation. 0011 4-bit data. 0100 5-bit data. 0101 6-bit data. 0110 7-bit data. 0111 8-bit data. 1000 9-bit data. 1001 10-bit data. 1010 11-bit data. 1011 12-bit data. 1100 13-bit data. 1101 14-bit data. 1110 15-bit data. 1111 16-bit data. + [3:0] + read-write + + + + + SSPCR1 + 0x00000004 + Control register 1, SSPCR1 on page 3-5 + 0x00000000 + + + SOD + Slave-mode output disable. This bit is relevant only in the slave mode, MS=1. In multiple-slave systems, it is possible for an PrimeCell SSP master to broadcast a message to all slaves in the system while ensuring that only one slave drives data onto its serial output line. In such systems the RXD lines from multiple slaves could be tied together. To operate in such systems, the SOD bit can be set if the PrimeCell SSP slave is not supposed to drive the SSPTXD line: 0 SSP can drive the SSPTXD output in slave mode. 1 SSP must not drive the SSPTXD output in slave mode. + [3:3] + read-write + + + MS + Master or slave mode select. This bit can be modified only when the PrimeCell SSP is disabled, SSE=0: 0 Device configured as master, default. 1 Device configured as slave. + [2:2] + read-write + + + SSE + Synchronous serial port enable: 0 SSP operation disabled. 1 SSP operation enabled. + [1:1] + read-write + + + LBM + Loop back mode: 0 Normal serial port operation enabled. 1 Output of transmit serial shifter is connected to input of receive serial shifter internally. + [0:0] + read-write + + + + + SSPDR + 0x00000008 + Data register, SSPDR on page 3-6 + 0x00000000 + + + DATA + Transmit/Receive FIFO: Read Receive FIFO. Write Transmit FIFO. You must right-justify data when the PrimeCell SSP is programmed for a data size that is less than 16 bits. Unused bits at the top are ignored by transmit logic. The receive logic automatically right-justifies. + [15:0] + read-write + modify + + + + + SSPSR + 0x0000000c + Status register, SSPSR on page 3-7 + 0x00000003 + + + BSY + PrimeCell SSP busy flag, RO: 0 SSP is idle. 1 SSP is currently transmitting and/or receiving a frame or the transmit FIFO is not empty. + [4:4] + read-only + + + RFF + Receive FIFO full, RO: 0 Receive FIFO is not full. 1 Receive FIFO is full. + [3:3] + read-only + + + RNE + Receive FIFO not empty, RO: 0 Receive FIFO is empty. 1 Receive FIFO is not empty. + [2:2] + read-only + + + TNF + Transmit FIFO not full, RO: 0 Transmit FIFO is full. 1 Transmit FIFO is not full. + [1:1] + read-only + + + TFE + Transmit FIFO empty, RO: 0 Transmit FIFO is not empty. 1 Transmit FIFO is empty. + [0:0] + read-only + + + + + SSPCPSR + 0x00000010 + Clock prescale register, SSPCPSR on page 3-8 + 0x00000000 + + + CPSDVSR + Clock prescale divisor. Must be an even number from 2-254, depending on the frequency of SSPCLK. The least significant bit always returns zero on reads. + [7:0] + read-write + + + + + SSPIMSC + 0x00000014 + Interrupt mask set or clear register, SSPIMSC on page 3-9 + 0x00000000 + + + TXIM + Transmit FIFO interrupt mask: 0 Transmit FIFO half empty or less condition interrupt is masked. 1 Transmit FIFO half empty or less condition interrupt is not masked. + [3:3] + read-write + + + RXIM + Receive FIFO interrupt mask: 0 Receive FIFO half full or less condition interrupt is masked. 1 Receive FIFO half full or less condition interrupt is not masked. + [2:2] + read-write + + + RTIM + Receive timeout interrupt mask: 0 Receive FIFO not empty and no read prior to timeout period interrupt is masked. 1 Receive FIFO not empty and no read prior to timeout period interrupt is not masked. + [1:1] + read-write + + + RORIM + Receive overrun interrupt mask: 0 Receive FIFO written to while full condition interrupt is masked. 1 Receive FIFO written to while full condition interrupt is not masked. + [0:0] + read-write + + + + + SSPRIS + 0x00000018 + Raw interrupt status register, SSPRIS on page 3-10 + 0x00000008 + + + TXRIS + Gives the raw interrupt state, prior to masking, of the SSPTXINTR interrupt + [3:3] + read-only + + + RXRIS + Gives the raw interrupt state, prior to masking, of the SSPRXINTR interrupt + [2:2] + read-only + + + RTRIS + Gives the raw interrupt state, prior to masking, of the SSPRTINTR interrupt + [1:1] + read-only + + + RORRIS + Gives the raw interrupt state, prior to masking, of the SSPRORINTR interrupt + [0:0] + read-only + + + + + SSPMIS + 0x0000001c + Masked interrupt status register, SSPMIS on page 3-11 + 0x00000000 + + + TXMIS + Gives the transmit FIFO masked interrupt state, after masking, of the SSPTXINTR interrupt + [3:3] + read-only + + + RXMIS + Gives the receive FIFO masked interrupt state, after masking, of the SSPRXINTR interrupt + [2:2] + read-only + + + RTMIS + Gives the receive timeout masked interrupt state, after masking, of the SSPRTINTR interrupt + [1:1] + read-only + + + RORMIS + Gives the receive over run masked interrupt status, after masking, of the SSPRORINTR interrupt + [0:0] + read-only + + + + + SSPICR + 0x00000020 + Interrupt clear register, SSPICR on page 3-11 + 0x00000000 + + + RTIC + Clears the SSPRTINTR interrupt + [1:1] + read-write + oneToClear + + + RORIC + Clears the SSPRORINTR interrupt + [0:0] + read-write + oneToClear + + + + + SSPDMACR + 0x00000024 + DMA control register, SSPDMACR on page 3-12 + 0x00000000 + + + TXDMAE + Transmit DMA Enable. If this bit is set to 1, DMA for the transmit FIFO is enabled. + [1:1] + read-write + + + RXDMAE + Receive DMA Enable. If this bit is set to 1, DMA for the receive FIFO is enabled. + [0:0] + read-write + + + + + SSPPERIPHID0 + 0x00000fe0 + Peripheral identification registers, SSPPeriphID0-3 on page 3-13 + 0x00000022 + + + PARTNUMBER0 + These bits read back as 0x22 + [7:0] + read-only + + + + + SSPPERIPHID1 + 0x00000fe4 + Peripheral identification registers, SSPPeriphID0-3 on page 3-13 + 0x00000010 + + + DESIGNER0 + These bits read back as 0x1 + [7:4] + read-only + + + PARTNUMBER1 + These bits read back as 0x0 + [3:0] + read-only + + + + + SSPPERIPHID2 + 0x00000fe8 + Peripheral identification registers, SSPPeriphID0-3 on page 3-13 + 0x00000034 + + + REVISION + These bits return the peripheral revision + [7:4] + read-only + + + DESIGNER1 + These bits read back as 0x4 + [3:0] + read-only + + + + + SSPPERIPHID3 + 0x00000fec + Peripheral identification registers, SSPPeriphID0-3 on page 3-13 + 0x00000000 + + + CONFIGURATION + These bits read back as 0x00 + [7:0] + read-only + + + + + SSPPCELLID0 + 0x00000ff0 + PrimeCell identification registers, SSPPCellID0-3 on page 3-16 + 0x0000000d + + + SSPPCELLID0 + These bits read back as 0x0D + [7:0] + read-only + + + + + SSPPCELLID1 + 0x00000ff4 + PrimeCell identification registers, SSPPCellID0-3 on page 3-16 + 0x000000f0 + + + SSPPCELLID1 + These bits read back as 0xF0 + [7:0] + read-only + + + + + SSPPCELLID2 + 0x00000ff8 + PrimeCell identification registers, SSPPCellID0-3 on page 3-16 + 0x00000005 + + + SSPPCELLID2 + These bits read back as 0x05 + [7:0] + read-only + + + + + SSPPCELLID3 + 0x00000ffc + PrimeCell identification registers, SSPPCellID0-3 on page 3-16 + 0x000000b1 + + + SSPPCELLID3 + These bits read back as 0xB1 + [7:0] + read-only + + + + + + + SPI1 + 0x40088000 + + SPI1_IRQ + 32 + + + + PIO0 + Programmable IO block + 0x50200000 + + 0 + 392 + registers + + + PIO0_IRQ_0 + 15 + + + PIO0_IRQ_1 + 16 + + + + CTRL + 0x00000000 + PIO control register + 0x00000000 + + + NEXTPREV_CLKDIV_RESTART + Write 1 to restart the clock dividers of state machines in neighbouring PIO blocks, as specified by NEXT_PIO_MASK and PREV_PIO_MASK in the same write. + + This is equivalent to writing 1 to the corresponding CLKDIV_RESTART bits in those PIOs' CTRL registers. + [26:26] + write-only + + + NEXTPREV_SM_DISABLE + Write 1 to disable state machines in neighbouring PIO blocks, as specified by NEXT_PIO_MASK and PREV_PIO_MASK in the same write. + + This is equivalent to clearing the corresponding SM_ENABLE bits in those PIOs' CTRL registers. + [25:25] + write-only + + + NEXTPREV_SM_ENABLE + Write 1 to enable state machines in neighbouring PIO blocks, as specified by NEXT_PIO_MASK and PREV_PIO_MASK in the same write. + + This is equivalent to setting the corresponding SM_ENABLE bits in those PIOs' CTRL registers. + + If both OTHERS_SM_ENABLE and OTHERS_SM_DISABLE are set, the disable takes precedence. + [24:24] + write-only + + + NEXT_PIO_MASK + A mask of state machines in the neighbouring higher-numbered PIO block in the system (or PIO block 0 if this is the highest-numbered PIO block) to which to apply the operations specified by NEXTPREV_CLKDIV_RESTART, NEXTPREV_SM_ENABLE, and NEXTPREV_SM_DISABLE in the same write. + + This allows state machines in a neighbouring PIO block to be started/stopped/clock-synced exactly simultaneously with a write to this PIO block's CTRL register. + + Note that in a system with two PIOs, NEXT_PIO_MASK and PREV_PIO_MASK actually indicate the same PIO block. In this case the effects are applied cumulatively (as though the masks were OR'd together). + + Neighbouring PIO blocks are disconnected (status signals tied to 0 and control signals ignored) if one block is accessible to NonSecure code, and one is not. + [23:20] + write-only + + + PREV_PIO_MASK + A mask of state machines in the neighbouring lower-numbered PIO block in the system (or the highest-numbered PIO block if this is PIO block 0) to which to apply the operations specified by OP_CLKDIV_RESTART, OP_ENABLE, OP_DISABLE in the same write. + + This allows state machines in a neighbouring PIO block to be started/stopped/clock-synced exactly simultaneously with a write to this PIO block's CTRL register. + + Neighbouring PIO blocks are disconnected (status signals tied to 0 and control signals ignored) if one block is accessible to NonSecure code, and one is not. + [19:16] + write-only + + + CLKDIV_RESTART + Restart a state machine's clock divider from an initial phase of 0. Clock dividers are free-running, so once started, their output (including fractional jitter) is completely determined by the integer/fractional divisor configured in SMx_CLKDIV. This means that, if multiple clock dividers with the same divisor are restarted simultaneously, by writing multiple 1 bits to this field, the execution clocks of those state machines will run in precise lockstep. + + Note that setting/clearing SM_ENABLE does not stop the clock divider from running, so once multiple state machines' clocks are synchronised, it is safe to disable/reenable a state machine, whilst keeping the clock dividers in sync. + + Note also that CLKDIV_RESTART can be written to whilst the state machine is running, and this is useful to resynchronise clock dividers after the divisors (SMx_CLKDIV) have been changed on-the-fly. + [11:8] + write-only + + + SM_RESTART + Write 1 to instantly clear internal SM state which may be otherwise difficult to access and will affect future execution. + + Specifically, the following are cleared: input and output shift counters; the contents of the input shift register; the delay counter; the waiting-on-IRQ state; any stalled instruction written to SMx_INSTR or run by OUT/MOV EXEC; any pin write left asserted due to OUT_STICKY. + + The contents of the output shift register and the X/Y scratch registers are not affected. + [7:4] + write-only + + + SM_ENABLE + Enable/disable each of the four state machines by writing 1/0 to each of these four bits. When disabled, a state machine will cease executing instructions, except those written directly to SMx_INSTR by the system. Multiple bits can be set/cleared at once to run/halt multiple state machines simultaneously. + [3:0] + read-write + + + + + FSTAT + 0x00000004 + FIFO status register + 0x0f000f00 + + + TXEMPTY + State machine TX FIFO is empty + [27:24] + read-only + + + TXFULL + State machine TX FIFO is full + [19:16] + read-only + + + RXEMPTY + State machine RX FIFO is empty + [11:8] + read-only + + + RXFULL + State machine RX FIFO is full + [3:0] + read-only + + + + + FDEBUG + 0x00000008 + FIFO debug register + 0x00000000 + + + TXSTALL + State machine has stalled on empty TX FIFO during a blocking PULL, or an OUT with autopull enabled. Write 1 to clear. + [27:24] + read-write + oneToClear + + + TXOVER + TX FIFO overflow (i.e. write-on-full by the system) has occurred. Write 1 to clear. Note that write-on-full does not alter the state or contents of the FIFO in any way, but the data that the system attempted to write is dropped, so if this flag is set, your software has quite likely dropped some data on the floor. + [19:16] + read-write + oneToClear + + + RXUNDER + RX FIFO underflow (i.e. read-on-empty by the system) has occurred. Write 1 to clear. Note that read-on-empty does not perturb the state of the FIFO in any way, but the data returned by reading from an empty FIFO is undefined, so this flag generally only becomes set due to some kind of software error. + [11:8] + read-write + oneToClear + + + RXSTALL + State machine has stalled on full RX FIFO during a blocking PUSH, or an IN with autopush enabled. This flag is also set when a nonblocking PUSH to a full FIFO took place, in which case the state machine has dropped data. Write 1 to clear. + [3:0] + read-write + oneToClear + + + + + FLEVEL + 0x0000000c + FIFO levels + 0x00000000 + + + RX3 + [31:28] + read-only + + + TX3 + [27:24] + read-only + + + RX2 + [23:20] + read-only + + + TX2 + [19:16] + read-only + + + RX1 + [15:12] + read-only + + + TX1 + [11:8] + read-only + + + RX0 + [7:4] + read-only + + + TX0 + [3:0] + read-only + + + + + TXF0 + 0x00000010 + Direct write access to the TX FIFO for this state machine. Each write pushes one word to the FIFO. Attempting to write to a full FIFO has no effect on the FIFO state or contents, and sets the sticky FDEBUG_TXOVER error flag for this FIFO. + 0x00000000 + + + TXF0 + [31:0] + write-only + + + + + TXF1 + 0x00000014 + Direct write access to the TX FIFO for this state machine. Each write pushes one word to the FIFO. Attempting to write to a full FIFO has no effect on the FIFO state or contents, and sets the sticky FDEBUG_TXOVER error flag for this FIFO. + 0x00000000 + + + TXF1 + [31:0] + write-only + + + + + TXF2 + 0x00000018 + Direct write access to the TX FIFO for this state machine. Each write pushes one word to the FIFO. Attempting to write to a full FIFO has no effect on the FIFO state or contents, and sets the sticky FDEBUG_TXOVER error flag for this FIFO. + 0x00000000 + + + TXF2 + [31:0] + write-only + + + + + TXF3 + 0x0000001c + Direct write access to the TX FIFO for this state machine. Each write pushes one word to the FIFO. Attempting to write to a full FIFO has no effect on the FIFO state or contents, and sets the sticky FDEBUG_TXOVER error flag for this FIFO. + 0x00000000 + + + TXF3 + [31:0] + write-only + + + + + RXF0 + 0x00000020 + Direct read access to the RX FIFO for this state machine. Each read pops one word from the FIFO. Attempting to read from an empty FIFO has no effect on the FIFO state, and sets the sticky FDEBUG_RXUNDER error flag for this FIFO. The data returned to the system on a read from an empty FIFO is undefined. + 0x00000000 + + + RXF0 + [31:0] + read-only + modify + + + + + RXF1 + 0x00000024 + Direct read access to the RX FIFO for this state machine. Each read pops one word from the FIFO. Attempting to read from an empty FIFO has no effect on the FIFO state, and sets the sticky FDEBUG_RXUNDER error flag for this FIFO. The data returned to the system on a read from an empty FIFO is undefined. + 0x00000000 + + + RXF1 + [31:0] + read-only + modify + + + + + RXF2 + 0x00000028 + Direct read access to the RX FIFO for this state machine. Each read pops one word from the FIFO. Attempting to read from an empty FIFO has no effect on the FIFO state, and sets the sticky FDEBUG_RXUNDER error flag for this FIFO. The data returned to the system on a read from an empty FIFO is undefined. + 0x00000000 + + + RXF2 + [31:0] + read-only + modify + + + + + RXF3 + 0x0000002c + Direct read access to the RX FIFO for this state machine. Each read pops one word from the FIFO. Attempting to read from an empty FIFO has no effect on the FIFO state, and sets the sticky FDEBUG_RXUNDER error flag for this FIFO. The data returned to the system on a read from an empty FIFO is undefined. + 0x00000000 + + + RXF3 + [31:0] + read-only + modify + + + + + IRQ + 0x00000030 + State machine IRQ flags register. Write 1 to clear. There are eight state machine IRQ flags, which can be set, cleared, and waited on by the state machines. There's no fixed association between flags and state machines -- any state machine can use any flag. + + Any of the eight flags can be used for timing synchronisation between state machines, using IRQ and WAIT instructions. Any combination of the eight flags can also routed out to either of the two system-level interrupt requests, alongside FIFO status interrupts -- see e.g. IRQ0_INTE. + 0x00000000 + + + IRQ + [7:0] + read-write + oneToClear + + + + + IRQ_FORCE + 0x00000034 + Writing a 1 to each of these bits will forcibly assert the corresponding IRQ. Note this is different to the INTF register: writing here affects PIO internal state. INTF just asserts the processor-facing IRQ signal for testing ISRs, and is not visible to the state machines. + 0x00000000 + + + IRQ_FORCE + [7:0] + write-only + + + + + INPUT_SYNC_BYPASS + 0x00000038 + There is a 2-flipflop synchronizer on each GPIO input, which protects PIO logic from metastabilities. This increases input delay, and for fast synchronous IO (e.g. SPI) these synchronizers may need to be bypassed. Each bit in this register corresponds to one GPIO. + 0 -> input is synchronized (default) + 1 -> synchronizer is bypassed + If in doubt, leave this register as all zeroes. + 0x00000000 + + + INPUT_SYNC_BYPASS + [31:0] + read-write + + + + + DBG_PADOUT + 0x0000003c + Read to sample the pad output values PIO is currently driving to the GPIOs. On RP2040 there are 30 GPIOs, so the two most significant bits are hardwired to 0. + 0x00000000 + + + DBG_PADOUT + [31:0] + read-only + + + + + DBG_PADOE + 0x00000040 + Read to sample the pad output enables (direction) PIO is currently driving to the GPIOs. On RP2040 there are 30 GPIOs, so the two most significant bits are hardwired to 0. + 0x00000000 + + + DBG_PADOE + [31:0] + read-only + + + + + DBG_CFGINFO + 0x00000044 + The PIO hardware has some free parameters that may vary between chip products. + These should be provided in the chip datasheet, but are also exposed here. + 0x10000000 + + + VERSION + Version of the core PIO hardware. + [31:28] + read-only + + + v0 + 0 + Version 0 (RP2040) + + + v1 + 1 + Version 1 (RP2350) + + + + + IMEM_SIZE + The size of the instruction memory, measured in units of one instruction + [21:16] + read-only + + + SM_COUNT + The number of state machines this PIO instance is equipped with. + [11:8] + read-only + + + FIFO_DEPTH + The depth of the state machine TX/RX FIFOs, measured in words. + Joining fifos via SHIFTCTRL_FJOIN gives one FIFO with double + this depth. + [5:0] + read-only + + + + + INSTR_MEM0 + 0x00000048 + Write-only access to instruction memory location 0 + 0x00000000 + + + INSTR_MEM0 + [15:0] + write-only + + + + + INSTR_MEM1 + 0x0000004c + Write-only access to instruction memory location 1 + 0x00000000 + + + INSTR_MEM1 + [15:0] + write-only + + + + + INSTR_MEM2 + 0x00000050 + Write-only access to instruction memory location 2 + 0x00000000 + + + INSTR_MEM2 + [15:0] + write-only + + + + + INSTR_MEM3 + 0x00000054 + Write-only access to instruction memory location 3 + 0x00000000 + + + INSTR_MEM3 + [15:0] + write-only + + + + + INSTR_MEM4 + 0x00000058 + Write-only access to instruction memory location 4 + 0x00000000 + + + INSTR_MEM4 + [15:0] + write-only + + + + + INSTR_MEM5 + 0x0000005c + Write-only access to instruction memory location 5 + 0x00000000 + + + INSTR_MEM5 + [15:0] + write-only + + + + + INSTR_MEM6 + 0x00000060 + Write-only access to instruction memory location 6 + 0x00000000 + + + INSTR_MEM6 + [15:0] + write-only + + + + + INSTR_MEM7 + 0x00000064 + Write-only access to instruction memory location 7 + 0x00000000 + + + INSTR_MEM7 + [15:0] + write-only + + + + + INSTR_MEM8 + 0x00000068 + Write-only access to instruction memory location 8 + 0x00000000 + + + INSTR_MEM8 + [15:0] + write-only + + + + + INSTR_MEM9 + 0x0000006c + Write-only access to instruction memory location 9 + 0x00000000 + + + INSTR_MEM9 + [15:0] + write-only + + + + + INSTR_MEM10 + 0x00000070 + Write-only access to instruction memory location 10 + 0x00000000 + + + INSTR_MEM10 + [15:0] + write-only + + + + + INSTR_MEM11 + 0x00000074 + Write-only access to instruction memory location 11 + 0x00000000 + + + INSTR_MEM11 + [15:0] + write-only + + + + + INSTR_MEM12 + 0x00000078 + Write-only access to instruction memory location 12 + 0x00000000 + + + INSTR_MEM12 + [15:0] + write-only + + + + + INSTR_MEM13 + 0x0000007c + Write-only access to instruction memory location 13 + 0x00000000 + + + INSTR_MEM13 + [15:0] + write-only + + + + + INSTR_MEM14 + 0x00000080 + Write-only access to instruction memory location 14 + 0x00000000 + + + INSTR_MEM14 + [15:0] + write-only + + + + + INSTR_MEM15 + 0x00000084 + Write-only access to instruction memory location 15 + 0x00000000 + + + INSTR_MEM15 + [15:0] + write-only + + + + + INSTR_MEM16 + 0x00000088 + Write-only access to instruction memory location 16 + 0x00000000 + + + INSTR_MEM16 + [15:0] + write-only + + + + + INSTR_MEM17 + 0x0000008c + Write-only access to instruction memory location 17 + 0x00000000 + + + INSTR_MEM17 + [15:0] + write-only + + + + + INSTR_MEM18 + 0x00000090 + Write-only access to instruction memory location 18 + 0x00000000 + + + INSTR_MEM18 + [15:0] + write-only + + + + + INSTR_MEM19 + 0x00000094 + Write-only access to instruction memory location 19 + 0x00000000 + + + INSTR_MEM19 + [15:0] + write-only + + + + + INSTR_MEM20 + 0x00000098 + Write-only access to instruction memory location 20 + 0x00000000 + + + INSTR_MEM20 + [15:0] + write-only + + + + + INSTR_MEM21 + 0x0000009c + Write-only access to instruction memory location 21 + 0x00000000 + + + INSTR_MEM21 + [15:0] + write-only + + + + + INSTR_MEM22 + 0x000000a0 + Write-only access to instruction memory location 22 + 0x00000000 + + + INSTR_MEM22 + [15:0] + write-only + + + + + INSTR_MEM23 + 0x000000a4 + Write-only access to instruction memory location 23 + 0x00000000 + + + INSTR_MEM23 + [15:0] + write-only + + + + + INSTR_MEM24 + 0x000000a8 + Write-only access to instruction memory location 24 + 0x00000000 + + + INSTR_MEM24 + [15:0] + write-only + + + + + INSTR_MEM25 + 0x000000ac + Write-only access to instruction memory location 25 + 0x00000000 + + + INSTR_MEM25 + [15:0] + write-only + + + + + INSTR_MEM26 + 0x000000b0 + Write-only access to instruction memory location 26 + 0x00000000 + + + INSTR_MEM26 + [15:0] + write-only + + + + + INSTR_MEM27 + 0x000000b4 + Write-only access to instruction memory location 27 + 0x00000000 + + + INSTR_MEM27 + [15:0] + write-only + + + + + INSTR_MEM28 + 0x000000b8 + Write-only access to instruction memory location 28 + 0x00000000 + + + INSTR_MEM28 + [15:0] + write-only + + + + + INSTR_MEM29 + 0x000000bc + Write-only access to instruction memory location 29 + 0x00000000 + + + INSTR_MEM29 + [15:0] + write-only + + + + + INSTR_MEM30 + 0x000000c0 + Write-only access to instruction memory location 30 + 0x00000000 + + + INSTR_MEM30 + [15:0] + write-only + + + + + INSTR_MEM31 + 0x000000c4 + Write-only access to instruction memory location 31 + 0x00000000 + + + INSTR_MEM31 + [15:0] + write-only + + + + + SM0_CLKDIV + 0x000000c8 + Clock divisor register for state machine 0 + Frequency = clock freq / (CLKDIV_INT + CLKDIV_FRAC / 256) + 0x00010000 + + + INT + Effective frequency is sysclk/(int + frac/256). + Value of 0 is interpreted as 65536. If INT is 0, FRAC must also be 0. + [31:16] + read-write + + + FRAC + Fractional part of clock divisor + [15:8] + read-write + + + + + SM0_EXECCTRL + 0x000000cc + Execution/behavioural settings for state machine 0 + 0x0001f000 + + + EXEC_STALLED + If 1, an instruction written to SMx_INSTR is stalled, and latched by the state machine. Will clear to 0 once this instruction completes. + [31:31] + read-only + + + SIDE_EN + If 1, the MSB of the Delay/Side-set instruction field is used as side-set enable, rather than a side-set data bit. This allows instructions to perform side-set optionally, rather than on every instruction, but the maximum possible side-set width is reduced from 5 to 4. Note that the value of PINCTRL_SIDESET_COUNT is inclusive of this enable bit. + [30:30] + read-write + + + SIDE_PINDIR + If 1, side-set data is asserted to pin directions, instead of pin values + [29:29] + read-write + + + JMP_PIN + The GPIO number to use as condition for JMP PIN. Unaffected by input mapping. + [28:24] + read-write + + + OUT_EN_SEL + Which data bit to use for inline OUT enable + [23:19] + read-write + + + INLINE_OUT_EN + If 1, use a bit of OUT data as an auxiliary write enable + When used in conjunction with OUT_STICKY, writes with an enable of 0 will + deassert the latest pin write. This can create useful masking/override behaviour + due to the priority ordering of state machine pin writes (SM0 < SM1 < ...) + [18:18] + read-write + + + OUT_STICKY + Continuously assert the most recent OUT/SET to the pins + [17:17] + read-write + + + WRAP_TOP + After reaching this address, execution is wrapped to wrap_bottom. + If the instruction is a jump, and the jump condition is true, the jump takes priority. + [16:12] + read-write + + + WRAP_BOTTOM + After reaching wrap_top, execution is wrapped to this address. + [11:7] + read-write + + + STATUS_SEL + Comparison used for the MOV x, STATUS instruction. + [6:5] + read-write + + + TXLEVEL + 0 + All-ones if TX FIFO level < N, otherwise all-zeroes + + + RXLEVEL + 1 + All-ones if RX FIFO level < N, otherwise all-zeroes + + + IRQ + 2 + All-ones if the indexed IRQ flag is raised, otherwise all-zeroes + + + + + STATUS_N + Comparison level or IRQ index for the MOV x, STATUS instruction. + + If STATUS_SEL is TXLEVEL or RXLEVEL, then values of STATUS_N greater than the current FIFO depth are reserved, and have undefined behaviour. + [4:0] + read-write + + + IRQ + 0 + Index 0-7 of an IRQ flag in this PIO block + + + IRQ_PREVPIO + 8 + Index 0-7 of an IRQ flag in the next lower-numbered PIO block + + + IRQ_NEXTPIO + 16 + Index 0-7 of an IRQ flag in the next higher-numbered PIO block + + + + + + + SM0_SHIFTCTRL + 0x000000d0 + Control behaviour of the input/output shift registers for state machine 0 + 0x000c0000 + + + FJOIN_RX + When 1, RX FIFO steals the TX FIFO's storage, and becomes twice as deep. + TX FIFO is disabled as a result (always reads as both full and empty). + FIFOs are flushed when this bit is changed. + [31:31] + read-write + + + FJOIN_TX + When 1, TX FIFO steals the RX FIFO's storage, and becomes twice as deep. + RX FIFO is disabled as a result (always reads as both full and empty). + FIFOs are flushed when this bit is changed. + [30:30] + read-write + + + PULL_THRESH + Number of bits shifted out of OSR before autopull, or conditional pull (PULL IFEMPTY), will take place. + Write 0 for value of 32. + [29:25] + read-write + + + PUSH_THRESH + Number of bits shifted into ISR before autopush, or conditional push (PUSH IFFULL), will take place. + Write 0 for value of 32. + [24:20] + read-write + + + OUT_SHIFTDIR + 1 = shift out of output shift register to right. 0 = to left. + [19:19] + read-write + + + IN_SHIFTDIR + 1 = shift input shift register to right (data enters from left). 0 = to left. + [18:18] + read-write + + + AUTOPULL + Pull automatically when the output shift register is emptied, i.e. on or following an OUT instruction which causes the output shift counter to reach or exceed PULL_THRESH. + [17:17] + read-write + + + AUTOPUSH + Push automatically when the input shift register is filled, i.e. on an IN instruction which causes the input shift counter to reach or exceed PUSH_THRESH. + [16:16] + read-write + + + FJOIN_RX_PUT + If 1, disable this state machine's RX FIFO, make its storage available for random write access by the state machine (using the `put` instruction) and, unless FJOIN_RX_GET is also set, random read access by the processor (through the RXFx_PUTGETy registers). + + If FJOIN_RX_PUT and FJOIN_RX_GET are both set, then the RX FIFO's registers can be randomly read/written by the state machine, but are completely inaccessible to the processor. + + Setting this bit will clear the FJOIN_TX and FJOIN_RX bits. + [15:15] + read-write + + + FJOIN_RX_GET + If 1, disable this state machine's RX FIFO, make its storage available for random read access by the state machine (using the `get` instruction) and, unless FJOIN_RX_PUT is also set, random write access by the processor (through the RXFx_PUTGETy registers). + + If FJOIN_RX_PUT and FJOIN_RX_GET are both set, then the RX FIFO's registers can be randomly read/written by the state machine, but are completely inaccessible to the processor. + + Setting this bit will clear the FJOIN_TX and FJOIN_RX bits. + [14:14] + read-write + + + IN_COUNT + Set the number of pins which are not masked to 0 when read by an IN PINS, WAIT PIN or MOV x, PINS instruction. + + For example, an IN_COUNT of 5 means that the 5 LSBs of the IN pin group are visible (bits 4:0), but the remaining 27 MSBs are masked to 0. A count of 32 is encoded with a field value of 0, so the default behaviour is to not perform any masking. + + Note this masking is applied in addition to the masking usually performed by the IN instruction. This is mainly useful for the MOV x, PINS instruction, which otherwise has no way of masking pins. + [4:0] + read-write + + + + + SM0_ADDR + 0x000000d4 + Current instruction address of state machine 0 + 0x00000000 + + + SM0_ADDR + [4:0] + read-only + + + + + SM0_INSTR + 0x000000d8 + Read to see the instruction currently addressed by state machine 0's program counter + Write to execute an instruction immediately (including jumps) and then resume execution. + 0x00000000 + + + SM0_INSTR + [15:0] + read-write + + + + + SM0_PINCTRL + 0x000000dc + State machine pin control + 0x14000000 + + + SIDESET_COUNT + The number of MSBs of the Delay/Side-set instruction field which are used for side-set. Inclusive of the enable bit, if present. Minimum of 0 (all delay bits, no side-set) and maximum of 5 (all side-set, no delay). + [31:29] + read-write + + + SET_COUNT + The number of pins asserted by a SET. In the range 0 to 5 inclusive. + [28:26] + read-write + + + OUT_COUNT + The number of pins asserted by an OUT PINS, OUT PINDIRS or MOV PINS instruction. In the range 0 to 32 inclusive. + [25:20] + read-write + + + IN_BASE + The pin which is mapped to the least-significant bit of a state machine's IN data bus. Higher-numbered pins are mapped to consecutively more-significant data bits, with a modulo of 32 applied to pin number. + [19:15] + read-write + + + SIDESET_BASE + The lowest-numbered pin that will be affected by a side-set operation. The MSBs of an instruction's side-set/delay field (up to 5, determined by SIDESET_COUNT) are used for side-set data, with the remaining LSBs used for delay. The least-significant bit of the side-set portion is the bit written to this pin, with more-significant bits written to higher-numbered pins. + [14:10] + read-write + + + SET_BASE + The lowest-numbered pin that will be affected by a SET PINS or SET PINDIRS instruction. The data written to this pin is the least-significant bit of the SET data. + [9:5] + read-write + + + OUT_BASE + The lowest-numbered pin that will be affected by an OUT PINS, OUT PINDIRS or MOV PINS instruction. The data written to this pin will always be the least-significant bit of the OUT or MOV data. + [4:0] + read-write + + + + + SM1_CLKDIV + 0x000000e0 + Clock divisor register for state machine 1 + Frequency = clock freq / (CLKDIV_INT + CLKDIV_FRAC / 256) + 0x00010000 + + + INT + Effective frequency is sysclk/(int + frac/256). + Value of 0 is interpreted as 65536. If INT is 0, FRAC must also be 0. + [31:16] + read-write + + + FRAC + Fractional part of clock divisor + [15:8] + read-write + + + + + SM1_EXECCTRL + 0x000000e4 + Execution/behavioural settings for state machine 1 + 0x0001f000 + + + EXEC_STALLED + If 1, an instruction written to SMx_INSTR is stalled, and latched by the state machine. Will clear to 0 once this instruction completes. + [31:31] + read-only + + + SIDE_EN + If 1, the MSB of the Delay/Side-set instruction field is used as side-set enable, rather than a side-set data bit. This allows instructions to perform side-set optionally, rather than on every instruction, but the maximum possible side-set width is reduced from 5 to 4. Note that the value of PINCTRL_SIDESET_COUNT is inclusive of this enable bit. + [30:30] + read-write + + + SIDE_PINDIR + If 1, side-set data is asserted to pin directions, instead of pin values + [29:29] + read-write + + + JMP_PIN + The GPIO number to use as condition for JMP PIN. Unaffected by input mapping. + [28:24] + read-write + + + OUT_EN_SEL + Which data bit to use for inline OUT enable + [23:19] + read-write + + + INLINE_OUT_EN + If 1, use a bit of OUT data as an auxiliary write enable + When used in conjunction with OUT_STICKY, writes with an enable of 0 will + deassert the latest pin write. This can create useful masking/override behaviour + due to the priority ordering of state machine pin writes (SM0 < SM1 < ...) + [18:18] + read-write + + + OUT_STICKY + Continuously assert the most recent OUT/SET to the pins + [17:17] + read-write + + + WRAP_TOP + After reaching this address, execution is wrapped to wrap_bottom. + If the instruction is a jump, and the jump condition is true, the jump takes priority. + [16:12] + read-write + + + WRAP_BOTTOM + After reaching wrap_top, execution is wrapped to this address. + [11:7] + read-write + + + STATUS_SEL + Comparison used for the MOV x, STATUS instruction. + [6:5] + read-write + + + TXLEVEL + 0 + All-ones if TX FIFO level < N, otherwise all-zeroes + + + RXLEVEL + 1 + All-ones if RX FIFO level < N, otherwise all-zeroes + + + IRQ + 2 + All-ones if the indexed IRQ flag is raised, otherwise all-zeroes + + + + + STATUS_N + Comparison level or IRQ index for the MOV x, STATUS instruction. + + If STATUS_SEL is TXLEVEL or RXLEVEL, then values of STATUS_N greater than the current FIFO depth are reserved, and have undefined behaviour. + [4:0] + read-write + + + IRQ + 0 + Index 0-7 of an IRQ flag in this PIO block + + + IRQ_PREVPIO + 8 + Index 0-7 of an IRQ flag in the next lower-numbered PIO block + + + IRQ_NEXTPIO + 16 + Index 0-7 of an IRQ flag in the next higher-numbered PIO block + + + + + + + SM1_SHIFTCTRL + 0x000000e8 + Control behaviour of the input/output shift registers for state machine 1 + 0x000c0000 + + + FJOIN_RX + When 1, RX FIFO steals the TX FIFO's storage, and becomes twice as deep. + TX FIFO is disabled as a result (always reads as both full and empty). + FIFOs are flushed when this bit is changed. + [31:31] + read-write + + + FJOIN_TX + When 1, TX FIFO steals the RX FIFO's storage, and becomes twice as deep. + RX FIFO is disabled as a result (always reads as both full and empty). + FIFOs are flushed when this bit is changed. + [30:30] + read-write + + + PULL_THRESH + Number of bits shifted out of OSR before autopull, or conditional pull (PULL IFEMPTY), will take place. + Write 0 for value of 32. + [29:25] + read-write + + + PUSH_THRESH + Number of bits shifted into ISR before autopush, or conditional push (PUSH IFFULL), will take place. + Write 0 for value of 32. + [24:20] + read-write + + + OUT_SHIFTDIR + 1 = shift out of output shift register to right. 0 = to left. + [19:19] + read-write + + + IN_SHIFTDIR + 1 = shift input shift register to right (data enters from left). 0 = to left. + [18:18] + read-write + + + AUTOPULL + Pull automatically when the output shift register is emptied, i.e. on or following an OUT instruction which causes the output shift counter to reach or exceed PULL_THRESH. + [17:17] + read-write + + + AUTOPUSH + Push automatically when the input shift register is filled, i.e. on an IN instruction which causes the input shift counter to reach or exceed PUSH_THRESH. + [16:16] + read-write + + + FJOIN_RX_PUT + If 1, disable this state machine's RX FIFO, make its storage available for random write access by the state machine (using the `put` instruction) and, unless FJOIN_RX_GET is also set, random read access by the processor (through the RXFx_PUTGETy registers). + + If FJOIN_RX_PUT and FJOIN_RX_GET are both set, then the RX FIFO's registers can be randomly read/written by the state machine, but are completely inaccessible to the processor. + + Setting this bit will clear the FJOIN_TX and FJOIN_RX bits. + [15:15] + read-write + + + FJOIN_RX_GET + If 1, disable this state machine's RX FIFO, make its storage available for random read access by the state machine (using the `get` instruction) and, unless FJOIN_RX_PUT is also set, random write access by the processor (through the RXFx_PUTGETy registers). + + If FJOIN_RX_PUT and FJOIN_RX_GET are both set, then the RX FIFO's registers can be randomly read/written by the state machine, but are completely inaccessible to the processor. + + Setting this bit will clear the FJOIN_TX and FJOIN_RX bits. + [14:14] + read-write + + + IN_COUNT + Set the number of pins which are not masked to 0 when read by an IN PINS, WAIT PIN or MOV x, PINS instruction. + + For example, an IN_COUNT of 5 means that the 5 LSBs of the IN pin group are visible (bits 4:0), but the remaining 27 MSBs are masked to 0. A count of 32 is encoded with a field value of 0, so the default behaviour is to not perform any masking. + + Note this masking is applied in addition to the masking usually performed by the IN instruction. This is mainly useful for the MOV x, PINS instruction, which otherwise has no way of masking pins. + [4:0] + read-write + + + + + SM1_ADDR + 0x000000ec + Current instruction address of state machine 1 + 0x00000000 + + + SM1_ADDR + [4:0] + read-only + + + + + SM1_INSTR + 0x000000f0 + Read to see the instruction currently addressed by state machine 1's program counter + Write to execute an instruction immediately (including jumps) and then resume execution. + 0x00000000 + + + SM1_INSTR + [15:0] + read-write + + + + + SM1_PINCTRL + 0x000000f4 + State machine pin control + 0x14000000 + + + SIDESET_COUNT + The number of MSBs of the Delay/Side-set instruction field which are used for side-set. Inclusive of the enable bit, if present. Minimum of 0 (all delay bits, no side-set) and maximum of 5 (all side-set, no delay). + [31:29] + read-write + + + SET_COUNT + The number of pins asserted by a SET. In the range 0 to 5 inclusive. + [28:26] + read-write + + + OUT_COUNT + The number of pins asserted by an OUT PINS, OUT PINDIRS or MOV PINS instruction. In the range 0 to 32 inclusive. + [25:20] + read-write + + + IN_BASE + The pin which is mapped to the least-significant bit of a state machine's IN data bus. Higher-numbered pins are mapped to consecutively more-significant data bits, with a modulo of 32 applied to pin number. + [19:15] + read-write + + + SIDESET_BASE + The lowest-numbered pin that will be affected by a side-set operation. The MSBs of an instruction's side-set/delay field (up to 5, determined by SIDESET_COUNT) are used for side-set data, with the remaining LSBs used for delay. The least-significant bit of the side-set portion is the bit written to this pin, with more-significant bits written to higher-numbered pins. + [14:10] + read-write + + + SET_BASE + The lowest-numbered pin that will be affected by a SET PINS or SET PINDIRS instruction. The data written to this pin is the least-significant bit of the SET data. + [9:5] + read-write + + + OUT_BASE + The lowest-numbered pin that will be affected by an OUT PINS, OUT PINDIRS or MOV PINS instruction. The data written to this pin will always be the least-significant bit of the OUT or MOV data. + [4:0] + read-write + + + + + SM2_CLKDIV + 0x000000f8 + Clock divisor register for state machine 2 + Frequency = clock freq / (CLKDIV_INT + CLKDIV_FRAC / 256) + 0x00010000 + + + INT + Effective frequency is sysclk/(int + frac/256). + Value of 0 is interpreted as 65536. If INT is 0, FRAC must also be 0. + [31:16] + read-write + + + FRAC + Fractional part of clock divisor + [15:8] + read-write + + + + + SM2_EXECCTRL + 0x000000fc + Execution/behavioural settings for state machine 2 + 0x0001f000 + + + EXEC_STALLED + If 1, an instruction written to SMx_INSTR is stalled, and latched by the state machine. Will clear to 0 once this instruction completes. + [31:31] + read-only + + + SIDE_EN + If 1, the MSB of the Delay/Side-set instruction field is used as side-set enable, rather than a side-set data bit. This allows instructions to perform side-set optionally, rather than on every instruction, but the maximum possible side-set width is reduced from 5 to 4. Note that the value of PINCTRL_SIDESET_COUNT is inclusive of this enable bit. + [30:30] + read-write + + + SIDE_PINDIR + If 1, side-set data is asserted to pin directions, instead of pin values + [29:29] + read-write + + + JMP_PIN + The GPIO number to use as condition for JMP PIN. Unaffected by input mapping. + [28:24] + read-write + + + OUT_EN_SEL + Which data bit to use for inline OUT enable + [23:19] + read-write + + + INLINE_OUT_EN + If 1, use a bit of OUT data as an auxiliary write enable + When used in conjunction with OUT_STICKY, writes with an enable of 0 will + deassert the latest pin write. This can create useful masking/override behaviour + due to the priority ordering of state machine pin writes (SM0 < SM1 < ...) + [18:18] + read-write + + + OUT_STICKY + Continuously assert the most recent OUT/SET to the pins + [17:17] + read-write + + + WRAP_TOP + After reaching this address, execution is wrapped to wrap_bottom. + If the instruction is a jump, and the jump condition is true, the jump takes priority. + [16:12] + read-write + + + WRAP_BOTTOM + After reaching wrap_top, execution is wrapped to this address. + [11:7] + read-write + + + STATUS_SEL + Comparison used for the MOV x, STATUS instruction. + [6:5] + read-write + + + TXLEVEL + 0 + All-ones if TX FIFO level < N, otherwise all-zeroes + + + RXLEVEL + 1 + All-ones if RX FIFO level < N, otherwise all-zeroes + + + IRQ + 2 + All-ones if the indexed IRQ flag is raised, otherwise all-zeroes + + + + + STATUS_N + Comparison level or IRQ index for the MOV x, STATUS instruction. + + If STATUS_SEL is TXLEVEL or RXLEVEL, then values of STATUS_N greater than the current FIFO depth are reserved, and have undefined behaviour. + [4:0] + read-write + + + IRQ + 0 + Index 0-7 of an IRQ flag in this PIO block + + + IRQ_PREVPIO + 8 + Index 0-7 of an IRQ flag in the next lower-numbered PIO block + + + IRQ_NEXTPIO + 16 + Index 0-7 of an IRQ flag in the next higher-numbered PIO block + + + + + + + SM2_SHIFTCTRL + 0x00000100 + Control behaviour of the input/output shift registers for state machine 2 + 0x000c0000 + + + FJOIN_RX + When 1, RX FIFO steals the TX FIFO's storage, and becomes twice as deep. + TX FIFO is disabled as a result (always reads as both full and empty). + FIFOs are flushed when this bit is changed. + [31:31] + read-write + + + FJOIN_TX + When 1, TX FIFO steals the RX FIFO's storage, and becomes twice as deep. + RX FIFO is disabled as a result (always reads as both full and empty). + FIFOs are flushed when this bit is changed. + [30:30] + read-write + + + PULL_THRESH + Number of bits shifted out of OSR before autopull, or conditional pull (PULL IFEMPTY), will take place. + Write 0 for value of 32. + [29:25] + read-write + + + PUSH_THRESH + Number of bits shifted into ISR before autopush, or conditional push (PUSH IFFULL), will take place. + Write 0 for value of 32. + [24:20] + read-write + + + OUT_SHIFTDIR + 1 = shift out of output shift register to right. 0 = to left. + [19:19] + read-write + + + IN_SHIFTDIR + 1 = shift input shift register to right (data enters from left). 0 = to left. + [18:18] + read-write + + + AUTOPULL + Pull automatically when the output shift register is emptied, i.e. on or following an OUT instruction which causes the output shift counter to reach or exceed PULL_THRESH. + [17:17] + read-write + + + AUTOPUSH + Push automatically when the input shift register is filled, i.e. on an IN instruction which causes the input shift counter to reach or exceed PUSH_THRESH. + [16:16] + read-write + + + FJOIN_RX_PUT + If 1, disable this state machine's RX FIFO, make its storage available for random write access by the state machine (using the `put` instruction) and, unless FJOIN_RX_GET is also set, random read access by the processor (through the RXFx_PUTGETy registers). + + If FJOIN_RX_PUT and FJOIN_RX_GET are both set, then the RX FIFO's registers can be randomly read/written by the state machine, but are completely inaccessible to the processor. + + Setting this bit will clear the FJOIN_TX and FJOIN_RX bits. + [15:15] + read-write + + + FJOIN_RX_GET + If 1, disable this state machine's RX FIFO, make its storage available for random read access by the state machine (using the `get` instruction) and, unless FJOIN_RX_PUT is also set, random write access by the processor (through the RXFx_PUTGETy registers). + + If FJOIN_RX_PUT and FJOIN_RX_GET are both set, then the RX FIFO's registers can be randomly read/written by the state machine, but are completely inaccessible to the processor. + + Setting this bit will clear the FJOIN_TX and FJOIN_RX bits. + [14:14] + read-write + + + IN_COUNT + Set the number of pins which are not masked to 0 when read by an IN PINS, WAIT PIN or MOV x, PINS instruction. + + For example, an IN_COUNT of 5 means that the 5 LSBs of the IN pin group are visible (bits 4:0), but the remaining 27 MSBs are masked to 0. A count of 32 is encoded with a field value of 0, so the default behaviour is to not perform any masking. + + Note this masking is applied in addition to the masking usually performed by the IN instruction. This is mainly useful for the MOV x, PINS instruction, which otherwise has no way of masking pins. + [4:0] + read-write + + + + + SM2_ADDR + 0x00000104 + Current instruction address of state machine 2 + 0x00000000 + + + SM2_ADDR + [4:0] + read-only + + + + + SM2_INSTR + 0x00000108 + Read to see the instruction currently addressed by state machine 2's program counter + Write to execute an instruction immediately (including jumps) and then resume execution. + 0x00000000 + + + SM2_INSTR + [15:0] + read-write + + + + + SM2_PINCTRL + 0x0000010c + State machine pin control + 0x14000000 + + + SIDESET_COUNT + The number of MSBs of the Delay/Side-set instruction field which are used for side-set. Inclusive of the enable bit, if present. Minimum of 0 (all delay bits, no side-set) and maximum of 5 (all side-set, no delay). + [31:29] + read-write + + + SET_COUNT + The number of pins asserted by a SET. In the range 0 to 5 inclusive. + [28:26] + read-write + + + OUT_COUNT + The number of pins asserted by an OUT PINS, OUT PINDIRS or MOV PINS instruction. In the range 0 to 32 inclusive. + [25:20] + read-write + + + IN_BASE + The pin which is mapped to the least-significant bit of a state machine's IN data bus. Higher-numbered pins are mapped to consecutively more-significant data bits, with a modulo of 32 applied to pin number. + [19:15] + read-write + + + SIDESET_BASE + The lowest-numbered pin that will be affected by a side-set operation. The MSBs of an instruction's side-set/delay field (up to 5, determined by SIDESET_COUNT) are used for side-set data, with the remaining LSBs used for delay. The least-significant bit of the side-set portion is the bit written to this pin, with more-significant bits written to higher-numbered pins. + [14:10] + read-write + + + SET_BASE + The lowest-numbered pin that will be affected by a SET PINS or SET PINDIRS instruction. The data written to this pin is the least-significant bit of the SET data. + [9:5] + read-write + + + OUT_BASE + The lowest-numbered pin that will be affected by an OUT PINS, OUT PINDIRS or MOV PINS instruction. The data written to this pin will always be the least-significant bit of the OUT or MOV data. + [4:0] + read-write + + + + + SM3_CLKDIV + 0x00000110 + Clock divisor register for state machine 3 + Frequency = clock freq / (CLKDIV_INT + CLKDIV_FRAC / 256) + 0x00010000 + + + INT + Effective frequency is sysclk/(int + frac/256). + Value of 0 is interpreted as 65536. If INT is 0, FRAC must also be 0. + [31:16] + read-write + + + FRAC + Fractional part of clock divisor + [15:8] + read-write + + + + + SM3_EXECCTRL + 0x00000114 + Execution/behavioural settings for state machine 3 + 0x0001f000 + + + EXEC_STALLED + If 1, an instruction written to SMx_INSTR is stalled, and latched by the state machine. Will clear to 0 once this instruction completes. + [31:31] + read-only + + + SIDE_EN + If 1, the MSB of the Delay/Side-set instruction field is used as side-set enable, rather than a side-set data bit. This allows instructions to perform side-set optionally, rather than on every instruction, but the maximum possible side-set width is reduced from 5 to 4. Note that the value of PINCTRL_SIDESET_COUNT is inclusive of this enable bit. + [30:30] + read-write + + + SIDE_PINDIR + If 1, side-set data is asserted to pin directions, instead of pin values + [29:29] + read-write + + + JMP_PIN + The GPIO number to use as condition for JMP PIN. Unaffected by input mapping. + [28:24] + read-write + + + OUT_EN_SEL + Which data bit to use for inline OUT enable + [23:19] + read-write + + + INLINE_OUT_EN + If 1, use a bit of OUT data as an auxiliary write enable + When used in conjunction with OUT_STICKY, writes with an enable of 0 will + deassert the latest pin write. This can create useful masking/override behaviour + due to the priority ordering of state machine pin writes (SM0 < SM1 < ...) + [18:18] + read-write + + + OUT_STICKY + Continuously assert the most recent OUT/SET to the pins + [17:17] + read-write + + + WRAP_TOP + After reaching this address, execution is wrapped to wrap_bottom. + If the instruction is a jump, and the jump condition is true, the jump takes priority. + [16:12] + read-write + + + WRAP_BOTTOM + After reaching wrap_top, execution is wrapped to this address. + [11:7] + read-write + + + STATUS_SEL + Comparison used for the MOV x, STATUS instruction. + [6:5] + read-write + + + TXLEVEL + 0 + All-ones if TX FIFO level < N, otherwise all-zeroes + + + RXLEVEL + 1 + All-ones if RX FIFO level < N, otherwise all-zeroes + + + IRQ + 2 + All-ones if the indexed IRQ flag is raised, otherwise all-zeroes + + + + + STATUS_N + Comparison level or IRQ index for the MOV x, STATUS instruction. + + If STATUS_SEL is TXLEVEL or RXLEVEL, then values of STATUS_N greater than the current FIFO depth are reserved, and have undefined behaviour. + [4:0] + read-write + + + IRQ + 0 + Index 0-7 of an IRQ flag in this PIO block + + + IRQ_PREVPIO + 8 + Index 0-7 of an IRQ flag in the next lower-numbered PIO block + + + IRQ_NEXTPIO + 16 + Index 0-7 of an IRQ flag in the next higher-numbered PIO block + + + + + + + SM3_SHIFTCTRL + 0x00000118 + Control behaviour of the input/output shift registers for state machine 3 + 0x000c0000 + + + FJOIN_RX + When 1, RX FIFO steals the TX FIFO's storage, and becomes twice as deep. + TX FIFO is disabled as a result (always reads as both full and empty). + FIFOs are flushed when this bit is changed. + [31:31] + read-write + + + FJOIN_TX + When 1, TX FIFO steals the RX FIFO's storage, and becomes twice as deep. + RX FIFO is disabled as a result (always reads as both full and empty). + FIFOs are flushed when this bit is changed. + [30:30] + read-write + + + PULL_THRESH + Number of bits shifted out of OSR before autopull, or conditional pull (PULL IFEMPTY), will take place. + Write 0 for value of 32. + [29:25] + read-write + + + PUSH_THRESH + Number of bits shifted into ISR before autopush, or conditional push (PUSH IFFULL), will take place. + Write 0 for value of 32. + [24:20] + read-write + + + OUT_SHIFTDIR + 1 = shift out of output shift register to right. 0 = to left. + [19:19] + read-write + + + IN_SHIFTDIR + 1 = shift input shift register to right (data enters from left). 0 = to left. + [18:18] + read-write + + + AUTOPULL + Pull automatically when the output shift register is emptied, i.e. on or following an OUT instruction which causes the output shift counter to reach or exceed PULL_THRESH. + [17:17] + read-write + + + AUTOPUSH + Push automatically when the input shift register is filled, i.e. on an IN instruction which causes the input shift counter to reach or exceed PUSH_THRESH. + [16:16] + read-write + + + FJOIN_RX_PUT + If 1, disable this state machine's RX FIFO, make its storage available for random write access by the state machine (using the `put` instruction) and, unless FJOIN_RX_GET is also set, random read access by the processor (through the RXFx_PUTGETy registers). + + If FJOIN_RX_PUT and FJOIN_RX_GET are both set, then the RX FIFO's registers can be randomly read/written by the state machine, but are completely inaccessible to the processor. + + Setting this bit will clear the FJOIN_TX and FJOIN_RX bits. + [15:15] + read-write + + + FJOIN_RX_GET + If 1, disable this state machine's RX FIFO, make its storage available for random read access by the state machine (using the `get` instruction) and, unless FJOIN_RX_PUT is also set, random write access by the processor (through the RXFx_PUTGETy registers). + + If FJOIN_RX_PUT and FJOIN_RX_GET are both set, then the RX FIFO's registers can be randomly read/written by the state machine, but are completely inaccessible to the processor. + + Setting this bit will clear the FJOIN_TX and FJOIN_RX bits. + [14:14] + read-write + + + IN_COUNT + Set the number of pins which are not masked to 0 when read by an IN PINS, WAIT PIN or MOV x, PINS instruction. + + For example, an IN_COUNT of 5 means that the 5 LSBs of the IN pin group are visible (bits 4:0), but the remaining 27 MSBs are masked to 0. A count of 32 is encoded with a field value of 0, so the default behaviour is to not perform any masking. + + Note this masking is applied in addition to the masking usually performed by the IN instruction. This is mainly useful for the MOV x, PINS instruction, which otherwise has no way of masking pins. + [4:0] + read-write + + + + + SM3_ADDR + 0x0000011c + Current instruction address of state machine 3 + 0x00000000 + + + SM3_ADDR + [4:0] + read-only + + + + + SM3_INSTR + 0x00000120 + Read to see the instruction currently addressed by state machine 3's program counter + Write to execute an instruction immediately (including jumps) and then resume execution. + 0x00000000 + + + SM3_INSTR + [15:0] + read-write + + + + + SM3_PINCTRL + 0x00000124 + State machine pin control + 0x14000000 + + + SIDESET_COUNT + The number of MSBs of the Delay/Side-set instruction field which are used for side-set. Inclusive of the enable bit, if present. Minimum of 0 (all delay bits, no side-set) and maximum of 5 (all side-set, no delay). + [31:29] + read-write + + + SET_COUNT + The number of pins asserted by a SET. In the range 0 to 5 inclusive. + [28:26] + read-write + + + OUT_COUNT + The number of pins asserted by an OUT PINS, OUT PINDIRS or MOV PINS instruction. In the range 0 to 32 inclusive. + [25:20] + read-write + + + IN_BASE + The pin which is mapped to the least-significant bit of a state machine's IN data bus. Higher-numbered pins are mapped to consecutively more-significant data bits, with a modulo of 32 applied to pin number. + [19:15] + read-write + + + SIDESET_BASE + The lowest-numbered pin that will be affected by a side-set operation. The MSBs of an instruction's side-set/delay field (up to 5, determined by SIDESET_COUNT) are used for side-set data, with the remaining LSBs used for delay. The least-significant bit of the side-set portion is the bit written to this pin, with more-significant bits written to higher-numbered pins. + [14:10] + read-write + + + SET_BASE + The lowest-numbered pin that will be affected by a SET PINS or SET PINDIRS instruction. The data written to this pin is the least-significant bit of the SET data. + [9:5] + read-write + + + OUT_BASE + The lowest-numbered pin that will be affected by an OUT PINS, OUT PINDIRS or MOV PINS instruction. The data written to this pin will always be the least-significant bit of the OUT or MOV data. + [4:0] + read-write + + + + + RXF0_PUTGET0 + 0x00000128 + Direct read/write access to entry 0 of SM0's RX FIFO, if SHIFTCTRL_FJOIN_RX_PUT xor SHIFTCTRL_FJOIN_RX_GET is set. + 0x00000000 + + + RXF0_PUTGET0 + [31:0] + read-write + + + + + RXF0_PUTGET1 + 0x0000012c + Direct read/write access to entry 1 of SM0's RX FIFO, if SHIFTCTRL_FJOIN_RX_PUT xor SHIFTCTRL_FJOIN_RX_GET is set. + 0x00000000 + + + RXF0_PUTGET1 + [31:0] + read-write + + + + + RXF0_PUTGET2 + 0x00000130 + Direct read/write access to entry 2 of SM0's RX FIFO, if SHIFTCTRL_FJOIN_RX_PUT xor SHIFTCTRL_FJOIN_RX_GET is set. + 0x00000000 + + + RXF0_PUTGET2 + [31:0] + read-write + + + + + RXF0_PUTGET3 + 0x00000134 + Direct read/write access to entry 3 of SM0's RX FIFO, if SHIFTCTRL_FJOIN_RX_PUT xor SHIFTCTRL_FJOIN_RX_GET is set. + 0x00000000 + + + RXF0_PUTGET3 + [31:0] + read-write + + + + + RXF1_PUTGET0 + 0x00000138 + Direct read/write access to entry 0 of SM1's RX FIFO, if SHIFTCTRL_FJOIN_RX_PUT xor SHIFTCTRL_FJOIN_RX_GET is set. + 0x00000000 + + + RXF1_PUTGET0 + [31:0] + read-write + + + + + RXF1_PUTGET1 + 0x0000013c + Direct read/write access to entry 1 of SM1's RX FIFO, if SHIFTCTRL_FJOIN_RX_PUT xor SHIFTCTRL_FJOIN_RX_GET is set. + 0x00000000 + + + RXF1_PUTGET1 + [31:0] + read-write + + + + + RXF1_PUTGET2 + 0x00000140 + Direct read/write access to entry 2 of SM1's RX FIFO, if SHIFTCTRL_FJOIN_RX_PUT xor SHIFTCTRL_FJOIN_RX_GET is set. + 0x00000000 + + + RXF1_PUTGET2 + [31:0] + read-write + + + + + RXF1_PUTGET3 + 0x00000144 + Direct read/write access to entry 3 of SM1's RX FIFO, if SHIFTCTRL_FJOIN_RX_PUT xor SHIFTCTRL_FJOIN_RX_GET is set. + 0x00000000 + + + RXF1_PUTGET3 + [31:0] + read-write + + + + + RXF2_PUTGET0 + 0x00000148 + Direct read/write access to entry 0 of SM2's RX FIFO, if SHIFTCTRL_FJOIN_RX_PUT xor SHIFTCTRL_FJOIN_RX_GET is set. + 0x00000000 + + + RXF2_PUTGET0 + [31:0] + read-write + + + + + RXF2_PUTGET1 + 0x0000014c + Direct read/write access to entry 1 of SM2's RX FIFO, if SHIFTCTRL_FJOIN_RX_PUT xor SHIFTCTRL_FJOIN_RX_GET is set. + 0x00000000 + + + RXF2_PUTGET1 + [31:0] + read-write + + + + + RXF2_PUTGET2 + 0x00000150 + Direct read/write access to entry 2 of SM2's RX FIFO, if SHIFTCTRL_FJOIN_RX_PUT xor SHIFTCTRL_FJOIN_RX_GET is set. + 0x00000000 + + + RXF2_PUTGET2 + [31:0] + read-write + + + + + RXF2_PUTGET3 + 0x00000154 + Direct read/write access to entry 3 of SM2's RX FIFO, if SHIFTCTRL_FJOIN_RX_PUT xor SHIFTCTRL_FJOIN_RX_GET is set. + 0x00000000 + + + RXF2_PUTGET3 + [31:0] + read-write + + + + + RXF3_PUTGET0 + 0x00000158 + Direct read/write access to entry 0 of SM3's RX FIFO, if SHIFTCTRL_FJOIN_RX_PUT xor SHIFTCTRL_FJOIN_RX_GET is set. + 0x00000000 + + + RXF3_PUTGET0 + [31:0] + read-write + + + + + RXF3_PUTGET1 + 0x0000015c + Direct read/write access to entry 1 of SM3's RX FIFO, if SHIFTCTRL_FJOIN_RX_PUT xor SHIFTCTRL_FJOIN_RX_GET is set. + 0x00000000 + + + RXF3_PUTGET1 + [31:0] + read-write + + + + + RXF3_PUTGET2 + 0x00000160 + Direct read/write access to entry 2 of SM3's RX FIFO, if SHIFTCTRL_FJOIN_RX_PUT xor SHIFTCTRL_FJOIN_RX_GET is set. + 0x00000000 + + + RXF3_PUTGET2 + [31:0] + read-write + + + + + RXF3_PUTGET3 + 0x00000164 + Direct read/write access to entry 3 of SM3's RX FIFO, if SHIFTCTRL_FJOIN_RX_PUT xor SHIFTCTRL_FJOIN_RX_GET is set. + 0x00000000 + + + RXF3_PUTGET3 + [31:0] + read-write + + + + + GPIOBASE + 0x00000168 + Relocate GPIO 0 (from PIO's point of view) in the system GPIO numbering, to access more than 32 GPIOs from PIO. + + Only the values 0 and 16 are supported (only bit 4 is writable). + 0x00000000 + + + GPIOBASE + [4:4] + read-write + + + + + INTR + 0x0000016c + Raw Interrupts + 0x00000000 + + + SM7 + [15:15] + read-only + + + SM6 + [14:14] + read-only + + + SM5 + [13:13] + read-only + + + SM4 + [12:12] + read-only + + + SM3 + [11:11] + read-only + + + SM2 + [10:10] + read-only + + + SM1 + [9:9] + read-only + + + SM0 + [8:8] + read-only + + + SM3_TXNFULL + [7:7] + read-only + + + SM2_TXNFULL + [6:6] + read-only + + + SM1_TXNFULL + [5:5] + read-only + + + SM0_TXNFULL + [4:4] + read-only + + + SM3_RXNEMPTY + [3:3] + read-only + + + SM2_RXNEMPTY + [2:2] + read-only + + + SM1_RXNEMPTY + [1:1] + read-only + + + SM0_RXNEMPTY + [0:0] + read-only + + + + + IRQ0_INTE + 0x00000170 + Interrupt Enable for irq0 + 0x00000000 + + + SM7 + [15:15] + read-write + + + SM6 + [14:14] + read-write + + + SM5 + [13:13] + read-write + + + SM4 + [12:12] + read-write + + + SM3 + [11:11] + read-write + + + SM2 + [10:10] + read-write + + + SM1 + [9:9] + read-write + + + SM0 + [8:8] + read-write + + + SM3_TXNFULL + [7:7] + read-write + + + SM2_TXNFULL + [6:6] + read-write + + + SM1_TXNFULL + [5:5] + read-write + + + SM0_TXNFULL + [4:4] + read-write + + + SM3_RXNEMPTY + [3:3] + read-write + + + SM2_RXNEMPTY + [2:2] + read-write + + + SM1_RXNEMPTY + [1:1] + read-write + + + SM0_RXNEMPTY + [0:0] + read-write + + + + + IRQ0_INTF + 0x00000174 + Interrupt Force for irq0 + 0x00000000 + + + SM7 + [15:15] + read-write + + + SM6 + [14:14] + read-write + + + SM5 + [13:13] + read-write + + + SM4 + [12:12] + read-write + + + SM3 + [11:11] + read-write + + + SM2 + [10:10] + read-write + + + SM1 + [9:9] + read-write + + + SM0 + [8:8] + read-write + + + SM3_TXNFULL + [7:7] + read-write + + + SM2_TXNFULL + [6:6] + read-write + + + SM1_TXNFULL + [5:5] + read-write + + + SM0_TXNFULL + [4:4] + read-write + + + SM3_RXNEMPTY + [3:3] + read-write + + + SM2_RXNEMPTY + [2:2] + read-write + + + SM1_RXNEMPTY + [1:1] + read-write + + + SM0_RXNEMPTY + [0:0] + read-write + + + + + IRQ0_INTS + 0x00000178 + Interrupt status after masking & forcing for irq0 + 0x00000000 + + + SM7 + [15:15] + read-only + + + SM6 + [14:14] + read-only + + + SM5 + [13:13] + read-only + + + SM4 + [12:12] + read-only + + + SM3 + [11:11] + read-only + + + SM2 + [10:10] + read-only + + + SM1 + [9:9] + read-only + + + SM0 + [8:8] + read-only + + + SM3_TXNFULL + [7:7] + read-only + + + SM2_TXNFULL + [6:6] + read-only + + + SM1_TXNFULL + [5:5] + read-only + + + SM0_TXNFULL + [4:4] + read-only + + + SM3_RXNEMPTY + [3:3] + read-only + + + SM2_RXNEMPTY + [2:2] + read-only + + + SM1_RXNEMPTY + [1:1] + read-only + + + SM0_RXNEMPTY + [0:0] + read-only + + + + + IRQ1_INTE + 0x0000017c + Interrupt Enable for irq1 + 0x00000000 + + + SM7 + [15:15] + read-write + + + SM6 + [14:14] + read-write + + + SM5 + [13:13] + read-write + + + SM4 + [12:12] + read-write + + + SM3 + [11:11] + read-write + + + SM2 + [10:10] + read-write + + + SM1 + [9:9] + read-write + + + SM0 + [8:8] + read-write + + + SM3_TXNFULL + [7:7] + read-write + + + SM2_TXNFULL + [6:6] + read-write + + + SM1_TXNFULL + [5:5] + read-write + + + SM0_TXNFULL + [4:4] + read-write + + + SM3_RXNEMPTY + [3:3] + read-write + + + SM2_RXNEMPTY + [2:2] + read-write + + + SM1_RXNEMPTY + [1:1] + read-write + + + SM0_RXNEMPTY + [0:0] + read-write + + + + + IRQ1_INTF + 0x00000180 + Interrupt Force for irq1 + 0x00000000 + + + SM7 + [15:15] + read-write + + + SM6 + [14:14] + read-write + + + SM5 + [13:13] + read-write + + + SM4 + [12:12] + read-write + + + SM3 + [11:11] + read-write + + + SM2 + [10:10] + read-write + + + SM1 + [9:9] + read-write + + + SM0 + [8:8] + read-write + + + SM3_TXNFULL + [7:7] + read-write + + + SM2_TXNFULL + [6:6] + read-write + + + SM1_TXNFULL + [5:5] + read-write + + + SM0_TXNFULL + [4:4] + read-write + + + SM3_RXNEMPTY + [3:3] + read-write + + + SM2_RXNEMPTY + [2:2] + read-write + + + SM1_RXNEMPTY + [1:1] + read-write + + + SM0_RXNEMPTY + [0:0] + read-write + + + + + IRQ1_INTS + 0x00000184 + Interrupt status after masking & forcing for irq1 + 0x00000000 + + + SM7 + [15:15] + read-only + + + SM6 + [14:14] + read-only + + + SM5 + [13:13] + read-only + + + SM4 + [12:12] + read-only + + + SM3 + [11:11] + read-only + + + SM2 + [10:10] + read-only + + + SM1 + [9:9] + read-only + + + SM0 + [8:8] + read-only + + + SM3_TXNFULL + [7:7] + read-only + + + SM2_TXNFULL + [6:6] + read-only + + + SM1_TXNFULL + [5:5] + read-only + + + SM0_TXNFULL + [4:4] + read-only + + + SM3_RXNEMPTY + [3:3] + read-only + + + SM2_RXNEMPTY + [2:2] + read-only + + + SM1_RXNEMPTY + [1:1] + read-only + + + SM0_RXNEMPTY + [0:0] + read-only + + + + + + + PIO1 + 0x50300000 + + PIO1_IRQ_0 + 17 + + + PIO1_IRQ_1 + 18 + + + + PIO2 + 0x50400000 + + PIO2_IRQ_0 + 19 + + + PIO2_IRQ_1 + 20 + + + + BUSCTRL + Register block for busfabric control signals and performance counters + 0x40068000 + + 0 + 44 + registers + + + + BUS_PRIORITY + 0x00000000 + Set the priority of each master for bus arbitration. + 0x00000000 + + + DMA_W + 0 - low priority, 1 - high priority + [12:12] + read-write + + + DMA_R + 0 - low priority, 1 - high priority + [8:8] + read-write + + + PROC1 + 0 - low priority, 1 - high priority + [4:4] + read-write + + + PROC0 + 0 - low priority, 1 - high priority + [0:0] + read-write + + + + + BUS_PRIORITY_ACK + 0x00000004 + Bus priority acknowledge + 0x00000000 + + + BUS_PRIORITY_ACK + Goes to 1 once all arbiters have registered the new global priority levels. + Arbiters update their local priority when servicing a new nonsequential access. + In normal circumstances this will happen almost immediately. + [0:0] + read-only + + + + + PERFCTR_EN + 0x00000008 + Enable the performance counters. If 0, the performance counters do not increment. This can be used to precisely start/stop event sampling around the profiled section of code. + + The performance counters are initially disabled, to save energy. + 0x00000000 + + + PERFCTR_EN + [0:0] + read-write + + + + + PERFCTR0 + 0x0000000c + Bus fabric performance counter 0 + 0x00000000 + + + PERFCTR0 + Busfabric saturating performance counter 0 + Count some event signal from the busfabric arbiters, if PERFCTR_EN is set. + Write any value to clear. Select an event to count using PERFSEL0 + [23:0] + read-write + oneToClear + + + + + PERFSEL0 + 0x00000010 + Bus fabric performance event select for PERFCTR0 + 0x0000001f + + + PERFSEL0 + Select an event for PERFCTR0. For each downstream port of the main crossbar, four events are available: ACCESS, an access took place; ACCESS_CONTESTED, an access took place that previously stalled due to contention from other masters; STALL_DOWNSTREAM, count cycles where any master stalled due to a stall on the downstream bus; STALL_UPSTREAM, count cycles where any master stalled for any reason, including contention from other masters. + [6:0] + read-write + + + siob_proc1_stall_upstream + 0 + + + siob_proc1_stall_downstream + 1 + + + siob_proc1_access_contested + 2 + + + siob_proc1_access + 3 + + + siob_proc0_stall_upstream + 4 + + + siob_proc0_stall_downstream + 5 + + + siob_proc0_access_contested + 6 + + + siob_proc0_access + 7 + + + apb_stall_upstream + 8 + + + apb_stall_downstream + 9 + + + apb_access_contested + 10 + + + apb_access + 11 + + + fastperi_stall_upstream + 12 + + + fastperi_stall_downstream + 13 + + + fastperi_access_contested + 14 + + + fastperi_access + 15 + + + sram9_stall_upstream + 16 + + + sram9_stall_downstream + 17 + + + sram9_access_contested + 18 + + + sram9_access + 19 + + + sram8_stall_upstream + 20 + + + sram8_stall_downstream + 21 + + + sram8_access_contested + 22 + + + sram8_access + 23 + + + sram7_stall_upstream + 24 + + + sram7_stall_downstream + 25 + + + sram7_access_contested + 26 + + + sram7_access + 27 + + + sram6_stall_upstream + 28 + + + sram6_stall_downstream + 29 + + + sram6_access_contested + 30 + + + sram6_access + 31 + + + sram5_stall_upstream + 32 + + + sram5_stall_downstream + 33 + + + sram5_access_contested + 34 + + + sram5_access + 35 + + + sram4_stall_upstream + 36 + + + sram4_stall_downstream + 37 + + + sram4_access_contested + 38 + + + sram4_access + 39 + + + sram3_stall_upstream + 40 + + + sram3_stall_downstream + 41 + + + sram3_access_contested + 42 + + + sram3_access + 43 + + + sram2_stall_upstream + 44 + + + sram2_stall_downstream + 45 + + + sram2_access_contested + 46 + + + sram2_access + 47 + + + sram1_stall_upstream + 48 + + + sram1_stall_downstream + 49 + + + sram1_access_contested + 50 + + + sram1_access + 51 + + + sram0_stall_upstream + 52 + + + sram0_stall_downstream + 53 + + + sram0_access_contested + 54 + + + sram0_access + 55 + + + xip_main1_stall_upstream + 56 + + + xip_main1_stall_downstream + 57 + + + xip_main1_access_contested + 58 + + + xip_main1_access + 59 + + + xip_main0_stall_upstream + 60 + + + xip_main0_stall_downstream + 61 + + + xip_main0_access_contested + 62 + + + xip_main0_access + 63 + + + rom_stall_upstream + 64 + + + rom_stall_downstream + 65 + + + rom_access_contested + 66 + + + rom_access + 67 + + + + + + + PERFCTR1 + 0x00000014 + Bus fabric performance counter 1 + 0x00000000 + + + PERFCTR1 + Busfabric saturating performance counter 1 + Count some event signal from the busfabric arbiters, if PERFCTR_EN is set. + Write any value to clear. Select an event to count using PERFSEL1 + [23:0] + read-write + oneToClear + + + + + PERFSEL1 + 0x00000018 + Bus fabric performance event select for PERFCTR1 + 0x0000001f + + + PERFSEL1 + Select an event for PERFCTR1. For each downstream port of the main crossbar, four events are available: ACCESS, an access took place; ACCESS_CONTESTED, an access took place that previously stalled due to contention from other masters; STALL_DOWNSTREAM, count cycles where any master stalled due to a stall on the downstream bus; STALL_UPSTREAM, count cycles where any master stalled for any reason, including contention from other masters. + [6:0] + read-write + + + siob_proc1_stall_upstream + 0 + + + siob_proc1_stall_downstream + 1 + + + siob_proc1_access_contested + 2 + + + siob_proc1_access + 3 + + + siob_proc0_stall_upstream + 4 + + + siob_proc0_stall_downstream + 5 + + + siob_proc0_access_contested + 6 + + + siob_proc0_access + 7 + + + apb_stall_upstream + 8 + + + apb_stall_downstream + 9 + + + apb_access_contested + 10 + + + apb_access + 11 + + + fastperi_stall_upstream + 12 + + + fastperi_stall_downstream + 13 + + + fastperi_access_contested + 14 + + + fastperi_access + 15 + + + sram9_stall_upstream + 16 + + + sram9_stall_downstream + 17 + + + sram9_access_contested + 18 + + + sram9_access + 19 + + + sram8_stall_upstream + 20 + + + sram8_stall_downstream + 21 + + + sram8_access_contested + 22 + + + sram8_access + 23 + + + sram7_stall_upstream + 24 + + + sram7_stall_downstream + 25 + + + sram7_access_contested + 26 + + + sram7_access + 27 + + + sram6_stall_upstream + 28 + + + sram6_stall_downstream + 29 + + + sram6_access_contested + 30 + + + sram6_access + 31 + + + sram5_stall_upstream + 32 + + + sram5_stall_downstream + 33 + + + sram5_access_contested + 34 + + + sram5_access + 35 + + + sram4_stall_upstream + 36 + + + sram4_stall_downstream + 37 + + + sram4_access_contested + 38 + + + sram4_access + 39 + + + sram3_stall_upstream + 40 + + + sram3_stall_downstream + 41 + + + sram3_access_contested + 42 + + + sram3_access + 43 + + + sram2_stall_upstream + 44 + + + sram2_stall_downstream + 45 + + + sram2_access_contested + 46 + + + sram2_access + 47 + + + sram1_stall_upstream + 48 + + + sram1_stall_downstream + 49 + + + sram1_access_contested + 50 + + + sram1_access + 51 + + + sram0_stall_upstream + 52 + + + sram0_stall_downstream + 53 + + + sram0_access_contested + 54 + + + sram0_access + 55 + + + xip_main1_stall_upstream + 56 + + + xip_main1_stall_downstream + 57 + + + xip_main1_access_contested + 58 + + + xip_main1_access + 59 + + + xip_main0_stall_upstream + 60 + + + xip_main0_stall_downstream + 61 + + + xip_main0_access_contested + 62 + + + xip_main0_access + 63 + + + rom_stall_upstream + 64 + + + rom_stall_downstream + 65 + + + rom_access_contested + 66 + + + rom_access + 67 + + + + + + + PERFCTR2 + 0x0000001c + Bus fabric performance counter 2 + 0x00000000 + + + PERFCTR2 + Busfabric saturating performance counter 2 + Count some event signal from the busfabric arbiters, if PERFCTR_EN is set. + Write any value to clear. Select an event to count using PERFSEL2 + [23:0] + read-write + oneToClear + + + + + PERFSEL2 + 0x00000020 + Bus fabric performance event select for PERFCTR2 + 0x0000001f + + + PERFSEL2 + Select an event for PERFCTR2. For each downstream port of the main crossbar, four events are available: ACCESS, an access took place; ACCESS_CONTESTED, an access took place that previously stalled due to contention from other masters; STALL_DOWNSTREAM, count cycles where any master stalled due to a stall on the downstream bus; STALL_UPSTREAM, count cycles where any master stalled for any reason, including contention from other masters. + [6:0] + read-write + + + siob_proc1_stall_upstream + 0 + + + siob_proc1_stall_downstream + 1 + + + siob_proc1_access_contested + 2 + + + siob_proc1_access + 3 + + + siob_proc0_stall_upstream + 4 + + + siob_proc0_stall_downstream + 5 + + + siob_proc0_access_contested + 6 + + + siob_proc0_access + 7 + + + apb_stall_upstream + 8 + + + apb_stall_downstream + 9 + + + apb_access_contested + 10 + + + apb_access + 11 + + + fastperi_stall_upstream + 12 + + + fastperi_stall_downstream + 13 + + + fastperi_access_contested + 14 + + + fastperi_access + 15 + + + sram9_stall_upstream + 16 + + + sram9_stall_downstream + 17 + + + sram9_access_contested + 18 + + + sram9_access + 19 + + + sram8_stall_upstream + 20 + + + sram8_stall_downstream + 21 + + + sram8_access_contested + 22 + + + sram8_access + 23 + + + sram7_stall_upstream + 24 + + + sram7_stall_downstream + 25 + + + sram7_access_contested + 26 + + + sram7_access + 27 + + + sram6_stall_upstream + 28 + + + sram6_stall_downstream + 29 + + + sram6_access_contested + 30 + + + sram6_access + 31 + + + sram5_stall_upstream + 32 + + + sram5_stall_downstream + 33 + + + sram5_access_contested + 34 + + + sram5_access + 35 + + + sram4_stall_upstream + 36 + + + sram4_stall_downstream + 37 + + + sram4_access_contested + 38 + + + sram4_access + 39 + + + sram3_stall_upstream + 40 + + + sram3_stall_downstream + 41 + + + sram3_access_contested + 42 + + + sram3_access + 43 + + + sram2_stall_upstream + 44 + + + sram2_stall_downstream + 45 + + + sram2_access_contested + 46 + + + sram2_access + 47 + + + sram1_stall_upstream + 48 + + + sram1_stall_downstream + 49 + + + sram1_access_contested + 50 + + + sram1_access + 51 + + + sram0_stall_upstream + 52 + + + sram0_stall_downstream + 53 + + + sram0_access_contested + 54 + + + sram0_access + 55 + + + xip_main1_stall_upstream + 56 + + + xip_main1_stall_downstream + 57 + + + xip_main1_access_contested + 58 + + + xip_main1_access + 59 + + + xip_main0_stall_upstream + 60 + + + xip_main0_stall_downstream + 61 + + + xip_main0_access_contested + 62 + + + xip_main0_access + 63 + + + rom_stall_upstream + 64 + + + rom_stall_downstream + 65 + + + rom_access_contested + 66 + + + rom_access + 67 + + + + + + + PERFCTR3 + 0x00000024 + Bus fabric performance counter 3 + 0x00000000 + + + PERFCTR3 + Busfabric saturating performance counter 3 + Count some event signal from the busfabric arbiters, if PERFCTR_EN is set. + Write any value to clear. Select an event to count using PERFSEL3 + [23:0] + read-write + oneToClear + + + + + PERFSEL3 + 0x00000028 + Bus fabric performance event select for PERFCTR3 + 0x0000001f + + + PERFSEL3 + Select an event for PERFCTR3. For each downstream port of the main crossbar, four events are available: ACCESS, an access took place; ACCESS_CONTESTED, an access took place that previously stalled due to contention from other masters; STALL_DOWNSTREAM, count cycles where any master stalled due to a stall on the downstream bus; STALL_UPSTREAM, count cycles where any master stalled for any reason, including contention from other masters. + [6:0] + read-write + + + siob_proc1_stall_upstream + 0 + + + siob_proc1_stall_downstream + 1 + + + siob_proc1_access_contested + 2 + + + siob_proc1_access + 3 + + + siob_proc0_stall_upstream + 4 + + + siob_proc0_stall_downstream + 5 + + + siob_proc0_access_contested + 6 + + + siob_proc0_access + 7 + + + apb_stall_upstream + 8 + + + apb_stall_downstream + 9 + + + apb_access_contested + 10 + + + apb_access + 11 + + + fastperi_stall_upstream + 12 + + + fastperi_stall_downstream + 13 + + + fastperi_access_contested + 14 + + + fastperi_access + 15 + + + sram9_stall_upstream + 16 + + + sram9_stall_downstream + 17 + + + sram9_access_contested + 18 + + + sram9_access + 19 + + + sram8_stall_upstream + 20 + + + sram8_stall_downstream + 21 + + + sram8_access_contested + 22 + + + sram8_access + 23 + + + sram7_stall_upstream + 24 + + + sram7_stall_downstream + 25 + + + sram7_access_contested + 26 + + + sram7_access + 27 + + + sram6_stall_upstream + 28 + + + sram6_stall_downstream + 29 + + + sram6_access_contested + 30 + + + sram6_access + 31 + + + sram5_stall_upstream + 32 + + + sram5_stall_downstream + 33 + + + sram5_access_contested + 34 + + + sram5_access + 35 + + + sram4_stall_upstream + 36 + + + sram4_stall_downstream + 37 + + + sram4_access_contested + 38 + + + sram4_access + 39 + + + sram3_stall_upstream + 40 + + + sram3_stall_downstream + 41 + + + sram3_access_contested + 42 + + + sram3_access + 43 + + + sram2_stall_upstream + 44 + + + sram2_stall_downstream + 45 + + + sram2_access_contested + 46 + + + sram2_access + 47 + + + sram1_stall_upstream + 48 + + + sram1_stall_downstream + 49 + + + sram1_access_contested + 50 + + + sram1_access + 51 + + + sram0_stall_upstream + 52 + + + sram0_stall_downstream + 53 + + + sram0_access_contested + 54 + + + sram0_access + 55 + + + xip_main1_stall_upstream + 56 + + + xip_main1_stall_downstream + 57 + + + xip_main1_access_contested + 58 + + + xip_main1_access + 59 + + + xip_main0_stall_upstream + 60 + + + xip_main0_stall_downstream + 61 + + + xip_main0_access_contested + 62 + + + xip_main0_access + 63 + + + rom_stall_upstream + 64 + + + rom_stall_downstream + 65 + + + rom_access_contested + 66 + + + rom_access + 67 + + + + + + + + + SIO + Single-cycle IO block + Provides core-local and inter-core hardware for the two processors, with single-cycle access. + 0xd0000000 + + 0 + 488 + registers + + + SIO_IRQ_FIFO + 25 + + + SIO_IRQ_BELL + 26 + + + SIO_IRQ_FIFO_NS + 27 + + + SIO_IRQ_BELL_NS + 28 + + + SIO_IRQ_MTIMECMP + 29 + + + + CPUID + 0x00000000 + Processor core identifier + 0x00000000 + + + CPUID + Value is 0 when read from processor core 0, and 1 when read from processor core 1. + [31:0] + read-only + + + + + GPIO_IN + 0x00000004 + Input value for GPIO0...31. + + In the Non-secure SIO, Secure-only GPIOs (as per ACCESSCTRL) appear as zero. + 0x00000000 + + + GPIO_IN + [31:0] + read-only + + + + + GPIO_HI_IN + 0x00000008 + Input value on GPIO32...47, QSPI IOs and USB pins + + In the Non-secure SIO, Secure-only GPIOs (as per ACCESSCTRL) appear as zero. + 0x00000000 + + + QSPI_SD + Input value on QSPI SD0 (MOSI), SD1 (MISO), SD2 and SD3 pins + [31:28] + read-only + + + QSPI_CSN + Input value on QSPI CSn pin + [27:27] + read-only + + + QSPI_SCK + Input value on QSPI SCK pin + [26:26] + read-only + + + USB_DM + Input value on USB D- pin + [25:25] + read-only + + + USB_DP + Input value on USB D+ pin + [24:24] + read-only + + + GPIO + Input value on GPIO32...47 + [15:0] + read-only + + + + + GPIO_OUT + 0x00000010 + GPIO0...31 output value + 0x00000000 + + + GPIO_OUT + Set output level (1/0 -> high/low) for GPIO0...31. Reading back gives the last value written, NOT the input value from the pins. + + If core 0 and core 1 both write to GPIO_OUT simultaneously (or to a SET/CLR/XOR alias), the result is as though the write from core 0 took place first, and the write from core 1 was then applied to that intermediate result. + + In the Non-secure SIO, Secure-only GPIOs (as per ACCESSCTRL) ignore writes, and their output status reads back as zero. This is also true for SET/CLR/XOR aliases of this register. + [31:0] + read-write + + + + + GPIO_HI_OUT + 0x00000014 + Output value for GPIO32...47, QSPI IOs and USB pins. + + Write to set output level (1/0 -> high/low). Reading back gives the last value written, NOT the input value from the pins. If core 0 and core 1 both write to GPIO_HI_OUT simultaneously (or to a SET/CLR/XOR alias), the result is as though the write from core 0 took place first, and the write from core 1 was then applied to that intermediate result. + + In the Non-secure SIO, Secure-only GPIOs (as per ACCESSCTRL) ignore writes, and their output status reads back as zero. This is also true for SET/CLR/XOR aliases of this register. + 0x00000000 + + + QSPI_SD + Output value for QSPI SD0 (MOSI), SD1 (MISO), SD2 and SD3 pins + [31:28] + read-write + + + QSPI_CSN + Output value for QSPI CSn pin + [27:27] + read-write + + + QSPI_SCK + Output value for QSPI SCK pin + [26:26] + read-write + + + USB_DM + Output value for USB D- pin + [25:25] + read-write + + + USB_DP + Output value for USB D+ pin + [24:24] + read-write + + + GPIO + Output value for GPIO32...47 + [15:0] + read-write + + + + + GPIO_OUT_SET + 0x00000018 + GPIO0...31 output value set + 0x00000000 + + + GPIO_OUT_SET + Perform an atomic bit-set on GPIO_OUT, i.e. `GPIO_OUT |= wdata` + [31:0] + write-only + + + + + GPIO_HI_OUT_SET + 0x0000001c + Output value set for GPIO32..47, QSPI IOs and USB pins. + Perform an atomic bit-set on GPIO_HI_OUT, i.e. `GPIO_HI_OUT |= wdata` + 0x00000000 + + + QSPI_SD + [31:28] + write-only + + + QSPI_CSN + [27:27] + write-only + + + QSPI_SCK + [26:26] + write-only + + + USB_DM + [25:25] + write-only + + + USB_DP + [24:24] + write-only + + + GPIO + [15:0] + write-only + + + + + GPIO_OUT_CLR + 0x00000020 + GPIO0...31 output value clear + 0x00000000 + + + GPIO_OUT_CLR + Perform an atomic bit-clear on GPIO_OUT, i.e. `GPIO_OUT &= ~wdata` + [31:0] + write-only + + + + + GPIO_HI_OUT_CLR + 0x00000024 + Output value clear for GPIO32..47, QSPI IOs and USB pins. + Perform an atomic bit-clear on GPIO_HI_OUT, i.e. `GPIO_HI_OUT &= ~wdata` + 0x00000000 + + + QSPI_SD + [31:28] + write-only + + + QSPI_CSN + [27:27] + write-only + + + QSPI_SCK + [26:26] + write-only + + + USB_DM + [25:25] + write-only + + + USB_DP + [24:24] + write-only + + + GPIO + [15:0] + write-only + + + + + GPIO_OUT_XOR + 0x00000028 + GPIO0...31 output value XOR + 0x00000000 + + + GPIO_OUT_XOR + Perform an atomic bitwise XOR on GPIO_OUT, i.e. `GPIO_OUT ^= wdata` + [31:0] + write-only + + + + + GPIO_HI_OUT_XOR + 0x0000002c + Output value XOR for GPIO32..47, QSPI IOs and USB pins. + Perform an atomic bitwise XOR on GPIO_HI_OUT, i.e. `GPIO_HI_OUT ^= wdata` + 0x00000000 + + + QSPI_SD + [31:28] + write-only + + + QSPI_CSN + [27:27] + write-only + + + QSPI_SCK + [26:26] + write-only + + + USB_DM + [25:25] + write-only + + + USB_DP + [24:24] + write-only + + + GPIO + [15:0] + write-only + + + + + GPIO_OE + 0x00000030 + GPIO0...31 output enable + 0x00000000 + + + GPIO_OE + Set output enable (1/0 -> output/input) for GPIO0...31. Reading back gives the last value written. + + If core 0 and core 1 both write to GPIO_OE simultaneously (or to a SET/CLR/XOR alias), the result is as though the write from core 0 took place first, and the write from core 1 was then applied to that intermediate result. + + In the Non-secure SIO, Secure-only GPIOs (as per ACCESSCTRL) ignore writes, and their output status reads back as zero. This is also true for SET/CLR/XOR aliases of this register. + [31:0] + read-write + + + + + GPIO_HI_OE + 0x00000034 + Output enable value for GPIO32...47, QSPI IOs and USB pins. + + Write output enable (1/0 -> output/input). Reading back gives the last value written. If core 0 and core 1 both write to GPIO_HI_OE simultaneously (or to a SET/CLR/XOR alias), the result is as though the write from core 0 took place first, and the write from core 1 was then applied to that intermediate result. + + In the Non-secure SIO, Secure-only GPIOs (as per ACCESSCTRL) ignore writes, and their output status reads back as zero. This is also true for SET/CLR/XOR aliases of this register. + 0x00000000 + + + QSPI_SD + Output enable value for QSPI SD0 (MOSI), SD1 (MISO), SD2 and SD3 pins + [31:28] + read-write + + + QSPI_CSN + Output enable value for QSPI CSn pin + [27:27] + read-write + + + QSPI_SCK + Output enable value for QSPI SCK pin + [26:26] + read-write + + + USB_DM + Output enable value for USB D- pin + [25:25] + read-write + + + USB_DP + Output enable value for USB D+ pin + [24:24] + read-write + + + GPIO + Output enable value for GPIO32...47 + [15:0] + read-write + + + + + GPIO_OE_SET + 0x00000038 + GPIO0...31 output enable set + 0x00000000 + + + GPIO_OE_SET + Perform an atomic bit-set on GPIO_OE, i.e. `GPIO_OE |= wdata` + [31:0] + write-only + + + + + GPIO_HI_OE_SET + 0x0000003c + Output enable set for GPIO32...47, QSPI IOs and USB pins. + Perform an atomic bit-set on GPIO_HI_OE, i.e. `GPIO_HI_OE |= wdata` + 0x00000000 + + + QSPI_SD + [31:28] + write-only + + + QSPI_CSN + [27:27] + write-only + + + QSPI_SCK + [26:26] + write-only + + + USB_DM + [25:25] + write-only + + + USB_DP + [24:24] + write-only + + + GPIO + [15:0] + write-only + + + + + GPIO_OE_CLR + 0x00000040 + GPIO0...31 output enable clear + 0x00000000 + + + GPIO_OE_CLR + Perform an atomic bit-clear on GPIO_OE, i.e. `GPIO_OE &= ~wdata` + [31:0] + write-only + + + + + GPIO_HI_OE_CLR + 0x00000044 + Output enable clear for GPIO32...47, QSPI IOs and USB pins. + Perform an atomic bit-clear on GPIO_HI_OE, i.e. `GPIO_HI_OE &= ~wdata` + 0x00000000 + + + QSPI_SD + [31:28] + write-only + + + QSPI_CSN + [27:27] + write-only + + + QSPI_SCK + [26:26] + write-only + + + USB_DM + [25:25] + write-only + + + USB_DP + [24:24] + write-only + + + GPIO + [15:0] + write-only + + + + + GPIO_OE_XOR + 0x00000048 + GPIO0...31 output enable XOR + 0x00000000 + + + GPIO_OE_XOR + Perform an atomic bitwise XOR on GPIO_OE, i.e. `GPIO_OE ^= wdata` + [31:0] + write-only + + + + + GPIO_HI_OE_XOR + 0x0000004c + Output enable XOR for GPIO32...47, QSPI IOs and USB pins. + Perform an atomic bitwise XOR on GPIO_HI_OE, i.e. `GPIO_HI_OE ^= wdata` + 0x00000000 + + + QSPI_SD + [31:28] + write-only + + + QSPI_CSN + [27:27] + write-only + + + QSPI_SCK + [26:26] + write-only + + + USB_DM + [25:25] + write-only + + + USB_DP + [24:24] + write-only + + + GPIO + [15:0] + write-only + + + + + FIFO_ST + 0x00000050 + Status register for inter-core FIFOs (mailboxes). + There is one FIFO in the core 0 -> core 1 direction, and one core 1 -> core 0. Both are 32 bits wide and 8 words deep. + Core 0 can see the read side of the 1->0 FIFO (RX), and the write side of 0->1 FIFO (TX). + Core 1 can see the read side of the 0->1 FIFO (RX), and the write side of 1->0 FIFO (TX). + The SIO IRQ for each core is the logical OR of the VLD, WOF and ROE fields of its FIFO_ST register. + 0x00000002 + + + ROE + Sticky flag indicating the RX FIFO was read when empty. This read was ignored by the FIFO. + [3:3] + read-write + oneToClear + + + WOF + Sticky flag indicating the TX FIFO was written when full. This write was ignored by the FIFO. + [2:2] + read-write + oneToClear + + + RDY + Value is 1 if this core's TX FIFO is not full (i.e. if FIFO_WR is ready for more data) + [1:1] + read-only + + + VLD + Value is 1 if this core's RX FIFO is not empty (i.e. if FIFO_RD is valid) + [0:0] + read-only + + + + + FIFO_WR + 0x00000054 + Write access to this core's TX FIFO + 0x00000000 + + + FIFO_WR + [31:0] + write-only + + + + + FIFO_RD + 0x00000058 + Read access to this core's RX FIFO + 0x00000000 + + + FIFO_RD + [31:0] + read-only + modify + + + + + SPINLOCK_ST + 0x0000005c + Spinlock state + A bitmap containing the state of all 32 spinlocks (1=locked). + Mainly intended for debugging. + 0x00000000 + + + SPINLOCK_ST + [31:0] + read-only + + + + + INTERP0_ACCUM0 + 0x00000080 + Read/write access to accumulator 0 + 0x00000000 + + + INTERP0_ACCUM0 + [31:0] + read-write + + + + + INTERP0_ACCUM1 + 0x00000084 + Read/write access to accumulator 1 + 0x00000000 + + + INTERP0_ACCUM1 + [31:0] + read-write + + + + + INTERP0_BASE0 + 0x00000088 + Read/write access to BASE0 register. + 0x00000000 + + + INTERP0_BASE0 + [31:0] + read-write + + + + + INTERP0_BASE1 + 0x0000008c + Read/write access to BASE1 register. + 0x00000000 + + + INTERP0_BASE1 + [31:0] + read-write + + + + + INTERP0_BASE2 + 0x00000090 + Read/write access to BASE2 register. + 0x00000000 + + + INTERP0_BASE2 + [31:0] + read-write + + + + + INTERP0_POP_LANE0 + 0x00000094 + Read LANE0 result, and simultaneously write lane results to both accumulators (POP). + 0x00000000 + + + INTERP0_POP_LANE0 + [31:0] + read-only + + + + + INTERP0_POP_LANE1 + 0x00000098 + Read LANE1 result, and simultaneously write lane results to both accumulators (POP). + 0x00000000 + + + INTERP0_POP_LANE1 + [31:0] + read-only + + + + + INTERP0_POP_FULL + 0x0000009c + Read FULL result, and simultaneously write lane results to both accumulators (POP). + 0x00000000 + + + INTERP0_POP_FULL + [31:0] + read-only + + + + + INTERP0_PEEK_LANE0 + 0x000000a0 + Read LANE0 result, without altering any internal state (PEEK). + 0x00000000 + + + INTERP0_PEEK_LANE0 + [31:0] + read-only + + + + + INTERP0_PEEK_LANE1 + 0x000000a4 + Read LANE1 result, without altering any internal state (PEEK). + 0x00000000 + + + INTERP0_PEEK_LANE1 + [31:0] + read-only + + + + + INTERP0_PEEK_FULL + 0x000000a8 + Read FULL result, without altering any internal state (PEEK). + 0x00000000 + + + INTERP0_PEEK_FULL + [31:0] + read-only + + + + + INTERP0_CTRL_LANE0 + 0x000000ac + Control register for lane 0 + 0x00000000 + + + OVERF + Set if either OVERF0 or OVERF1 is set. + [25:25] + read-only + + + OVERF1 + Indicates if any masked-off MSBs in ACCUM1 are set. + [24:24] + read-only + + + OVERF0 + Indicates if any masked-off MSBs in ACCUM0 are set. + [23:23] + read-only + + + BLEND + Only present on INTERP0 on each core. If BLEND mode is enabled: + - LANE1 result is a linear interpolation between BASE0 and BASE1, controlled + by the 8 LSBs of lane 1 shift and mask value (a fractional number between + 0 and 255/256ths) + - LANE0 result does not have BASE0 added (yields only the 8 LSBs of lane 1 shift+mask value) + - FULL result does not have lane 1 shift+mask value added (BASE2 + lane 0 shift+mask) + LANE1 SIGNED flag controls whether the interpolation is signed or unsigned. + [21:21] + read-write + + + FORCE_MSB + ORed into bits 29:28 of the lane result presented to the processor on the bus. + No effect on the internal 32-bit datapath. Handy for using a lane to generate sequence + of pointers into flash or SRAM. + [20:19] + read-write + + + ADD_RAW + If 1, mask + shift is bypassed for LANE0 result. This does not affect FULL result. + [18:18] + read-write + + + CROSS_RESULT + If 1, feed the opposite lane's result into this lane's accumulator on POP. + [17:17] + read-write + + + CROSS_INPUT + If 1, feed the opposite lane's accumulator into this lane's shift + mask hardware. + Takes effect even if ADD_RAW is set (the CROSS_INPUT mux is before the shift+mask bypass) + [16:16] + read-write + + + SIGNED + If SIGNED is set, the shifted and masked accumulator value is sign-extended to 32 bits + before adding to BASE0, and LANE0 PEEK/POP appear extended to 32 bits when read by processor. + [15:15] + read-write + + + MASK_MSB + The most-significant bit allowed to pass by the mask (inclusive) + Setting MSB < LSB may cause chip to turn inside-out + [14:10] + read-write + + + MASK_LSB + The least-significant bit allowed to pass by the mask (inclusive) + [9:5] + read-write + + + SHIFT + Right-rotate applied to accumulator before masking. By appropriately configuring the masks, left and right shifts can be synthesised. + [4:0] + read-write + + + + + INTERP0_CTRL_LANE1 + 0x000000b0 + Control register for lane 1 + 0x00000000 + + + FORCE_MSB + ORed into bits 29:28 of the lane result presented to the processor on the bus. + No effect on the internal 32-bit datapath. Handy for using a lane to generate sequence + of pointers into flash or SRAM. + [20:19] + read-write + + + ADD_RAW + If 1, mask + shift is bypassed for LANE1 result. This does not affect FULL result. + [18:18] + read-write + + + CROSS_RESULT + If 1, feed the opposite lane's result into this lane's accumulator on POP. + [17:17] + read-write + + + CROSS_INPUT + If 1, feed the opposite lane's accumulator into this lane's shift + mask hardware. + Takes effect even if ADD_RAW is set (the CROSS_INPUT mux is before the shift+mask bypass) + [16:16] + read-write + + + SIGNED + If SIGNED is set, the shifted and masked accumulator value is sign-extended to 32 bits + before adding to BASE1, and LANE1 PEEK/POP appear extended to 32 bits when read by processor. + [15:15] + read-write + + + MASK_MSB + The most-significant bit allowed to pass by the mask (inclusive) + Setting MSB < LSB may cause chip to turn inside-out + [14:10] + read-write + + + MASK_LSB + The least-significant bit allowed to pass by the mask (inclusive) + [9:5] + read-write + + + SHIFT + Right-rotate applied to accumulator before masking. By appropriately configuring the masks, left and right shifts can be synthesised. + [4:0] + read-write + + + + + INTERP0_ACCUM0_ADD + 0x000000b4 + Values written here are atomically added to ACCUM0 + Reading yields lane 0's raw shift and mask value (BASE0 not added). + 0x00000000 + + + INTERP0_ACCUM0_ADD + [23:0] + read-write + + + + + INTERP0_ACCUM1_ADD + 0x000000b8 + Values written here are atomically added to ACCUM1 + Reading yields lane 1's raw shift and mask value (BASE1 not added). + 0x00000000 + + + INTERP0_ACCUM1_ADD + [23:0] + read-write + + + + + INTERP0_BASE_1AND0 + 0x000000bc + On write, the lower 16 bits go to BASE0, upper bits to BASE1 simultaneously. + Each half is sign-extended to 32 bits if that lane's SIGNED flag is set. + 0x00000000 + + + INTERP0_BASE_1AND0 + [31:0] + write-only + + + + + INTERP1_ACCUM0 + 0x000000c0 + Read/write access to accumulator 0 + 0x00000000 + + + INTERP1_ACCUM0 + [31:0] + read-write + + + + + INTERP1_ACCUM1 + 0x000000c4 + Read/write access to accumulator 1 + 0x00000000 + + + INTERP1_ACCUM1 + [31:0] + read-write + + + + + INTERP1_BASE0 + 0x000000c8 + Read/write access to BASE0 register. + 0x00000000 + + + INTERP1_BASE0 + [31:0] + read-write + + + + + INTERP1_BASE1 + 0x000000cc + Read/write access to BASE1 register. + 0x00000000 + + + INTERP1_BASE1 + [31:0] + read-write + + + + + INTERP1_BASE2 + 0x000000d0 + Read/write access to BASE2 register. + 0x00000000 + + + INTERP1_BASE2 + [31:0] + read-write + + + + + INTERP1_POP_LANE0 + 0x000000d4 + Read LANE0 result, and simultaneously write lane results to both accumulators (POP). + 0x00000000 + + + INTERP1_POP_LANE0 + [31:0] + read-only + + + + + INTERP1_POP_LANE1 + 0x000000d8 + Read LANE1 result, and simultaneously write lane results to both accumulators (POP). + 0x00000000 + + + INTERP1_POP_LANE1 + [31:0] + read-only + + + + + INTERP1_POP_FULL + 0x000000dc + Read FULL result, and simultaneously write lane results to both accumulators (POP). + 0x00000000 + + + INTERP1_POP_FULL + [31:0] + read-only + + + + + INTERP1_PEEK_LANE0 + 0x000000e0 + Read LANE0 result, without altering any internal state (PEEK). + 0x00000000 + + + INTERP1_PEEK_LANE0 + [31:0] + read-only + + + + + INTERP1_PEEK_LANE1 + 0x000000e4 + Read LANE1 result, without altering any internal state (PEEK). + 0x00000000 + + + INTERP1_PEEK_LANE1 + [31:0] + read-only + + + + + INTERP1_PEEK_FULL + 0x000000e8 + Read FULL result, without altering any internal state (PEEK). + 0x00000000 + + + INTERP1_PEEK_FULL + [31:0] + read-only + + + + + INTERP1_CTRL_LANE0 + 0x000000ec + Control register for lane 0 + 0x00000000 + + + OVERF + Set if either OVERF0 or OVERF1 is set. + [25:25] + read-only + + + OVERF1 + Indicates if any masked-off MSBs in ACCUM1 are set. + [24:24] + read-only + + + OVERF0 + Indicates if any masked-off MSBs in ACCUM0 are set. + [23:23] + read-only + + + CLAMP + Only present on INTERP1 on each core. If CLAMP mode is enabled: + - LANE0 result is shifted and masked ACCUM0, clamped by a lower bound of + BASE0 and an upper bound of BASE1. + - Signedness of these comparisons is determined by LANE0_CTRL_SIGNED + [22:22] + read-write + + + FORCE_MSB + ORed into bits 29:28 of the lane result presented to the processor on the bus. + No effect on the internal 32-bit datapath. Handy for using a lane to generate sequence + of pointers into flash or SRAM. + [20:19] + read-write + + + ADD_RAW + If 1, mask + shift is bypassed for LANE0 result. This does not affect FULL result. + [18:18] + read-write + + + CROSS_RESULT + If 1, feed the opposite lane's result into this lane's accumulator on POP. + [17:17] + read-write + + + CROSS_INPUT + If 1, feed the opposite lane's accumulator into this lane's shift + mask hardware. + Takes effect even if ADD_RAW is set (the CROSS_INPUT mux is before the shift+mask bypass) + [16:16] + read-write + + + SIGNED + If SIGNED is set, the shifted and masked accumulator value is sign-extended to 32 bits + before adding to BASE0, and LANE0 PEEK/POP appear extended to 32 bits when read by processor. + [15:15] + read-write + + + MASK_MSB + The most-significant bit allowed to pass by the mask (inclusive) + Setting MSB < LSB may cause chip to turn inside-out + [14:10] + read-write + + + MASK_LSB + The least-significant bit allowed to pass by the mask (inclusive) + [9:5] + read-write + + + SHIFT + Right-rotate applied to accumulator before masking. By appropriately configuring the masks, left and right shifts can be synthesised. + [4:0] + read-write + + + + + INTERP1_CTRL_LANE1 + 0x000000f0 + Control register for lane 1 + 0x00000000 + + + FORCE_MSB + ORed into bits 29:28 of the lane result presented to the processor on the bus. + No effect on the internal 32-bit datapath. Handy for using a lane to generate sequence + of pointers into flash or SRAM. + [20:19] + read-write + + + ADD_RAW + If 1, mask + shift is bypassed for LANE1 result. This does not affect FULL result. + [18:18] + read-write + + + CROSS_RESULT + If 1, feed the opposite lane's result into this lane's accumulator on POP. + [17:17] + read-write + + + CROSS_INPUT + If 1, feed the opposite lane's accumulator into this lane's shift + mask hardware. + Takes effect even if ADD_RAW is set (the CROSS_INPUT mux is before the shift+mask bypass) + [16:16] + read-write + + + SIGNED + If SIGNED is set, the shifted and masked accumulator value is sign-extended to 32 bits + before adding to BASE1, and LANE1 PEEK/POP appear extended to 32 bits when read by processor. + [15:15] + read-write + + + MASK_MSB + The most-significant bit allowed to pass by the mask (inclusive) + Setting MSB < LSB may cause chip to turn inside-out + [14:10] + read-write + + + MASK_LSB + The least-significant bit allowed to pass by the mask (inclusive) + [9:5] + read-write + + + SHIFT + Right-rotate applied to accumulator before masking. By appropriately configuring the masks, left and right shifts can be synthesised. + [4:0] + read-write + + + + + INTERP1_ACCUM0_ADD + 0x000000f4 + Values written here are atomically added to ACCUM0 + Reading yields lane 0's raw shift and mask value (BASE0 not added). + 0x00000000 + + + INTERP1_ACCUM0_ADD + [23:0] + read-write + + + + + INTERP1_ACCUM1_ADD + 0x000000f8 + Values written here are atomically added to ACCUM1 + Reading yields lane 1's raw shift and mask value (BASE1 not added). + 0x00000000 + + + INTERP1_ACCUM1_ADD + [23:0] + read-write + + + + + INTERP1_BASE_1AND0 + 0x000000fc + On write, the lower 16 bits go to BASE0, upper bits to BASE1 simultaneously. + Each half is sign-extended to 32 bits if that lane's SIGNED flag is set. + 0x00000000 + + + INTERP1_BASE_1AND0 + [31:0] + write-only + + + + + SPINLOCK0 + 0x00000100 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK0 + [31:0] + read-write + modify + + + + + SPINLOCK1 + 0x00000104 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK1 + [31:0] + read-write + modify + + + + + SPINLOCK2 + 0x00000108 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK2 + [31:0] + read-write + modify + + + + + SPINLOCK3 + 0x0000010c + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK3 + [31:0] + read-write + modify + + + + + SPINLOCK4 + 0x00000110 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK4 + [31:0] + read-write + modify + + + + + SPINLOCK5 + 0x00000114 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK5 + [31:0] + read-write + modify + + + + + SPINLOCK6 + 0x00000118 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK6 + [31:0] + read-write + modify + + + + + SPINLOCK7 + 0x0000011c + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK7 + [31:0] + read-write + modify + + + + + SPINLOCK8 + 0x00000120 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK8 + [31:0] + read-write + modify + + + + + SPINLOCK9 + 0x00000124 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK9 + [31:0] + read-write + modify + + + + + SPINLOCK10 + 0x00000128 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK10 + [31:0] + read-write + modify + + + + + SPINLOCK11 + 0x0000012c + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK11 + [31:0] + read-write + modify + + + + + SPINLOCK12 + 0x00000130 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK12 + [31:0] + read-write + modify + + + + + SPINLOCK13 + 0x00000134 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK13 + [31:0] + read-write + modify + + + + + SPINLOCK14 + 0x00000138 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK14 + [31:0] + read-write + modify + + + + + SPINLOCK15 + 0x0000013c + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK15 + [31:0] + read-write + modify + + + + + SPINLOCK16 + 0x00000140 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK16 + [31:0] + read-write + modify + + + + + SPINLOCK17 + 0x00000144 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK17 + [31:0] + read-write + modify + + + + + SPINLOCK18 + 0x00000148 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK18 + [31:0] + read-write + modify + + + + + SPINLOCK19 + 0x0000014c + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK19 + [31:0] + read-write + modify + + + + + SPINLOCK20 + 0x00000150 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK20 + [31:0] + read-write + modify + + + + + SPINLOCK21 + 0x00000154 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK21 + [31:0] + read-write + modify + + + + + SPINLOCK22 + 0x00000158 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK22 + [31:0] + read-write + modify + + + + + SPINLOCK23 + 0x0000015c + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK23 + [31:0] + read-write + modify + + + + + SPINLOCK24 + 0x00000160 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK24 + [31:0] + read-write + modify + + + + + SPINLOCK25 + 0x00000164 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK25 + [31:0] + read-write + modify + + + + + SPINLOCK26 + 0x00000168 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK26 + [31:0] + read-write + modify + + + + + SPINLOCK27 + 0x0000016c + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK27 + [31:0] + read-write + modify + + + + + SPINLOCK28 + 0x00000170 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK28 + [31:0] + read-write + modify + + + + + SPINLOCK29 + 0x00000174 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK29 + [31:0] + read-write + modify + + + + + SPINLOCK30 + 0x00000178 + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK30 + [31:0] + read-write + modify + + + + + SPINLOCK31 + 0x0000017c + Reading from a spinlock address will: + - Return 0 if lock is already locked + - Otherwise return nonzero, and simultaneously claim the lock + + Writing (any value) releases the lock. + If core 0 and core 1 attempt to claim the same lock simultaneously, core 0 wins. + The value returned on success is 0x1 << lock number. + 0x00000000 + + + SPINLOCK31 + [31:0] + read-write + modify + + + + + DOORBELL_OUT_SET + 0x00000180 + Trigger a doorbell interrupt on the opposite core. + + Write 1 to a bit to set the corresponding bit in DOORBELL_IN on the opposite core. This raises the opposite core's doorbell interrupt. + + Read to get the status of the doorbells currently asserted on the opposite core. This is equivalent to that core reading its own DOORBELL_IN status. + 0x00000000 + + + DOORBELL_OUT_SET + [7:0] + read-write + + + + + DOORBELL_OUT_CLR + 0x00000184 + Clear doorbells which have been posted to the opposite core. This register is intended for debugging and initialisation purposes. + + Writing 1 to a bit in DOORBELL_OUT_CLR clears the corresponding bit in DOORBELL_IN on the opposite core. Clearing all bits will cause that core's doorbell interrupt to deassert. Since the usual order of events is for software to send events using DOORBELL_OUT_SET, and acknowledge incoming events by writing to DOORBELL_IN_CLR, this register should be used with caution to avoid race conditions. + + Reading returns the status of the doorbells currently asserted on the other core, i.e. is equivalent to that core reading its own DOORBELL_IN status. + 0x00000000 + + + DOORBELL_OUT_CLR + [7:0] + read-write + oneToClear + + + + + DOORBELL_IN_SET + 0x00000188 + Write 1s to trigger doorbell interrupts on this core. Read to get status of doorbells currently asserted on this core. + 0x00000000 + + + DOORBELL_IN_SET + [7:0] + read-write + + + + + DOORBELL_IN_CLR + 0x0000018c + Check and acknowledge doorbells posted to this core. This core's doorbell interrupt is asserted when any bit in this register is 1. + + Write 1 to each bit to clear that bit. The doorbell interrupt deasserts once all bits are cleared. Read to get status of doorbells currently asserted on this core. + 0x00000000 + + + DOORBELL_IN_CLR + [7:0] + read-write + oneToClear + + + + + PERI_NONSEC + 0x00000190 + Detach certain core-local peripherals from Secure SIO, and attach them to Non-secure SIO, so that Non-secure software can use them. Attempting to access one of these peripherals from the Secure SIO when it is attached to the Non-secure SIO, or vice versa, will generate a bus error. + + This register is per-core, and is only present on the Secure SIO. + + Most SIO hardware is duplicated across the Secure and Non-secure SIO, so is not listed in this register. + 0x00000000 + + + TMDS + IF 1, detach TMDS encoder (of this core) from the Secure SIO, and attach to the Non-secure SIO. + [5:5] + read-write + + + INTERP1 + If 1, detach interpolator 1 (of this core) from the Secure SIO, and attach to the Non-secure SIO. + [1:1] + read-write + + + INTERP0 + If 1, detach interpolator 0 (of this core) from the Secure SIO, and attach to the Non-secure SIO. + [0:0] + read-write + + + + + RISCV_SOFTIRQ + 0x000001a0 + Control the assertion of the standard software interrupt (MIP.MSIP) on the RISC-V cores. + + Unlike the RISC-V timer, this interrupt is not routed to a normal system-level interrupt line, so can not be used by the Arm cores. + + It is safe for both cores to write to this register on the same cycle. The set/clear effect is accumulated across both cores, and then applied. If a flag is both set and cleared on the same cycle, only the set takes effect. + 0x00000000 + + + CORE1_CLR + Write 1 to atomically clear the core 1 software interrupt flag. Read to get the status of this flag. + [9:9] + read-write + + + CORE0_CLR + Write 1 to atomically clear the core 0 software interrupt flag. Read to get the status of this flag. + [8:8] + read-write + + + CORE1_SET + Write 1 to atomically set the core 1 software interrupt flag. Read to get the status of this flag. + [1:1] + read-write + + + CORE0_SET + Write 1 to atomically set the core 0 software interrupt flag. Read to get the status of this flag. + [0:0] + read-write + + + + + MTIME_CTRL + 0x000001a4 + Control register for the RISC-V 64-bit Machine-mode timer. This timer is only present in the Secure SIO, so is only accessible to an Arm core in Secure mode or a RISC-V core in Machine mode. + + Note whilst this timer follows the RISC-V privileged specification, it is equally usable by the Arm cores. The interrupts are routed to normal system-level interrupt lines as well as to the MIP.MTIP inputs on the RISC-V cores. + 0x0000000d + + + DBGPAUSE_CORE1 + If 1, the timer pauses when core 1 is in the debug halt state. + [3:3] + read-write + + + DBGPAUSE_CORE0 + If 1, the timer pauses when core 0 is in the debug halt state. + [2:2] + read-write + + + FULLSPEED + If 1, increment the timer every cycle (i.e. run directly from the system clock), rather than incrementing on the system-level timer tick input. + [1:1] + read-write + + + EN + Timer enable bit. When 0, the timer will not increment automatically. + [0:0] + read-write + + + + + MTIME + 0x000001b0 + Read/write access to the high half of RISC-V Machine-mode timer. This register is shared between both cores. If both cores write on the same cycle, core 1 takes precedence. + 0x00000000 + + + MTIME + [31:0] + read-write + + + + + MTIMEH + 0x000001b4 + Read/write access to the high half of RISC-V Machine-mode timer. This register is shared between both cores. If both cores write on the same cycle, core 1 takes precedence. + 0x00000000 + + + MTIMEH + [31:0] + read-write + + + + + MTIMECMP + 0x000001b8 + Low half of RISC-V Machine-mode timer comparator. This register is core-local, i.e., each core gets a copy of this register, with the comparison result routed to its own interrupt line. + + The timer interrupt is asserted whenever MTIME is greater than or equal to MTIMECMP. This comparison is unsigned, and performed on the full 64-bit values. + 0xffffffff + + + MTIMECMP + [31:0] + read-write + + + + + MTIMECMPH + 0x000001bc + High half of RISC-V Machine-mode timer comparator. This register is core-local. + + The timer interrupt is asserted whenever MTIME is greater than or equal to MTIMECMP. This comparison is unsigned, and performed on the full 64-bit values. + 0xffffffff + + + MTIMECMPH + [31:0] + read-write + + + + + TMDS_CTRL + 0x000001c0 + Control register for TMDS encoder. + 0x00000000 + + + CLEAR_BALANCE + Clear the running DC balance state of the TMDS encoders. This bit should be written once at the beginning of each scanline. + [28:28] + write-only + + + PIX2_NOSHIFT + When encoding two pixels's worth of symbols in one cycle (a read of a PEEK/POP_DOUBLE register), the second encoder sees a shifted version of the colour data register. + + This control disables that shift, so that both encoder layers see the same pixel data. This is used for pixel doubling. + [27:27] + read-write + + + PIX_SHIFT + Shift applied to the colour data register with each read of a POP alias register. + + Reading from the POP_SINGLE register, or reading from the POP_DOUBLE register with PIX2_NOSHIFT set (for pixel doubling), shifts by the indicated amount. + + Reading from a POP_DOUBLE register when PIX2_NOSHIFT is clear will shift by double the indicated amount. (Shift by 32 means no shift.) + [26:24] + read-write + + + 0 + 0 + Do not shift the colour data register. + + + 1 + 1 + Shift the colour data register by 1 bit + + + 2 + 2 + Shift the colour data register by 2 bits + + + 4 + 3 + Shift the colour data register by 4 bits + + + 8 + 4 + Shift the colour data register by 8 bits + + + 16 + 5 + Shift the colour data register by 16 bits + + + + + INTERLEAVE + Enable lane interleaving for reads of PEEK_SINGLE/POP_SINGLE. + + When interleaving is disabled, each of the 3 symbols appears as a contiguous 10-bit field, with lane 0 being the least-significant and starting at bit 0 of the register. + + When interleaving is enabled, the symbols are packed into 5 chunks of 3 lanes times 2 bits (30 bits total). Each chunk contains two bits of a TMDS symbol per lane, with lane 0 being the least significant. + [23:23] + read-write + + + L2_NBITS + Number of valid colour MSBs for lane 2 (1-8 bits, encoded as 0 through 7). Remaining LSBs are masked to 0 after the rotate. + [20:18] + read-write + + + L1_NBITS + Number of valid colour MSBs for lane 1 (1-8 bits, encoded as 0 through 7). Remaining LSBs are masked to 0 after the rotate. + [17:15] + read-write + + + L0_NBITS + Number of valid colour MSBs for lane 0 (1-8 bits, encoded as 0 through 7). Remaining LSBs are masked to 0 after the rotate. + [14:12] + read-write + + + L2_ROT + Right-rotate the 16 LSBs of the colour accumulator by 0-15 bits, in order to get the MSB of the lane 2 (red) colour data aligned with the MSB of the 8-bit encoder input. + + For example, for RGB565 (red most significant), red is bits 15:11, so should be right-rotated by 8 bits to align with bits 7:3 of the encoder input. + [11:8] + read-write + + + L1_ROT + Right-rotate the 16 LSBs of the colour accumulator by 0-15 bits, in order to get the MSB of the lane 1 (green) colour data aligned with the MSB of the 8-bit encoder input. + + For example, for RGB565, green is bits 10:5, so should be right-rotated by 3 bits to align with bits 7:2 of the encoder input. + [7:4] + read-write + + + L0_ROT + Right-rotate the 16 LSBs of the colour accumulator by 0-15 bits, in order to get the MSB of the lane 0 (blue) colour data aligned with the MSB of the 8-bit encoder input. + + For example, for RGB565 (red most significant), blue is bits 4:0, so should be right-rotated by 13 to align with bits 7:3 of the encoder input. + [3:0] + read-write + + + + + TMDS_WDATA + 0x000001c4 + Write-only access to the TMDS colour data register. + 0x00000000 + + + TMDS_WDATA + [31:0] + write-only + + + + + TMDS_PEEK_SINGLE + 0x000001c8 + Get the encoding of one pixel's worth of colour data, packed into a 32-bit value (3x10-bit symbols). + + The PEEK alias does not shift the colour register when read, but still advances the running DC balance state of each encoder. This is useful for pixel doubling. + 0x00000000 + + + TMDS_PEEK_SINGLE + [31:0] + read-only + modify + + + + + TMDS_POP_SINGLE + 0x000001cc + Get the encoding of one pixel's worth of colour data, packed into a 32-bit value. The packing is 5 chunks of 3 lanes times 2 bits (30 bits total). Each chunk contains two bits of a TMDS symbol per lane. This format is intended for shifting out with the HSTX peripheral on RP2350. + + The POP alias shifts the colour register when read, as well as advancing the running DC balance state of each encoder. + 0x00000000 + + + TMDS_POP_SINGLE + [31:0] + read-only + modify + + + + + TMDS_PEEK_DOUBLE_L0 + 0x000001d0 + Get lane 0 of the encoding of two pixels' worth of colour data. Two 10-bit TMDS symbols are packed at the bottom of a 32-bit word. + + The PEEK alias does not shift the colour register when read, but still advances the lane 0 DC balance state. This is useful if all 3 lanes' worth of encode are to be read at once, rather than processing the entire scanline for one lane before moving to the next lane. + 0x00000000 + + + TMDS_PEEK_DOUBLE_L0 + [31:0] + read-only + modify + + + + + TMDS_POP_DOUBLE_L0 + 0x000001d4 + Get lane 0 of the encoding of two pixels' worth of colour data. Two 10-bit TMDS symbols are packed at the bottom of a 32-bit word. + + The POP alias shifts the colour register when read, according to the values of PIX_SHIFT and PIX2_NOSHIFT. + 0x00000000 + + + TMDS_POP_DOUBLE_L0 + [31:0] + read-only + modify + + + + + TMDS_PEEK_DOUBLE_L1 + 0x000001d8 + Get lane 1 of the encoding of two pixels' worth of colour data. Two 10-bit TMDS symbols are packed at the bottom of a 32-bit word. + + The PEEK alias does not shift the colour register when read, but still advances the lane 1 DC balance state. This is useful if all 3 lanes' worth of encode are to be read at once, rather than processing the entire scanline for one lane before moving to the next lane. + 0x00000000 + + + TMDS_PEEK_DOUBLE_L1 + [31:0] + read-only + modify + + + + + TMDS_POP_DOUBLE_L1 + 0x000001dc + Get lane 1 of the encoding of two pixels' worth of colour data. Two 10-bit TMDS symbols are packed at the bottom of a 32-bit word. + + The POP alias shifts the colour register when read, according to the values of PIX_SHIFT and PIX2_NOSHIFT. + 0x00000000 + + + TMDS_POP_DOUBLE_L1 + [31:0] + read-only + modify + + + + + TMDS_PEEK_DOUBLE_L2 + 0x000001e0 + Get lane 2 of the encoding of two pixels' worth of colour data. Two 10-bit TMDS symbols are packed at the bottom of a 32-bit word. + + The PEEK alias does not shift the colour register when read, but still advances the lane 2 DC balance state. This is useful if all 3 lanes' worth of encode are to be read at once, rather than processing the entire scanline for one lane before moving to the next lane. + 0x00000000 + + + TMDS_PEEK_DOUBLE_L2 + [31:0] + read-only + modify + + + + + TMDS_POP_DOUBLE_L2 + 0x000001e4 + Get lane 2 of the encoding of two pixels' worth of colour data. Two 10-bit TMDS symbols are packed at the bottom of a 32-bit word. + + The POP alias shifts the colour register when read, according to the values of PIX_SHIFT and PIX2_NOSHIFT. + 0x00000000 + + + TMDS_POP_DOUBLE_L2 + [31:0] + read-only + modify + + + + + + + SIO_NS + 0xd0020000 + + + BOOTRAM + Additional registers mapped adjacent to the bootram, for use by the bootrom. + 0x400e0000 + + 0 + 2092 + registers + + + + WRITE_ONCE0 + 0x00000800 + This registers always ORs writes into its current contents. Once a bit is set, it can only be cleared by a reset. + 0x00000000 + + + WRITE_ONCE0 + [31:0] + read-write + + + + + WRITE_ONCE1 + 0x00000804 + This registers always ORs writes into its current contents. Once a bit is set, it can only be cleared by a reset. + 0x00000000 + + + WRITE_ONCE1 + [31:0] + read-write + + + + + BOOTLOCK_STAT + 0x00000808 + Bootlock status register. 1=unclaimed, 0=claimed. These locks function identically to the SIO spinlocks, but are reserved for bootrom use. + 0x000000ff + + + BOOTLOCK_STAT + [7:0] + read-write + + + + + BOOTLOCK0 + 0x0000080c + Read to claim and check. Write to unclaim. The value returned on successful claim is 1 << n, and on failed claim is zero. + 0x00000000 + + + BOOTLOCK0 + [31:0] + read-write + + + + + BOOTLOCK1 + 0x00000810 + Read to claim and check. Write to unclaim. The value returned on successful claim is 1 << n, and on failed claim is zero. + 0x00000000 + + + BOOTLOCK1 + [31:0] + read-write + + + + + BOOTLOCK2 + 0x00000814 + Read to claim and check. Write to unclaim. The value returned on successful claim is 1 << n, and on failed claim is zero. + 0x00000000 + + + BOOTLOCK2 + [31:0] + read-write + + + + + BOOTLOCK3 + 0x00000818 + Read to claim and check. Write to unclaim. The value returned on successful claim is 1 << n, and on failed claim is zero. + 0x00000000 + + + BOOTLOCK3 + [31:0] + read-write + + + + + BOOTLOCK4 + 0x0000081c + Read to claim and check. Write to unclaim. The value returned on successful claim is 1 << n, and on failed claim is zero. + 0x00000000 + + + BOOTLOCK4 + [31:0] + read-write + + + + + BOOTLOCK5 + 0x00000820 + Read to claim and check. Write to unclaim. The value returned on successful claim is 1 << n, and on failed claim is zero. + 0x00000000 + + + BOOTLOCK5 + [31:0] + read-write + + + + + BOOTLOCK6 + 0x00000824 + Read to claim and check. Write to unclaim. The value returned on successful claim is 1 << n, and on failed claim is zero. + 0x00000000 + + + BOOTLOCK6 + [31:0] + read-write + + + + + BOOTLOCK7 + 0x00000828 + Read to claim and check. Write to unclaim. The value returned on successful claim is 1 << n, and on failed claim is zero. + 0x00000000 + + + BOOTLOCK7 + [31:0] + read-write + + + + + + + CORESIGHT_TRACE + Coresight block - RP specific registers + 0x50700000 + + 0 + 8 + registers + + + + CTRL_STATUS + 0x00000000 + Control and status register + 0x00000001 + + + TRACE_CAPTURE_FIFO_OVERFLOW + This status flag is set high when trace data has been dropped due to the FIFO being full at the point trace data was sampled. Write 1 to acknowledge and clear the bit. + [1:1] + read-write + + + TRACE_CAPTURE_FIFO_FLUSH + Set to 1 to continuously hold the trace FIFO in a flushed state and prevent overflow. + + Before clearing this flag, configure and start a DMA channel with the correct DREQ for the TRACE_CAPTURE_FIFO register. + + Clear this flag to begin sampling trace data, and set once again once the trace capture buffer is full. You must configure the TPIU in order to generate trace packets to be captured, as well as components like the ETM further upstream to generate the event stream propagated to the TPIU. + [0:0] + read-write + + + + + TRACE_CAPTURE_FIFO + 0x00000004 + FIFO for trace data captured from the TPIU + 0x00000000 + + + RDATA + Read from an 8 x 32-bit FIFO containing trace data captured from the TPIU. + + Hardware pushes to the FIFO on rising edges of clk_sys, when either of the following is true: + + * TPIU TRACECTL output is low (normal trace data) + + * TPIU TRACETCL output is high, and TPIU TRACEDATA0 and TRACEDATA1 are both low (trigger packet) + + These conditions are in accordance with Arm Coresight Architecture Spec v3.0 section D3.3.3: Decoding requirements for Trace Capture Devices + + The data captured into the FIFO is the full 32-bit TRACEDATA bus output by the TPIU. Note that the TPIU is a DDR output at half of clk_sys, therefore this interface can capture the full 32-bit TPIU DDR output bandwidth as it samples once per active edge of the TPIU output clock. + [31:0] + read-only + modify + + + + + + + USB + USB FS/LS controller device registers + 0x50110000 + + 0 + 280 + registers + + + USBCTRL_IRQ + 14 + + + + ADDR_ENDP + 0x00000000 + Device address and endpoint control + 0x00000000 + + + ENDPOINT + Device endpoint to send data to. Only valid for HOST mode. + [19:16] + read-write + + + ADDRESS + In device mode, the address that the device should respond to. Set in response to a SET_ADDR setup packet from the host. In host mode set to the address of the device to communicate with. + [6:0] + read-write + + + + + ADDR_ENDP1 + 0x00000004 + Interrupt endpoint 1. Only valid for HOST mode. + 0x00000000 + + + INTEP_PREAMBLE + Interrupt EP requires preamble (is a low speed device on a full speed hub) + [26:26] + read-write + + + INTEP_DIR + Direction of the interrupt endpoint. In=0, Out=1 + [25:25] + read-write + + + ENDPOINT + Endpoint number of the interrupt endpoint + [19:16] + read-write + + + ADDRESS + Device address + [6:0] + read-write + + + + + ADDR_ENDP2 + 0x00000008 + Interrupt endpoint 2. Only valid for HOST mode. + 0x00000000 + + + INTEP_PREAMBLE + Interrupt EP requires preamble (is a low speed device on a full speed hub) + [26:26] + read-write + + + INTEP_DIR + Direction of the interrupt endpoint. In=0, Out=1 + [25:25] + read-write + + + ENDPOINT + Endpoint number of the interrupt endpoint + [19:16] + read-write + + + ADDRESS + Device address + [6:0] + read-write + + + + + ADDR_ENDP3 + 0x0000000c + Interrupt endpoint 3. Only valid for HOST mode. + 0x00000000 + + + INTEP_PREAMBLE + Interrupt EP requires preamble (is a low speed device on a full speed hub) + [26:26] + read-write + + + INTEP_DIR + Direction of the interrupt endpoint. In=0, Out=1 + [25:25] + read-write + + + ENDPOINT + Endpoint number of the interrupt endpoint + [19:16] + read-write + + + ADDRESS + Device address + [6:0] + read-write + + + + + ADDR_ENDP4 + 0x00000010 + Interrupt endpoint 4. Only valid for HOST mode. + 0x00000000 + + + INTEP_PREAMBLE + Interrupt EP requires preamble (is a low speed device on a full speed hub) + [26:26] + read-write + + + INTEP_DIR + Direction of the interrupt endpoint. In=0, Out=1 + [25:25] + read-write + + + ENDPOINT + Endpoint number of the interrupt endpoint + [19:16] + read-write + + + ADDRESS + Device address + [6:0] + read-write + + + + + ADDR_ENDP5 + 0x00000014 + Interrupt endpoint 5. Only valid for HOST mode. + 0x00000000 + + + INTEP_PREAMBLE + Interrupt EP requires preamble (is a low speed device on a full speed hub) + [26:26] + read-write + + + INTEP_DIR + Direction of the interrupt endpoint. In=0, Out=1 + [25:25] + read-write + + + ENDPOINT + Endpoint number of the interrupt endpoint + [19:16] + read-write + + + ADDRESS + Device address + [6:0] + read-write + + + + + ADDR_ENDP6 + 0x00000018 + Interrupt endpoint 6. Only valid for HOST mode. + 0x00000000 + + + INTEP_PREAMBLE + Interrupt EP requires preamble (is a low speed device on a full speed hub) + [26:26] + read-write + + + INTEP_DIR + Direction of the interrupt endpoint. In=0, Out=1 + [25:25] + read-write + + + ENDPOINT + Endpoint number of the interrupt endpoint + [19:16] + read-write + + + ADDRESS + Device address + [6:0] + read-write + + + + + ADDR_ENDP7 + 0x0000001c + Interrupt endpoint 7. Only valid for HOST mode. + 0x00000000 + + + INTEP_PREAMBLE + Interrupt EP requires preamble (is a low speed device on a full speed hub) + [26:26] + read-write + + + INTEP_DIR + Direction of the interrupt endpoint. In=0, Out=1 + [25:25] + read-write + + + ENDPOINT + Endpoint number of the interrupt endpoint + [19:16] + read-write + + + ADDRESS + Device address + [6:0] + read-write + + + + + ADDR_ENDP8 + 0x00000020 + Interrupt endpoint 8. Only valid for HOST mode. + 0x00000000 + + + INTEP_PREAMBLE + Interrupt EP requires preamble (is a low speed device on a full speed hub) + [26:26] + read-write + + + INTEP_DIR + Direction of the interrupt endpoint. In=0, Out=1 + [25:25] + read-write + + + ENDPOINT + Endpoint number of the interrupt endpoint + [19:16] + read-write + + + ADDRESS + Device address + [6:0] + read-write + + + + + ADDR_ENDP9 + 0x00000024 + Interrupt endpoint 9. Only valid for HOST mode. + 0x00000000 + + + INTEP_PREAMBLE + Interrupt EP requires preamble (is a low speed device on a full speed hub) + [26:26] + read-write + + + INTEP_DIR + Direction of the interrupt endpoint. In=0, Out=1 + [25:25] + read-write + + + ENDPOINT + Endpoint number of the interrupt endpoint + [19:16] + read-write + + + ADDRESS + Device address + [6:0] + read-write + + + + + ADDR_ENDP10 + 0x00000028 + Interrupt endpoint 10. Only valid for HOST mode. + 0x00000000 + + + INTEP_PREAMBLE + Interrupt EP requires preamble (is a low speed device on a full speed hub) + [26:26] + read-write + + + INTEP_DIR + Direction of the interrupt endpoint. In=0, Out=1 + [25:25] + read-write + + + ENDPOINT + Endpoint number of the interrupt endpoint + [19:16] + read-write + + + ADDRESS + Device address + [6:0] + read-write + + + + + ADDR_ENDP11 + 0x0000002c + Interrupt endpoint 11. Only valid for HOST mode. + 0x00000000 + + + INTEP_PREAMBLE + Interrupt EP requires preamble (is a low speed device on a full speed hub) + [26:26] + read-write + + + INTEP_DIR + Direction of the interrupt endpoint. In=0, Out=1 + [25:25] + read-write + + + ENDPOINT + Endpoint number of the interrupt endpoint + [19:16] + read-write + + + ADDRESS + Device address + [6:0] + read-write + + + + + ADDR_ENDP12 + 0x00000030 + Interrupt endpoint 12. Only valid for HOST mode. + 0x00000000 + + + INTEP_PREAMBLE + Interrupt EP requires preamble (is a low speed device on a full speed hub) + [26:26] + read-write + + + INTEP_DIR + Direction of the interrupt endpoint. In=0, Out=1 + [25:25] + read-write + + + ENDPOINT + Endpoint number of the interrupt endpoint + [19:16] + read-write + + + ADDRESS + Device address + [6:0] + read-write + + + + + ADDR_ENDP13 + 0x00000034 + Interrupt endpoint 13. Only valid for HOST mode. + 0x00000000 + + + INTEP_PREAMBLE + Interrupt EP requires preamble (is a low speed device on a full speed hub) + [26:26] + read-write + + + INTEP_DIR + Direction of the interrupt endpoint. In=0, Out=1 + [25:25] + read-write + + + ENDPOINT + Endpoint number of the interrupt endpoint + [19:16] + read-write + + + ADDRESS + Device address + [6:0] + read-write + + + + + ADDR_ENDP14 + 0x00000038 + Interrupt endpoint 14. Only valid for HOST mode. + 0x00000000 + + + INTEP_PREAMBLE + Interrupt EP requires preamble (is a low speed device on a full speed hub) + [26:26] + read-write + + + INTEP_DIR + Direction of the interrupt endpoint. In=0, Out=1 + [25:25] + read-write + + + ENDPOINT + Endpoint number of the interrupt endpoint + [19:16] + read-write + + + ADDRESS + Device address + [6:0] + read-write + + + + + ADDR_ENDP15 + 0x0000003c + Interrupt endpoint 15. Only valid for HOST mode. + 0x00000000 + + + INTEP_PREAMBLE + Interrupt EP requires preamble (is a low speed device on a full speed hub) + [26:26] + read-write + + + INTEP_DIR + Direction of the interrupt endpoint. In=0, Out=1 + [25:25] + read-write + + + ENDPOINT + Endpoint number of the interrupt endpoint + [19:16] + read-write + + + ADDRESS + Device address + [6:0] + read-write + + + + + MAIN_CTRL + 0x00000040 + Main control register + 0x00000004 + + + SIM_TIMING + Reduced timings for simulation + [31:31] + read-write + + + PHY_ISO + Isolates USB phy after controller power-up + Remove isolation once software has configured the controller + Not isolated = 0, Isolated = 1 + [2:2] + read-write + + + HOST_NDEVICE + Device mode = 0, Host mode = 1 + [1:1] + read-write + + + CONTROLLER_EN + Enable controller + [0:0] + read-write + + + + + SOF_WR + 0x00000044 + Set the SOF (Start of Frame) frame number in the host controller. The SOF packet is sent every 1ms and the host will increment the frame number by 1 each time. + 0x00000000 + + + COUNT + [10:0] + write-only + + + + + SOF_RD + 0x00000048 + Read the last SOF (Start of Frame) frame number seen. In device mode the last SOF received from the host. In host mode the last SOF sent by the host. + 0x00000000 + + + COUNT + [10:0] + read-only + + + + + SIE_CTRL + 0x0000004c + SIE control register + 0x00008000 + + + EP0_INT_STALL + Device: Set bit in EP_STATUS_STALL_NAK when EP0 sends a STALL + [31:31] + read-write + + + EP0_DOUBLE_BUF + Device: EP0 single buffered = 0, double buffered = 1 + [30:30] + read-write + + + EP0_INT_1BUF + Device: Set bit in BUFF_STATUS for every buffer completed on EP0 + [29:29] + read-write + + + EP0_INT_2BUF + Device: Set bit in BUFF_STATUS for every 2 buffers completed on EP0 + [28:28] + read-write + + + EP0_INT_NAK + Device: Set bit in EP_STATUS_STALL_NAK when EP0 sends a NAK + [27:27] + read-write + + + DIRECT_EN + Direct bus drive enable + [26:26] + read-write + + + DIRECT_DP + Direct control of DP + [25:25] + read-write + + + DIRECT_DM + Direct control of DM + [24:24] + read-write + + + EP0_STOP_ON_SHORT_PACKET + Device: Stop EP0 on a short packet. + [19:19] + read-write + + + TRANSCEIVER_PD + Power down bus transceiver + [18:18] + read-write + + + RPU_OPT + Device: Pull-up strength (0=1K2, 1=2k3) + [17:17] + read-write + + + PULLUP_EN + Device: Enable pull up resistor + [16:16] + read-write + + + PULLDOWN_EN + Host: Enable pull down resistors + [15:15] + read-write + + + RESET_BUS + Host: Reset bus + [13:13] + write-only + + + RESUME + Device: Remote wakeup. Device can initiate its own resume after suspend. + [12:12] + write-only + + + VBUS_EN + Host: Enable VBUS + [11:11] + read-write + + + KEEP_ALIVE_EN + Host: Enable keep alive packet (for low speed bus) + [10:10] + read-write + + + SOF_EN + Host: Enable SOF generation (for full speed bus) + [9:9] + read-write + + + SOF_SYNC + Host: Delay packet(s) until after SOF + [8:8] + read-write + + + PREAMBLE_EN + Host: Preable enable for LS device on FS hub + [6:6] + read-write + + + STOP_TRANS + Host: Stop transaction + [4:4] + write-only + + + RECEIVE_DATA + Host: Receive transaction (IN to host) + [3:3] + read-write + + + SEND_DATA + Host: Send transaction (OUT from host) + [2:2] + read-write + + + SEND_SETUP + Host: Send Setup packet + [1:1] + read-write + + + START_TRANS + Host: Start transaction + [0:0] + write-only + + + + + SIE_STATUS + 0x00000050 + SIE status register + 0x00000000 + + + DATA_SEQ_ERROR + Data Sequence Error. + + The device can raise a sequence error in the following conditions: + + * A SETUP packet is received followed by a DATA1 packet (data phase should always be DATA0) * An OUT packet is received from the host but doesn't match the data pid in the buffer control register read from DPSRAM + + The host can raise a data sequence error in the following conditions: + + * An IN packet from the device has the wrong data PID + [31:31] + read-write + oneToClear + + + ACK_REC + ACK received. Raised by both host and device. + [30:30] + read-write + oneToClear + + + STALL_REC + Host: STALL received + [29:29] + read-write + oneToClear + + + NAK_REC + Host: NAK received + [28:28] + read-write + oneToClear + + + RX_TIMEOUT + RX timeout is raised by both the host and device if an ACK is not received in the maximum time specified by the USB spec. + [27:27] + read-write + oneToClear + + + RX_OVERFLOW + RX overflow is raised by the Serial RX engine if the incoming data is too fast. + [26:26] + read-write + oneToClear + + + BIT_STUFF_ERROR + Bit Stuff Error. Raised by the Serial RX engine. + [25:25] + read-write + oneToClear + + + CRC_ERROR + CRC Error. Raised by the Serial RX engine. + [24:24] + read-write + oneToClear + + + ENDPOINT_ERROR + An endpoint has encountered an error. Read the ep_rx_error and ep_tx_error registers to find out which endpoint had an error. + [23:23] + read-write + oneToClear + + + BUS_RESET + Device: bus reset received + [19:19] + read-write + oneToClear + + + TRANS_COMPLETE + Transaction complete. + + Raised by device if: + + * An IN or OUT packet is sent with the `LAST_BUFF` bit set in the buffer control register + + Raised by host if: + + * A setup packet is sent when no data in or data out transaction follows * An IN packet is received and the `LAST_BUFF` bit is set in the buffer control register * An IN packet is received with zero length * An OUT packet is sent and the `LAST_BUFF` bit is set + [18:18] + read-write + oneToClear + + + SETUP_REC + Device: Setup packet received + [17:17] + read-write + oneToClear + + + CONNECTED + Device: connected + [16:16] + read-only + + + RX_SHORT_PACKET + Device or Host has received a short packet. This is when the data received is less than configured in the buffer control register. Device: If using double buffered mode on device the buffer select will not be toggled after writing status back to the buffer control register. This is to prevent any further transactions on that endpoint until the user has reset the buffer control registers. Host: the current transfer will be stopped early. + [12:12] + read-write + oneToClear + + + RESUME + Host: Device has initiated a remote resume. Device: host has initiated a resume. + [11:11] + read-write + oneToClear + + + VBUS_OVER_CURR + VBUS over current detected + [10:10] + read-only + + + SPEED + Host: device speed. Disconnected = 00, LS = 01, FS = 10 + [9:8] + read-only + + + SUSPENDED + Bus in suspended state. Valid for device. Device will go into suspend if neither Keep Alive / SOF frames are enabled. + [4:4] + read-write + oneToClear + + + LINE_STATE + USB bus line state + [3:2] + read-only + + + VBUS_DETECTED + Device: VBUS Detected + [0:0] + read-only + + + + + INT_EP_CTRL + 0x00000054 + interrupt endpoint control register + 0x00000000 + + + INT_EP_ACTIVE + Host: Enable interrupt endpoint 1 -> 15 + [15:1] + read-write + + + + + BUFF_STATUS + 0x00000058 + Buffer status register. A bit set here indicates that a buffer has completed on the endpoint (if the buffer interrupt is enabled). It is possible for 2 buffers to be completed, so clearing the buffer status bit may instantly re set it on the next clock cycle. + 0x00000000 + + + EP15_OUT + [31:31] + read-write + oneToClear + + + EP15_IN + [30:30] + read-write + oneToClear + + + EP14_OUT + [29:29] + read-write + oneToClear + + + EP14_IN + [28:28] + read-write + oneToClear + + + EP13_OUT + [27:27] + read-write + oneToClear + + + EP13_IN + [26:26] + read-write + oneToClear + + + EP12_OUT + [25:25] + read-write + oneToClear + + + EP12_IN + [24:24] + read-write + oneToClear + + + EP11_OUT + [23:23] + read-write + oneToClear + + + EP11_IN + [22:22] + read-write + oneToClear + + + EP10_OUT + [21:21] + read-write + oneToClear + + + EP10_IN + [20:20] + read-write + oneToClear + + + EP9_OUT + [19:19] + read-write + oneToClear + + + EP9_IN + [18:18] + read-write + oneToClear + + + EP8_OUT + [17:17] + read-write + oneToClear + + + EP8_IN + [16:16] + read-write + oneToClear + + + EP7_OUT + [15:15] + read-write + oneToClear + + + EP7_IN + [14:14] + read-write + oneToClear + + + EP6_OUT + [13:13] + read-write + oneToClear + + + EP6_IN + [12:12] + read-write + oneToClear + + + EP5_OUT + [11:11] + read-write + oneToClear + + + EP5_IN + [10:10] + read-write + oneToClear + + + EP4_OUT + [9:9] + read-write + oneToClear + + + EP4_IN + [8:8] + read-write + oneToClear + + + EP3_OUT + [7:7] + read-write + oneToClear + + + EP3_IN + [6:6] + read-write + oneToClear + + + EP2_OUT + [5:5] + read-write + oneToClear + + + EP2_IN + [4:4] + read-write + oneToClear + + + EP1_OUT + [3:3] + read-write + oneToClear + + + EP1_IN + [2:2] + read-write + oneToClear + + + EP0_OUT + [1:1] + read-write + oneToClear + + + EP0_IN + [0:0] + read-write + oneToClear + + + + + BUFF_CPU_SHOULD_HANDLE + 0x0000005c + Which of the double buffers should be handled. Only valid if using an interrupt per buffer (i.e. not per 2 buffers). Not valid for host interrupt endpoint polling because they are only single buffered. + 0x00000000 + + + EP15_OUT + [31:31] + read-only + + + EP15_IN + [30:30] + read-only + + + EP14_OUT + [29:29] + read-only + + + EP14_IN + [28:28] + read-only + + + EP13_OUT + [27:27] + read-only + + + EP13_IN + [26:26] + read-only + + + EP12_OUT + [25:25] + read-only + + + EP12_IN + [24:24] + read-only + + + EP11_OUT + [23:23] + read-only + + + EP11_IN + [22:22] + read-only + + + EP10_OUT + [21:21] + read-only + + + EP10_IN + [20:20] + read-only + + + EP9_OUT + [19:19] + read-only + + + EP9_IN + [18:18] + read-only + + + EP8_OUT + [17:17] + read-only + + + EP8_IN + [16:16] + read-only + + + EP7_OUT + [15:15] + read-only + + + EP7_IN + [14:14] + read-only + + + EP6_OUT + [13:13] + read-only + + + EP6_IN + [12:12] + read-only + + + EP5_OUT + [11:11] + read-only + + + EP5_IN + [10:10] + read-only + + + EP4_OUT + [9:9] + read-only + + + EP4_IN + [8:8] + read-only + + + EP3_OUT + [7:7] + read-only + + + EP3_IN + [6:6] + read-only + + + EP2_OUT + [5:5] + read-only + + + EP2_IN + [4:4] + read-only + + + EP1_OUT + [3:3] + read-only + + + EP1_IN + [2:2] + read-only + + + EP0_OUT + [1:1] + read-only + + + EP0_IN + [0:0] + read-only + + + + + EP_ABORT + 0x00000060 + Device only: Can be set to ignore the buffer control register for this endpoint in case you would like to revoke a buffer. A NAK will be sent for every access to the endpoint until this bit is cleared. A corresponding bit in `EP_ABORT_DONE` is set when it is safe to modify the buffer control register. + 0x00000000 + + + EP15_OUT + [31:31] + read-write + + + EP15_IN + [30:30] + read-write + + + EP14_OUT + [29:29] + read-write + + + EP14_IN + [28:28] + read-write + + + EP13_OUT + [27:27] + read-write + + + EP13_IN + [26:26] + read-write + + + EP12_OUT + [25:25] + read-write + + + EP12_IN + [24:24] + read-write + + + EP11_OUT + [23:23] + read-write + + + EP11_IN + [22:22] + read-write + + + EP10_OUT + [21:21] + read-write + + + EP10_IN + [20:20] + read-write + + + EP9_OUT + [19:19] + read-write + + + EP9_IN + [18:18] + read-write + + + EP8_OUT + [17:17] + read-write + + + EP8_IN + [16:16] + read-write + + + EP7_OUT + [15:15] + read-write + + + EP7_IN + [14:14] + read-write + + + EP6_OUT + [13:13] + read-write + + + EP6_IN + [12:12] + read-write + + + EP5_OUT + [11:11] + read-write + + + EP5_IN + [10:10] + read-write + + + EP4_OUT + [9:9] + read-write + + + EP4_IN + [8:8] + read-write + + + EP3_OUT + [7:7] + read-write + + + EP3_IN + [6:6] + read-write + + + EP2_OUT + [5:5] + read-write + + + EP2_IN + [4:4] + read-write + + + EP1_OUT + [3:3] + read-write + + + EP1_IN + [2:2] + read-write + + + EP0_OUT + [1:1] + read-write + + + EP0_IN + [0:0] + read-write + + + + + EP_ABORT_DONE + 0x00000064 + Device only: Used in conjunction with `EP_ABORT`. Set once an endpoint is idle so the programmer knows it is safe to modify the buffer control register. + 0x00000000 + + + EP15_OUT + [31:31] + read-write + oneToClear + + + EP15_IN + [30:30] + read-write + oneToClear + + + EP14_OUT + [29:29] + read-write + oneToClear + + + EP14_IN + [28:28] + read-write + oneToClear + + + EP13_OUT + [27:27] + read-write + oneToClear + + + EP13_IN + [26:26] + read-write + oneToClear + + + EP12_OUT + [25:25] + read-write + oneToClear + + + EP12_IN + [24:24] + read-write + oneToClear + + + EP11_OUT + [23:23] + read-write + oneToClear + + + EP11_IN + [22:22] + read-write + oneToClear + + + EP10_OUT + [21:21] + read-write + oneToClear + + + EP10_IN + [20:20] + read-write + oneToClear + + + EP9_OUT + [19:19] + read-write + oneToClear + + + EP9_IN + [18:18] + read-write + oneToClear + + + EP8_OUT + [17:17] + read-write + oneToClear + + + EP8_IN + [16:16] + read-write + oneToClear + + + EP7_OUT + [15:15] + read-write + oneToClear + + + EP7_IN + [14:14] + read-write + oneToClear + + + EP6_OUT + [13:13] + read-write + oneToClear + + + EP6_IN + [12:12] + read-write + oneToClear + + + EP5_OUT + [11:11] + read-write + oneToClear + + + EP5_IN + [10:10] + read-write + oneToClear + + + EP4_OUT + [9:9] + read-write + oneToClear + + + EP4_IN + [8:8] + read-write + oneToClear + + + EP3_OUT + [7:7] + read-write + oneToClear + + + EP3_IN + [6:6] + read-write + oneToClear + + + EP2_OUT + [5:5] + read-write + oneToClear + + + EP2_IN + [4:4] + read-write + oneToClear + + + EP1_OUT + [3:3] + read-write + oneToClear + + + EP1_IN + [2:2] + read-write + oneToClear + + + EP0_OUT + [1:1] + read-write + oneToClear + + + EP0_IN + [0:0] + read-write + oneToClear + + + + + EP_STALL_ARM + 0x00000068 + Device: this bit must be set in conjunction with the `STALL` bit in the buffer control register to send a STALL on EP0. The device controller clears these bits when a SETUP packet is received because the USB spec requires that a STALL condition is cleared when a SETUP packet is received. + 0x00000000 + + + EP0_OUT + [1:1] + read-write + + + EP0_IN + [0:0] + read-write + + + + + NAK_POLL + 0x0000006c + Used by the host controller. Sets the wait time in microseconds before trying again if the device replies with a NAK. + 0x00100010 + + + RETRY_COUNT_HI + Bits 9:6 of nak_retry count + [31:28] + read-only + + + EPX_STOPPED_ON_NAK + EPX polling has stopped because a nak was received + [27:27] + read-write + oneToClear + + + STOP_EPX_ON_NAK + Stop polling epx when a nak is received + [26:26] + read-write + + + DELAY_FS + NAK polling interval for a full speed device + [25:16] + read-write + + + RETRY_COUNT_LO + Bits 5:0 of nak_retry_count + [15:10] + read-only + + + DELAY_LS + NAK polling interval for a low speed device + [9:0] + read-write + + + + + EP_STATUS_STALL_NAK + 0x00000070 + Device: bits are set when the `IRQ_ON_NAK` or `IRQ_ON_STALL` bits are set. For EP0 this comes from `SIE_CTRL`. For all other endpoints it comes from the endpoint control register. + 0x00000000 + + + EP15_OUT + [31:31] + read-write + oneToClear + + + EP15_IN + [30:30] + read-write + oneToClear + + + EP14_OUT + [29:29] + read-write + oneToClear + + + EP14_IN + [28:28] + read-write + oneToClear + + + EP13_OUT + [27:27] + read-write + oneToClear + + + EP13_IN + [26:26] + read-write + oneToClear + + + EP12_OUT + [25:25] + read-write + oneToClear + + + EP12_IN + [24:24] + read-write + oneToClear + + + EP11_OUT + [23:23] + read-write + oneToClear + + + EP11_IN + [22:22] + read-write + oneToClear + + + EP10_OUT + [21:21] + read-write + oneToClear + + + EP10_IN + [20:20] + read-write + oneToClear + + + EP9_OUT + [19:19] + read-write + oneToClear + + + EP9_IN + [18:18] + read-write + oneToClear + + + EP8_OUT + [17:17] + read-write + oneToClear + + + EP8_IN + [16:16] + read-write + oneToClear + + + EP7_OUT + [15:15] + read-write + oneToClear + + + EP7_IN + [14:14] + read-write + oneToClear + + + EP6_OUT + [13:13] + read-write + oneToClear + + + EP6_IN + [12:12] + read-write + oneToClear + + + EP5_OUT + [11:11] + read-write + oneToClear + + + EP5_IN + [10:10] + read-write + oneToClear + + + EP4_OUT + [9:9] + read-write + oneToClear + + + EP4_IN + [8:8] + read-write + oneToClear + + + EP3_OUT + [7:7] + read-write + oneToClear + + + EP3_IN + [6:6] + read-write + oneToClear + + + EP2_OUT + [5:5] + read-write + oneToClear + + + EP2_IN + [4:4] + read-write + oneToClear + + + EP1_OUT + [3:3] + read-write + oneToClear + + + EP1_IN + [2:2] + read-write + oneToClear + + + EP0_OUT + [1:1] + read-write + oneToClear + + + EP0_IN + [0:0] + read-write + oneToClear + + + + + USB_MUXING + 0x00000074 + Where to connect the USB controller. Should be to_phy by default. + 0x00000001 + + + SWAP_DPDM + Swap the USB PHY DP and DM pins and all related controls and flip receive differential data. Can be used to switch USB DP/DP on the PCB. + This is done at a low level so overrides all other controls. + [31:31] + read-write + + + USBPHY_AS_GPIO + Use the usb DP and DM pins as GPIO pins instead of connecting them to the USB controller. + [4:4] + read-write + + + SOFTCON + [3:3] + read-write + + + TO_DIGITAL_PAD + [2:2] + read-write + + + TO_EXTPHY + [1:1] + read-write + + + TO_PHY + [0:0] + read-write + + + + + USB_PWR + 0x00000078 + Overrides for the power signals in the event that the VBUS signals are not hooked up to GPIO. Set the value of the override and then the override enable to switch over to the override value. + 0x00000000 + + + OVERCURR_DETECT_EN + [5:5] + read-write + + + OVERCURR_DETECT + [4:4] + read-write + + + VBUS_DETECT_OVERRIDE_EN + [3:3] + read-write + + + VBUS_DETECT + [2:2] + read-write + + + VBUS_EN_OVERRIDE_EN + [1:1] + read-write + + + VBUS_EN + [0:0] + read-write + + + + + USBPHY_DIRECT + 0x0000007c + This register allows for direct control of the USB phy. Use in conjunction with usbphy_direct_override register to enable each override bit. + 0x00000000 + + + RX_DM_OVERRIDE + Override rx_dm value into controller + [25:25] + read-write + + + RX_DP_OVERRIDE + Override rx_dp value into controller + [24:24] + read-write + + + RX_DD_OVERRIDE + Override rx_dd value into controller + [23:23] + read-write + + + DM_OVV + DM over voltage + [22:22] + read-only + + + DP_OVV + DP over voltage + [21:21] + read-only + + + DM_OVCN + DM overcurrent + [20:20] + read-only + + + DP_OVCN + DP overcurrent + [19:19] + read-only + + + RX_DM + DPM pin state + [18:18] + read-only + + + RX_DP + DPP pin state + [17:17] + read-only + + + RX_DD + Differential RX + [16:16] + read-only + + + TX_DIFFMODE + TX_DIFFMODE=0: Single ended mode + TX_DIFFMODE=1: Differential drive mode (TX_DM, TX_DM_OE ignored) + [15:15] + read-write + + + TX_FSSLEW + TX_FSSLEW=0: Low speed slew rate + TX_FSSLEW=1: Full speed slew rate + [14:14] + read-write + + + TX_PD + TX power down override (if override enable is set). 1 = powered down. + [13:13] + read-write + + + RX_PD + RX power down override (if override enable is set). 1 = powered down. + [12:12] + read-write + + + TX_DM + Output data. TX_DIFFMODE=1, Ignored + TX_DIFFMODE=0, Drives DPM only. TX_DM_OE=1 to enable drive. DPM=TX_DM + [11:11] + read-write + + + TX_DP + Output data. If TX_DIFFMODE=1, Drives DPP/DPM diff pair. TX_DP_OE=1 to enable drive. DPP=TX_DP, DPM=~TX_DP + If TX_DIFFMODE=0, Drives DPP only. TX_DP_OE=1 to enable drive. DPP=TX_DP + [10:10] + read-write + + + TX_DM_OE + Output enable. If TX_DIFFMODE=1, Ignored. + If TX_DIFFMODE=0, OE for DPM only. 0 - DPM in Hi-Z state; 1 - DPM driving + [9:9] + read-write + + + TX_DP_OE + Output enable. If TX_DIFFMODE=1, OE for DPP/DPM diff pair. 0 - DPP/DPM in Hi-Z state; 1 - DPP/DPM driving + If TX_DIFFMODE=0, OE for DPP only. 0 - DPP in Hi-Z state; 1 - DPP driving + [8:8] + read-write + + + DM_PULLDN_EN + DM pull down enable + [6:6] + read-write + + + DM_PULLUP_EN + DM pull up enable + [5:5] + read-write + + + DM_PULLUP_HISEL + Enable the second DM pull up resistor. 0 - Pull = Rpu2; 1 - Pull = Rpu1 + Rpu2 + [4:4] + read-write + + + DP_PULLDN_EN + DP pull down enable + [2:2] + read-write + + + DP_PULLUP_EN + DP pull up enable + [1:1] + read-write + + + DP_PULLUP_HISEL + Enable the second DP pull up resistor. 0 - Pull = Rpu2; 1 - Pull = Rpu1 + Rpu2 + [0:0] + read-write + + + + + USBPHY_DIRECT_OVERRIDE + 0x00000080 + Override enable for each control in usbphy_direct + 0x00000000 + + + RX_DM_OVERRIDE_EN + [18:18] + read-write + + + RX_DP_OVERRIDE_EN + [17:17] + read-write + + + RX_DD_OVERRIDE_EN + [16:16] + read-write + + + TX_DIFFMODE_OVERRIDE_EN + [15:15] + read-write + + + DM_PULLUP_OVERRIDE_EN + [12:12] + read-write + + + TX_FSSLEW_OVERRIDE_EN + [11:11] + read-write + + + TX_PD_OVERRIDE_EN + [10:10] + read-write + + + RX_PD_OVERRIDE_EN + [9:9] + read-write + + + TX_DM_OVERRIDE_EN + [8:8] + read-write + + + TX_DP_OVERRIDE_EN + [7:7] + read-write + + + TX_DM_OE_OVERRIDE_EN + [6:6] + read-write + + + TX_DP_OE_OVERRIDE_EN + [5:5] + read-write + + + DM_PULLDN_EN_OVERRIDE_EN + [4:4] + read-write + + + DP_PULLDN_EN_OVERRIDE_EN + [3:3] + read-write + + + DP_PULLUP_EN_OVERRIDE_EN + [2:2] + read-write + + + DM_PULLUP_HISEL_OVERRIDE_EN + [1:1] + read-write + + + DP_PULLUP_HISEL_OVERRIDE_EN + [0:0] + read-write + + + + + USBPHY_TRIM + 0x00000084 + Used to adjust trim values of USB phy pull down resistors. + 0x00001f1f + + + DM_PULLDN_TRIM + Value to drive to USB PHY + DM pulldown resistor trim control + Experimental data suggests that the reset value will work, but this register allows adjustment if required + [12:8] + read-write + + + DP_PULLDN_TRIM + Value to drive to USB PHY + DP pulldown resistor trim control + Experimental data suggests that the reset value will work, but this register allows adjustment if required + [4:0] + read-write + + + + + LINESTATE_TUNING + 0x00000088 + Used for debug only. + 0x000000f8 + + + SPARE_FIX + [11:8] + read-write + + + DEV_LS_WAKE_FIX + Device - exit suspend on any non-idle signalling, not qualified with a 1ms timer + [7:7] + read-write + + + DEV_RX_ERR_QUIESCE + Device - suppress repeated errors until the device FSM is next in the process of decoding an inbound packet. + [6:6] + read-write + + + SIE_RX_CHATTER_SE0_FIX + RX - when recovering from line chatter or bitstuff errors, treat SE0 as the end of chatter as well as + 8 consecutive idle bits. + [5:5] + read-write + + + SIE_RX_BITSTUFF_FIX + RX - when a bitstuff error is signalled by rx_dasm, unconditionally terminate RX decode to + avoid a hang during certain packet phases. + [4:4] + read-write + + + DEV_BUFF_CONTROL_DOUBLE_READ_FIX + Device - the controller FSM performs two reads of the buffer status memory address to + avoid sampling metastable data. An enabled buffer is only used if both reads match. + [3:3] + read-write + + + MULTI_HUB_FIX + Host - increase inter-packet and turnaround timeouts to accommodate worst-case hub delays. + [2:2] + read-write + + + LINESTATE_DELAY + Device/Host - add an extra 1-bit debounce of linestate sampling. + [1:1] + read-write + + + RCV_DELAY + Device - register the received data to account for hub bit dribble before EOP. Only affects certain hubs. + [0:0] + read-write + + + + + INTR + 0x0000008c + Raw Interrupts + 0x00000000 + + + EPX_STOPPED_ON_NAK + Source: NAK_POLL.EPX_STOPPED_ON_NAK + [23:23] + read-only + + + DEV_SM_WATCHDOG_FIRED + Source: DEV_SM_WATCHDOG.FIRED + [22:22] + read-only + + + ENDPOINT_ERROR + Source: SIE_STATUS.ENDPOINT_ERROR + [21:21] + read-only + + + RX_SHORT_PACKET + Source: SIE_STATUS.RX_SHORT_PACKET + [20:20] + read-only + + + EP_STALL_NAK + Raised when any bit in EP_STATUS_STALL_NAK is set. Clear by clearing all bits in EP_STATUS_STALL_NAK. + [19:19] + read-only + + + ABORT_DONE + Raised when any bit in ABORT_DONE is set. Clear by clearing all bits in ABORT_DONE. + [18:18] + read-only + + + DEV_SOF + Set every time the device receives a SOF (Start of Frame) packet. Cleared by reading SOF_RD + [17:17] + read-only + + + SETUP_REQ + Device. Source: SIE_STATUS.SETUP_REC + [16:16] + read-only + + + DEV_RESUME_FROM_HOST + Set when the device receives a resume from the host. Cleared by writing to SIE_STATUS.RESUME + [15:15] + read-only + + + DEV_SUSPEND + Set when the device suspend state changes. Cleared by writing to SIE_STATUS.SUSPENDED + [14:14] + read-only + + + DEV_CONN_DIS + Set when the device connection state changes. Cleared by writing to SIE_STATUS.CONNECTED + [13:13] + read-only + + + BUS_RESET + Source: SIE_STATUS.BUS_RESET + [12:12] + read-only + + + VBUS_DETECT + Source: SIE_STATUS.VBUS_DETECTED + [11:11] + read-only + + + STALL + Source: SIE_STATUS.STALL_REC + [10:10] + read-only + + + ERROR_CRC + Source: SIE_STATUS.CRC_ERROR + [9:9] + read-only + + + ERROR_BIT_STUFF + Source: SIE_STATUS.BIT_STUFF_ERROR + [8:8] + read-only + + + ERROR_RX_OVERFLOW + Source: SIE_STATUS.RX_OVERFLOW + [7:7] + read-only + + + ERROR_RX_TIMEOUT + Source: SIE_STATUS.RX_TIMEOUT + [6:6] + read-only + + + ERROR_DATA_SEQ + Source: SIE_STATUS.DATA_SEQ_ERROR + [5:5] + read-only + + + BUFF_STATUS + Raised when any bit in BUFF_STATUS is set. Clear by clearing all bits in BUFF_STATUS. + [4:4] + read-only + + + TRANS_COMPLETE + Raised every time SIE_STATUS.TRANS_COMPLETE is set. Clear by writing to this bit. + [3:3] + read-only + + + HOST_SOF + Host: raised every time the host sends a SOF (Start of Frame). Cleared by reading SOF_RD + [2:2] + read-only + + + HOST_RESUME + Host: raised when a device wakes up the host. Cleared by writing to SIE_STATUS.RESUME + [1:1] + read-only + + + HOST_CONN_DIS + Host: raised when a device is connected or disconnected (i.e. when SIE_STATUS.SPEED changes). Cleared by writing to SIE_STATUS.SPEED + [0:0] + read-only + + + + + INTE + 0x00000090 + Interrupt Enable + 0x00000000 + + + EPX_STOPPED_ON_NAK + Source: NAK_POLL.EPX_STOPPED_ON_NAK + [23:23] + read-write + + + DEV_SM_WATCHDOG_FIRED + Source: DEV_SM_WATCHDOG.FIRED + [22:22] + read-write + + + ENDPOINT_ERROR + Source: SIE_STATUS.ENDPOINT_ERROR + [21:21] + read-write + + + RX_SHORT_PACKET + Source: SIE_STATUS.RX_SHORT_PACKET + [20:20] + read-write + + + EP_STALL_NAK + Raised when any bit in EP_STATUS_STALL_NAK is set. Clear by clearing all bits in EP_STATUS_STALL_NAK. + [19:19] + read-write + + + ABORT_DONE + Raised when any bit in ABORT_DONE is set. Clear by clearing all bits in ABORT_DONE. + [18:18] + read-write + + + DEV_SOF + Set every time the device receives a SOF (Start of Frame) packet. Cleared by reading SOF_RD + [17:17] + read-write + + + SETUP_REQ + Device. Source: SIE_STATUS.SETUP_REC + [16:16] + read-write + + + DEV_RESUME_FROM_HOST + Set when the device receives a resume from the host. Cleared by writing to SIE_STATUS.RESUME + [15:15] + read-write + + + DEV_SUSPEND + Set when the device suspend state changes. Cleared by writing to SIE_STATUS.SUSPENDED + [14:14] + read-write + + + DEV_CONN_DIS + Set when the device connection state changes. Cleared by writing to SIE_STATUS.CONNECTED + [13:13] + read-write + + + BUS_RESET + Source: SIE_STATUS.BUS_RESET + [12:12] + read-write + + + VBUS_DETECT + Source: SIE_STATUS.VBUS_DETECTED + [11:11] + read-write + + + STALL + Source: SIE_STATUS.STALL_REC + [10:10] + read-write + + + ERROR_CRC + Source: SIE_STATUS.CRC_ERROR + [9:9] + read-write + + + ERROR_BIT_STUFF + Source: SIE_STATUS.BIT_STUFF_ERROR + [8:8] + read-write + + + ERROR_RX_OVERFLOW + Source: SIE_STATUS.RX_OVERFLOW + [7:7] + read-write + + + ERROR_RX_TIMEOUT + Source: SIE_STATUS.RX_TIMEOUT + [6:6] + read-write + + + ERROR_DATA_SEQ + Source: SIE_STATUS.DATA_SEQ_ERROR + [5:5] + read-write + + + BUFF_STATUS + Raised when any bit in BUFF_STATUS is set. Clear by clearing all bits in BUFF_STATUS. + [4:4] + read-write + + + TRANS_COMPLETE + Raised every time SIE_STATUS.TRANS_COMPLETE is set. Clear by writing to this bit. + [3:3] + read-write + + + HOST_SOF + Host: raised every time the host sends a SOF (Start of Frame). Cleared by reading SOF_RD + [2:2] + read-write + + + HOST_RESUME + Host: raised when a device wakes up the host. Cleared by writing to SIE_STATUS.RESUME + [1:1] + read-write + + + HOST_CONN_DIS + Host: raised when a device is connected or disconnected (i.e. when SIE_STATUS.SPEED changes). Cleared by writing to SIE_STATUS.SPEED + [0:0] + read-write + + + + + INTF + 0x00000094 + Interrupt Force + 0x00000000 + + + EPX_STOPPED_ON_NAK + Source: NAK_POLL.EPX_STOPPED_ON_NAK + [23:23] + read-write + + + DEV_SM_WATCHDOG_FIRED + Source: DEV_SM_WATCHDOG.FIRED + [22:22] + read-write + + + ENDPOINT_ERROR + Source: SIE_STATUS.ENDPOINT_ERROR + [21:21] + read-write + + + RX_SHORT_PACKET + Source: SIE_STATUS.RX_SHORT_PACKET + [20:20] + read-write + + + EP_STALL_NAK + Raised when any bit in EP_STATUS_STALL_NAK is set. Clear by clearing all bits in EP_STATUS_STALL_NAK. + [19:19] + read-write + + + ABORT_DONE + Raised when any bit in ABORT_DONE is set. Clear by clearing all bits in ABORT_DONE. + [18:18] + read-write + + + DEV_SOF + Set every time the device receives a SOF (Start of Frame) packet. Cleared by reading SOF_RD + [17:17] + read-write + + + SETUP_REQ + Device. Source: SIE_STATUS.SETUP_REC + [16:16] + read-write + + + DEV_RESUME_FROM_HOST + Set when the device receives a resume from the host. Cleared by writing to SIE_STATUS.RESUME + [15:15] + read-write + + + DEV_SUSPEND + Set when the device suspend state changes. Cleared by writing to SIE_STATUS.SUSPENDED + [14:14] + read-write + + + DEV_CONN_DIS + Set when the device connection state changes. Cleared by writing to SIE_STATUS.CONNECTED + [13:13] + read-write + + + BUS_RESET + Source: SIE_STATUS.BUS_RESET + [12:12] + read-write + + + VBUS_DETECT + Source: SIE_STATUS.VBUS_DETECTED + [11:11] + read-write + + + STALL + Source: SIE_STATUS.STALL_REC + [10:10] + read-write + + + ERROR_CRC + Source: SIE_STATUS.CRC_ERROR + [9:9] + read-write + + + ERROR_BIT_STUFF + Source: SIE_STATUS.BIT_STUFF_ERROR + [8:8] + read-write + + + ERROR_RX_OVERFLOW + Source: SIE_STATUS.RX_OVERFLOW + [7:7] + read-write + + + ERROR_RX_TIMEOUT + Source: SIE_STATUS.RX_TIMEOUT + [6:6] + read-write + + + ERROR_DATA_SEQ + Source: SIE_STATUS.DATA_SEQ_ERROR + [5:5] + read-write + + + BUFF_STATUS + Raised when any bit in BUFF_STATUS is set. Clear by clearing all bits in BUFF_STATUS. + [4:4] + read-write + + + TRANS_COMPLETE + Raised every time SIE_STATUS.TRANS_COMPLETE is set. Clear by writing to this bit. + [3:3] + read-write + + + HOST_SOF + Host: raised every time the host sends a SOF (Start of Frame). Cleared by reading SOF_RD + [2:2] + read-write + + + HOST_RESUME + Host: raised when a device wakes up the host. Cleared by writing to SIE_STATUS.RESUME + [1:1] + read-write + + + HOST_CONN_DIS + Host: raised when a device is connected or disconnected (i.e. when SIE_STATUS.SPEED changes). Cleared by writing to SIE_STATUS.SPEED + [0:0] + read-write + + + + + INTS + 0x00000098 + Interrupt status after masking & forcing + 0x00000000 + + + EPX_STOPPED_ON_NAK + Source: NAK_POLL.EPX_STOPPED_ON_NAK + [23:23] + read-only + + + DEV_SM_WATCHDOG_FIRED + Source: DEV_SM_WATCHDOG.FIRED + [22:22] + read-only + + + ENDPOINT_ERROR + Source: SIE_STATUS.ENDPOINT_ERROR + [21:21] + read-only + + + RX_SHORT_PACKET + Source: SIE_STATUS.RX_SHORT_PACKET + [20:20] + read-only + + + EP_STALL_NAK + Raised when any bit in EP_STATUS_STALL_NAK is set. Clear by clearing all bits in EP_STATUS_STALL_NAK. + [19:19] + read-only + + + ABORT_DONE + Raised when any bit in ABORT_DONE is set. Clear by clearing all bits in ABORT_DONE. + [18:18] + read-only + + + DEV_SOF + Set every time the device receives a SOF (Start of Frame) packet. Cleared by reading SOF_RD + [17:17] + read-only + + + SETUP_REQ + Device. Source: SIE_STATUS.SETUP_REC + [16:16] + read-only + + + DEV_RESUME_FROM_HOST + Set when the device receives a resume from the host. Cleared by writing to SIE_STATUS.RESUME + [15:15] + read-only + + + DEV_SUSPEND + Set when the device suspend state changes. Cleared by writing to SIE_STATUS.SUSPENDED + [14:14] + read-only + + + DEV_CONN_DIS + Set when the device connection state changes. Cleared by writing to SIE_STATUS.CONNECTED + [13:13] + read-only + + + BUS_RESET + Source: SIE_STATUS.BUS_RESET + [12:12] + read-only + + + VBUS_DETECT + Source: SIE_STATUS.VBUS_DETECTED + [11:11] + read-only + + + STALL + Source: SIE_STATUS.STALL_REC + [10:10] + read-only + + + ERROR_CRC + Source: SIE_STATUS.CRC_ERROR + [9:9] + read-only + + + ERROR_BIT_STUFF + Source: SIE_STATUS.BIT_STUFF_ERROR + [8:8] + read-only + + + ERROR_RX_OVERFLOW + Source: SIE_STATUS.RX_OVERFLOW + [7:7] + read-only + + + ERROR_RX_TIMEOUT + Source: SIE_STATUS.RX_TIMEOUT + [6:6] + read-only + + + ERROR_DATA_SEQ + Source: SIE_STATUS.DATA_SEQ_ERROR + [5:5] + read-only + + + BUFF_STATUS + Raised when any bit in BUFF_STATUS is set. Clear by clearing all bits in BUFF_STATUS. + [4:4] + read-only + + + TRANS_COMPLETE + Raised every time SIE_STATUS.TRANS_COMPLETE is set. Clear by writing to this bit. + [3:3] + read-only + + + HOST_SOF + Host: raised every time the host sends a SOF (Start of Frame). Cleared by reading SOF_RD + [2:2] + read-only + + + HOST_RESUME + Host: raised when a device wakes up the host. Cleared by writing to SIE_STATUS.RESUME + [1:1] + read-only + + + HOST_CONN_DIS + Host: raised when a device is connected or disconnected (i.e. when SIE_STATUS.SPEED changes). Cleared by writing to SIE_STATUS.SPEED + [0:0] + read-only + + + + + SOF_TIMESTAMP_RAW + 0x00000100 + Device only. Raw value of free-running PHY clock counter @48MHz. Used to calculate time between SOF events. + 0x00000000 + + + SOF_TIMESTAMP_RAW + [20:0] + read-only + + + + + SOF_TIMESTAMP_LAST + 0x00000104 + Device only. Value of free-running PHY clock counter @48MHz when last SOF event occurred. + 0x00000000 + + + SOF_TIMESTAMP_LAST + [20:0] + read-only + + + + + SM_STATE + 0x00000108 + 0x00000000 + + + RX_DASM + [11:8] + read-only + + + BC_STATE + [7:5] + read-only + + + STATE + [4:0] + read-only + + + + + EP_TX_ERROR + 0x0000010c + TX error count for each endpoint. Write to each field to reset the counter to 0. + 0x00000000 + + + EP15 + [31:30] + read-write + oneToClear + + + EP14 + [29:28] + read-write + oneToClear + + + EP13 + [27:26] + read-write + oneToClear + + + EP12 + [25:24] + read-write + oneToClear + + + EP11 + [23:22] + read-write + oneToClear + + + EP10 + [21:20] + read-write + oneToClear + + + EP9 + [19:18] + read-write + oneToClear + + + EP8 + [17:16] + read-write + oneToClear + + + EP7 + [15:14] + read-write + oneToClear + + + EP6 + [13:12] + read-write + oneToClear + + + EP5 + [11:10] + read-write + oneToClear + + + EP4 + [9:8] + read-write + oneToClear + + + EP3 + [7:6] + read-write + oneToClear + + + EP2 + [5:4] + read-write + oneToClear + + + EP1 + [3:2] + read-write + oneToClear + + + EP0 + [1:0] + read-write + oneToClear + + + + + EP_RX_ERROR + 0x00000110 + RX error count for each endpoint. Write to each field to reset the counter to 0. + 0x00000000 + + + EP15_SEQ + [31:31] + read-write + oneToClear + + + EP15_TRANSACTION + [30:30] + read-write + oneToClear + + + EP14_SEQ + [29:29] + read-write + oneToClear + + + EP14_TRANSACTION + [28:28] + read-write + oneToClear + + + EP13_SEQ + [27:27] + read-write + oneToClear + + + EP13_TRANSACTION + [26:26] + read-write + oneToClear + + + EP12_SEQ + [25:25] + read-write + oneToClear + + + EP12_TRANSACTION + [24:24] + read-write + oneToClear + + + EP11_SEQ + [23:23] + read-write + oneToClear + + + EP11_TRANSACTION + [22:22] + read-write + oneToClear + + + EP10_SEQ + [21:21] + read-write + oneToClear + + + EP10_TRANSACTION + [20:20] + read-write + oneToClear + + + EP9_SEQ + [19:19] + read-write + oneToClear + + + EP9_TRANSACTION + [18:18] + read-write + oneToClear + + + EP8_SEQ + [17:17] + read-write + oneToClear + + + EP8_TRANSACTION + [16:16] + read-write + oneToClear + + + EP7_SEQ + [15:15] + read-write + oneToClear + + + EP7_TRANSACTION + [14:14] + read-write + oneToClear + + + EP6_SEQ + [13:13] + read-write + oneToClear + + + EP6_TRANSACTION + [12:12] + read-write + oneToClear + + + EP5_SEQ + [11:11] + read-write + oneToClear + + + EP5_TRANSACTION + [10:10] + read-write + oneToClear + + + EP4_SEQ + [9:9] + read-write + oneToClear + + + EP4_TRANSACTION + [8:8] + read-write + oneToClear + + + EP3_SEQ + [7:7] + read-write + oneToClear + + + EP3_TRANSACTION + [6:6] + read-write + oneToClear + + + EP2_SEQ + [5:5] + read-write + oneToClear + + + EP2_TRANSACTION + [4:4] + read-write + oneToClear + + + EP1_SEQ + [3:3] + read-write + oneToClear + + + EP1_TRANSACTION + [2:2] + read-write + oneToClear + + + EP0_SEQ + [1:1] + read-write + oneToClear + + + EP0_TRANSACTION + [0:0] + read-write + oneToClear + + + + + DEV_SM_WATCHDOG + 0x00000114 + Watchdog that forces the device state machine to idle and raises an interrupt if the device stays in a state that isn't idle for the configured limit. The counter is reset on every state transition. + Set limit while enable is low and then set the enable. + 0x00000000 + + + FIRED + [20:20] + read-write + oneToClear + + + RESET + Set to 1 to forcibly reset the device state machine on watchdog expiry + [19:19] + read-write + + + ENABLE + [18:18] + read-write + + + LIMIT + [17:0] + read-write + + + + + + + TRNG + ARM TrustZone RNG register block + 0x400f0000 + + 0 + 492 + registers + + + TRNG_IRQ + 39 + + + + RNG_IMR + 0x00000100 + Interrupt masking. + 0x0000000f + + + RESERVED + RESERVED + [31:4] + read-only + + + VN_ERR_INT_MASK + 1'b1-mask interrupt, no interrupt will be generated. See RNG_ISR for an explanation on this interrupt. + [3:3] + read-write + + + CRNGT_ERR_INT_MASK + 1'b1-mask interrupt, no interrupt will be generated. See RNG_ISR for an explanation on this interrupt. + [2:2] + read-write + + + AUTOCORR_ERR_INT_MASK + 1'b1-mask interrupt, no interrupt will be generated. See RNG_ISR for an explanation on this interrupt. + [1:1] + read-write + + + EHR_VALID_INT_MASK + 1'b1-mask interrupt, no interrupt will be generated. See RNG_ISR for an explanation on this interrupt. + [0:0] + read-write + + + + + RNG_ISR + 0x00000104 + RNG status register. If corresponding RNG_IMR bit is unmasked, an interrupt will be generated. + 0x00000000 + + + RESERVED + RESERVED + [31:4] + read-only + + + VN_ERR + 1'b1 indicates Von Neuman error. Error in von Neuman occurs if 32 consecutive collected bits are identical, ZERO or ONE. + [3:3] + read-only + + + CRNGT_ERR + 1'b1 indicates CRNGT in the RNG test failed. Failure occurs when two consecutive blocks of 16 collected bits are equal. + [2:2] + read-only + + + AUTOCORR_ERR + 1'b1 indicates Autocorrelation test failed four times in a row. When set, RNG cease from functioning until next reset. + [1:1] + read-only + + + EHR_VALID + 1'b1 indicates that 192 bits have been collected in the RNG, and are ready to be read. + [0:0] + read-only + + + + + RNG_ICR + 0x00000108 + Interrupt/status bit clear Register. + 0x00000000 + + + RESERVED + RESERVED + [31:4] + read-only + + + VN_ERR + Write 1'b1 - clear corresponding bit in RNG_ISR. + [3:3] + read-write + + + CRNGT_ERR + Write 1'b1 - clear corresponding bit in RNG_ISR. + [2:2] + read-write + + + AUTOCORR_ERR + Cannot be cleared by SW! Only RNG reset clears this bit. + [1:1] + read-write + + + EHR_VALID + Write 1'b1 - clear corresponding bit in RNG_ISR. + [0:0] + read-write + + + + + TRNG_CONFIG + 0x0000010c + Selecting the inverter-chain length. + 0x00000000 + + + RESERVED + RESERVED + [31:2] + read-only + + + RND_SRC_SEL + Selects the number of inverters (out of four possible selections) in the ring oscillator (the entropy source). + [1:0] + read-write + + + + + TRNG_VALID + 0x00000110 + 192 bit collection indication. + 0x00000000 + + + RESERVED + RESERVED + [31:1] + read-only + + + EHR_VALID + 1'b1 indicates that collection of bits in the RNG is completed, and data can be read from EHR_DATA register. + [0:0] + read-only + + + + + EHR_DATA0 + 0x00000114 + RNG collected bits. + 0x00000000 + + + EHR_DATA0 + Bits [31:0] of Entropy Holding Register (EHR) - RNG output register + [31:0] + read-only + + + + + EHR_DATA1 + 0x00000118 + RNG collected bits. + 0x00000000 + + + EHR_DATA1 + Bits [63:32] of Entropy Holding Register (EHR) - RNG output register + [31:0] + read-only + + + + + EHR_DATA2 + 0x0000011c + RNG collected bits. + 0x00000000 + + + EHR_DATA2 + Bits [95:64] of Entropy Holding Register (EHR) - RNG output register + [31:0] + read-only + + + + + EHR_DATA3 + 0x00000120 + RNG collected bits. + 0x00000000 + + + EHR_DATA3 + Bits [127:96] of Entropy Holding Register (EHR) - RNG output register + [31:0] + read-only + + + + + EHR_DATA4 + 0x00000124 + RNG collected bits. + 0x00000000 + + + EHR_DATA4 + Bits [159:128] of Entropy Holding Register (EHR) - RNG output register + [31:0] + read-only + + + + + EHR_DATA5 + 0x00000128 + RNG collected bits. + 0x00000000 + + + EHR_DATA5 + Bits [191:160] of Entropy Holding Register (EHR) - RNG output register + [31:0] + read-only + + + + + RND_SOURCE_ENABLE + 0x0000012c + Enable signal for the random source. + 0x00000000 + + + RESERVED + RESERVED + [31:1] + read-only + + + RND_SRC_EN + * 1'b1 - entropy source is enabled. *1'b0 - entropy source is disabled + [0:0] + read-write + + + + + SAMPLE_CNT1 + 0x00000130 + Counts clocks between sampling of random bit. + 0x0000ffff + + + SAMPLE_CNTR1 + Sets the number of rng_clk cycles between two consecutive ring oscillator samples. Note! If the Von-Neuman is bypassed, the minimum value for sample counter must not be less then decimal seventeen + [31:0] + read-write + + + + + AUTOCORR_STATISTIC + 0x00000134 + Statistic about Autocorrelation test activations. + 0x00000000 + + + RESERVED + RESERVED + [31:22] + read-only + + + AUTOCORR_FAILS + Count each time an autocorrelation test fails. Any write to the register reset the counter. Stop collecting statistic if one of the counters reached the limit. + [21:14] + read-write + + + AUTOCORR_TRYS + Count each time an autocorrelation test starts. Any write to the register reset the counter. Stop collecting statistic if one of the counters reached the limit. + [13:0] + read-write + + + + + TRNG_DEBUG_CONTROL + 0x00000138 + Debug register. + 0x00000000 + + + AUTO_CORRELATE_BYPASS + When set, the autocorrelation test in the TRNG module is bypassed. + [3:3] + read-write + + + TRNG_CRNGT_BYPASS + When set, the CRNGT test in the RNG is bypassed. + [2:2] + read-write + + + VNC_BYPASS + When set, the Von-Neuman balancer is bypassed (including the 32 consecutive bits test). + [1:1] + read-write + + + RESERVED + N/A + [0:0] + read-only + + + + + TRNG_SW_RESET + 0x00000140 + Generate internal SW reset within the RNG block. + 0x00000000 + + + RESERVED + RESERVED + [31:1] + read-only + + + TRNG_SW_RESET + Writing 1'b1 to this register causes an internal RNG reset. + [0:0] + read-write + + + + + RNG_DEBUG_EN_INPUT + 0x000001b4 + Enable the RNG debug mode + 0x00000000 + + + RESERVED + RESERVED + [31:1] + read-only + + + RNG_DEBUG_EN + * 1'b1 - debug mode is enabled. *1'b0 - debug mode is disabled + [0:0] + read-write + + + + + TRNG_BUSY + 0x000001b8 + RNG Busy indication. + 0x00000000 + + + RESERVED + RESERVED + [31:1] + read-only + + + TRNG_BUSY + Reflects rng_busy status. + [0:0] + read-only + + + + + RST_BITS_COUNTER + 0x000001bc + Reset the counter of collected bits in the RNG. + 0x00000000 + + + RESERVED + RESERVED + [31:1] + read-only + + + RST_BITS_COUNTER + Writing any value to this address will reset the bits counter and RNG valid registers. RND_SORCE_ENABLE register must be unset in order for the reset to take place. + [0:0] + read-write + + + + + RNG_VERSION + 0x000001c0 + Displays the version settings of the TRNG. + 0x00000000 + + + RESERVED + RESERVED + [31:8] + read-only + + + RNG_USE_5_SBOXES + * 1'b1 - 5 SBOX AES. *1'b0 - 20 SBOX AES + [7:7] + read-only + + + RESEEDING_EXISTS + * 1'b1 - Exists. *1'b0 - Does not exist + [6:6] + read-only + + + KAT_EXISTS + * 1'b1 - Exists. *1'b0 - Does not exist + [5:5] + read-only + + + PRNG_EXISTS + * 1'b1 - Exists. *1'b0 - Does not exist + [4:4] + read-only + + + TRNG_TESTS_BYPASS_EN + * 1'b1 - Exists. *1'b0 - Does not exist + [3:3] + read-only + + + AUTOCORR_EXISTS + * 1'b1 - Exists. *1'b0 - Does not exist + [2:2] + read-only + + + CRNGT_EXISTS + * 1'b1 - Exists. *1'b0 - Does not exist + [1:1] + read-only + + + EHR_WIDTH_192 + * 1'b1 - 192-bit EHR. *1'b0 - 128-bit EHR + [0:0] + read-only + + + + + RNG_BIST_CNTR_0 + 0x000001e0 + Collected BIST results. + 0x00000000 + + + RESERVED + RESERVED + [31:22] + read-only + + + ROSC_CNTR_VAL + Reflects the results of RNG BIST counter. + [21:0] + read-only + + + + + RNG_BIST_CNTR_1 + 0x000001e4 + Collected BIST results. + 0x00000000 + + + RESERVED + RESERVED + [31:22] + read-only + + + ROSC_CNTR_VAL + Reflects the results of RNG BIST counter. + [21:0] + read-only + + + + + RNG_BIST_CNTR_2 + 0x000001e8 + Collected BIST results. + 0x00000000 + + + RESERVED + RESERVED + [31:22] + read-only + + + ROSC_CNTR_VAL + Reflects the results of RNG BIST counter. + [21:0] + read-only + + + + + + + GLITCH_DETECTOR + Glitch detector controls + 0x40158000 + + 0 + 24 + registers + + + + ARM + 0x00000000 + Forcibly arm the glitch detectors, if they are not already armed by OTP. When armed, any individual detector trigger will cause a restart of the switched core power domain's power-on reset state machine. + + Glitch detector triggers are recorded accumulatively in TRIG_STATUS. If the system is reset by a glitch detector trigger, this is recorded in POWMAN_CHIP_RESET. + + This register is Secure read/write only. + 0x00005bad + + + ARM + [15:0] + read-write + + + no + 23469 + Do not force the glitch detectors to be armed + + + yes + 0 + Force the glitch detectors to be armed. (Any value other than ARM_NO counts as YES) + + + + + + + DISARM + 0x00000004 + 0x00000000 + + + DISARM + Forcibly disarm the glitch detectors, if they are armed by OTP. Ignored if ARM is YES. + + This register is Secure read/write only. + [15:0] + read-write + + + no + 0 + Do not disarm the glitch detectors. (Any value other than DISARM_YES counts as NO) + + + yes + 56495 + Disarm the glitch detectors + + + + + + + SENSITIVITY + 0x00000008 + Adjust the sensitivity of glitch detectors to values other than their OTP-provided defaults. + + This register is Secure read/write only. + 0x00000000 + + + DEFAULT + [31:24] + read-write + + + yes + 0 + Use the default sensitivity configured in OTP for all detectors. (Any value other than DEFAULT_NO counts as YES) + + + no + 222 + Do not use the default sensitivity configured in OTP. Instead use the value from this register. + + + + + DET3_INV + Must be the inverse of DET3, else the default value is used. + [15:14] + read-write + + + DET2_INV + Must be the inverse of DET2, else the default value is used. + [13:12] + read-write + + + DET1_INV + Must be the inverse of DET1, else the default value is used. + [11:10] + read-write + + + DET0_INV + Must be the inverse of DET0, else the default value is used. + [9:8] + read-write + + + DET3 + Set sensitivity for detector 3. Higher values are more sensitive. + [7:6] + read-write + + + DET2 + Set sensitivity for detector 2. Higher values are more sensitive. + [5:4] + read-write + + + DET1 + Set sensitivity for detector 1. Higher values are more sensitive. + [3:2] + read-write + + + DET0 + Set sensitivity for detector 0. Higher values are more sensitive. + [1:0] + read-write + + + + + LOCK + 0x0000000c + 0x00000000 + + + LOCK + Write any nonzero value to disable writes to ARM, DISARM, SENSITIVITY and LOCK. This register is Secure read/write only. + [7:0] + read-write + + + + + TRIG_STATUS + 0x00000010 + Set when a detector output triggers. Write-1-clear. + + (May immediately return high if the detector remains in a failed state. Detectors can only be cleared by a full reset of the switched core power domain.) + + This register is Secure read/write only. + 0x00000000 + + + DET3 + [3:3] + read-write + oneToClear + + + DET2 + [2:2] + read-write + oneToClear + + + DET1 + [1:1] + read-write + oneToClear + + + DET0 + [0:0] + read-write + oneToClear + + + + + TRIG_FORCE + 0x00000014 + Simulate the firing of one or more detectors. Writing ones to this register will set the matching bits in STATUS_TRIG. + + If the glitch detectors are currently armed, writing ones will also immediately reset the switched core power domain, and set the reset reason latches in POWMAN_CHIP_RESET to indicate a glitch detector resets. + + This register is Secure read/write only. + 0x00000000 + + + TRIG_FORCE + [3:0] + write-only + + + + + + + OTP + SNPS OTP control IF (SBPI and RPi wrapper control) + 0x40120000 + + 0 + 372 + registers + + + OTP_IRQ + 38 + + + + SW_LOCK0 + 0x00000000 + Software lock register for page 0. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK1 + 0x00000004 + Software lock register for page 1. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK2 + 0x00000008 + Software lock register for page 2. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK3 + 0x0000000c + Software lock register for page 3. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK4 + 0x00000010 + Software lock register for page 4. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK5 + 0x00000014 + Software lock register for page 5. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK6 + 0x00000018 + Software lock register for page 6. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK7 + 0x0000001c + Software lock register for page 7. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK8 + 0x00000020 + Software lock register for page 8. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK9 + 0x00000024 + Software lock register for page 9. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK10 + 0x00000028 + Software lock register for page 10. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK11 + 0x0000002c + Software lock register for page 11. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK12 + 0x00000030 + Software lock register for page 12. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK13 + 0x00000034 + Software lock register for page 13. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK14 + 0x00000038 + Software lock register for page 14. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK15 + 0x0000003c + Software lock register for page 15. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK16 + 0x00000040 + Software lock register for page 16. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK17 + 0x00000044 + Software lock register for page 17. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK18 + 0x00000048 + Software lock register for page 18. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK19 + 0x0000004c + Software lock register for page 19. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK20 + 0x00000050 + Software lock register for page 20. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK21 + 0x00000054 + Software lock register for page 21. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK22 + 0x00000058 + Software lock register for page 22. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK23 + 0x0000005c + Software lock register for page 23. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK24 + 0x00000060 + Software lock register for page 24. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK25 + 0x00000064 + Software lock register for page 25. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK26 + 0x00000068 + Software lock register for page 26. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK27 + 0x0000006c + Software lock register for page 27. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK28 + 0x00000070 + Software lock register for page 28. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK29 + 0x00000074 + Software lock register for page 29. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK30 + 0x00000078 + Software lock register for page 30. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK31 + 0x0000007c + Software lock register for page 31. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK32 + 0x00000080 + Software lock register for page 32. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK33 + 0x00000084 + Software lock register for page 33. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK34 + 0x00000088 + Software lock register for page 34. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK35 + 0x0000008c + Software lock register for page 35. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK36 + 0x00000090 + Software lock register for page 36. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK37 + 0x00000094 + Software lock register for page 37. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK38 + 0x00000098 + Software lock register for page 38. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK39 + 0x0000009c + Software lock register for page 39. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK40 + 0x000000a0 + Software lock register for page 40. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK41 + 0x000000a4 + Software lock register for page 41. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK42 + 0x000000a8 + Software lock register for page 42. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK43 + 0x000000ac + Software lock register for page 43. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK44 + 0x000000b0 + Software lock register for page 44. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK45 + 0x000000b4 + Software lock register for page 45. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK46 + 0x000000b8 + Software lock register for page 46. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK47 + 0x000000bc + Software lock register for page 47. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK48 + 0x000000c0 + Software lock register for page 48. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK49 + 0x000000c4 + Software lock register for page 49. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK50 + 0x000000c8 + Software lock register for page 50. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK51 + 0x000000cc + Software lock register for page 51. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK52 + 0x000000d0 + Software lock register for page 52. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK53 + 0x000000d4 + Software lock register for page 53. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK54 + 0x000000d8 + Software lock register for page 54. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK55 + 0x000000dc + Software lock register for page 55. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK56 + 0x000000e0 + Software lock register for page 56. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK57 + 0x000000e4 + Software lock register for page 57. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK58 + 0x000000e8 + Software lock register for page 58. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK59 + 0x000000ec + Software lock register for page 59. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK60 + 0x000000f0 + Software lock register for page 60. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK61 + 0x000000f4 + Software lock register for page 61. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK62 + 0x000000f8 + Software lock register for page 62. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SW_LOCK63 + 0x000000fc + Software lock register for page 63. + + Locks are initialised from the OTP lock pages at reset. This register can be written to further advance the lock state of each page (until next reset), and read to check the current lock state of a page. + 0x00000000 + + + NSEC + Non-secure lock status. Writes are OR'd with the current value. + [3:2] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + SEC + Secure lock status. Writes are OR'd with the current value. This field is read-only to Non-secure code. + [1:0] + read-write + + + read_write + 0 + + + read_only + 1 + + + inaccessible + 3 + + + + + + + SBPI_INSTR + 0x00000100 + Dispatch instructions to the SBPI interface, used for programming the OTP fuses. + 0x00000000 + + + EXEC + Execute instruction + [30:30] + write-only + + + IS_WR + Payload type is write + [29:29] + read-write + + + HAS_PAYLOAD + Instruction has payload (data to be written or to be read) + [28:28] + read-write + + + PAYLOAD_SIZE_M1 + Instruction payload size in bytes minus 1 + [27:24] + read-write + + + TARGET + Instruction target, it can be PMC (0x3a) or DAP (0x02) + [23:16] + read-write + + + CMD + [15:8] + read-write + + + SHORT_WDATA + wdata to be used only when payload_size_m1=0 + [7:0] + read-write + + + + + SBPI_WDATA_0 + 0x00000104 + SBPI write payload bytes 3..0 + 0x00000000 + + + SBPI_WDATA_0 + [31:0] + read-write + + + + + SBPI_WDATA_1 + 0x00000108 + SBPI write payload bytes 7..4 + 0x00000000 + + + SBPI_WDATA_1 + [31:0] + read-write + + + + + SBPI_WDATA_2 + 0x0000010c + SBPI write payload bytes 11..8 + 0x00000000 + + + SBPI_WDATA_2 + [31:0] + read-write + + + + + SBPI_WDATA_3 + 0x00000110 + SBPI write payload bytes 15..12 + 0x00000000 + + + SBPI_WDATA_3 + [31:0] + read-write + + + + + SBPI_RDATA_0 + 0x00000114 + Read payload bytes 3..0. Once read, the data in the register will automatically clear to 0. + 0x00000000 + + + SBPI_RDATA_0 + [31:0] + read-only + modify + + + + + SBPI_RDATA_1 + 0x00000118 + Read payload bytes 7..4. Once read, the data in the register will automatically clear to 0. + 0x00000000 + + + SBPI_RDATA_1 + [31:0] + read-only + modify + + + + + SBPI_RDATA_2 + 0x0000011c + Read payload bytes 11..8. Once read, the data in the register will automatically clear to 0. + 0x00000000 + + + SBPI_RDATA_2 + [31:0] + read-only + modify + + + + + SBPI_RDATA_3 + 0x00000120 + Read payload bytes 15..12. Once read, the data in the register will automatically clear to 0. + 0x00000000 + + + SBPI_RDATA_3 + [31:0] + read-only + modify + + + + + SBPI_STATUS + 0x00000124 + 0x00000000 + + + MISO + SBPI MISO (master in - slave out): response from SBPI + [23:16] + read-only + + + FLAG + SBPI flag + [12:12] + read-only + + + INSTR_MISS + Last instruction missed (dropped), as the previous has not finished running + [8:8] + read-write + oneToClear + + + INSTR_DONE + Last instruction done + [4:4] + read-write + oneToClear + + + RDATA_VLD + Read command has returned data + [0:0] + read-write + oneToClear + + + + + USR + 0x00000128 + Controls for APB data read interface (USER interface) + 0x00000001 + + + PD + Power-down; 1 disables current reference. Must be 0 to read data from the OTP. + [4:4] + read-write + + + DCTRL + 1 enables USER interface; 0 disables USER interface (enables SBPI). + + This bit must be cleared before performing any SBPI access, such as when programming the OTP. The APB data read interface (USER interface) will be inaccessible during this time, and will return a bus error if any read is attempted. + [0:0] + read-write + + + + + DBG + 0x0000012c + Debug for OTP power-on state machine + 0x00000000 + + + CUSTOMER_RMA_FLAG + The chip is in RMA mode + [12:12] + read-only + + + PSM_STATE + Monitor the PSM FSM's state + [7:4] + read-only + + + ROSC_UP + Ring oscillator is up and running + [3:3] + read-only + + + ROSC_UP_SEEN + Ring oscillator was seen up and running + [2:2] + read-write + oneToClear + + + BOOT_DONE + PSM boot done status flag + [1:1] + read-only + + + PSM_DONE + PSM done status flag + [0:0] + read-only + + + + + BIST + 0x00000134 + During BIST, count address locations that have at least one leaky bit + 0x0fff0000 + + + CNT_FAIL + Flag if the count of address locations with at least one leaky bit exceeds cnt_max + [30:30] + read-only + + + CNT_CLR + Clear counter before use + [29:29] + write-only + + + CNT_ENA + Enable the counter before the BIST function is initiated + [28:28] + read-write + + + CNT_MAX + The cnt_fail flag will be set if the number of leaky locations exceeds this number + [27:16] + read-write + + + CNT + Number of locations that have at least one leaky bit. Note: This count is true only if the BIST was initiated without the fix option. + [12:0] + read-only + + + + + CRT_KEY_W0 + 0x00000138 + Word 0 (bits 31..0) of the key. Write only, read returns 0x0 + 0x00000000 + + + CRT_KEY_W0 + [31:0] + write-only + + + + + CRT_KEY_W1 + 0x0000013c + Word 1 (bits 63..32) of the key. Write only, read returns 0x0 + 0x00000000 + + + CRT_KEY_W1 + [31:0] + write-only + + + + + CRT_KEY_W2 + 0x00000140 + Word 2 (bits 95..64) of the key. Write only, read returns 0x0 + 0x00000000 + + + CRT_KEY_W2 + [31:0] + write-only + + + + + CRT_KEY_W3 + 0x00000144 + Word 3 (bits 127..96) of the key. Write only, read returns 0x0 + 0x00000000 + + + CRT_KEY_W3 + [31:0] + write-only + + + + + CRITICAL + 0x00000148 + Quickly check values of critical flags read during boot up + 0x00000000 + + + RISCV_DISABLE + [17:17] + read-only + + + ARM_DISABLE + [16:16] + read-only + + + GLITCH_DETECTOR_SENS + [6:5] + read-only + + + GLITCH_DETECTOR_ENABLE + [4:4] + read-only + + + DEFAULT_ARCHSEL + [3:3] + read-only + + + DEBUG_DISABLE + [2:2] + read-only + + + SECURE_DEBUG_DISABLE + [1:1] + read-only + + + SECURE_BOOT_ENABLE + [0:0] + read-only + + + + + KEY_VALID + 0x0000014c + Which keys were valid (enrolled) at boot time + 0x00000000 + + + KEY_VALID + [7:0] + read-only + + + + + DEBUGEN + 0x00000150 + Enable a debug feature that has been disabled. Debug features are disabled if one of the relevant critical boot flags is set in OTP (DEBUG_DISABLE or SECURE_DEBUG_DISABLE), OR if a debug key is marked valid in OTP, and the matching key value has not been supplied over SWD. + + Specifically: + + - The DEBUG_DISABLE flag disables all debug features. This can be fully overridden by setting all bits of this register. + + - The SECURE_DEBUG_DISABLE flag disables secure processor debug. This can be fully overridden by setting the PROC0_SECURE and PROC1_SECURE bits of this register. + + - If a single debug key has been registered, and no matching key value has been supplied over SWD, then all debug features are disabled. This can be fully overridden by setting all bits of this register. + + - If both debug keys have been registered, and the Non-secure key's value (key 6) has been supplied over SWD, secure processor debug is disabled. This can be fully overridden by setting the PROC0_SECURE and PROC1_SECURE bits of this register. + + - If both debug keys have been registered, and the Secure key's value (key 5) has been supplied over SWD, then no debug features are disabled by the key mechanism. However, note that in this case debug features may still be disabled by the critical boot flags. + 0x00000000 + + + MISC + Enable other debug components. Specifically, the CTI, and the APB-AP used to access the RISC-V Debug Module. + + These components are disabled by default if either of the debug disable critical flags is set, or if at least one debug key has been enrolled and the least secure of these enrolled key values has not been provided over SWD. + [8:8] + read-write + + + PROC1_SECURE + Permit core 1's Mem-AP to generate Secure accesses, assuming it is enabled at all. Also enable secure debug of core 1 (SPIDEN and SPNIDEN). + + Secure debug of core 1 is disabled by default if the secure debug disable critical flag is set, or if at least one debug key has been enrolled and the most secure of these enrolled key values not yet provided over SWD. + [3:3] + read-write + + + PROC1 + Enable core 1's Mem-AP if it is currently disabled. + + The Mem-AP is disabled by default if either of the debug disable critical flags is set, or if at least one debug key has been enrolled and the least secure of these enrolled key values has not been provided over SWD. + [2:2] + read-write + + + PROC0_SECURE + Permit core 0's Mem-AP to generate Secure accesses, assuming it is enabled at all. Also enable secure debug of core 0 (SPIDEN and SPNIDEN). + + Secure debug of core 0 is disabled by default if the secure debug disable critical flag is set, or if at least one debug key has been enrolled and the most secure of these enrolled key values not yet provided over SWD. + + Note also that core Mem-APs are unconditionally disabled when a core is switched to RISC-V mode (by setting the ARCHSEL bit and performing a warm reset of the core). + [1:1] + read-write + + + PROC0 + Enable core 0's Mem-AP if it is currently disabled. + + The Mem-AP is disabled by default if either of the debug disable critical flags is set, or if at least one debug key has been enrolled and the least secure of these enrolled key values has not been provided over SWD. + + Note also that core Mem-APs are unconditionally disabled when a core is switched to RISC-V mode (by setting the ARCHSEL bit and performing a warm reset of the core). + [0:0] + read-write + + + + + DEBUGEN_LOCK + 0x00000154 + Write 1s to lock corresponding bits in DEBUGEN. This register is reset by the processor cold reset. + 0x00000000 + + + MISC + Write 1 to lock the MISC bit of DEBUGEN. Can't be cleared once set. + [8:8] + read-write + + + PROC1_SECURE + Write 1 to lock the PROC1_SECURE bit of DEBUGEN. Can't be cleared once set. + [3:3] + read-write + + + PROC1 + Write 1 to lock the PROC1 bit of DEBUGEN. Can't be cleared once set. + [2:2] + read-write + + + PROC0_SECURE + Write 1 to lock the PROC0_SECURE bit of DEBUGEN. Can't be cleared once set. + [1:1] + read-write + + + PROC0 + Write 1 to lock the PROC0 bit of DEBUGEN. Can't be cleared once set. + [0:0] + read-write + + + + + ARCHSEL + 0x00000158 + Architecture select (Arm/RISC-V). The default and allowable values of this register are constrained by the critical boot flags. + + This register is reset by the earliest reset in the switched core power domain (before a processor cold reset). + + Cores sample their architecture select signal on a warm reset. The source of the warm reset could be the system power-up state machine, the watchdog timer, Arm SYSRESETREQ or from RISC-V hartresetreq. + + Note that when an Arm core is deselected, its cold reset domain is also held in reset, since in particular the SYSRESETREQ bit becomes inaccessible once the core is deselected. Note also the RISC-V cores do not have a cold reset domain, since their corresponding controls are located in the Debug Module. + 0x00000000 + + + CORE1 + Select architecture for core 1. + [1:1] + read-write + + + arm + 0 + Switch core 1 to Arm (Cortex-M33) + + + riscv + 1 + Switch core 1 to RISC-V (Hazard3) + + + + + CORE0 + Select architecture for core 0. + [0:0] + read-write + + + arm + 0 + Switch core 0 to Arm (Cortex-M33) + + + riscv + 1 + Switch core 0 to RISC-V (Hazard3) + + + + + + + ARCHSEL_STATUS + 0x0000015c + Get the current architecture select state of each core. Cores sample the current value of the ARCHSEL register when their warm reset is released, at which point the corresponding bit in this register will also update. + 0x00000000 + + + CORE1 + Current architecture for core 0. Updated on processor warm reset. + [1:1] + read-only + + + arm + 0 + Core 1 is currently Arm (Cortex-M33) + + + riscv + 1 + Core 1 is currently RISC-V (Hazard3) + + + + + CORE0 + Current architecture for core 0. Updated on processor warm reset. + [0:0] + read-only + + + arm + 0 + Core 0 is currently Arm (Cortex-M33) + + + riscv + 1 + Core 0 is currently RISC-V (Hazard3) + + + + + + + BOOTDIS + 0x00000160 + Tell the bootrom to ignore scratch register boot vectors (both power manager and watchdog) on the next power up. + + If an early boot stage has soft-locked some OTP pages in order to protect their contents from later stages, there is a risk that Secure code running at a later stage can unlock the pages by performing a watchdog reset that resets the OTP. + + This register can be used to ensure that the bootloader runs as normal on the next power up, preventing Secure code at a later stage from accessing OTP in its unlocked state. + + Should be used in conjunction with the power manager BOOTDIS register. + 0x00000000 + + + NEXT + This flag always ORs writes into its current contents. It can be set but not cleared by software. + + The BOOTDIS_NEXT bit is OR'd into the BOOTDIS_NOW bit when the core is powered down. Simultaneously, the BOOTDIS_NEXT bit is cleared. Setting this bit means that the boot scratch registers will be ignored following the next core power down. + + This flag should be set by an early boot stage that has soft-locked OTP pages, to prevent later stages from unlocking it via watchdog reset. + [1:1] + read-write + + + NOW + When the core is powered down, the current value of BOOTDIS_NEXT is OR'd into BOOTDIS_NOW, and BOOTDIS_NEXT is cleared. + + The bootrom checks this flag before reading the boot scratch registers. If it is set, the bootrom clears it, and ignores the BOOT registers. This prevents Secure software from diverting the boot path before a bootloader has had the chance to soft lock OTP pages containing sensitive data. + [0:0] + read-write + oneToClear + + + + + INTR + 0x00000164 + Raw Interrupts + 0x00000000 + + + APB_RD_NSEC_FAIL + [4:4] + read-write + oneToClear + + + APB_RD_SEC_FAIL + [3:3] + read-write + oneToClear + + + APB_DCTRL_FAIL + [2:2] + read-write + oneToClear + + + SBPI_WR_FAIL + [1:1] + read-write + oneToClear + + + SBPI_FLAG_N + [0:0] + read-only + + + + + INTE + 0x00000168 + Interrupt Enable + 0x00000000 + + + APB_RD_NSEC_FAIL + [4:4] + read-write + + + APB_RD_SEC_FAIL + [3:3] + read-write + + + APB_DCTRL_FAIL + [2:2] + read-write + + + SBPI_WR_FAIL + [1:1] + read-write + + + SBPI_FLAG_N + [0:0] + read-write + + + + + INTF + 0x0000016c + Interrupt Force + 0x00000000 + + + APB_RD_NSEC_FAIL + [4:4] + read-write + + + APB_RD_SEC_FAIL + [3:3] + read-write + + + APB_DCTRL_FAIL + [2:2] + read-write + + + SBPI_WR_FAIL + [1:1] + read-write + + + SBPI_FLAG_N + [0:0] + read-write + + + + + INTS + 0x00000170 + Interrupt status after masking & forcing + 0x00000000 + + + APB_RD_NSEC_FAIL + [4:4] + read-only + + + APB_RD_SEC_FAIL + [3:3] + read-only + + + APB_DCTRL_FAIL + [2:2] + read-only + + + SBPI_WR_FAIL + [1:1] + read-only + + + SBPI_FLAG_N + [0:0] + read-only + + + + + + + OTP_DATA + Predefined OTP data layout for RP2350 + 0x40130000 + + 0 + 7920 + registers + + + + CHIPID0 + 0x0000 + Bits 15:0 of public device ID. (ECC) + + The CHIPID0..3 rows contain a 64-bit random identifier for this chip, which can be read from the USB bootloader PICOBOOT interface or from the get_sys_info ROM API. + + The number of random bits makes the occurrence of twins exceedingly unlikely: for example, a fleet of a hundred million devices has a 99.97% probability of no twinned IDs. This is estimated to be lower than the occurrence of process errors in the assignment of sequential random IDs, and for practical purposes CHIPID may be treated as unique. + 16 + 0x0000 + + + CHIPID0 + [15:0] + read-only + + + + + CHIPID1 + 0x0002 + Bits 31:16 of public device ID (ECC) + 16 + 0x0000 + + + CHIPID1 + [15:0] + read-only + + + + + CHIPID2 + 0x0004 + Bits 47:32 of public device ID (ECC) + 16 + 0x0000 + + + CHIPID2 + [15:0] + read-only + + + + + CHIPID3 + 0x0006 + Bits 63:48 of public device ID (ECC) + 16 + 0x0000 + + + CHIPID3 + [15:0] + read-only + + + + + RANDID0 + 0x0008 + Bits 15:0 of private per-device random number (ECC) + + The RANDID0..7 rows form a 128-bit random number generated during device test. + + This ID is not exposed through the USB PICOBOOT GET_INFO command or the ROM `get_sys_info()` API. However note that the USB PICOBOOT OTP access point can read the entirety of page 0, so this value is not meaningfully private unless the USB PICOBOOT interface is disabled via the DISABLE_BOOTSEL_USB_PICOBOOT_IFC flag in BOOT_FLAGS0. + 16 + 0x0000 + + + RANDID0 + [15:0] + read-only + + + + + RANDID1 + 0x000a + Bits 31:16 of private per-device random number (ECC) + 16 + 0x0000 + + + RANDID1 + [15:0] + read-only + + + + + RANDID2 + 0x000c + Bits 47:32 of private per-device random number (ECC) + 16 + 0x0000 + + + RANDID2 + [15:0] + read-only + + + + + RANDID3 + 0x000e + Bits 63:48 of private per-device random number (ECC) + 16 + 0x0000 + + + RANDID3 + [15:0] + read-only + + + + + RANDID4 + 0x0010 + Bits 79:64 of private per-device random number (ECC) + 16 + 0x0000 + + + RANDID4 + [15:0] + read-only + + + + + RANDID5 + 0x0012 + Bits 95:80 of private per-device random number (ECC) + 16 + 0x0000 + + + RANDID5 + [15:0] + read-only + + + + + RANDID6 + 0x0014 + Bits 111:96 of private per-device random number (ECC) + 16 + 0x0000 + + + RANDID6 + [15:0] + read-only + + + + + RANDID7 + 0x0016 + Bits 127:112 of private per-device random number (ECC) + 16 + 0x0000 + + + RANDID7 + [15:0] + read-only + + + + + ROSC_CALIB + 0x0020 + Ring oscillator frequency in kHz, measured during manufacturing (ECC) + + This is measured at 1.1 V, at room temperature, with the ROSC configuration registers in their reset state. + 16 + 0x0000 + + + ROSC_CALIB + [15:0] + read-only + + + + + LPOSC_CALIB + 0x0022 + Low-power oscillator frequency in Hz, measured during manufacturing (ECC) + + This is measured at 1.1V, at room temperature, with the LPOSC trim register in its reset state. + 16 + 0x0000 + + + LPOSC_CALIB + [15:0] + read-only + + + + + NUM_GPIOS + 0x0030 + The number of main user GPIOs (bank 0). Should read 48 in the QFN80 package, and 30 in the QFN60 package. (ECC) + 16 + 0x0000 + + + NUM_GPIOS + [7:0] + read-only + + + + + INFO_CRC0 + 0x006c + Lower 16 bits of CRC32 of OTP addresses 0x00 through 0x6b (polynomial 0x4c11db7, input reflected, output reflected, seed all-ones, final XOR all-ones) (ECC) + 16 + 0x0000 + + + INFO_CRC0 + [15:0] + read-only + + + + + INFO_CRC1 + 0x006e + Upper 16 bits of CRC32 of OTP addresses 0x00 through 0x6b (ECC) + 16 + 0x0000 + + + INFO_CRC1 + [15:0] + read-only + + + + + FLASH_DEVINFO + 0x00a8 + Stores information about external flash device(s). (ECC) + + Assumed to be valid if BOOT_FLAGS0_FLASH_DEVINFO_ENABLE is set. + 16 + 0x0000 + + + CS1_SIZE + The size of the flash/PSRAM device on chip select 1 (addressable at 0x11000000 through 0x11ffffff). + + A value of zero is decoded as a size of zero (no device). Nonzero values are decoded as 4kiB << CS1_SIZE. For example, four megabytes is encoded with a CS1_SIZE value of 10, and 16 megabytes is encoded with a CS1_SIZE value of 12. + + When BOOT_FLAGS0_FLASH_DEVINFO_ENABLE is not set, a default of zero is used. + [15:12] + read-only + + + NONE + 0 + + + 8K + 1 + + + 16K + 2 + + + 32K + 3 + + + 64k + 4 + + + 128K + 5 + + + 256K + 6 + + + 512K + 7 + + + 1M + 8 + + + 2M + 9 + + + 4M + 10 + + + 8M + 11 + + + 16M + 12 + + + + + CS0_SIZE + The size of the flash/PSRAM device on chip select 0 (addressable at 0x10000000 through 0x10ffffff). + + A value of zero is decoded as a size of zero (no device). Nonzero values are decoded as 4kiB << CS0_SIZE. For example, four megabytes is encoded with a CS0_SIZE value of 10, and 16 megabytes is encoded with a CS0_SIZE value of 12. + + When BOOT_FLAGS0_FLASH_DEVINFO_ENABLE is not set, a default of 12 (16 MiB) is used. + [11:8] + read-only + + + NONE + 0 + + + 8K + 1 + + + 16K + 2 + + + 32K + 3 + + + 64k + 4 + + + 128K + 5 + + + 256K + 6 + + + 512K + 7 + + + 1M + 8 + + + 2M + 9 + + + 4M + 10 + + + 8M + 11 + + + 16M + 12 + + + + + D8H_ERASE_SUPPORTED + If true, all attached devices are assumed to support (or ignore, in the case of PSRAM) a block erase command with a command prefix of D8h, an erase size of 64 kiB, and a 24-bit address. Almost all 25-series flash devices support this command. + + If set, the bootrom will use the D8h erase command where it is able, to accelerate bulk erase operations. This makes flash programming faster. + + When BOOT_FLAGS0_FLASH_DEVINFO_ENABLE is not set, this field defaults to false. + [7:7] + read-only + + + CS1_GPIO + Indicate a GPIO number to be used for the secondary flash chip select (CS1), which selects the external QSPI device mapped at system addresses 0x11000000 through 0x11ffffff. There is no such configuration for CS0, as the primary chip select has a dedicated pin. + + On RP2350 the permissible GPIO numbers are 0, 8, 19 and 47. + + Ignored if CS1_size is zero. If CS1_SIZE is nonzero, the bootrom will automatically configure this GPIO as a second chip select upon entering the flash boot path, or entering any other path that may use the QSPI flash interface, such as BOOTSEL mode (nsboot). + [5:0] + read-only + + + + + FLASH_PARTITION_SLOT_SIZE + 0x00aa + Gap between partition table slot 0 and slot 1 at the start of flash (the default size is 4096 bytes) (ECC) Enabled by the OVERRIDE_FLASH_PARTITION_SLOT_SIZE bit in BOOT_FLAGS, the size is 4096 * (value + 1) + 16 + 0x0000 + + + FLASH_PARTITION_SLOT_SIZE + [15:0] + read-only + + + + + BOOTSEL_LED_CFG + 0x00ac + Pin configuration for LED status, used by USB bootloader. (ECC) + Must be valid if BOOT_FLAGS0_ENABLE_BOOTSEL_LED is set. + 16 + 0x0000 + + + ACTIVELOW + LED is active-low. (Default: active-high.) + [8:8] + read-only + + + PIN + GPIO index to use for bootloader activity LED. + [5:0] + read-only + + + + + BOOTSEL_PLL_CFG + 0x00ae + Optional PLL configuration for BOOTSEL mode. (ECC) + + This should be configured to produce an exact 48 MHz based on the crystal oscillator frequency. User mode software may also use this value to calculate the expected crystal frequency based on an assumed 48 MHz PLL output. + + If no configuration is given, the crystal is assumed to be 12 MHz. + + The PLL frequency can be calculated as: + + PLL out = (XOSC frequency / (REFDIV+1)) x FBDIV / (POSTDIV1 x POSTDIV2) + + Conversely the crystal frequency can be calculated as: + + XOSC frequency = 48 MHz x (REFDIV+1) x (POSTDIV1 x POSTDIV2) / FBDIV + + (Note the +1 on REFDIV is because the value stored in this OTP location is the actual divisor value minus one.) + + Used if and only if ENABLE_BOOTSEL_NON_DEFAULT_PLL_XOSC_CFG is set in BOOT_FLAGS0. That bit should be set only after this row and BOOTSEL_XOSC_CFG are both correctly programmed. + 16 + 0x0000 + + + REFDIV + PLL reference divisor, minus one. + + Programming a value of 0 means a reference divisor of 1. Programming a value of 1 means a reference divisor of 2 (for exceptionally fast XIN inputs) + [15:15] + read-only + + + POSTDIV2 + PLL post-divide 2 divisor, in the range 1..7 inclusive. + [14:12] + read-only + + + POSTDIV1 + PLL post-divide 1 divisor, in the range 1..7 inclusive. + [11:9] + read-only + + + FBDIV + PLL feedback divisor, in the range 16..320 inclusive. + [8:0] + read-only + + + + + BOOTSEL_XOSC_CFG + 0x00b0 + Non-default crystal oscillator configuration for the USB bootloader. (ECC) + + These values may also be used by user code configuring the crystal oscillator. + + Used if and only if ENABLE_BOOTSEL_NON_DEFAULT_PLL_XOSC_CFG is set in BOOT_FLAGS0. That bit should be set only after this row and BOOTSEL_PLL_CFG are both correctly programmed. + 16 + 0x0000 + + + RANGE + Value of the XOSC_CTRL_FREQ_RANGE register. + [15:14] + read-only + + + 1_15MHZ + 0 + + + 10_30MHZ + 1 + + + 25_60MHZ + 2 + + + 40_100MHZ + 3 + + + + + STARTUP + Value of the XOSC_STARTUP register + [13:0] + read-only + + + + + USB_WHITE_LABEL_ADDR + 0x00b8 + Row index of the USB_WHITE_LABEL structure within OTP (ECC) + + The table has 16 rows, each of which are also ECC and marked valid by the corresponding valid bit in USB_BOOT_FLAGS (ECC). + + The entries are either _VALUEs where the 16 bit value is used as is, or _STRDEFs which acts as a pointers to a string value. + + The value stored in a _STRDEF is two separate bytes: The low seven bits of the first (LSB) byte indicates the number of characters in the string, and the top bit of the first (LSB) byte if set to indicate that each character in the string is two bytes (Unicode) versus one byte if unset. The second (MSB) byte represents the location of the string data, and is encoded as the number of rows from this USB_WHITE_LABEL_ADDR; i.e. the row of the start of the string is USB_WHITE_LABEL_ADDR value + msb_byte. + + In each case, the corresponding valid bit enables replacing the default value for the corresponding item provided by the boot rom. + + Note that Unicode _STRDEFs are only supported for USB_DEVICE_PRODUCT_STRDEF, USB_DEVICE_SERIAL_NUMBER_STRDEF and USB_DEVICE_MANUFACTURER_STRDEF. Unicode values will be ignored if specified for other fields, and non-unicode values for these three items will be converted to Unicode characters by setting the upper 8 bits to zero. + + Note that if the USB_WHITE_LABEL structure or the corresponding strings are not readable by BOOTSEL mode based on OTP permissions, or if alignment requirements are not met, then the corresponding default values are used. + + The index values indicate where each field is located (row USB_WHITE_LABEL_ADDR value + index): + 16 + 0x0000 + + + USB_WHITE_LABEL_ADDR + [15:0] + read-only + + + INDEX_USB_DEVICE_VID_VALUE + 0 + + + INDEX_USB_DEVICE_PID_VALUE + 1 + + + INDEX_USB_DEVICE_BCD_DEVICE_VALUE + 2 + + + INDEX_USB_DEVICE_LANG_ID_VALUE + 3 + + + INDEX_USB_DEVICE_MANUFACTURER_STRDEF + 4 + + + INDEX_USB_DEVICE_PRODUCT_STRDEF + 5 + + + INDEX_USB_DEVICE_SERIAL_NUMBER_STRDEF + 6 + + + INDEX_USB_CONFIG_ATTRIBUTES_MAX_POWER_VALUES + 7 + + + INDEX_VOLUME_LABEL_STRDEF + 8 + + + INDEX_SCSI_INQUIRY_VENDOR_STRDEF + 9 + + + INDEX_SCSI_INQUIRY_PRODUCT_STRDEF + 10 + + + INDEX_SCSI_INQUIRY_VERSION_STRDEF + 11 + + + INDEX_INDEX_HTM_REDIRECT_URL_STRDEF + 12 + + + INDEX_INDEX_HTM_REDIRECT_NAME_STRDEF + 13 + + + INDEX_INFO_UF2_TXT_MODEL_STRDEF + 14 + + + INDEX_INFO_UF2_TXT_BOARD_ID_STRDEF + 15 + + + + + + + OTPBOOT_SRC + 0x00bc + OTP start row for the OTP boot image. (ECC) + + If OTP boot is enabled, the bootrom will load from this location into SRAM and then directly enter the loaded image. Note that the image must be signed if SECURE_BOOT_ENABLE is set. The image itself is assumed to be ECC-protected. + + This must be an even number. Equivalently, the OTP boot image must start at a word-aligned location in the ECC read data address window. + 16 + 0x0000 + + + OTPBOOT_SRC + [15:0] + read-only + + + + + OTPBOOT_LEN + 0x00be + Length in rows of the OTP boot image. (ECC) + + OTPBOOT_LEN must be even. The total image size must be a multiple of 4 bytes (32 bits). + 16 + 0x0000 + + + OTPBOOT_LEN + [15:0] + read-only + + + + + OTPBOOT_DST0 + 0x00c0 + Bits 15:0 of the OTP boot image load destination (and entry point). (ECC) + + This must be a location in main SRAM (main SRAM is addresses 0x20000000 through 0x20082000) and must be word-aligned. + 16 + 0x0000 + + + OTPBOOT_DST0 + [15:0] + read-only + + + + + OTPBOOT_DST1 + 0x00c2 + Bits 31:16 of the OTP boot image load destination (and entry point). (ECC) + + This must be a location in main SRAM (main SRAM is addresses 0x20000000 through 0x20082000) and must be word-aligned. + 16 + 0x0000 + + + OTPBOOT_DST1 + [15:0] + read-only + + + + + BOOTKEY0_0 + 0x0100 + Bits 15:0 of SHA-256 hash of boot key 0 (ECC) + 16 + 0x0000 + + + BOOTKEY0_0 + [15:0] + read-only + + + + + BOOTKEY0_1 + 0x0102 + Bits 31:16 of SHA-256 hash of boot key 0 (ECC) + 16 + 0x0000 + + + BOOTKEY0_1 + [15:0] + read-only + + + + + BOOTKEY0_2 + 0x0104 + Bits 47:32 of SHA-256 hash of boot key 0 (ECC) + 16 + 0x0000 + + + BOOTKEY0_2 + [15:0] + read-only + + + + + BOOTKEY0_3 + 0x0106 + Bits 63:48 of SHA-256 hash of boot key 0 (ECC) + 16 + 0x0000 + + + BOOTKEY0_3 + [15:0] + read-only + + + + + BOOTKEY0_4 + 0x0108 + Bits 79:64 of SHA-256 hash of boot key 0 (ECC) + 16 + 0x0000 + + + BOOTKEY0_4 + [15:0] + read-only + + + + + BOOTKEY0_5 + 0x010a + Bits 95:80 of SHA-256 hash of boot key 0 (ECC) + 16 + 0x0000 + + + BOOTKEY0_5 + [15:0] + read-only + + + + + BOOTKEY0_6 + 0x010c + Bits 111:96 of SHA-256 hash of boot key 0 (ECC) + 16 + 0x0000 + + + BOOTKEY0_6 + [15:0] + read-only + + + + + BOOTKEY0_7 + 0x010e + Bits 127:112 of SHA-256 hash of boot key 0 (ECC) + 16 + 0x0000 + + + BOOTKEY0_7 + [15:0] + read-only + + + + + BOOTKEY0_8 + 0x0110 + Bits 143:128 of SHA-256 hash of boot key 0 (ECC) + 16 + 0x0000 + + + BOOTKEY0_8 + [15:0] + read-only + + + + + BOOTKEY0_9 + 0x0112 + Bits 159:144 of SHA-256 hash of boot key 0 (ECC) + 16 + 0x0000 + + + BOOTKEY0_9 + [15:0] + read-only + + + + + BOOTKEY0_10 + 0x0114 + Bits 175:160 of SHA-256 hash of boot key 0 (ECC) + 16 + 0x0000 + + + BOOTKEY0_10 + [15:0] + read-only + + + + + BOOTKEY0_11 + 0x0116 + Bits 191:176 of SHA-256 hash of boot key 0 (ECC) + 16 + 0x0000 + + + BOOTKEY0_11 + [15:0] + read-only + + + + + BOOTKEY0_12 + 0x0118 + Bits 207:192 of SHA-256 hash of boot key 0 (ECC) + 16 + 0x0000 + + + BOOTKEY0_12 + [15:0] + read-only + + + + + BOOTKEY0_13 + 0x011a + Bits 223:208 of SHA-256 hash of boot key 0 (ECC) + 16 + 0x0000 + + + BOOTKEY0_13 + [15:0] + read-only + + + + + BOOTKEY0_14 + 0x011c + Bits 239:224 of SHA-256 hash of boot key 0 (ECC) + 16 + 0x0000 + + + BOOTKEY0_14 + [15:0] + read-only + + + + + BOOTKEY0_15 + 0x011e + Bits 255:240 of SHA-256 hash of boot key 0 (ECC) + 16 + 0x0000 + + + BOOTKEY0_15 + [15:0] + read-only + + + + + BOOTKEY1_0 + 0x0120 + Bits 15:0 of SHA-256 hash of boot key 1 (ECC) + 16 + 0x0000 + + + BOOTKEY1_0 + [15:0] + read-only + + + + + BOOTKEY1_1 + 0x0122 + Bits 31:16 of SHA-256 hash of boot key 1 (ECC) + 16 + 0x0000 + + + BOOTKEY1_1 + [15:0] + read-only + + + + + BOOTKEY1_2 + 0x0124 + Bits 47:32 of SHA-256 hash of boot key 1 (ECC) + 16 + 0x0000 + + + BOOTKEY1_2 + [15:0] + read-only + + + + + BOOTKEY1_3 + 0x0126 + Bits 63:48 of SHA-256 hash of boot key 1 (ECC) + 16 + 0x0000 + + + BOOTKEY1_3 + [15:0] + read-only + + + + + BOOTKEY1_4 + 0x0128 + Bits 79:64 of SHA-256 hash of boot key 1 (ECC) + 16 + 0x0000 + + + BOOTKEY1_4 + [15:0] + read-only + + + + + BOOTKEY1_5 + 0x012a + Bits 95:80 of SHA-256 hash of boot key 1 (ECC) + 16 + 0x0000 + + + BOOTKEY1_5 + [15:0] + read-only + + + + + BOOTKEY1_6 + 0x012c + Bits 111:96 of SHA-256 hash of boot key 1 (ECC) + 16 + 0x0000 + + + BOOTKEY1_6 + [15:0] + read-only + + + + + BOOTKEY1_7 + 0x012e + Bits 127:112 of SHA-256 hash of boot key 1 (ECC) + 16 + 0x0000 + + + BOOTKEY1_7 + [15:0] + read-only + + + + + BOOTKEY1_8 + 0x0130 + Bits 143:128 of SHA-256 hash of boot key 1 (ECC) + 16 + 0x0000 + + + BOOTKEY1_8 + [15:0] + read-only + + + + + BOOTKEY1_9 + 0x0132 + Bits 159:144 of SHA-256 hash of boot key 1 (ECC) + 16 + 0x0000 + + + BOOTKEY1_9 + [15:0] + read-only + + + + + BOOTKEY1_10 + 0x0134 + Bits 175:160 of SHA-256 hash of boot key 1 (ECC) + 16 + 0x0000 + + + BOOTKEY1_10 + [15:0] + read-only + + + + + BOOTKEY1_11 + 0x0136 + Bits 191:176 of SHA-256 hash of boot key 1 (ECC) + 16 + 0x0000 + + + BOOTKEY1_11 + [15:0] + read-only + + + + + BOOTKEY1_12 + 0x0138 + Bits 207:192 of SHA-256 hash of boot key 1 (ECC) + 16 + 0x0000 + + + BOOTKEY1_12 + [15:0] + read-only + + + + + BOOTKEY1_13 + 0x013a + Bits 223:208 of SHA-256 hash of boot key 1 (ECC) + 16 + 0x0000 + + + BOOTKEY1_13 + [15:0] + read-only + + + + + BOOTKEY1_14 + 0x013c + Bits 239:224 of SHA-256 hash of boot key 1 (ECC) + 16 + 0x0000 + + + BOOTKEY1_14 + [15:0] + read-only + + + + + BOOTKEY1_15 + 0x013e + Bits 255:240 of SHA-256 hash of boot key 1 (ECC) + 16 + 0x0000 + + + BOOTKEY1_15 + [15:0] + read-only + + + + + BOOTKEY2_0 + 0x0140 + Bits 15:0 of SHA-256 hash of boot key 2 (ECC) + 16 + 0x0000 + + + BOOTKEY2_0 + [15:0] + read-only + + + + + BOOTKEY2_1 + 0x0142 + Bits 31:16 of SHA-256 hash of boot key 2 (ECC) + 16 + 0x0000 + + + BOOTKEY2_1 + [15:0] + read-only + + + + + BOOTKEY2_2 + 0x0144 + Bits 47:32 of SHA-256 hash of boot key 2 (ECC) + 16 + 0x0000 + + + BOOTKEY2_2 + [15:0] + read-only + + + + + BOOTKEY2_3 + 0x0146 + Bits 63:48 of SHA-256 hash of boot key 2 (ECC) + 16 + 0x0000 + + + BOOTKEY2_3 + [15:0] + read-only + + + + + BOOTKEY2_4 + 0x0148 + Bits 79:64 of SHA-256 hash of boot key 2 (ECC) + 16 + 0x0000 + + + BOOTKEY2_4 + [15:0] + read-only + + + + + BOOTKEY2_5 + 0x014a + Bits 95:80 of SHA-256 hash of boot key 2 (ECC) + 16 + 0x0000 + + + BOOTKEY2_5 + [15:0] + read-only + + + + + BOOTKEY2_6 + 0x014c + Bits 111:96 of SHA-256 hash of boot key 2 (ECC) + 16 + 0x0000 + + + BOOTKEY2_6 + [15:0] + read-only + + + + + BOOTKEY2_7 + 0x014e + Bits 127:112 of SHA-256 hash of boot key 2 (ECC) + 16 + 0x0000 + + + BOOTKEY2_7 + [15:0] + read-only + + + + + BOOTKEY2_8 + 0x0150 + Bits 143:128 of SHA-256 hash of boot key 2 (ECC) + 16 + 0x0000 + + + BOOTKEY2_8 + [15:0] + read-only + + + + + BOOTKEY2_9 + 0x0152 + Bits 159:144 of SHA-256 hash of boot key 2 (ECC) + 16 + 0x0000 + + + BOOTKEY2_9 + [15:0] + read-only + + + + + BOOTKEY2_10 + 0x0154 + Bits 175:160 of SHA-256 hash of boot key 2 (ECC) + 16 + 0x0000 + + + BOOTKEY2_10 + [15:0] + read-only + + + + + BOOTKEY2_11 + 0x0156 + Bits 191:176 of SHA-256 hash of boot key 2 (ECC) + 16 + 0x0000 + + + BOOTKEY2_11 + [15:0] + read-only + + + + + BOOTKEY2_12 + 0x0158 + Bits 207:192 of SHA-256 hash of boot key 2 (ECC) + 16 + 0x0000 + + + BOOTKEY2_12 + [15:0] + read-only + + + + + BOOTKEY2_13 + 0x015a + Bits 223:208 of SHA-256 hash of boot key 2 (ECC) + 16 + 0x0000 + + + BOOTKEY2_13 + [15:0] + read-only + + + + + BOOTKEY2_14 + 0x015c + Bits 239:224 of SHA-256 hash of boot key 2 (ECC) + 16 + 0x0000 + + + BOOTKEY2_14 + [15:0] + read-only + + + + + BOOTKEY2_15 + 0x015e + Bits 255:240 of SHA-256 hash of boot key 2 (ECC) + 16 + 0x0000 + + + BOOTKEY2_15 + [15:0] + read-only + + + + + BOOTKEY3_0 + 0x0160 + Bits 15:0 of SHA-256 hash of boot key 3 (ECC) + 16 + 0x0000 + + + BOOTKEY3_0 + [15:0] + read-only + + + + + BOOTKEY3_1 + 0x0162 + Bits 31:16 of SHA-256 hash of boot key 3 (ECC) + 16 + 0x0000 + + + BOOTKEY3_1 + [15:0] + read-only + + + + + BOOTKEY3_2 + 0x0164 + Bits 47:32 of SHA-256 hash of boot key 3 (ECC) + 16 + 0x0000 + + + BOOTKEY3_2 + [15:0] + read-only + + + + + BOOTKEY3_3 + 0x0166 + Bits 63:48 of SHA-256 hash of boot key 3 (ECC) + 16 + 0x0000 + + + BOOTKEY3_3 + [15:0] + read-only + + + + + BOOTKEY3_4 + 0x0168 + Bits 79:64 of SHA-256 hash of boot key 3 (ECC) + 16 + 0x0000 + + + BOOTKEY3_4 + [15:0] + read-only + + + + + BOOTKEY3_5 + 0x016a + Bits 95:80 of SHA-256 hash of boot key 3 (ECC) + 16 + 0x0000 + + + BOOTKEY3_5 + [15:0] + read-only + + + + + BOOTKEY3_6 + 0x016c + Bits 111:96 of SHA-256 hash of boot key 3 (ECC) + 16 + 0x0000 + + + BOOTKEY3_6 + [15:0] + read-only + + + + + BOOTKEY3_7 + 0x016e + Bits 127:112 of SHA-256 hash of boot key 3 (ECC) + 16 + 0x0000 + + + BOOTKEY3_7 + [15:0] + read-only + + + + + BOOTKEY3_8 + 0x0170 + Bits 143:128 of SHA-256 hash of boot key 3 (ECC) + 16 + 0x0000 + + + BOOTKEY3_8 + [15:0] + read-only + + + + + BOOTKEY3_9 + 0x0172 + Bits 159:144 of SHA-256 hash of boot key 3 (ECC) + 16 + 0x0000 + + + BOOTKEY3_9 + [15:0] + read-only + + + + + BOOTKEY3_10 + 0x0174 + Bits 175:160 of SHA-256 hash of boot key 3 (ECC) + 16 + 0x0000 + + + BOOTKEY3_10 + [15:0] + read-only + + + + + BOOTKEY3_11 + 0x0176 + Bits 191:176 of SHA-256 hash of boot key 3 (ECC) + 16 + 0x0000 + + + BOOTKEY3_11 + [15:0] + read-only + + + + + BOOTKEY3_12 + 0x0178 + Bits 207:192 of SHA-256 hash of boot key 3 (ECC) + 16 + 0x0000 + + + BOOTKEY3_12 + [15:0] + read-only + + + + + BOOTKEY3_13 + 0x017a + Bits 223:208 of SHA-256 hash of boot key 3 (ECC) + 16 + 0x0000 + + + BOOTKEY3_13 + [15:0] + read-only + + + + + BOOTKEY3_14 + 0x017c + Bits 239:224 of SHA-256 hash of boot key 3 (ECC) + 16 + 0x0000 + + + BOOTKEY3_14 + [15:0] + read-only + + + + + BOOTKEY3_15 + 0x017e + Bits 255:240 of SHA-256 hash of boot key 3 (ECC) + 16 + 0x0000 + + + BOOTKEY3_15 + [15:0] + read-only + + + + + KEY1_0 + 0x1e90 + Bits 15:0 of OTP access key 1 (ECC) + 16 + 0x0000 + + + KEY1_0 + [15:0] + read-only + + + + + KEY1_1 + 0x1e92 + Bits 31:16 of OTP access key 1 (ECC) + 16 + 0x0000 + + + KEY1_1 + [15:0] + read-only + + + + + KEY1_2 + 0x1e94 + Bits 47:32 of OTP access key 1 (ECC) + 16 + 0x0000 + + + KEY1_2 + [15:0] + read-only + + + + + KEY1_3 + 0x1e96 + Bits 63:48 of OTP access key 1 (ECC) + 16 + 0x0000 + + + KEY1_3 + [15:0] + read-only + + + + + KEY1_4 + 0x1e98 + Bits 79:64 of OTP access key 1 (ECC) + 16 + 0x0000 + + + KEY1_4 + [15:0] + read-only + + + + + KEY1_5 + 0x1e9a + Bits 95:80 of OTP access key 1 (ECC) + 16 + 0x0000 + + + KEY1_5 + [15:0] + read-only + + + + + KEY1_6 + 0x1e9c + Bits 111:96 of OTP access key 1 (ECC) + 16 + 0x0000 + + + KEY1_6 + [15:0] + read-only + + + + + KEY1_7 + 0x1e9e + Bits 127:112 of OTP access key 1 (ECC) + 16 + 0x0000 + + + KEY1_7 + [15:0] + read-only + + + + + KEY2_0 + 0x1ea0 + Bits 15:0 of OTP access key 2 (ECC) + 16 + 0x0000 + + + KEY2_0 + [15:0] + read-only + + + + + KEY2_1 + 0x1ea2 + Bits 31:16 of OTP access key 2 (ECC) + 16 + 0x0000 + + + KEY2_1 + [15:0] + read-only + + + + + KEY2_2 + 0x1ea4 + Bits 47:32 of OTP access key 2 (ECC) + 16 + 0x0000 + + + KEY2_2 + [15:0] + read-only + + + + + KEY2_3 + 0x1ea6 + Bits 63:48 of OTP access key 2 (ECC) + 16 + 0x0000 + + + KEY2_3 + [15:0] + read-only + + + + + KEY2_4 + 0x1ea8 + Bits 79:64 of OTP access key 2 (ECC) + 16 + 0x0000 + + + KEY2_4 + [15:0] + read-only + + + + + KEY2_5 + 0x1eaa + Bits 95:80 of OTP access key 2 (ECC) + 16 + 0x0000 + + + KEY2_5 + [15:0] + read-only + + + + + KEY2_6 + 0x1eac + Bits 111:96 of OTP access key 2 (ECC) + 16 + 0x0000 + + + KEY2_6 + [15:0] + read-only + + + + + KEY2_7 + 0x1eae + Bits 127:112 of OTP access key 2 (ECC) + 16 + 0x0000 + + + KEY2_7 + [15:0] + read-only + + + + + KEY3_0 + 0x1eb0 + Bits 15:0 of OTP access key 3 (ECC) + 16 + 0x0000 + + + KEY3_0 + [15:0] + read-only + + + + + KEY3_1 + 0x1eb2 + Bits 31:16 of OTP access key 3 (ECC) + 16 + 0x0000 + + + KEY3_1 + [15:0] + read-only + + + + + KEY3_2 + 0x1eb4 + Bits 47:32 of OTP access key 3 (ECC) + 16 + 0x0000 + + + KEY3_2 + [15:0] + read-only + + + + + KEY3_3 + 0x1eb6 + Bits 63:48 of OTP access key 3 (ECC) + 16 + 0x0000 + + + KEY3_3 + [15:0] + read-only + + + + + KEY3_4 + 0x1eb8 + Bits 79:64 of OTP access key 3 (ECC) + 16 + 0x0000 + + + KEY3_4 + [15:0] + read-only + + + + + KEY3_5 + 0x1eba + Bits 95:80 of OTP access key 3 (ECC) + 16 + 0x0000 + + + KEY3_5 + [15:0] + read-only + + + + + KEY3_6 + 0x1ebc + Bits 111:96 of OTP access key 3 (ECC) + 16 + 0x0000 + + + KEY3_6 + [15:0] + read-only + + + + + KEY3_7 + 0x1ebe + Bits 127:112 of OTP access key 3 (ECC) + 16 + 0x0000 + + + KEY3_7 + [15:0] + read-only + + + + + KEY4_0 + 0x1ec0 + Bits 15:0 of OTP access key 4 (ECC) + 16 + 0x0000 + + + KEY4_0 + [15:0] + read-only + + + + + KEY4_1 + 0x1ec2 + Bits 31:16 of OTP access key 4 (ECC) + 16 + 0x0000 + + + KEY4_1 + [15:0] + read-only + + + + + KEY4_2 + 0x1ec4 + Bits 47:32 of OTP access key 4 (ECC) + 16 + 0x0000 + + + KEY4_2 + [15:0] + read-only + + + + + KEY4_3 + 0x1ec6 + Bits 63:48 of OTP access key 4 (ECC) + 16 + 0x0000 + + + KEY4_3 + [15:0] + read-only + + + + + KEY4_4 + 0x1ec8 + Bits 79:64 of OTP access key 4 (ECC) + 16 + 0x0000 + + + KEY4_4 + [15:0] + read-only + + + + + KEY4_5 + 0x1eca + Bits 95:80 of OTP access key 4 (ECC) + 16 + 0x0000 + + + KEY4_5 + [15:0] + read-only + + + + + KEY4_6 + 0x1ecc + Bits 111:96 of OTP access key 4 (ECC) + 16 + 0x0000 + + + KEY4_6 + [15:0] + read-only + + + + + KEY4_7 + 0x1ece + Bits 127:112 of OTP access key 4 (ECC) + 16 + 0x0000 + + + KEY4_7 + [15:0] + read-only + + + + + KEY5_0 + 0x1ed0 + Bits 15:0 of OTP access key 5 (ECC) + 16 + 0x0000 + + + KEY5_0 + [15:0] + read-only + + + + + KEY5_1 + 0x1ed2 + Bits 31:16 of OTP access key 5 (ECC) + 16 + 0x0000 + + + KEY5_1 + [15:0] + read-only + + + + + KEY5_2 + 0x1ed4 + Bits 47:32 of OTP access key 5 (ECC) + 16 + 0x0000 + + + KEY5_2 + [15:0] + read-only + + + + + KEY5_3 + 0x1ed6 + Bits 63:48 of OTP access key 5 (ECC) + 16 + 0x0000 + + + KEY5_3 + [15:0] + read-only + + + + + KEY5_4 + 0x1ed8 + Bits 79:64 of OTP access key 5 (ECC) + 16 + 0x0000 + + + KEY5_4 + [15:0] + read-only + + + + + KEY5_5 + 0x1eda + Bits 95:80 of OTP access key 5 (ECC) + 16 + 0x0000 + + + KEY5_5 + [15:0] + read-only + + + + + KEY5_6 + 0x1edc + Bits 111:96 of OTP access key 5 (ECC) + 16 + 0x0000 + + + KEY5_6 + [15:0] + read-only + + + + + KEY5_7 + 0x1ede + Bits 127:112 of OTP access key 5 (ECC) + 16 + 0x0000 + + + KEY5_7 + [15:0] + read-only + + + + + KEY6_0 + 0x1ee0 + Bits 15:0 of OTP access key 6 (ECC) + 16 + 0x0000 + + + KEY6_0 + [15:0] + read-only + + + + + KEY6_1 + 0x1ee2 + Bits 31:16 of OTP access key 6 (ECC) + 16 + 0x0000 + + + KEY6_1 + [15:0] + read-only + + + + + KEY6_2 + 0x1ee4 + Bits 47:32 of OTP access key 6 (ECC) + 16 + 0x0000 + + + KEY6_2 + [15:0] + read-only + + + + + KEY6_3 + 0x1ee6 + Bits 63:48 of OTP access key 6 (ECC) + 16 + 0x0000 + + + KEY6_3 + [15:0] + read-only + + + + + KEY6_4 + 0x1ee8 + Bits 79:64 of OTP access key 6 (ECC) + 16 + 0x0000 + + + KEY6_4 + [15:0] + read-only + + + + + KEY6_5 + 0x1eea + Bits 95:80 of OTP access key 6 (ECC) + 16 + 0x0000 + + + KEY6_5 + [15:0] + read-only + + + + + KEY6_6 + 0x1eec + Bits 111:96 of OTP access key 6 (ECC) + 16 + 0x0000 + + + KEY6_6 + [15:0] + read-only + + + + + KEY6_7 + 0x1eee + Bits 127:112 of OTP access key 6 (ECC) + 16 + 0x0000 + + + KEY6_7 + [15:0] + read-only + + + + + + + OTP_DATA_RAW + Predefined OTP data layout for RP2350 + 0x40134000 + + 0 + 16384 + registers + + + + CHIPID0 + 0x00000000 + Bits 15:0 of public device ID. (ECC) + + The CHIPID0..3 rows contain a 64-bit random identifier for this chip, which can be read from the USB bootloader PICOBOOT interface or from the get_sys_info ROM API. + + The number of random bits makes the occurrence of twins exceedingly unlikely: for example, a fleet of a hundred million devices has a 99.97% probability of no twinned IDs. This is estimated to be lower than the occurrence of process errors in the assignment of sequential random IDs, and for practical purposes CHIPID may be treated as unique. + 0x00000000 + + + CHIPID0 + [15:0] + read-only + + + + + CHIPID1 + 0x00000004 + Bits 31:16 of public device ID (ECC) + 0x00000000 + + + CHIPID1 + [15:0] + read-only + + + + + CHIPID2 + 0x00000008 + Bits 47:32 of public device ID (ECC) + 0x00000000 + + + CHIPID2 + [15:0] + read-only + + + + + CHIPID3 + 0x0000000c + Bits 63:48 of public device ID (ECC) + 0x00000000 + + + CHIPID3 + [15:0] + read-only + + + + + RANDID0 + 0x00000010 + Bits 15:0 of private per-device random number (ECC) + + The RANDID0..7 rows form a 128-bit random number generated during device test. + + This ID is not exposed through the USB PICOBOOT GET_INFO command or the ROM `get_sys_info()` API. However note that the USB PICOBOOT OTP access point can read the entirety of page 0, so this value is not meaningfully private unless the USB PICOBOOT interface is disabled via the DISABLE_BOOTSEL_USB_PICOBOOT_IFC flag in BOOT_FLAGS0. + 0x00000000 + + + RANDID0 + [15:0] + read-only + + + + + RANDID1 + 0x00000014 + Bits 31:16 of private per-device random number (ECC) + 0x00000000 + + + RANDID1 + [15:0] + read-only + + + + + RANDID2 + 0x00000018 + Bits 47:32 of private per-device random number (ECC) + 0x00000000 + + + RANDID2 + [15:0] + read-only + + + + + RANDID3 + 0x0000001c + Bits 63:48 of private per-device random number (ECC) + 0x00000000 + + + RANDID3 + [15:0] + read-only + + + + + RANDID4 + 0x00000020 + Bits 79:64 of private per-device random number (ECC) + 0x00000000 + + + RANDID4 + [15:0] + read-only + + + + + RANDID5 + 0x00000024 + Bits 95:80 of private per-device random number (ECC) + 0x00000000 + + + RANDID5 + [15:0] + read-only + + + + + RANDID6 + 0x00000028 + Bits 111:96 of private per-device random number (ECC) + 0x00000000 + + + RANDID6 + [15:0] + read-only + + + + + RANDID7 + 0x0000002c + Bits 127:112 of private per-device random number (ECC) + 0x00000000 + + + RANDID7 + [15:0] + read-only + + + + + ROSC_CALIB + 0x00000040 + Ring oscillator frequency in kHz, measured during manufacturing (ECC) + + This is measured at 1.1 V, at room temperature, with the ROSC configuration registers in their reset state. + 0x00000000 + + + ROSC_CALIB + [15:0] + read-only + + + + + LPOSC_CALIB + 0x00000044 + Low-power oscillator frequency in Hz, measured during manufacturing (ECC) + + This is measured at 1.1V, at room temperature, with the LPOSC trim register in its reset state. + 0x00000000 + + + LPOSC_CALIB + [15:0] + read-only + + + + + NUM_GPIOS + 0x00000060 + The number of main user GPIOs (bank 0). Should read 48 in the QFN80 package, and 30 in the QFN60 package. (ECC) + 0x00000000 + + + NUM_GPIOS + [7:0] + read-only + + + + + INFO_CRC0 + 0x000000d8 + Lower 16 bits of CRC32 of OTP addresses 0x00 through 0x6b (polynomial 0x4c11db7, input reflected, output reflected, seed all-ones, final XOR all-ones) (ECC) + 0x00000000 + + + INFO_CRC0 + [15:0] + read-only + + + + + INFO_CRC1 + 0x000000dc + Upper 16 bits of CRC32 of OTP addresses 0x00 through 0x6b (ECC) + 0x00000000 + + + INFO_CRC1 + [15:0] + read-only + + + + + CRIT0 + 0x000000e0 + Page 0 critical boot flags (RBIT-8) + 0x00000000 + + + RISCV_DISABLE + Permanently disable RISC-V processors (Hazard3) + [1:1] + read-only + + + ARM_DISABLE + Permanently disable ARM processors (Cortex-M33) + [0:0] + read-only + + + + + CRIT0_R1 + 0x000000e4 + Redundant copy of CRIT0 + 0x00000000 + + + CRIT0_R1 + [23:0] + read-only + + + + + CRIT0_R2 + 0x000000e8 + Redundant copy of CRIT0 + 0x00000000 + + + CRIT0_R2 + [23:0] + read-only + + + + + CRIT0_R3 + 0x000000ec + Redundant copy of CRIT0 + 0x00000000 + + + CRIT0_R3 + [23:0] + read-only + + + + + CRIT0_R4 + 0x000000f0 + Redundant copy of CRIT0 + 0x00000000 + + + CRIT0_R4 + [23:0] + read-only + + + + + CRIT0_R5 + 0x000000f4 + Redundant copy of CRIT0 + 0x00000000 + + + CRIT0_R5 + [23:0] + read-only + + + + + CRIT0_R6 + 0x000000f8 + Redundant copy of CRIT0 + 0x00000000 + + + CRIT0_R6 + [23:0] + read-only + + + + + CRIT0_R7 + 0x000000fc + Redundant copy of CRIT0 + 0x00000000 + + + CRIT0_R7 + [23:0] + read-only + + + + + CRIT1 + 0x00000100 + Page 1 critical boot flags (RBIT-8) + 0x00000000 + + + GLITCH_DETECTOR_SENS + Increase the sensitivity of the glitch detectors from their default. + [6:5] + read-only + + + GLITCH_DETECTOR_ENABLE + Arm the glitch detectors to reset the system if an abnormal clock/power event is observed. + [4:4] + read-only + + + BOOT_ARCH + Set the default boot architecture, 0=ARM 1=RISC-V. Ignored if ARM_DISABLE, RISCV_DISABLE or SECURE_BOOT_ENABLE is set. + [3:3] + read-only + + + DEBUG_DISABLE + Disable all debug access + [2:2] + read-only + + + SECURE_DEBUG_DISABLE + Disable Secure debug access + [1:1] + read-only + + + SECURE_BOOT_ENABLE + Enable boot signature enforcement, and permanently disable the RISC-V cores. + [0:0] + read-only + + + + + CRIT1_R1 + 0x00000104 + Redundant copy of CRIT1 + 0x00000000 + + + CRIT1_R1 + [23:0] + read-only + + + + + CRIT1_R2 + 0x00000108 + Redundant copy of CRIT1 + 0x00000000 + + + CRIT1_R2 + [23:0] + read-only + + + + + CRIT1_R3 + 0x0000010c + Redundant copy of CRIT1 + 0x00000000 + + + CRIT1_R3 + [23:0] + read-only + + + + + CRIT1_R4 + 0x00000110 + Redundant copy of CRIT1 + 0x00000000 + + + CRIT1_R4 + [23:0] + read-only + + + + + CRIT1_R5 + 0x00000114 + Redundant copy of CRIT1 + 0x00000000 + + + CRIT1_R5 + [23:0] + read-only + + + + + CRIT1_R6 + 0x00000118 + Redundant copy of CRIT1 + 0x00000000 + + + CRIT1_R6 + [23:0] + read-only + + + + + CRIT1_R7 + 0x0000011c + Redundant copy of CRIT1 + 0x00000000 + + + CRIT1_R7 + [23:0] + read-only + + + + + BOOT_FLAGS0 + 0x00000120 + Disable/Enable boot paths/features in the RP2350 mask ROM. Disables always supersede enables. Enables are provided where there are other configurations in OTP that must be valid. (RBIT-3) + 0x00000000 + + + DISABLE_SRAM_WINDOW_BOOT + [21:21] + read-only + + + DISABLE_XIP_ACCESS_ON_SRAM_ENTRY + Disable all access to XIP after entering an SRAM binary. + + Note that this will cause bootrom APIs that access XIP to fail, including APIs that interact with the partition table. + [20:20] + read-only + + + DISABLE_BOOTSEL_UART_BOOT + [19:19] + read-only + + + DISABLE_BOOTSEL_USB_PICOBOOT_IFC + [18:18] + read-only + + + DISABLE_BOOTSEL_USB_MSD_IFC + [17:17] + read-only + + + DISABLE_WATCHDOG_SCRATCH + [16:16] + read-only + + + DISABLE_POWER_SCRATCH + [15:15] + read-only + + + ENABLE_OTP_BOOT + Enable OTP boot. A number of OTP rows specified by OTPBOOT_LEN will be loaded, starting from OTPBOOT_SRC, into the SRAM location specified by OTPBOOT_DST1 and OTPBOOT_DST0. + + The loaded program image is stored with ECC, 16 bits per row, and must contain a valid IMAGE_DEF. Do not set this bit without first programming an image into OTP and configuring OTPBOOT_LEN, OTPBOOT_SRC, OTPBOOT_DST0 and OTPBOOT_DST1. + + Note that OTPBOOT_LEN and OTPBOOT_SRC must be even numbers of OTP rows. Equivalently, the image must be a multiple of 32 bits in size, and must start at a 32-bit-aligned address in the ECC read data address window. + [14:14] + read-only + + + DISABLE_OTP_BOOT + Takes precedence over ENABLE_OTP_BOOT. + [13:13] + read-only + + + DISABLE_FLASH_BOOT + [12:12] + read-only + + + ROLLBACK_REQUIRED + Require binaries to have a rollback version. Set automatically the first time a binary with a rollback version is booted. + [11:11] + read-only + + + HASHED_PARTITION_TABLE + Require a partition table to be hashed (if not signed) + [10:10] + read-only + + + SECURE_PARTITION_TABLE + Require a partition table to be signed + [9:9] + read-only + + + DISABLE_AUTO_SWITCH_ARCH + Disable auto-switch of CPU architecture on boot when the (only) binary to be booted is for the other Arm/RISC-V architecture and both architectures are enabled + [8:8] + read-only + + + SINGLE_FLASH_BINARY + Restrict flash boot path to use of a single binary at the start of flash + [7:7] + read-only + + + OVERRIDE_FLASH_PARTITION_SLOT_SIZE + Override the limit for default flash metadata scanning. + + The value is specified in FLASH_PARTITION_SLOT_SIZE. Make sure FLASH_PARTITION_SLOT_SIZE is valid before setting this bit + [6:6] + read-only + + + FLASH_DEVINFO_ENABLE + Mark FLASH_DEVINFO as containing valid, ECC'd data which describes external flash devices. + [5:5] + read-only + + + FAST_SIGCHECK_ROSC_DIV + Enable quartering of ROSC divisor during signature check, to reduce secure boot time + [4:4] + read-only + + + FLASH_IO_VOLTAGE_1V8 + If 1, configure the QSPI pads for 1.8 V operation when accessing flash for the first time from the bootrom, using the VOLTAGE_SELECT register for the QSPI pads bank. This slightly improves the input timing of the pads at low voltages, but does not affect their output characteristics. + + If 0, leave VOLTAGE_SELECT in its reset state (suitable for operation at and above 2.5 V) + [3:3] + read-only + + + ENABLE_BOOTSEL_NON_DEFAULT_PLL_XOSC_CFG + Enable loading of the non-default XOSC and PLL configuration before entering BOOTSEL mode. + + Ensure that BOOTSEL_XOSC_CFG and BOOTSEL_PLL_CFG are correctly programmed before setting this bit. + + If this bit is set, user software may use the contents of BOOTSEL_PLL_CFG to calculated the expected XOSC frequency based on the fixed USB boot frequency of 48 MHz. + [2:2] + read-only + + + ENABLE_BOOTSEL_LED + Enable bootloader activity LED. If set, bootsel_led_cfg is assumed to be valid + [1:1] + read-only + + + DISABLE_BOOTSEL_EXEC2 + [0:0] + read-only + + + + + BOOT_FLAGS0_R1 + 0x00000124 + Redundant copy of BOOT_FLAGS0 + 0x00000000 + + + BOOT_FLAGS0_R1 + [23:0] + read-only + + + + + BOOT_FLAGS0_R2 + 0x00000128 + Redundant copy of BOOT_FLAGS0 + 0x00000000 + + + BOOT_FLAGS0_R2 + [23:0] + read-only + + + + + BOOT_FLAGS1 + 0x0000012c + Disable/Enable boot paths/features in the RP2350 mask ROM. Disables always supersede enables. Enables are provided where there are other configurations in OTP that must be valid. (RBIT-3) + 0x00000000 + + + DOUBLE_TAP + Enable entering BOOTSEL mode via double-tap of the RUN/RSTn pin. Adds a significant delay to boot time, as configured by DOUBLE_TAP_DELAY. + + This functions by waiting at startup (i.e. following a reset) to see if a second reset is applied soon afterward. The second reset is detected by the bootrom with help of the POWMAN_CHIP_RESET_DOUBLE_TAP flag, which is not reset by the external reset pin, and the bootrom enters BOOTSEL mode (NSBOOT) to await further instruction over USB or UART. + [19:19] + read-only + + + DOUBLE_TAP_DELAY + Adjust how long to wait for a second reset when double tap BOOTSEL mode is enabled via DOUBLE_TAP. The minimum is 50 milliseconds, and each unit of this field adds an additional 50 milliseconds. + + For example, settings this field to its maximum value of 7 will cause the chip to wait for 400 milliseconds at boot to check for a second reset which requests entry to BOOTSEL mode. + + 200 milliseconds (DOUBLE_TAP_DELAY=3) is a good intermediate value. + [18:16] + read-only + + + KEY_INVALID + Mark a boot key as invalid, or prevent it from ever becoming valid. The bootrom will ignore any boot key marked as invalid during secure boot signature checks. + + Each bit in this field corresponds to one of the four 256-bit boot key hashes that may be stored in page 2 of the OTP. + + When provisioning boot keys, it's recommended to mark any boot key slots you don't intend to use as KEY_INVALID, so that spurious keys can not be installed at a later time. + [11:8] + read-only + + + KEY_VALID + Mark each of the possible boot keys as valid. The bootrom will check signatures against all valid boot keys, and ignore invalid boot keys. + + Each bit in this field corresponds to one of the four 256-bit boot key hashes that may be stored in page 2 of the OTP. + + A KEY_VALID bit is ignored if the corresponding KEY_INVALID bit is set. Boot keys are considered valid only when KEY_VALID is set and KEY_INVALID is clear. + + Do not mark a boot key as KEY_VALID if it does not contain a valid SHA-256 hash of your secp256k1 public key. Verify keys after programming, before setting the KEY_VALID bits -- a boot key with uncorrectable ECC faults will render your device unbootable if secure boot is enabled. + + Do not enable secure boot without first installing a valid key. This will render your device unbootable. + [3:0] + read-only + + + + + BOOT_FLAGS1_R1 + 0x00000130 + Redundant copy of BOOT_FLAGS1 + 0x00000000 + + + BOOT_FLAGS1_R1 + [23:0] + read-only + + + + + BOOT_FLAGS1_R2 + 0x00000134 + Redundant copy of BOOT_FLAGS1 + 0x00000000 + + + BOOT_FLAGS1_R2 + [23:0] + read-only + + + + + DEFAULT_BOOT_VERSION0 + 0x00000138 + Default boot version thermometer counter, bits 23:0 (RBIT-3) + 0x00000000 + + + DEFAULT_BOOT_VERSION0 + [23:0] + read-only + + + + + DEFAULT_BOOT_VERSION0_R1 + 0x0000013c + Redundant copy of DEFAULT_BOOT_VERSION0 + 0x00000000 + + + DEFAULT_BOOT_VERSION0_R1 + [23:0] + read-only + + + + + DEFAULT_BOOT_VERSION0_R2 + 0x00000140 + Redundant copy of DEFAULT_BOOT_VERSION0 + 0x00000000 + + + DEFAULT_BOOT_VERSION0_R2 + [23:0] + read-only + + + + + DEFAULT_BOOT_VERSION1 + 0x00000144 + Default boot version thermometer counter, bits 47:24 (RBIT-3) + 0x00000000 + + + DEFAULT_BOOT_VERSION1 + [23:0] + read-only + + + + + DEFAULT_BOOT_VERSION1_R1 + 0x00000148 + Redundant copy of DEFAULT_BOOT_VERSION1 + 0x00000000 + + + DEFAULT_BOOT_VERSION1_R1 + [23:0] + read-only + + + + + DEFAULT_BOOT_VERSION1_R2 + 0x0000014c + Redundant copy of DEFAULT_BOOT_VERSION1 + 0x00000000 + + + DEFAULT_BOOT_VERSION1_R2 + [23:0] + read-only + + + + + FLASH_DEVINFO + 0x00000150 + Stores information about external flash device(s). (ECC) + + Assumed to be valid if BOOT_FLAGS0_FLASH_DEVINFO_ENABLE is set. + 0x00000000 + + + CS1_SIZE + The size of the flash/PSRAM device on chip select 1 (addressable at 0x11000000 through 0x11ffffff). + + A value of zero is decoded as a size of zero (no device). Nonzero values are decoded as 4kiB << CS1_SIZE. For example, four megabytes is encoded with a CS1_SIZE value of 10, and 16 megabytes is encoded with a CS1_SIZE value of 12. + + When BOOT_FLAGS0_FLASH_DEVINFO_ENABLE is not set, a default of zero is used. + [15:12] + read-only + + + NONE + 0 + + + 8K + 1 + + + 16K + 2 + + + 32K + 3 + + + 64k + 4 + + + 128K + 5 + + + 256K + 6 + + + 512K + 7 + + + 1M + 8 + + + 2M + 9 + + + 4M + 10 + + + 8M + 11 + + + 16M + 12 + + + + + CS0_SIZE + The size of the flash/PSRAM device on chip select 0 (addressable at 0x10000000 through 0x10ffffff). + + A value of zero is decoded as a size of zero (no device). Nonzero values are decoded as 4kiB << CS0_SIZE. For example, four megabytes is encoded with a CS0_SIZE value of 10, and 16 megabytes is encoded with a CS0_SIZE value of 12. + + When BOOT_FLAGS0_FLASH_DEVINFO_ENABLE is not set, a default of 12 (16 MiB) is used. + [11:8] + read-only + + + NONE + 0 + + + 8K + 1 + + + 16K + 2 + + + 32K + 3 + + + 64k + 4 + + + 128K + 5 + + + 256K + 6 + + + 512K + 7 + + + 1M + 8 + + + 2M + 9 + + + 4M + 10 + + + 8M + 11 + + + 16M + 12 + + + + + D8H_ERASE_SUPPORTED + If true, all attached devices are assumed to support (or ignore, in the case of PSRAM) a block erase command with a command prefix of D8h, an erase size of 64 kiB, and a 24-bit address. Almost all 25-series flash devices support this command. + + If set, the bootrom will use the D8h erase command where it is able, to accelerate bulk erase operations. This makes flash programming faster. + + When BOOT_FLAGS0_FLASH_DEVINFO_ENABLE is not set, this field defaults to false. + [7:7] + read-only + + + CS1_GPIO + Indicate a GPIO number to be used for the secondary flash chip select (CS1), which selects the external QSPI device mapped at system addresses 0x11000000 through 0x11ffffff. There is no such configuration for CS0, as the primary chip select has a dedicated pin. + + On RP2350 the permissible GPIO numbers are 0, 8, 19 and 47. + + Ignored if CS1_size is zero. If CS1_SIZE is nonzero, the bootrom will automatically configure this GPIO as a second chip select upon entering the flash boot path, or entering any other path that may use the QSPI flash interface, such as BOOTSEL mode (nsboot). + [5:0] + read-only + + + + + FLASH_PARTITION_SLOT_SIZE + 0x00000154 + Gap between partition table slot 0 and slot 1 at the start of flash (the default size is 4096 bytes) (ECC) Enabled by the OVERRIDE_FLASH_PARTITION_SLOT_SIZE bit in BOOT_FLAGS, the size is 4096 * (value + 1) + 0x00000000 + + + FLASH_PARTITION_SLOT_SIZE + [15:0] + read-only + + + + + BOOTSEL_LED_CFG + 0x00000158 + Pin configuration for LED status, used by USB bootloader. (ECC) + Must be valid if BOOT_FLAGS0_ENABLE_BOOTSEL_LED is set. + 0x00000000 + + + ACTIVELOW + LED is active-low. (Default: active-high.) + [8:8] + read-only + + + PIN + GPIO index to use for bootloader activity LED. + [5:0] + read-only + + + + + BOOTSEL_PLL_CFG + 0x0000015c + Optional PLL configuration for BOOTSEL mode. (ECC) + + This should be configured to produce an exact 48 MHz based on the crystal oscillator frequency. User mode software may also use this value to calculate the expected crystal frequency based on an assumed 48 MHz PLL output. + + If no configuration is given, the crystal is assumed to be 12 MHz. + + The PLL frequency can be calculated as: + + PLL out = (XOSC frequency / (REFDIV+1)) x FBDIV / (POSTDIV1 x POSTDIV2) + + Conversely the crystal frequency can be calculated as: + + XOSC frequency = 48 MHz x (REFDIV+1) x (POSTDIV1 x POSTDIV2) / FBDIV + + (Note the +1 on REFDIV is because the value stored in this OTP location is the actual divisor value minus one.) + + Used if and only if ENABLE_BOOTSEL_NON_DEFAULT_PLL_XOSC_CFG is set in BOOT_FLAGS0. That bit should be set only after this row and BOOTSEL_XOSC_CFG are both correctly programmed. + 0x00000000 + + + REFDIV + PLL reference divisor, minus one. + + Programming a value of 0 means a reference divisor of 1. Programming a value of 1 means a reference divisor of 2 (for exceptionally fast XIN inputs) + [15:15] + read-only + + + POSTDIV2 + PLL post-divide 2 divisor, in the range 1..7 inclusive. + [14:12] + read-only + + + POSTDIV1 + PLL post-divide 1 divisor, in the range 1..7 inclusive. + [11:9] + read-only + + + FBDIV + PLL feedback divisor, in the range 16..320 inclusive. + [8:0] + read-only + + + + + BOOTSEL_XOSC_CFG + 0x00000160 + Non-default crystal oscillator configuration for the USB bootloader. (ECC) + + These values may also be used by user code configuring the crystal oscillator. + + Used if and only if ENABLE_BOOTSEL_NON_DEFAULT_PLL_XOSC_CFG is set in BOOT_FLAGS0. That bit should be set only after this row and BOOTSEL_PLL_CFG are both correctly programmed. + 0x00000000 + + + RANGE + Value of the XOSC_CTRL_FREQ_RANGE register. + [15:14] + read-only + + + 1_15MHZ + 0 + + + 10_30MHZ + 1 + + + 25_60MHZ + 2 + + + 40_100MHZ + 3 + + + + + STARTUP + Value of the XOSC_STARTUP register + [13:0] + read-only + + + + + USB_BOOT_FLAGS + 0x00000164 + USB boot specific feature flags (RBIT-3) + 0x00000000 + + + DP_DM_SWAP + Swap DM/DP during USB boot, to support board layouts with mirrored USB routing (deliberate or accidental). + [23:23] + read-only + + + WHITE_LABEL_ADDR_VALID + valid flag for INFO_UF2_TXT_BOARD_ID_STRDEF entry of the USB_WHITE_LABEL struct (index 15) + [22:22] + read-only + + + WL_INFO_UF2_TXT_BOARD_ID_STRDEF_VALID + valid flag for the USB_WHITE_LABEL_ADDR field + [15:15] + read-only + + + WL_INFO_UF2_TXT_MODEL_STRDEF_VALID + valid flag for INFO_UF2_TXT_MODEL_STRDEF entry of the USB_WHITE_LABEL struct (index 14) + [14:14] + read-only + + + WL_INDEX_HTM_REDIRECT_NAME_STRDEF_VALID + valid flag for INDEX_HTM_REDIRECT_NAME_STRDEF entry of the USB_WHITE_LABEL struct (index 13) + [13:13] + read-only + + + WL_INDEX_HTM_REDIRECT_URL_STRDEF_VALID + valid flag for INDEX_HTM_REDIRECT_URL_STRDEF entry of the USB_WHITE_LABEL struct (index 12) + [12:12] + read-only + + + WL_SCSI_INQUIRY_VERSION_STRDEF_VALID + valid flag for SCSI_INQUIRY_VERSION_STRDEF entry of the USB_WHITE_LABEL struct (index 11) + [11:11] + read-only + + + WL_SCSI_INQUIRY_PRODUCT_STRDEF_VALID + valid flag for SCSI_INQUIRY_PRODUCT_STRDEF entry of the USB_WHITE_LABEL struct (index 10) + [10:10] + read-only + + + WL_SCSI_INQUIRY_VENDOR_STRDEF_VALID + valid flag for SCSI_INQUIRY_VENDOR_STRDEF entry of the USB_WHITE_LABEL struct (index 9) + [9:9] + read-only + + + WL_VOLUME_LABEL_STRDEF_VALID + valid flag for VOLUME_LABEL_STRDEF entry of the USB_WHITE_LABEL struct (index 8) + [8:8] + read-only + + + WL_USB_CONFIG_ATTRIBUTES_MAX_POWER_VALUES_VALID + valid flag for USB_CONFIG_ATTRIBUTES_MAX_POWER_VALUES entry of the USB_WHITE_LABEL struct (index 7) + [7:7] + read-only + + + WL_USB_DEVICE_SERIAL_NUMBER_STRDEF_VALID + valid flag for USB_DEVICE_SERIAL_NUMBER_STRDEF entry of the USB_WHITE_LABEL struct (index 6) + [6:6] + read-only + + + WL_USB_DEVICE_PRODUCT_STRDEF_VALID + valid flag for USB_DEVICE_PRODUCT_STRDEF entry of the USB_WHITE_LABEL struct (index 5) + [5:5] + read-only + + + WL_USB_DEVICE_MANUFACTURER_STRDEF_VALID + valid flag for USB_DEVICE_MANUFACTURER_STRDEF entry of the USB_WHITE_LABEL struct (index 4) + [4:4] + read-only + + + WL_USB_DEVICE_LANG_ID_VALUE_VALID + valid flag for USB_DEVICE_LANG_ID_VALUE entry of the USB_WHITE_LABEL struct (index 3) + [3:3] + read-only + + + WL_USB_DEVICE_SERIAL_NUMBER_VALUE_VALID + valid flag for USB_DEVICE_BCD_DEVICEVALUE entry of the USB_WHITE_LABEL struct (index 2) + [2:2] + read-only + + + WL_USB_DEVICE_PID_VALUE_VALID + valid flag for USB_DEVICE_PID_VALUE entry of the USB_WHITE_LABEL struct (index 1) + [1:1] + read-only + + + WL_USB_DEVICE_VID_VALUE_VALID + valid flag for USB_DEVICE_VID_VALUE entry of the USB_WHITE_LABEL struct (index 0) + [0:0] + read-only + + + + + USB_BOOT_FLAGS_R1 + 0x00000168 + Redundant copy of USB_BOOT_FLAGS + 0x00000000 + + + USB_BOOT_FLAGS_R1 + [23:0] + read-only + + + + + USB_BOOT_FLAGS_R2 + 0x0000016c + Redundant copy of USB_BOOT_FLAGS + 0x00000000 + + + USB_BOOT_FLAGS_R2 + [23:0] + read-only + + + + + USB_WHITE_LABEL_ADDR + 0x00000170 + Row index of the USB_WHITE_LABEL structure within OTP (ECC) + + The table has 16 rows, each of which are also ECC and marked valid by the corresponding valid bit in USB_BOOT_FLAGS (ECC). + + The entries are either _VALUEs where the 16 bit value is used as is, or _STRDEFs which acts as a pointers to a string value. + + The value stored in a _STRDEF is two separate bytes: The low seven bits of the first (LSB) byte indicates the number of characters in the string, and the top bit of the first (LSB) byte if set to indicate that each character in the string is two bytes (Unicode) versus one byte if unset. The second (MSB) byte represents the location of the string data, and is encoded as the number of rows from this USB_WHITE_LABEL_ADDR; i.e. the row of the start of the string is USB_WHITE_LABEL_ADDR value + msb_byte. + + In each case, the corresponding valid bit enables replacing the default value for the corresponding item provided by the boot rom. + + Note that Unicode _STRDEFs are only supported for USB_DEVICE_PRODUCT_STRDEF, USB_DEVICE_SERIAL_NUMBER_STRDEF and USB_DEVICE_MANUFACTURER_STRDEF. Unicode values will be ignored if specified for other fields, and non-unicode values for these three items will be converted to Unicode characters by setting the upper 8 bits to zero. + + Note that if the USB_WHITE_LABEL structure or the corresponding strings are not readable by BOOTSEL mode based on OTP permissions, or if alignment requirements are not met, then the corresponding default values are used. + + The index values indicate where each field is located (row USB_WHITE_LABEL_ADDR value + index): + 0x00000000 + + + USB_WHITE_LABEL_ADDR + [15:0] + read-only + + + INDEX_USB_DEVICE_VID_VALUE + 0 + + + INDEX_USB_DEVICE_PID_VALUE + 1 + + + INDEX_USB_DEVICE_BCD_DEVICE_VALUE + 2 + + + INDEX_USB_DEVICE_LANG_ID_VALUE + 3 + + + INDEX_USB_DEVICE_MANUFACTURER_STRDEF + 4 + + + INDEX_USB_DEVICE_PRODUCT_STRDEF + 5 + + + INDEX_USB_DEVICE_SERIAL_NUMBER_STRDEF + 6 + + + INDEX_USB_CONFIG_ATTRIBUTES_MAX_POWER_VALUES + 7 + + + INDEX_VOLUME_LABEL_STRDEF + 8 + + + INDEX_SCSI_INQUIRY_VENDOR_STRDEF + 9 + + + INDEX_SCSI_INQUIRY_PRODUCT_STRDEF + 10 + + + INDEX_SCSI_INQUIRY_VERSION_STRDEF + 11 + + + INDEX_INDEX_HTM_REDIRECT_URL_STRDEF + 12 + + + INDEX_INDEX_HTM_REDIRECT_NAME_STRDEF + 13 + + + INDEX_INFO_UF2_TXT_MODEL_STRDEF + 14 + + + INDEX_INFO_UF2_TXT_BOARD_ID_STRDEF + 15 + + + + + + + OTPBOOT_SRC + 0x00000178 + OTP start row for the OTP boot image. (ECC) + + If OTP boot is enabled, the bootrom will load from this location into SRAM and then directly enter the loaded image. Note that the image must be signed if SECURE_BOOT_ENABLE is set. The image itself is assumed to be ECC-protected. + + This must be an even number. Equivalently, the OTP boot image must start at a word-aligned location in the ECC read data address window. + 0x00000000 + + + OTPBOOT_SRC + [15:0] + read-only + + + + + OTPBOOT_LEN + 0x0000017c + Length in rows of the OTP boot image. (ECC) + + OTPBOOT_LEN must be even. The total image size must be a multiple of 4 bytes (32 bits). + 0x00000000 + + + OTPBOOT_LEN + [15:0] + read-only + + + + + OTPBOOT_DST0 + 0x00000180 + Bits 15:0 of the OTP boot image load destination (and entry point). (ECC) + + This must be a location in main SRAM (main SRAM is addresses 0x20000000 through 0x20082000) and must be word-aligned. + 0x00000000 + + + OTPBOOT_DST0 + [15:0] + read-only + + + + + OTPBOOT_DST1 + 0x00000184 + Bits 31:16 of the OTP boot image load destination (and entry point). (ECC) + + This must be a location in main SRAM (main SRAM is addresses 0x20000000 through 0x20082000) and must be word-aligned. + 0x00000000 + + + OTPBOOT_DST1 + [15:0] + read-only + + + + + BOOTKEY0_0 + 0x00000200 + Bits 15:0 of SHA-256 hash of boot key 0 (ECC) + 0x00000000 + + + BOOTKEY0_0 + [15:0] + read-only + + + + + BOOTKEY0_1 + 0x00000204 + Bits 31:16 of SHA-256 hash of boot key 0 (ECC) + 0x00000000 + + + BOOTKEY0_1 + [15:0] + read-only + + + + + BOOTKEY0_2 + 0x00000208 + Bits 47:32 of SHA-256 hash of boot key 0 (ECC) + 0x00000000 + + + BOOTKEY0_2 + [15:0] + read-only + + + + + BOOTKEY0_3 + 0x0000020c + Bits 63:48 of SHA-256 hash of boot key 0 (ECC) + 0x00000000 + + + BOOTKEY0_3 + [15:0] + read-only + + + + + BOOTKEY0_4 + 0x00000210 + Bits 79:64 of SHA-256 hash of boot key 0 (ECC) + 0x00000000 + + + BOOTKEY0_4 + [15:0] + read-only + + + + + BOOTKEY0_5 + 0x00000214 + Bits 95:80 of SHA-256 hash of boot key 0 (ECC) + 0x00000000 + + + BOOTKEY0_5 + [15:0] + read-only + + + + + BOOTKEY0_6 + 0x00000218 + Bits 111:96 of SHA-256 hash of boot key 0 (ECC) + 0x00000000 + + + BOOTKEY0_6 + [15:0] + read-only + + + + + BOOTKEY0_7 + 0x0000021c + Bits 127:112 of SHA-256 hash of boot key 0 (ECC) + 0x00000000 + + + BOOTKEY0_7 + [15:0] + read-only + + + + + BOOTKEY0_8 + 0x00000220 + Bits 143:128 of SHA-256 hash of boot key 0 (ECC) + 0x00000000 + + + BOOTKEY0_8 + [15:0] + read-only + + + + + BOOTKEY0_9 + 0x00000224 + Bits 159:144 of SHA-256 hash of boot key 0 (ECC) + 0x00000000 + + + BOOTKEY0_9 + [15:0] + read-only + + + + + BOOTKEY0_10 + 0x00000228 + Bits 175:160 of SHA-256 hash of boot key 0 (ECC) + 0x00000000 + + + BOOTKEY0_10 + [15:0] + read-only + + + + + BOOTKEY0_11 + 0x0000022c + Bits 191:176 of SHA-256 hash of boot key 0 (ECC) + 0x00000000 + + + BOOTKEY0_11 + [15:0] + read-only + + + + + BOOTKEY0_12 + 0x00000230 + Bits 207:192 of SHA-256 hash of boot key 0 (ECC) + 0x00000000 + + + BOOTKEY0_12 + [15:0] + read-only + + + + + BOOTKEY0_13 + 0x00000234 + Bits 223:208 of SHA-256 hash of boot key 0 (ECC) + 0x00000000 + + + BOOTKEY0_13 + [15:0] + read-only + + + + + BOOTKEY0_14 + 0x00000238 + Bits 239:224 of SHA-256 hash of boot key 0 (ECC) + 0x00000000 + + + BOOTKEY0_14 + [15:0] + read-only + + + + + BOOTKEY0_15 + 0x0000023c + Bits 255:240 of SHA-256 hash of boot key 0 (ECC) + 0x00000000 + + + BOOTKEY0_15 + [15:0] + read-only + + + + + BOOTKEY1_0 + 0x00000240 + Bits 15:0 of SHA-256 hash of boot key 1 (ECC) + 0x00000000 + + + BOOTKEY1_0 + [15:0] + read-only + + + + + BOOTKEY1_1 + 0x00000244 + Bits 31:16 of SHA-256 hash of boot key 1 (ECC) + 0x00000000 + + + BOOTKEY1_1 + [15:0] + read-only + + + + + BOOTKEY1_2 + 0x00000248 + Bits 47:32 of SHA-256 hash of boot key 1 (ECC) + 0x00000000 + + + BOOTKEY1_2 + [15:0] + read-only + + + + + BOOTKEY1_3 + 0x0000024c + Bits 63:48 of SHA-256 hash of boot key 1 (ECC) + 0x00000000 + + + BOOTKEY1_3 + [15:0] + read-only + + + + + BOOTKEY1_4 + 0x00000250 + Bits 79:64 of SHA-256 hash of boot key 1 (ECC) + 0x00000000 + + + BOOTKEY1_4 + [15:0] + read-only + + + + + BOOTKEY1_5 + 0x00000254 + Bits 95:80 of SHA-256 hash of boot key 1 (ECC) + 0x00000000 + + + BOOTKEY1_5 + [15:0] + read-only + + + + + BOOTKEY1_6 + 0x00000258 + Bits 111:96 of SHA-256 hash of boot key 1 (ECC) + 0x00000000 + + + BOOTKEY1_6 + [15:0] + read-only + + + + + BOOTKEY1_7 + 0x0000025c + Bits 127:112 of SHA-256 hash of boot key 1 (ECC) + 0x00000000 + + + BOOTKEY1_7 + [15:0] + read-only + + + + + BOOTKEY1_8 + 0x00000260 + Bits 143:128 of SHA-256 hash of boot key 1 (ECC) + 0x00000000 + + + BOOTKEY1_8 + [15:0] + read-only + + + + + BOOTKEY1_9 + 0x00000264 + Bits 159:144 of SHA-256 hash of boot key 1 (ECC) + 0x00000000 + + + BOOTKEY1_9 + [15:0] + read-only + + + + + BOOTKEY1_10 + 0x00000268 + Bits 175:160 of SHA-256 hash of boot key 1 (ECC) + 0x00000000 + + + BOOTKEY1_10 + [15:0] + read-only + + + + + BOOTKEY1_11 + 0x0000026c + Bits 191:176 of SHA-256 hash of boot key 1 (ECC) + 0x00000000 + + + BOOTKEY1_11 + [15:0] + read-only + + + + + BOOTKEY1_12 + 0x00000270 + Bits 207:192 of SHA-256 hash of boot key 1 (ECC) + 0x00000000 + + + BOOTKEY1_12 + [15:0] + read-only + + + + + BOOTKEY1_13 + 0x00000274 + Bits 223:208 of SHA-256 hash of boot key 1 (ECC) + 0x00000000 + + + BOOTKEY1_13 + [15:0] + read-only + + + + + BOOTKEY1_14 + 0x00000278 + Bits 239:224 of SHA-256 hash of boot key 1 (ECC) + 0x00000000 + + + BOOTKEY1_14 + [15:0] + read-only + + + + + BOOTKEY1_15 + 0x0000027c + Bits 255:240 of SHA-256 hash of boot key 1 (ECC) + 0x00000000 + + + BOOTKEY1_15 + [15:0] + read-only + + + + + BOOTKEY2_0 + 0x00000280 + Bits 15:0 of SHA-256 hash of boot key 2 (ECC) + 0x00000000 + + + BOOTKEY2_0 + [15:0] + read-only + + + + + BOOTKEY2_1 + 0x00000284 + Bits 31:16 of SHA-256 hash of boot key 2 (ECC) + 0x00000000 + + + BOOTKEY2_1 + [15:0] + read-only + + + + + BOOTKEY2_2 + 0x00000288 + Bits 47:32 of SHA-256 hash of boot key 2 (ECC) + 0x00000000 + + + BOOTKEY2_2 + [15:0] + read-only + + + + + BOOTKEY2_3 + 0x0000028c + Bits 63:48 of SHA-256 hash of boot key 2 (ECC) + 0x00000000 + + + BOOTKEY2_3 + [15:0] + read-only + + + + + BOOTKEY2_4 + 0x00000290 + Bits 79:64 of SHA-256 hash of boot key 2 (ECC) + 0x00000000 + + + BOOTKEY2_4 + [15:0] + read-only + + + + + BOOTKEY2_5 + 0x00000294 + Bits 95:80 of SHA-256 hash of boot key 2 (ECC) + 0x00000000 + + + BOOTKEY2_5 + [15:0] + read-only + + + + + BOOTKEY2_6 + 0x00000298 + Bits 111:96 of SHA-256 hash of boot key 2 (ECC) + 0x00000000 + + + BOOTKEY2_6 + [15:0] + read-only + + + + + BOOTKEY2_7 + 0x0000029c + Bits 127:112 of SHA-256 hash of boot key 2 (ECC) + 0x00000000 + + + BOOTKEY2_7 + [15:0] + read-only + + + + + BOOTKEY2_8 + 0x000002a0 + Bits 143:128 of SHA-256 hash of boot key 2 (ECC) + 0x00000000 + + + BOOTKEY2_8 + [15:0] + read-only + + + + + BOOTKEY2_9 + 0x000002a4 + Bits 159:144 of SHA-256 hash of boot key 2 (ECC) + 0x00000000 + + + BOOTKEY2_9 + [15:0] + read-only + + + + + BOOTKEY2_10 + 0x000002a8 + Bits 175:160 of SHA-256 hash of boot key 2 (ECC) + 0x00000000 + + + BOOTKEY2_10 + [15:0] + read-only + + + + + BOOTKEY2_11 + 0x000002ac + Bits 191:176 of SHA-256 hash of boot key 2 (ECC) + 0x00000000 + + + BOOTKEY2_11 + [15:0] + read-only + + + + + BOOTKEY2_12 + 0x000002b0 + Bits 207:192 of SHA-256 hash of boot key 2 (ECC) + 0x00000000 + + + BOOTKEY2_12 + [15:0] + read-only + + + + + BOOTKEY2_13 + 0x000002b4 + Bits 223:208 of SHA-256 hash of boot key 2 (ECC) + 0x00000000 + + + BOOTKEY2_13 + [15:0] + read-only + + + + + BOOTKEY2_14 + 0x000002b8 + Bits 239:224 of SHA-256 hash of boot key 2 (ECC) + 0x00000000 + + + BOOTKEY2_14 + [15:0] + read-only + + + + + BOOTKEY2_15 + 0x000002bc + Bits 255:240 of SHA-256 hash of boot key 2 (ECC) + 0x00000000 + + + BOOTKEY2_15 + [15:0] + read-only + + + + + BOOTKEY3_0 + 0x000002c0 + Bits 15:0 of SHA-256 hash of boot key 3 (ECC) + 0x00000000 + + + BOOTKEY3_0 + [15:0] + read-only + + + + + BOOTKEY3_1 + 0x000002c4 + Bits 31:16 of SHA-256 hash of boot key 3 (ECC) + 0x00000000 + + + BOOTKEY3_1 + [15:0] + read-only + + + + + BOOTKEY3_2 + 0x000002c8 + Bits 47:32 of SHA-256 hash of boot key 3 (ECC) + 0x00000000 + + + BOOTKEY3_2 + [15:0] + read-only + + + + + BOOTKEY3_3 + 0x000002cc + Bits 63:48 of SHA-256 hash of boot key 3 (ECC) + 0x00000000 + + + BOOTKEY3_3 + [15:0] + read-only + + + + + BOOTKEY3_4 + 0x000002d0 + Bits 79:64 of SHA-256 hash of boot key 3 (ECC) + 0x00000000 + + + BOOTKEY3_4 + [15:0] + read-only + + + + + BOOTKEY3_5 + 0x000002d4 + Bits 95:80 of SHA-256 hash of boot key 3 (ECC) + 0x00000000 + + + BOOTKEY3_5 + [15:0] + read-only + + + + + BOOTKEY3_6 + 0x000002d8 + Bits 111:96 of SHA-256 hash of boot key 3 (ECC) + 0x00000000 + + + BOOTKEY3_6 + [15:0] + read-only + + + + + BOOTKEY3_7 + 0x000002dc + Bits 127:112 of SHA-256 hash of boot key 3 (ECC) + 0x00000000 + + + BOOTKEY3_7 + [15:0] + read-only + + + + + BOOTKEY3_8 + 0x000002e0 + Bits 143:128 of SHA-256 hash of boot key 3 (ECC) + 0x00000000 + + + BOOTKEY3_8 + [15:0] + read-only + + + + + BOOTKEY3_9 + 0x000002e4 + Bits 159:144 of SHA-256 hash of boot key 3 (ECC) + 0x00000000 + + + BOOTKEY3_9 + [15:0] + read-only + + + + + BOOTKEY3_10 + 0x000002e8 + Bits 175:160 of SHA-256 hash of boot key 3 (ECC) + 0x00000000 + + + BOOTKEY3_10 + [15:0] + read-only + + + + + BOOTKEY3_11 + 0x000002ec + Bits 191:176 of SHA-256 hash of boot key 3 (ECC) + 0x00000000 + + + BOOTKEY3_11 + [15:0] + read-only + + + + + BOOTKEY3_12 + 0x000002f0 + Bits 207:192 of SHA-256 hash of boot key 3 (ECC) + 0x00000000 + + + BOOTKEY3_12 + [15:0] + read-only + + + + + BOOTKEY3_13 + 0x000002f4 + Bits 223:208 of SHA-256 hash of boot key 3 (ECC) + 0x00000000 + + + BOOTKEY3_13 + [15:0] + read-only + + + + + BOOTKEY3_14 + 0x000002f8 + Bits 239:224 of SHA-256 hash of boot key 3 (ECC) + 0x00000000 + + + BOOTKEY3_14 + [15:0] + read-only + + + + + BOOTKEY3_15 + 0x000002fc + Bits 255:240 of SHA-256 hash of boot key 3 (ECC) + 0x00000000 + + + BOOTKEY3_15 + [15:0] + read-only + + + + + KEY1_0 + 0x00003d20 + Bits 15:0 of OTP access key 1 (ECC) + 0x00000000 + + + KEY1_0 + [15:0] + read-only + + + + + KEY1_1 + 0x00003d24 + Bits 31:16 of OTP access key 1 (ECC) + 0x00000000 + + + KEY1_1 + [15:0] + read-only + + + + + KEY1_2 + 0x00003d28 + Bits 47:32 of OTP access key 1 (ECC) + 0x00000000 + + + KEY1_2 + [15:0] + read-only + + + + + KEY1_3 + 0x00003d2c + Bits 63:48 of OTP access key 1 (ECC) + 0x00000000 + + + KEY1_3 + [15:0] + read-only + + + + + KEY1_4 + 0x00003d30 + Bits 79:64 of OTP access key 1 (ECC) + 0x00000000 + + + KEY1_4 + [15:0] + read-only + + + + + KEY1_5 + 0x00003d34 + Bits 95:80 of OTP access key 1 (ECC) + 0x00000000 + + + KEY1_5 + [15:0] + read-only + + + + + KEY1_6 + 0x00003d38 + Bits 111:96 of OTP access key 1 (ECC) + 0x00000000 + + + KEY1_6 + [15:0] + read-only + + + + + KEY1_7 + 0x00003d3c + Bits 127:112 of OTP access key 1 (ECC) + 0x00000000 + + + KEY1_7 + [15:0] + read-only + + + + + KEY2_0 + 0x00003d40 + Bits 15:0 of OTP access key 2 (ECC) + 0x00000000 + + + KEY2_0 + [15:0] + read-only + + + + + KEY2_1 + 0x00003d44 + Bits 31:16 of OTP access key 2 (ECC) + 0x00000000 + + + KEY2_1 + [15:0] + read-only + + + + + KEY2_2 + 0x00003d48 + Bits 47:32 of OTP access key 2 (ECC) + 0x00000000 + + + KEY2_2 + [15:0] + read-only + + + + + KEY2_3 + 0x00003d4c + Bits 63:48 of OTP access key 2 (ECC) + 0x00000000 + + + KEY2_3 + [15:0] + read-only + + + + + KEY2_4 + 0x00003d50 + Bits 79:64 of OTP access key 2 (ECC) + 0x00000000 + + + KEY2_4 + [15:0] + read-only + + + + + KEY2_5 + 0x00003d54 + Bits 95:80 of OTP access key 2 (ECC) + 0x00000000 + + + KEY2_5 + [15:0] + read-only + + + + + KEY2_6 + 0x00003d58 + Bits 111:96 of OTP access key 2 (ECC) + 0x00000000 + + + KEY2_6 + [15:0] + read-only + + + + + KEY2_7 + 0x00003d5c + Bits 127:112 of OTP access key 2 (ECC) + 0x00000000 + + + KEY2_7 + [15:0] + read-only + + + + + KEY3_0 + 0x00003d60 + Bits 15:0 of OTP access key 3 (ECC) + 0x00000000 + + + KEY3_0 + [15:0] + read-only + + + + + KEY3_1 + 0x00003d64 + Bits 31:16 of OTP access key 3 (ECC) + 0x00000000 + + + KEY3_1 + [15:0] + read-only + + + + + KEY3_2 + 0x00003d68 + Bits 47:32 of OTP access key 3 (ECC) + 0x00000000 + + + KEY3_2 + [15:0] + read-only + + + + + KEY3_3 + 0x00003d6c + Bits 63:48 of OTP access key 3 (ECC) + 0x00000000 + + + KEY3_3 + [15:0] + read-only + + + + + KEY3_4 + 0x00003d70 + Bits 79:64 of OTP access key 3 (ECC) + 0x00000000 + + + KEY3_4 + [15:0] + read-only + + + + + KEY3_5 + 0x00003d74 + Bits 95:80 of OTP access key 3 (ECC) + 0x00000000 + + + KEY3_5 + [15:0] + read-only + + + + + KEY3_6 + 0x00003d78 + Bits 111:96 of OTP access key 3 (ECC) + 0x00000000 + + + KEY3_6 + [15:0] + read-only + + + + + KEY3_7 + 0x00003d7c + Bits 127:112 of OTP access key 3 (ECC) + 0x00000000 + + + KEY3_7 + [15:0] + read-only + + + + + KEY4_0 + 0x00003d80 + Bits 15:0 of OTP access key 4 (ECC) + 0x00000000 + + + KEY4_0 + [15:0] + read-only + + + + + KEY4_1 + 0x00003d84 + Bits 31:16 of OTP access key 4 (ECC) + 0x00000000 + + + KEY4_1 + [15:0] + read-only + + + + + KEY4_2 + 0x00003d88 + Bits 47:32 of OTP access key 4 (ECC) + 0x00000000 + + + KEY4_2 + [15:0] + read-only + + + + + KEY4_3 + 0x00003d8c + Bits 63:48 of OTP access key 4 (ECC) + 0x00000000 + + + KEY4_3 + [15:0] + read-only + + + + + KEY4_4 + 0x00003d90 + Bits 79:64 of OTP access key 4 (ECC) + 0x00000000 + + + KEY4_4 + [15:0] + read-only + + + + + KEY4_5 + 0x00003d94 + Bits 95:80 of OTP access key 4 (ECC) + 0x00000000 + + + KEY4_5 + [15:0] + read-only + + + + + KEY4_6 + 0x00003d98 + Bits 111:96 of OTP access key 4 (ECC) + 0x00000000 + + + KEY4_6 + [15:0] + read-only + + + + + KEY4_7 + 0x00003d9c + Bits 127:112 of OTP access key 4 (ECC) + 0x00000000 + + + KEY4_7 + [15:0] + read-only + + + + + KEY5_0 + 0x00003da0 + Bits 15:0 of OTP access key 5 (ECC) + 0x00000000 + + + KEY5_0 + [15:0] + read-only + + + + + KEY5_1 + 0x00003da4 + Bits 31:16 of OTP access key 5 (ECC) + 0x00000000 + + + KEY5_1 + [15:0] + read-only + + + + + KEY5_2 + 0x00003da8 + Bits 47:32 of OTP access key 5 (ECC) + 0x00000000 + + + KEY5_2 + [15:0] + read-only + + + + + KEY5_3 + 0x00003dac + Bits 63:48 of OTP access key 5 (ECC) + 0x00000000 + + + KEY5_3 + [15:0] + read-only + + + + + KEY5_4 + 0x00003db0 + Bits 79:64 of OTP access key 5 (ECC) + 0x00000000 + + + KEY5_4 + [15:0] + read-only + + + + + KEY5_5 + 0x00003db4 + Bits 95:80 of OTP access key 5 (ECC) + 0x00000000 + + + KEY5_5 + [15:0] + read-only + + + + + KEY5_6 + 0x00003db8 + Bits 111:96 of OTP access key 5 (ECC) + 0x00000000 + + + KEY5_6 + [15:0] + read-only + + + + + KEY5_7 + 0x00003dbc + Bits 127:112 of OTP access key 5 (ECC) + 0x00000000 + + + KEY5_7 + [15:0] + read-only + + + + + KEY6_0 + 0x00003dc0 + Bits 15:0 of OTP access key 6 (ECC) + 0x00000000 + + + KEY6_0 + [15:0] + read-only + + + + + KEY6_1 + 0x00003dc4 + Bits 31:16 of OTP access key 6 (ECC) + 0x00000000 + + + KEY6_1 + [15:0] + read-only + + + + + KEY6_2 + 0x00003dc8 + Bits 47:32 of OTP access key 6 (ECC) + 0x00000000 + + + KEY6_2 + [15:0] + read-only + + + + + KEY6_3 + 0x00003dcc + Bits 63:48 of OTP access key 6 (ECC) + 0x00000000 + + + KEY6_3 + [15:0] + read-only + + + + + KEY6_4 + 0x00003dd0 + Bits 79:64 of OTP access key 6 (ECC) + 0x00000000 + + + KEY6_4 + [15:0] + read-only + + + + + KEY6_5 + 0x00003dd4 + Bits 95:80 of OTP access key 6 (ECC) + 0x00000000 + + + KEY6_5 + [15:0] + read-only + + + + + KEY6_6 + 0x00003dd8 + Bits 111:96 of OTP access key 6 (ECC) + 0x00000000 + + + KEY6_6 + [15:0] + read-only + + + + + KEY6_7 + 0x00003ddc + Bits 127:112 of OTP access key 6 (ECC) + 0x00000000 + + + KEY6_7 + [15:0] + read-only + + + + + KEY1_VALID + 0x00003de4 + Valid flag for key 1. Once the valid flag is set, the key can no longer be read or written, and becomes a valid fixed key for protecting OTP pages. + 0x00000000 + + + VALID_R2 + Redundant copy of VALID, with 3-way majority vote + [16:16] + read-only + + + VALID_R1 + Redundant copy of VALID, with 3-way majority vote + [8:8] + read-only + + + VALID + [0:0] + read-only + + + + + KEY2_VALID + 0x00003de8 + Valid flag for key 2. Once the valid flag is set, the key can no longer be read or written, and becomes a valid fixed key for protecting OTP pages. + 0x00000000 + + + VALID_R2 + Redundant copy of VALID, with 3-way majority vote + [16:16] + read-only + + + VALID_R1 + Redundant copy of VALID, with 3-way majority vote + [8:8] + read-only + + + VALID + [0:0] + read-only + + + + + KEY3_VALID + 0x00003dec + Valid flag for key 3. Once the valid flag is set, the key can no longer be read or written, and becomes a valid fixed key for protecting OTP pages. + 0x00000000 + + + VALID_R2 + Redundant copy of VALID, with 3-way majority vote + [16:16] + read-only + + + VALID_R1 + Redundant copy of VALID, with 3-way majority vote + [8:8] + read-only + + + VALID + [0:0] + read-only + + + + + KEY4_VALID + 0x00003df0 + Valid flag for key 4. Once the valid flag is set, the key can no longer be read or written, and becomes a valid fixed key for protecting OTP pages. + 0x00000000 + + + VALID_R2 + Redundant copy of VALID, with 3-way majority vote + [16:16] + read-only + + + VALID_R1 + Redundant copy of VALID, with 3-way majority vote + [8:8] + read-only + + + VALID + [0:0] + read-only + + + + + KEY5_VALID + 0x00003df4 + Valid flag for key 5. Once the valid flag is set, the key can no longer be read or written, and becomes a valid fixed key for protecting OTP pages. + 0x00000000 + + + VALID_R2 + Redundant copy of VALID, with 3-way majority vote + [16:16] + read-only + + + VALID_R1 + Redundant copy of VALID, with 3-way majority vote + [8:8] + read-only + + + VALID + [0:0] + read-only + + + + + KEY6_VALID + 0x00003df8 + Valid flag for key 6. Once the valid flag is set, the key can no longer be read or written, and becomes a valid fixed key for protecting OTP pages. + 0x00000000 + + + VALID_R2 + Redundant copy of VALID, with 3-way majority vote + [16:16] + read-only + + + VALID_R1 + Redundant copy of VALID, with 3-way majority vote + [8:8] + read-only + + + VALID + [0:0] + read-only + + + + + PAGE0_LOCK0 + 0x00003e00 + Lock configuration LSBs for page 0 (rows 0x0 through 0x3f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE0_LOCK1 + 0x00003e04 + Lock configuration MSBs for page 0 (rows 0x0 through 0x3f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE1_LOCK0 + 0x00003e08 + Lock configuration LSBs for page 1 (rows 0x40 through 0x7f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE1_LOCK1 + 0x00003e0c + Lock configuration MSBs for page 1 (rows 0x40 through 0x7f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE2_LOCK0 + 0x00003e10 + Lock configuration LSBs for page 2 (rows 0x80 through 0xbf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE2_LOCK1 + 0x00003e14 + Lock configuration MSBs for page 2 (rows 0x80 through 0xbf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE3_LOCK0 + 0x00003e18 + Lock configuration LSBs for page 3 (rows 0xc0 through 0xff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE3_LOCK1 + 0x00003e1c + Lock configuration MSBs for page 3 (rows 0xc0 through 0xff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE4_LOCK0 + 0x00003e20 + Lock configuration LSBs for page 4 (rows 0x100 through 0x13f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE4_LOCK1 + 0x00003e24 + Lock configuration MSBs for page 4 (rows 0x100 through 0x13f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE5_LOCK0 + 0x00003e28 + Lock configuration LSBs for page 5 (rows 0x140 through 0x17f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE5_LOCK1 + 0x00003e2c + Lock configuration MSBs for page 5 (rows 0x140 through 0x17f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE6_LOCK0 + 0x00003e30 + Lock configuration LSBs for page 6 (rows 0x180 through 0x1bf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE6_LOCK1 + 0x00003e34 + Lock configuration MSBs for page 6 (rows 0x180 through 0x1bf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE7_LOCK0 + 0x00003e38 + Lock configuration LSBs for page 7 (rows 0x1c0 through 0x1ff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE7_LOCK1 + 0x00003e3c + Lock configuration MSBs for page 7 (rows 0x1c0 through 0x1ff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE8_LOCK0 + 0x00003e40 + Lock configuration LSBs for page 8 (rows 0x200 through 0x23f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE8_LOCK1 + 0x00003e44 + Lock configuration MSBs for page 8 (rows 0x200 through 0x23f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE9_LOCK0 + 0x00003e48 + Lock configuration LSBs for page 9 (rows 0x240 through 0x27f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE9_LOCK1 + 0x00003e4c + Lock configuration MSBs for page 9 (rows 0x240 through 0x27f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE10_LOCK0 + 0x00003e50 + Lock configuration LSBs for page 10 (rows 0x280 through 0x2bf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE10_LOCK1 + 0x00003e54 + Lock configuration MSBs for page 10 (rows 0x280 through 0x2bf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE11_LOCK0 + 0x00003e58 + Lock configuration LSBs for page 11 (rows 0x2c0 through 0x2ff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE11_LOCK1 + 0x00003e5c + Lock configuration MSBs for page 11 (rows 0x2c0 through 0x2ff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE12_LOCK0 + 0x00003e60 + Lock configuration LSBs for page 12 (rows 0x300 through 0x33f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE12_LOCK1 + 0x00003e64 + Lock configuration MSBs for page 12 (rows 0x300 through 0x33f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE13_LOCK0 + 0x00003e68 + Lock configuration LSBs for page 13 (rows 0x340 through 0x37f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE13_LOCK1 + 0x00003e6c + Lock configuration MSBs for page 13 (rows 0x340 through 0x37f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE14_LOCK0 + 0x00003e70 + Lock configuration LSBs for page 14 (rows 0x380 through 0x3bf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE14_LOCK1 + 0x00003e74 + Lock configuration MSBs for page 14 (rows 0x380 through 0x3bf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE15_LOCK0 + 0x00003e78 + Lock configuration LSBs for page 15 (rows 0x3c0 through 0x3ff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE15_LOCK1 + 0x00003e7c + Lock configuration MSBs for page 15 (rows 0x3c0 through 0x3ff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE16_LOCK0 + 0x00003e80 + Lock configuration LSBs for page 16 (rows 0x400 through 0x43f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE16_LOCK1 + 0x00003e84 + Lock configuration MSBs for page 16 (rows 0x400 through 0x43f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE17_LOCK0 + 0x00003e88 + Lock configuration LSBs for page 17 (rows 0x440 through 0x47f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE17_LOCK1 + 0x00003e8c + Lock configuration MSBs for page 17 (rows 0x440 through 0x47f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE18_LOCK0 + 0x00003e90 + Lock configuration LSBs for page 18 (rows 0x480 through 0x4bf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE18_LOCK1 + 0x00003e94 + Lock configuration MSBs for page 18 (rows 0x480 through 0x4bf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE19_LOCK0 + 0x00003e98 + Lock configuration LSBs for page 19 (rows 0x4c0 through 0x4ff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE19_LOCK1 + 0x00003e9c + Lock configuration MSBs for page 19 (rows 0x4c0 through 0x4ff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE20_LOCK0 + 0x00003ea0 + Lock configuration LSBs for page 20 (rows 0x500 through 0x53f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE20_LOCK1 + 0x00003ea4 + Lock configuration MSBs for page 20 (rows 0x500 through 0x53f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE21_LOCK0 + 0x00003ea8 + Lock configuration LSBs for page 21 (rows 0x540 through 0x57f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE21_LOCK1 + 0x00003eac + Lock configuration MSBs for page 21 (rows 0x540 through 0x57f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE22_LOCK0 + 0x00003eb0 + Lock configuration LSBs for page 22 (rows 0x580 through 0x5bf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE22_LOCK1 + 0x00003eb4 + Lock configuration MSBs for page 22 (rows 0x580 through 0x5bf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE23_LOCK0 + 0x00003eb8 + Lock configuration LSBs for page 23 (rows 0x5c0 through 0x5ff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE23_LOCK1 + 0x00003ebc + Lock configuration MSBs for page 23 (rows 0x5c0 through 0x5ff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE24_LOCK0 + 0x00003ec0 + Lock configuration LSBs for page 24 (rows 0x600 through 0x63f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE24_LOCK1 + 0x00003ec4 + Lock configuration MSBs for page 24 (rows 0x600 through 0x63f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE25_LOCK0 + 0x00003ec8 + Lock configuration LSBs for page 25 (rows 0x640 through 0x67f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE25_LOCK1 + 0x00003ecc + Lock configuration MSBs for page 25 (rows 0x640 through 0x67f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE26_LOCK0 + 0x00003ed0 + Lock configuration LSBs for page 26 (rows 0x680 through 0x6bf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE26_LOCK1 + 0x00003ed4 + Lock configuration MSBs for page 26 (rows 0x680 through 0x6bf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE27_LOCK0 + 0x00003ed8 + Lock configuration LSBs for page 27 (rows 0x6c0 through 0x6ff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE27_LOCK1 + 0x00003edc + Lock configuration MSBs for page 27 (rows 0x6c0 through 0x6ff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE28_LOCK0 + 0x00003ee0 + Lock configuration LSBs for page 28 (rows 0x700 through 0x73f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE28_LOCK1 + 0x00003ee4 + Lock configuration MSBs for page 28 (rows 0x700 through 0x73f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE29_LOCK0 + 0x00003ee8 + Lock configuration LSBs for page 29 (rows 0x740 through 0x77f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE29_LOCK1 + 0x00003eec + Lock configuration MSBs for page 29 (rows 0x740 through 0x77f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE30_LOCK0 + 0x00003ef0 + Lock configuration LSBs for page 30 (rows 0x780 through 0x7bf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE30_LOCK1 + 0x00003ef4 + Lock configuration MSBs for page 30 (rows 0x780 through 0x7bf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE31_LOCK0 + 0x00003ef8 + Lock configuration LSBs for page 31 (rows 0x7c0 through 0x7ff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE31_LOCK1 + 0x00003efc + Lock configuration MSBs for page 31 (rows 0x7c0 through 0x7ff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE32_LOCK0 + 0x00003f00 + Lock configuration LSBs for page 32 (rows 0x800 through 0x83f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE32_LOCK1 + 0x00003f04 + Lock configuration MSBs for page 32 (rows 0x800 through 0x83f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE33_LOCK0 + 0x00003f08 + Lock configuration LSBs for page 33 (rows 0x840 through 0x87f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE33_LOCK1 + 0x00003f0c + Lock configuration MSBs for page 33 (rows 0x840 through 0x87f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE34_LOCK0 + 0x00003f10 + Lock configuration LSBs for page 34 (rows 0x880 through 0x8bf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE34_LOCK1 + 0x00003f14 + Lock configuration MSBs for page 34 (rows 0x880 through 0x8bf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE35_LOCK0 + 0x00003f18 + Lock configuration LSBs for page 35 (rows 0x8c0 through 0x8ff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE35_LOCK1 + 0x00003f1c + Lock configuration MSBs for page 35 (rows 0x8c0 through 0x8ff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE36_LOCK0 + 0x00003f20 + Lock configuration LSBs for page 36 (rows 0x900 through 0x93f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE36_LOCK1 + 0x00003f24 + Lock configuration MSBs for page 36 (rows 0x900 through 0x93f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE37_LOCK0 + 0x00003f28 + Lock configuration LSBs for page 37 (rows 0x940 through 0x97f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE37_LOCK1 + 0x00003f2c + Lock configuration MSBs for page 37 (rows 0x940 through 0x97f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE38_LOCK0 + 0x00003f30 + Lock configuration LSBs for page 38 (rows 0x980 through 0x9bf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE38_LOCK1 + 0x00003f34 + Lock configuration MSBs for page 38 (rows 0x980 through 0x9bf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE39_LOCK0 + 0x00003f38 + Lock configuration LSBs for page 39 (rows 0x9c0 through 0x9ff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE39_LOCK1 + 0x00003f3c + Lock configuration MSBs for page 39 (rows 0x9c0 through 0x9ff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE40_LOCK0 + 0x00003f40 + Lock configuration LSBs for page 40 (rows 0xa00 through 0xa3f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE40_LOCK1 + 0x00003f44 + Lock configuration MSBs for page 40 (rows 0xa00 through 0xa3f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE41_LOCK0 + 0x00003f48 + Lock configuration LSBs for page 41 (rows 0xa40 through 0xa7f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE41_LOCK1 + 0x00003f4c + Lock configuration MSBs for page 41 (rows 0xa40 through 0xa7f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE42_LOCK0 + 0x00003f50 + Lock configuration LSBs for page 42 (rows 0xa80 through 0xabf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE42_LOCK1 + 0x00003f54 + Lock configuration MSBs for page 42 (rows 0xa80 through 0xabf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE43_LOCK0 + 0x00003f58 + Lock configuration LSBs for page 43 (rows 0xac0 through 0xaff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE43_LOCK1 + 0x00003f5c + Lock configuration MSBs for page 43 (rows 0xac0 through 0xaff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE44_LOCK0 + 0x00003f60 + Lock configuration LSBs for page 44 (rows 0xb00 through 0xb3f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE44_LOCK1 + 0x00003f64 + Lock configuration MSBs for page 44 (rows 0xb00 through 0xb3f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE45_LOCK0 + 0x00003f68 + Lock configuration LSBs for page 45 (rows 0xb40 through 0xb7f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE45_LOCK1 + 0x00003f6c + Lock configuration MSBs for page 45 (rows 0xb40 through 0xb7f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE46_LOCK0 + 0x00003f70 + Lock configuration LSBs for page 46 (rows 0xb80 through 0xbbf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE46_LOCK1 + 0x00003f74 + Lock configuration MSBs for page 46 (rows 0xb80 through 0xbbf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE47_LOCK0 + 0x00003f78 + Lock configuration LSBs for page 47 (rows 0xbc0 through 0xbff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE47_LOCK1 + 0x00003f7c + Lock configuration MSBs for page 47 (rows 0xbc0 through 0xbff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE48_LOCK0 + 0x00003f80 + Lock configuration LSBs for page 48 (rows 0xc00 through 0xc3f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE48_LOCK1 + 0x00003f84 + Lock configuration MSBs for page 48 (rows 0xc00 through 0xc3f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE49_LOCK0 + 0x00003f88 + Lock configuration LSBs for page 49 (rows 0xc40 through 0xc7f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE49_LOCK1 + 0x00003f8c + Lock configuration MSBs for page 49 (rows 0xc40 through 0xc7f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE50_LOCK0 + 0x00003f90 + Lock configuration LSBs for page 50 (rows 0xc80 through 0xcbf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE50_LOCK1 + 0x00003f94 + Lock configuration MSBs for page 50 (rows 0xc80 through 0xcbf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE51_LOCK0 + 0x00003f98 + Lock configuration LSBs for page 51 (rows 0xcc0 through 0xcff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE51_LOCK1 + 0x00003f9c + Lock configuration MSBs for page 51 (rows 0xcc0 through 0xcff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE52_LOCK0 + 0x00003fa0 + Lock configuration LSBs for page 52 (rows 0xd00 through 0xd3f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE52_LOCK1 + 0x00003fa4 + Lock configuration MSBs for page 52 (rows 0xd00 through 0xd3f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE53_LOCK0 + 0x00003fa8 + Lock configuration LSBs for page 53 (rows 0xd40 through 0xd7f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE53_LOCK1 + 0x00003fac + Lock configuration MSBs for page 53 (rows 0xd40 through 0xd7f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE54_LOCK0 + 0x00003fb0 + Lock configuration LSBs for page 54 (rows 0xd80 through 0xdbf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE54_LOCK1 + 0x00003fb4 + Lock configuration MSBs for page 54 (rows 0xd80 through 0xdbf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE55_LOCK0 + 0x00003fb8 + Lock configuration LSBs for page 55 (rows 0xdc0 through 0xdff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE55_LOCK1 + 0x00003fbc + Lock configuration MSBs for page 55 (rows 0xdc0 through 0xdff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE56_LOCK0 + 0x00003fc0 + Lock configuration LSBs for page 56 (rows 0xe00 through 0xe3f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE56_LOCK1 + 0x00003fc4 + Lock configuration MSBs for page 56 (rows 0xe00 through 0xe3f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE57_LOCK0 + 0x00003fc8 + Lock configuration LSBs for page 57 (rows 0xe40 through 0xe7f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE57_LOCK1 + 0x00003fcc + Lock configuration MSBs for page 57 (rows 0xe40 through 0xe7f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE58_LOCK0 + 0x00003fd0 + Lock configuration LSBs for page 58 (rows 0xe80 through 0xebf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE58_LOCK1 + 0x00003fd4 + Lock configuration MSBs for page 58 (rows 0xe80 through 0xebf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE59_LOCK0 + 0x00003fd8 + Lock configuration LSBs for page 59 (rows 0xec0 through 0xeff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE59_LOCK1 + 0x00003fdc + Lock configuration MSBs for page 59 (rows 0xec0 through 0xeff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE60_LOCK0 + 0x00003fe0 + Lock configuration LSBs for page 60 (rows 0xf00 through 0xf3f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE60_LOCK1 + 0x00003fe4 + Lock configuration MSBs for page 60 (rows 0xf00 through 0xf3f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE61_LOCK0 + 0x00003fe8 + Lock configuration LSBs for page 61 (rows 0xf40 through 0xf7f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE61_LOCK1 + 0x00003fec + Lock configuration MSBs for page 61 (rows 0xf40 through 0xf7f). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE62_LOCK0 + 0x00003ff0 + Lock configuration LSBs for page 62 (rows 0xf80 through 0xfbf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE62_LOCK1 + 0x00003ff4 + Lock configuration MSBs for page 62 (rows 0xf80 through 0xfbf). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + PAGE63_LOCK0 + 0x00003ff8 + Lock configuration LSBs for page 63 (rows 0xfc0 through 0xfff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + RMA + Decommission for RMA of a suspected faulty device. This re-enables the factory test JTAG interface, and makes pages 3 through 61 of the OTP permanently inaccessible. + [7:7] + read-only + + + NO_KEY_STATE + State when at least one key is registered for this page and no matching key has been entered. + [6:6] + read-only + + + read_only + 0 + + + inaccessible + 1 + + + + + KEY_R + Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required. + [5:3] + read-only + + + KEY_W + Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required. + [2:0] + read-only + + + + + PAGE63_LOCK1 + 0x00003ffc + Lock configuration MSBs for page 63 (rows 0xfc0 through 0xfff). Locks are stored with 3-way majority vote encoding, so that bits can be set independently. + + This OTP location is always readable, and is write-protected by its own permissions. + 0x00000000 + + + R2 + Redundant copy of bits 7:0 + [23:16] + read-only + + + R1 + Redundant copy of bits 7:0 + [15:8] + read-only + + + LOCK_BL + Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions. No hardware effect, and no corresponding SW_LOCKx registers. + [5:4] + read-only + + + read_write + 0 + Bootloader permits user reads and writes to this page + + + read_only + 1 + Bootloader permits user reads of this page + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE + + + inaccessible + 3 + Bootloader does not permit user access to this page + + + + + LOCK_NS + Lock state for Non-secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + + Note that READ_WRITE and READ_ONLY are equivalent in hardware, as the SBPI programming interface is not accessible to Non-secure software. However, Secure software may check these bits to apply write permissions to a Non-secure OTP programming API. + [3:2] + read-only + + + read_write + 0 + Page can be read by Non-secure software, and Secure software may permit Non-secure writes. + + + read_only + 1 + Page can be read by Non-secure software + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Non-secure software. + + + + + LOCK_S + Lock state for Secure accesses to this page. Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP. Software can also advance the lock state temporarily (until next OTP reset) using the SW_LOCKx registers. + [1:0] + read-only + + + read_write + 0 + Page is fully accessible by Secure software. + + + read_only + 1 + Page can be read by Secure software, but can not be written. + + + reserved + 2 + Do not use. Behaves the same as INACCESSIBLE. + + + inaccessible + 3 + Page can not be accessed by Secure software. + + + + + + + + + TBMAN + For managing simulation testbenches + 0x40160000 + + 0 + 4 + registers + + + + PLATFORM + 0x00000000 + Indicates the type of platform in use + 0x00000001 + + + HDLSIM + Indicates the platform is a simulation + [2:2] + read-only + + + FPGA + Indicates the platform is an FPGA + [1:1] + read-only + + + ASIC + Indicates the platform is an ASIC + [0:0] + read-only + + + + + + + USB_DPRAM + DPRAM layout for USB device. + 0x50100000 + + 0 + 256 + registers + + + + SETUP_PACKET_LOW + 0x00000000 + Bytes 0-3 of the SETUP packet from the host. + 0x00000000 + + + WVALUE + [31:16] + read-write + + + BREQUEST + [15:8] + read-write + + + BMREQUESTTYPE + [7:0] + read-write + + + + + SETUP_PACKET_HIGH + 0x00000004 + Bytes 4-7 of the setup packet from the host. + 0x00000000 + + + WLENGTH + [31:16] + read-write + + + WINDEX + [15:0] + read-write + + + + + EP1_IN_CONTROL + 0x00000008 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP1_OUT_CONTROL + 0x0000000c + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP2_IN_CONTROL + 0x00000010 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP2_OUT_CONTROL + 0x00000014 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP3_IN_CONTROL + 0x00000018 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP3_OUT_CONTROL + 0x0000001c + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP4_IN_CONTROL + 0x00000020 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP4_OUT_CONTROL + 0x00000024 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP5_IN_CONTROL + 0x00000028 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP5_OUT_CONTROL + 0x0000002c + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP6_IN_CONTROL + 0x00000030 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP6_OUT_CONTROL + 0x00000034 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP7_IN_CONTROL + 0x00000038 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP7_OUT_CONTROL + 0x0000003c + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP8_IN_CONTROL + 0x00000040 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP8_OUT_CONTROL + 0x00000044 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP9_IN_CONTROL + 0x00000048 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP9_OUT_CONTROL + 0x0000004c + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP10_IN_CONTROL + 0x00000050 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP10_OUT_CONTROL + 0x00000054 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP11_IN_CONTROL + 0x00000058 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP11_OUT_CONTROL + 0x0000005c + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP12_IN_CONTROL + 0x00000060 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP12_OUT_CONTROL + 0x00000064 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP13_IN_CONTROL + 0x00000068 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP13_OUT_CONTROL + 0x0000006c + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP14_IN_CONTROL + 0x00000070 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP14_OUT_CONTROL + 0x00000074 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP15_IN_CONTROL + 0x00000078 + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP15_OUT_CONTROL + 0x0000007c + 0x00000000 + + + ENABLE + Enable this endpoint. The device will not reply to any packets for this endpoint if this bit is not set. + [31:31] + read-write + + + DOUBLE_BUFFERED + This endpoint is double buffered. + [30:30] + read-write + + + INTERRUPT_PER_BUFF + Trigger an interrupt each time a buffer is done. + [29:29] + read-write + + + INTERRUPT_PER_DOUBLE_BUFF + Trigger an interrupt each time both buffers are done. Only valid in double buffered mode. + [28:28] + read-write + + + ENDPOINT_TYPE + [27:26] + read-write + + + Control + 0 + + + Isochronous + 1 + + + Bulk + 2 + + + Interrupt + 3 + + + + + INTERRUPT_ON_STALL + Trigger an interrupt if a STALL is sent. Intended for debug only. + [17:17] + read-write + + + INTERRUPT_ON_NAK + Trigger an interrupt if a NAK is sent. Intended for debug only. + [16:16] + read-write + + + BUFFER_ADDRESS + 64 byte aligned buffer address for this EP (bits 0-5 are ignored). Relative to the start of the DPRAM. + [15:0] + read-write + + + + + EP0_IN_BUFFER_CONTROL + 0x00000080 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP0_OUT_BUFFER_CONTROL + 0x00000084 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP1_IN_BUFFER_CONTROL + 0x00000088 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP1_OUT_BUFFER_CONTROL + 0x0000008c + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP2_IN_BUFFER_CONTROL + 0x00000090 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP2_OUT_BUFFER_CONTROL + 0x00000094 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP3_IN_BUFFER_CONTROL + 0x00000098 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP3_OUT_BUFFER_CONTROL + 0x0000009c + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP4_IN_BUFFER_CONTROL + 0x000000a0 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP4_OUT_BUFFER_CONTROL + 0x000000a4 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP5_IN_BUFFER_CONTROL + 0x000000a8 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP5_OUT_BUFFER_CONTROL + 0x000000ac + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP6_IN_BUFFER_CONTROL + 0x000000b0 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP6_OUT_BUFFER_CONTROL + 0x000000b4 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP7_IN_BUFFER_CONTROL + 0x000000b8 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP7_OUT_BUFFER_CONTROL + 0x000000bc + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP8_IN_BUFFER_CONTROL + 0x000000c0 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP8_OUT_BUFFER_CONTROL + 0x000000c4 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP9_IN_BUFFER_CONTROL + 0x000000c8 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP9_OUT_BUFFER_CONTROL + 0x000000cc + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP10_IN_BUFFER_CONTROL + 0x000000d0 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP10_OUT_BUFFER_CONTROL + 0x000000d4 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP11_IN_BUFFER_CONTROL + 0x000000d8 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP11_OUT_BUFFER_CONTROL + 0x000000dc + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP12_IN_BUFFER_CONTROL + 0x000000e0 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP12_OUT_BUFFER_CONTROL + 0x000000e4 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP13_IN_BUFFER_CONTROL + 0x000000e8 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP13_OUT_BUFFER_CONTROL + 0x000000ec + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP14_IN_BUFFER_CONTROL + 0x000000f0 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP14_OUT_BUFFER_CONTROL + 0x000000f4 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP15_IN_BUFFER_CONTROL + 0x000000f8 + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + EP15_OUT_BUFFER_CONTROL + 0x000000fc + Buffer control for both buffers of an endpoint. Fields ending in a _1 are for buffer 1. + Fields ending in a _0 are for buffer 0. Buffer 1 controls are only valid if the endpoint is in double buffered mode. + 0x00000000 + + + FULL_1 + Buffer 1 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [31:31] + read-write + + + LAST_1 + Buffer 1 is the last buffer of the transfer. + [30:30] + read-write + + + PID_1 + The data pid of buffer 1. + [29:29] + read-write + + + DOUBLE_BUFFER_ISO_OFFSET + The number of bytes buffer 1 is offset from buffer 0 in Isochronous mode. Only valid in double buffered mode for an Isochronous endpoint. + For a non Isochronous endpoint the offset is always 64 bytes. + [28:27] + read-write + + + 128 + 0 + + + 256 + 1 + + + 512 + 2 + + + 1024 + 3 + + + + + AVAILABLE_1 + Buffer 1 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [26:26] + read-write + + + LENGTH_1 + The length of the data in buffer 1. + [25:16] + read-write + + + FULL_0 + Buffer 0 is full. For an IN transfer (TX to the host) the bit is set to indicate the data is valid. For an OUT transfer (RX from the host) this bit should be left as a 0. The host will set it when it has filled the buffer with data. + [15:15] + read-write + + + LAST_0 + Buffer 0 is the last buffer of the transfer. + [14:14] + read-write + + + PID_0 + The data pid of buffer 0. + [13:13] + read-write + + + RESET + Reset the buffer selector to buffer 0. + [12:12] + read-write + + + STALL + Reply with a stall (valid for both buffers). + [11:11] + read-write + + + AVAILABLE_0 + Buffer 0 is available. This bit is set to indicate the buffer can be used by the controller. The controller clears the available bit when writing the status back. + [10:10] + read-write + + + LENGTH_0 + The length of the data in buffer 1. + [9:0] + read-write + + + + + + + diff --git a/WEEK04/slides/WEEK04-IMG00.svg b/WEEK04/slides/WEEK04-IMG00.svg new file mode 100644 index 0000000..2b2aae1 --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 04 + + +Variables in Embedded Systems: +Debugging and Hacking Variables +w/ GPIO Output Basics + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK04/slides/WEEK04-IMG01.svg b/WEEK04/slides/WEEK04-IMG01.svg new file mode 100644 index 0000000..5331adf --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG01.svg @@ -0,0 +1,96 @@ + + + + + +What is a Variable? +Labeled Boxes in Memory (SRAM) + + + +Memory — A Row of Numbered Boxes + + + +42 +age +Box 0 + + + +17 +score +Box 1 + + + +0 +count +Box 2 + + + +255 +max +Box 3 + + + +99 +temp +Box 4 + + + +Anatomy of a Declaration + + +uint8_t age = 42; + +uint8_t +Data type (1 byte) + +age +Variable name (label) + += 42 +Initial value + +; +End of statement + + + +Key Concepts + + +Declaration +name + type + + +Definition +allocates memory + + +Initialization +assigns value + + + +Important Rule +You MUST declare a +variable BEFORE you +use it! +Compiler needs to know the type + \ No newline at end of file diff --git a/WEEK04/slides/WEEK04-IMG02.svg b/WEEK04/slides/WEEK04-IMG02.svg new file mode 100644 index 0000000..8933406 --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG02.svg @@ -0,0 +1,86 @@ + + + + + +Data Types & Sizes +How Much Memory Each Type Uses + + + + + + +Type +Size +Range +Description + + + +uint8_t +1 byte +0 — 255 +Unsigned 8-bit + + + +int8_t +1 byte +-128 — 127 +Signed 8-bit + + + +uint16_t +2 bytes +0 — 65,535 +Unsigned 16-bit + + + +int16_t +2 bytes +-32,768 — 32,767 +Signed 16-bit + + + +uint32_t +4 bytes +0 — 4,294,967,295 +Unsigned 32-bit + + + +int32_t +4 bytes +-2.1B — 2.1B +Signed 32-bit + + +Size Comparison + + +1B +uint8_t + + +2B +uint16_t + + +4 Bytes +uint32_t + \ No newline at end of file diff --git a/WEEK04/slides/WEEK04-IMG03.svg b/WEEK04/slides/WEEK04-IMG03.svg new file mode 100644 index 0000000..0cbcf5a --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG03.svg @@ -0,0 +1,63 @@ + + + + + +Memory Sections +Where Variables Live After Compilation + + + + +.data +Flash -> copied to RAM at startup +Contains: Initialized global/static variables +int counter = 42; +Initial value stored in flash, copied to SRAM by data_cpy + + + + +.bss +RAM — zeroed at startup +Contains: Uninitialized global/static variables +int counter; +NOT stored in binary (saves space!) — memset to 0 at boot + + + + +.rodata +Flash — read only +Contains: Constants and string literals +const int MAX = 100; +Lives in flash permanently — cannot be modified at runtime + + + +.data +RAM +Writable +Initialized globals + +.bss +RAM +Writable +Uninitialized globals (zeroed) + +.rodata +Flash +Read-only +Constants & strings + \ No newline at end of file diff --git a/WEEK04/slides/WEEK04-IMG04.svg b/WEEK04/slides/WEEK04-IMG04.svg new file mode 100644 index 0000000..82437d2 --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG04.svg @@ -0,0 +1,79 @@ + + + + + +GPIO Basics +General Purpose Input/Output on RP2350 + + + +Pico 2 GPIO Pins + +GPIO 16 + +Red LED + +GPIO 17 + +Green LED + +GPIO 18 + +Blue LED + +GPIO 25 + +Onboard LED + +Software-controlled switches + + + +Pico SDK Functions + + +gpio_init(pin) +Init pin + + +gpio_set_dir(pin,d) +I/O dir + + +gpio_put(pin,val) +Set H/L + + +sleep_ms(ms) +Delay + + + +Basic LED Blink Code + + +#define LED_PIN 16 +int main(void) { +gpio_init(LED_PIN); +gpio_set_dir(LED_PIN, GPIO_OUT); +while (true) { +gpio_put(LED_PIN, 1); +// ON +sleep_ms(500); +gpio_put(LED_PIN, 0); +// OFF +sleep_ms(500); +}} + \ No newline at end of file diff --git a/WEEK04/slides/WEEK04-IMG05.svg b/WEEK04/slides/WEEK04-IMG05.svg new file mode 100644 index 0000000..c6a0ef6 --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG05.svg @@ -0,0 +1,79 @@ + + + + + +Ghidra Binary Analysis +Analyzing a Raw .bin Without Symbols + + + +1. Import + +File -> Import +Language: +ARM Cortex 32 LE +Block: +.text +Base: +10000000 +XIP address for RP2350 + + + +2. Analyze + +Auto-Analyze: Yes +Ghidra finds: +FUN_1000019a +FUN_10000210 +FUN_10000234 +Auto-generated names + + + +3. Resolve + +Edit Function Sig +Rename to: +data_cpy +frame_dummy +main +Fix signatures + + + +Decompiled main() in Ghidra + + +Before Resolving: +void FUN_10000234(void){ +FUN_10002f54(); +do { +FUN_100030e4( +DAT_10000244,0x2b); +} while(true); +} + + +After Resolving: +int main(void) { +stdio_init_all(); +do { +printf( +"age: %d\r\n" +, 0x2b); +} while(true); +} + \ No newline at end of file diff --git a/WEEK04/slides/WEEK04-IMG06.svg b/WEEK04/slides/WEEK04-IMG06.svg new file mode 100644 index 0000000..156aeeb --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG06.svg @@ -0,0 +1,77 @@ + + + + + +Compiler Optimization +Why Your Variable Disappeared + + + +Source Code + + +int main(void) { +uint8_t age = 42; +age = 43; +stdio_init_all(); +while (true) +printf("age: %d", age); + + + +Compiler Thinks... + + +age = 42 is NEVER read + + +Dead store -> REMOVED + + +age = 43 -> constant fold + +Replaces variable with literal + + + +Resulting Assembly + + +1000023a +2b 21 +movs r1, #0x2b +; 0x2b = 43 +No age=42 instruction — compiler removed it + + + +Key Takeaway + + +Source Code +age = 42 +age = 43 + +-> + + +Binary +movs r1, #0x2b + + +Compiler +Optimizes dead +stores away! + \ No newline at end of file diff --git a/WEEK04/slides/WEEK04-IMG07.svg b/WEEK04/slides/WEEK04-IMG07.svg new file mode 100644 index 0000000..2041444 --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG07.svg @@ -0,0 +1,86 @@ + + + + + +Binary Patching +Changing Values in the Binary + + + +Before Patch + + +1000023a +2b 21 +movs r1,#0x2b + +0x2b = 43 decimal +Output: +age: 43 +Compiler-optimized constant + + + +After Patch + + +1000023a +46 21 +movs r1,#0x46 + +0x46 = 70 decimal +Output: +age: 70 +Changed program behavior! + + + +How to Patch in Ghidra + + +1. Find Instr + +-> + + +2. Rt-Click + +-> + + +3. Patch Val + +-> + + +Done! + +Patch Instruction: change operand + + + +Export Patched Binary + + +File: Export + + +Format: Raw Bytes + + +Save as *-h.bin + +Exported binary has your patches + \ No newline at end of file diff --git a/WEEK04/slides/WEEK04-IMG08.svg b/WEEK04/slides/WEEK04-IMG08.svg new file mode 100644 index 0000000..eada6e7 --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG08.svg @@ -0,0 +1,99 @@ + + + + + +GPIO Hacking +Patching GPIO 16 to GPIO 17 + + + +Original: GPIO 16 +Red LED on pin 16 + + +1000023a +10 20 +movs r0,#0x10 + + +10000244 +10 23 +movs r3,#0x10 + + +10000252 +10 24 +movs r4,#0x10 + +0x10 = 16, three locations + + + +Patched: GPIO 17 +Green LED on pin 17 + + +1000023a +11 20 +movs r0,#0x11 + + +10000244 +11 23 +movs r3,#0x11 + + +10000252 +11 24 +movs r4,#0x11 + +0x11 = 17, all patched! + + + +What Each Patch Controls + + +gpio_init +r0 + + +gpio_set_dir +r3 + + +gpio_put +r4 + +ALL pin refs must be patched + + + +Bonus: Change Print Value + + +00 21 +movs r1,#0x0 +age: 0 + +-> + + +42 21 +movs r1,#0x42 +age: 66 + +Changed value: 0 to 66 (0x42) + \ No newline at end of file diff --git a/WEEK04/slides/WEEK04-IMG09.svg b/WEEK04/slides/WEEK04-IMG09.svg new file mode 100644 index 0000000..6743432 --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG09.svg @@ -0,0 +1,78 @@ + + + + + +GPIO Coprocessor +RP2350 Single-Cycle I/O via mcrr + + + +mcrr Instruction Breakdown + + +mcrr p0, #4, r4, r5, c0 + +mcrr +Move to Coprocessor (2 regs) +p0 +Coprocessor 0 (GPIO) +r4 +GPIO pin number +r5 +Value (0=LOW, 1=HIGH) + + + +Output Value (c0) + + +mcrr p0,#4,r4,r5,c0 + +r4 = pin number +r5 = 0 or 1 +Controls GPIO output state + + +Output Enable (c4) + + +mcrr p0,#4,r4,r5,c4 + +r4 = pin number +r5 = 1 (enable output) +Sets pin direction to OUTPUT + + + +gpio_init(16) Sequence + + +Step 1: Config Pad +addr 0x40038044 + + +Step 2: Set Func +FUNCSEL = 5 (SIO) + + +Step 3: Enable Out +mcrr p0,#4,r4,r5,c4 + + + + +Pad: clear OD, set IE, clear ISO +SIO = fast single-cycle GPIO access + \ No newline at end of file diff --git a/WEEK04/slides/WEEK04-IMG10.svg b/WEEK04/slides/WEEK04-IMG10.svg new file mode 100644 index 0000000..23741fe --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG10.svg @@ -0,0 +1,119 @@ + + + + + +Full Patching Pipeline +End-to-End Binary Hacking Workflow + + + + +1 +Import .bin +Ghidra: Import +ARM Cortex 32 LE +Base: 0x10000000 + + + + + + + +2 +Analyze +Auto-analyze +Rename functions +Fix signatures + + + + + + + +3 +Find Target +Listing window +Find movs rN,#val +Identify bytes to change + + + + +4 +Patch +Right-click: +Patch Instruction +Change operand value + + + + + + + +5 +Export +File: Export +Format: Raw Bytes +Save as *-h.bin + + + + + + + +6 +Convert UF2 +uf2conv.py +--family 0xe48bff59 +RP2350 family ID + + + +UF2 Command + +python uf2conv.py file.bin --base 0x10000000 -o hacked.uf2 + + + +Flash to Pico 2 +1. Hold BOOTSEL + USB +2. Drop hacked.uf2 +3. Pico reboots hacked +RPI-RP2 drive in BOOTSEL + + + +Key Sections + +.text +Flash +Code + +.rodata +Flash +Constants + +.data +RAM +Init globals + +.bss +RAM +Zeroed globals + \ No newline at end of file diff --git a/WEEK05/WEEK05-BN.md b/WEEK05/WEEK05-BN.md new file mode 100644 index 0000000..201ecd0 --- /dev/null +++ b/WEEK05/WEEK05-BN.md @@ -0,0 +1,2003 @@ +# Week 5-BN: Binary Ninja Personal — Decode, Hack, and Patch IEEE 754 Floats and Doubles (Raw `.bin`) + +*** + +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +- Build the two lesson projects with `Release` and get both an `.elf` and a raw `.bin` +- Dump the **ELF symbol map** with `arm-none-eabi-nm` and use it as ground truth +- Load the raw `.bin` into Binary Ninja at `0x10000000` +- Read how a `float`/`double` constant is materialized from the compiler's literal pool +- **Break at `main`** on live silicon, even though `main` moves between these two programs +- Reconstruct a 64-bit `double` from the ABI register pair `r2:r3` and hack it live +- **Resolve the functions in the Binary Ninja GUI** using the ELF symbol map, including the `pico_double` formatting helpers `printf` pulls in +- **Patch** the constant bytes, export the image, convert to UF2, and flash it +- Prove why `42.5 -> 99.0` is a one-word patch but `42.52525 -> 99.99` needs two + +--- + +## How This Guide Works + +The build produces two files for each project: + +| File | What it is | How we use it | +| ---- | ---------- | ------------- | +| `.elf` | The linked image with a full symbol table | Ground truth for every function address and name | +| `.bin` | The raw flash image, no headers, no symbols | The image we load into Binary Ninja and reverse | + +The `.bin` is built **from** the `.elf`, so the ELF tells you exactly what is at every address. We use the ELF symbol map to resolve functions in Binary Ninja, and we reverse-engineer the raw `.bin` the way a real extracted firmware image is reversed. + +> **Build `Release`, not `Debug`.** Every address in this guide matches the Week 5 lesson, and the Week 5 lesson is a `Release` build. `Release` optimizes the code the same way the original lesson was built: it folds the `float`/`double` initializer into the literal pool and links the `pico_double` formatting helpers straight into the `printf` path. If you build `Debug`, the SDK function addresses move and the float/double helpers are laid out differently, so nothing lines up. Always build `Release` for this lesson. + +The order is **dynamic first, static second**, twice — once per project: + +1. Break on the live target and prove what the code does. +2. Hack it live in the debugger and watch the output change. +3. Resolve the functions in Binary Ninja using the ELF symbol map. +4. Patch the bytes, export, convert, and flash. + +| Project | Prints | The hack | +| ------- | ------ | -------- | +| `0x000e_floating-point-data-type` | `fav_num: 42.500000` | change the printed double `42.5` to `99.0` (one word) | +| `0x0011_double-floating-point-data-type` | `fav_num: 42.525250` | change the printed double `42.52525` to `99.99` (two words) | + +> **Addresses come from your build.** Every address here is from the `Release` build produced in Step 3. Confirm against your own `.elf` with the command in Step 4. + +> **`main` is not at the same address in both projects this week.** It is `0x10000234` in Project 1 and `0x10000238` in Project 2, because each program materializes its constant slightly differently. We still anchor to `main` through the one byte-identical place that always names it: the middle `blx` in `platform_entry` (Step 12). + +--- + +## Part 1: Build, Flash, and Get the Symbol Map + +### Step 1: Install the toolchain + +**Windows x64** + +- Install the **Raspberry Pi Pico** extension in VS Code. It installs the ARM GNU toolchain, CMake, Ninja, and the Pico SDK. +- Install **Binary Ninja Personal** and complete its license activation. +- Install **PuTTY** for the serial monitor. + +**macOS Apple Silicon** + +```bash +brew install cmake ninja +``` + +- Install **Binary Ninja Personal** and complete its license activation. +- Install the **Arm GNU Toolchain**, or let the VS Code Pico extension manage it. + +**Linux x64** + +```bash +sudo apt install cmake ninja-build gcc-arm-none-eabi libnewlib-arm-none-eabi git python3 openocd minicom +``` + +- Install **Binary Ninja Personal** and complete its license activation. + +### Step 2: Verify your tools are the right architecture (do not skip this) + +On **macOS Apple Silicon**, the most common failure is an Intel `x86_64` tool on your `PATH`: + +``` +zsh: bad CPU type in executable: cmake +``` + +You may have **two Homebrews**: the arm64 one at `/opt/homebrew` and the Intel one at `/usr/local`. If `/usr/local/bin` wins, every `brew` tool is x86_64. Check: + +```bash +file "$(which cmake)" +file "$(which ninja)" +file "$(which arm-none-eabi-gdb)" +file "$(which arm-none-eabi-nm)" +file "$(which openocd)" +file "$(which telnet)" +``` + +All must report `arm64`. If any is `x86_64`, put the Apple Silicon prefix first for the session and check again: + +```bash +export PATH="/opt/homebrew/bin:$PATH" +hash -r +file "$(which cmake)" +``` + +To make it permanent, add that `export` to `~/.zshrc`. Do not use Rosetta as a fix; OpenOCD and GDB are exactly the kind of programs where a translation layer produces failures that look like debugger bugs. + +**`telnet` is special — and optional.** The GDB MI workflow does not need it; it is only used by the command-port fallback. macOS no longer ships `telnet`, and the Homebrew build is often the Intel one, so `telnet 127.0.0.1 4444` fails with `bad CPU type in executable`. Your `brew` command itself may also be the Intel one: if `brew install telnet` fails with `.../portable-ruby/.../ruby: Bad CPU type in executable`, you are running the Intel Homebrew. Call the Apple Silicon Homebrew explicitly: + +```bash +/opt/homebrew/bin/brew install telnet +``` + +If you would rather not install anything, macOS ships an arm64 `nc`, which can connect to the same OpenOCD port: + +```bash +nc 127.0.0.1 4444 +``` + +**Windows x64** and **Linux x64** do not have this problem. Skip to Step 3. + +### Step 3: Build the two projects with `Release` + +Run this once inside `0x000e_floating-point-data-type/` and once inside `0x0011_double-floating-point-data-type/`: + +```bash +cmake -B build -G Ninja -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 -DCMAKE_BUILD_TYPE=Release +cmake --build build +``` + +**Point Binary Ninja at this repository (once).** Every console snippet below reads the repo root from `~/.embedded-hacking-repo`, so Binary Ninja never needs a database open and nothing is hardcoded. From the repo root, run once: + +**macOS / Linux:** + +```bash +pwd > ~/.embedded-hacking-repo +``` + +**Windows (PowerShell):** + +```powershell +(Get-Location).Path | Set-Content "$env:USERPROFILE\.embedded-hacking-repo" +``` + +**Then build from the Binary Ninja console**, so the whole build -> patch -> flash loop stays inside Binary Ninja. The console inherits a minimal `PATH` — on macOS just `/usr/bin:/bin:/usr/sbin:/sbin` — so it does not see Homebrew; add your package manager's `bin` first, then run plain `cmake`. + +**macOS Apple Silicon:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +os.environ["PATH"] = "/opt/homebrew/bin:" + os.environ["PATH"] # the console's PATH omits Homebrew +for name in ("0x000e_floating-point-data-type", "0x0011_double-floating-point-data-type"): + proj = os.path.join(root, name) + subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) + subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +**Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +for name in ("0x000e_floating-point-data-type", "0x0011_double-floating-point-data-type"): + proj = os.path.join(root, name) + subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) + subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +for name in ("0x000e_floating-point-data-type", "0x0011_double-floating-point-data-type"): + proj = os.path.join(root, name) + subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) + subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +Each build directory now contains the pair we need: + +- `0x000e_floating-point-data-type/build/0x000e_floating-point-data-type.elf` and `.bin` — `.bin` is **15308** bytes (`0x3bcc`) +- `0x0011_double-floating-point-data-type/build/0x0011_double-floating-point-data-type.elf` and `.bin` — `.bin` is **15324** bytes (`0x3bdc`) + +If the ARM toolchain is not on your `PATH`, add `-DPICO_TOOLCHAIN_PATH=...`: + +| OS | Typical toolchain path | +| -- | ---------------------- | +| Windows x64 | `C:/Program Files/Arm GNU Toolchain arm-none-eabi/14.2 rel1/bin` | +| macOS Apple Silicon | `~/.pico-sdk/toolchain/14_2_Rel1/bin` | +| Linux x64 | `/usr` | + +> **This guide's toolchain lives at `~/.pico-sdk/toolchain/14_2_Rel1/bin`.** All of `arm-none-eabi-nm`, `arm-none-eabi-objdump`, and `arm-none-eabi-gdb` resolved in this document come from there. If your install is elsewhere, `which arm-none-eabi-nm` tells you where to point. + +### Step 4: Dump the ELF symbol map + +This is the ground truth for the whole lesson. Run `arm-none-eabi-nm` on each ELF and keep the output in a terminal or a text file: + +**macOS Apple Silicon / Linux x64:** + +```bash +arm-none-eabi-nm -n --defined-only build/0x000e_floating-point-data-type.elf | grep -E ' [Tt] ' +arm-none-eabi-nm -n --defined-only build/0x0011_double-floating-point-data-type.elf | grep -E ' [Tt] ' +``` + +**Windows x64:** + +```powershell +arm-none-eabi-nm -n --defined-only build\0x000e_floating-point-data-type.elf | Select-String ' [Tt] ' +arm-none-eabi-nm -n --defined-only build\0x0011_double-floating-point-data-type.elf | Select-String ' [Tt] ' +``` + +Each line is `address type name`. The `T`/`t` type is a function. Here are the functions this lesson uses. The signatures come from the ELF's DWARF debug info queried with `arm-none-eabi-gdb -batch -ex "ptype "`, so they are exact. + +**Project 1 — `0x000e_floating-point-data-type`:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | reset entry | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | calls `runtime_init`, `main`, `exit` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | copies `.data` from flash to SRAM | +| `0x100001e4` | `_init` | `void _init(void)` | runs `.init_array` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | C runtime boilerplate | +| `0x10000234` | `main` | `int main(void)` | the lesson function | +| `0x10000254` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | SDK GPIO helper | +| `0x10000da8` | `time_us_64` | `uint64_t time_us_64(void)` | SDK microsecond clock | +| `0x10000e18` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | SDK UART init | +| `0x10002ca8` | `vfctprintf` | `int vfctprintf(void (*)(char, void*), void*, const char*, va_list)` | printf format engine | +| `0x10002d04` | `exit` | `void exit(int)` | C runtime exit | +| `0x10002d0c` | `runtime_init` | `void runtime_init(void)` | SDK runtime init | +| `0x10002d38` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | CRLF output driver | +| `0x10002e48` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | buffered string output | +| `0x10002f34` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | enable the UART driver | +| `0x10002f5c` | `stdio_init_all` | `bool stdio_init_all(void)` | SDK serial init | +| `0x10003028` | `__wrap_vprintf` | `int __wrap_vprintf(const char*, va_list)` | printf core | +| `0x100030ec` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | the `printf` wrapper | +| `0x100032a8` | `stdio_uart_init` | `void stdio_uart_init(void)` | SDK UART stdio init | +| `0x100033e8` | `strlen` | `size_t strlen(const char*)` | C runtime string length | + +Project 1 also links in the `pico_double` formatting helpers that `printf`'s `%f` path calls. These are reachable from `main` through `__wrap_printf`: + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x10001384` | `__wrap___aeabi_dadd` | `double __wrap___aeabi_dadd(double, double)` | double add | +| `0x100013ac` | `__wrap___aeabi_dsub` | `double __wrap___aeabi_dsub(double, double)` | double subtract | +| `0x100013d4` | `__wrap___aeabi_dmul` | `double __wrap___aeabi_dmul(double, double)` | double multiply | +| `0x10001420` | `__wrap___aeabi_ddiv` | `double __wrap___aeabi_ddiv(double, double)` | double divide | +| `0x100014bc` | `__wrap___aeabi_i2d` | `double __wrap___aeabi_i2d(int)` | int -> double | +| `0x100014e0` | `__wrap___aeabi_ui2d` | `double __wrap___aeabi_ui2d(unsigned)` | unsigned -> double | +| `0x10001504` | `__wrap___aeabi_d2iz` | `int __wrap___aeabi_d2iz(double)` | double -> int | +| `0x10001528` | `__wrap___aeabi_d2uiz` | `unsigned __wrap___aeabi_d2uiz(double)` | double -> unsigned | +| `0x1000154c` | `__wrap___aeabi_dcmpun` | `int __wrap___aeabi_dcmpun(double, double)` | unordered compare | +| `0x10001570` | `__wrap___aeabi_dcmplt` | `int __wrap___aeabi_dcmplt(double, double)` | less-than compare | +| `0x10001598` | `__wrap___aeabi_dcmple` | `int __wrap___aeabi_dcmple(double, double)` | less-or-equal compare | +| `0x100015c0` | `__wrap___aeabi_dcmpge` | `int __wrap___aeabi_dcmpge(double, double)` | greater-or-equal compare | +| `0x100015e8` | `__wrap___aeabi_dcmpgt` | `int __wrap___aeabi_dcmpgt(double, double)` | greater-than compare | +| `0x1000160c` | `_out_rev` | `unsigned _out_rev(out_fct_type, char*, size_t, size_t, const char*, size_t, unsigned, unsigned)` | reversed-digit output | +| `0x100016a8` | `_ntoa_format` | `unsigned _ntoa_format(out_fct_type, char*, size_t, size_t, char*, size_t, bool, unsigned, unsigned, unsigned, unsigned)` | number formatter | +| `0x1000187c` | `_out_char` | `void _out_char(char, void*, size_t, size_t)` | single-char sink | +| `0x10001890` | `_ftoa` | `unsigned int _ftoa(out_fct_type, char*, size_t, size_t, double, unsigned int, unsigned int, unsigned int)` | fixed-point float formatter | +| `0x10001d50` | `_etoa` | `unsigned int _etoa(out_fct_type, char*, size_t, size_t, double, unsigned int, unsigned int, unsigned int)` | exponential float formatter | +| `0x100022c4` | `_vsnprintf` | `int _vsnprintf(out_fct_type, char*, size_t, const char*, va_list)` | the format dispatcher | +| `0x10000dbc` | `busy_wait_us` | `void busy_wait_us(uint64_t)` | UART timing loop | +| `0x10000fec` | `clock_get_hz` | `unsigned long clock_get_hz(clock_handle_t)` | UART clock lookup | + +**Project 2 — `0x0011_double-floating-point-data-type`:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | reset entry | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | calls `runtime_init`, `main`, `exit` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | copies `.data` from flash to SRAM | +| `0x100001e4` | `_init` | `void _init(void)` | runs `.init_array` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | C runtime boilerplate | +| `0x10000238` | `main` | `int main(void)` | the lesson function | +| `0x1000025c` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | SDK GPIO helper | +| `0x10000db0` | `time_us_64` | `uint64_t time_us_64(void)` | SDK microsecond clock | +| `0x10000e20` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | SDK UART init | +| `0x10002cb0` | `vfctprintf` | `int vfctprintf(void (*)(char, void*), void*, const char*, va_list)` | printf format engine | +| `0x10002d0c` | `exit` | `void exit(int)` | C runtime exit | +| `0x10002d14` | `runtime_init` | `void runtime_init(void)` | SDK runtime init | +| `0x10002d40` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | CRLF output driver | +| `0x10002e50` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | buffered string output | +| `0x10002f3c` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | enable the UART driver | +| `0x10002f64` | `stdio_init_all` | `bool stdio_init_all(void)` | SDK serial init | +| `0x10003030` | `__wrap_vprintf` | `int __wrap_vprintf(const char*, va_list)` | printf core | +| `0x100030f4` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | the `printf` wrapper | +| `0x100032b0` | `stdio_uart_init` | `void stdio_uart_init(void)` | SDK UART stdio init | +| `0x100033f0` | `strlen` | `size_t strlen(const char*)` | C runtime string length | + +Project 2 uses the same `pico_double` formatting helpers as Project 1, shifted by eight bytes because `main` moved: + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x1000138c` | `__wrap___aeabi_dadd` | `double __wrap___aeabi_dadd(double, double)` | double add | +| `0x100013b4` | `__wrap___aeabi_dsub` | `double __wrap___aeabi_dsub(double, double)` | double subtract | +| `0x100013dc` | `__wrap___aeabi_dmul` | `double __wrap___aeabi_dmul(double, double)` | double multiply | +| `0x10001428` | `__wrap___aeabi_ddiv` | `double __wrap___aeabi_ddiv(double, double)` | double divide | +| `0x100014c4` | `__wrap___aeabi_i2d` | `double __wrap___aeabi_i2d(int)` | int -> double | +| `0x100014e8` | `__wrap___aeabi_ui2d` | `double __wrap___aeabi_ui2d(unsigned)` | unsigned -> double | +| `0x1000150c` | `__wrap___aeabi_d2iz` | `int __wrap___aeabi_d2iz(double)` | double -> int | +| `0x10001530` | `__wrap___aeabi_d2uiz` | `unsigned __wrap___aeabi_d2uiz(double)` | double -> unsigned | +| `0x10001554` | `__wrap___aeabi_dcmpun` | `int __wrap___aeabi_dcmpun(double, double)` | unordered compare | +| `0x10001578` | `__wrap___aeabi_dcmplt` | `int __wrap___aeabi_dcmplt(double, double)` | less-than compare | +| `0x100015a0` | `__wrap___aeabi_dcmple` | `int __wrap___aeabi_dcmple(double, double)` | less-or-equal compare | +| `0x100015c8` | `__wrap___aeabi_dcmpge` | `int __wrap___aeabi_dcmpge(double, double)` | greater-or-equal compare | +| `0x100015f0` | `__wrap___aeabi_dcmpgt` | `int __wrap___aeabi_dcmpgt(double, double)` | greater-than compare | +| `0x10001614` | `_out_rev` | `unsigned _out_rev(out_fct_type, char*, size_t, size_t, const char*, size_t, unsigned, unsigned)` | reversed-digit output | +| `0x100016b0` | `_ntoa_format` | `unsigned _ntoa_format(out_fct_type, char*, size_t, size_t, char*, size_t, bool, unsigned, unsigned, unsigned, unsigned)` | number formatter | +| `0x10001884` | `_out_char` | `void _out_char(char, void*, size_t, size_t)` | single-char sink | +| `0x10001898` | `_ftoa` | `unsigned int _ftoa(out_fct_type, char*, size_t, size_t, double, unsigned int, unsigned int, unsigned int)` | fixed-point float formatter | +| `0x10001d58` | `_etoa` | `unsigned int _etoa(out_fct_type, char*, size_t, size_t, double, unsigned int, unsigned int, unsigned int)` | exponential float formatter | +| `0x100022cc` | `_vsnprintf` | `int _vsnprintf(out_fct_type, char*, size_t, const char*, va_list)` | the format dispatcher | +| `0x10000dc4` | `busy_wait_us` | `void busy_wait_us(uint64_t)` | UART timing loop | +| `0x10000ff4` | `clock_get_hz` | `unsigned long clock_get_hz(clock_handle_t)` | UART clock lookup | + +> **A literal pool is still a pool.** The `float`/`double` initializer does not survive as a C variable, but the compiler still has to place the IEEE-754 bit pattern somewhere. It parks the 32-bit word (or word pair) right after `main`'s code and reaches it with a PC-relative `ldr`/`ldrd`. That is why the value you patch is a `.word` in the image, not a stack store. + +### Step 5: Flash Project 1 and confirm `fav_num: 42.500000` + +A `.bin` has no headers, so OpenOCD must be told the base address `0x10000000`. From the repository root: + +**macOS Apple Silicon / Linux x64:** + +```bash +./flash.sh 0x000e_floating-point-data-type/build/0x000e_floating-point-data-type.bin +``` + +**Windows x64 (PowerShell):** + +```powershell +.\flash.ps1 -Bin 0x000e_floating-point-data-type\build\0x000e_floating-point-data-type.bin +``` + +**Or flash from the Binary Ninja console** (the console reads the repo root from the marker file, so it works with no database open): + +**macOS Apple Silicon / Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x000e_floating-point-data-type", "build", "0x000e_floating-point-data-type.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("flashing in the background; log:", log) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x000e_floating-point-data-type", "build", "0x000e_floating-point-data-type.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # free the probe first +subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "flash.ps1"), "-Bin", bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT) +print("flashing in the background; log:", log) +``` + +Wait for `wrote 15308 bytes ...` and `** Verified OK **`. Open a serial monitor at **115200** baud: + +- **Windows x64:** PuTTY -> Connection type **Serial**, the Pico's COM port, speed `115200`. +- **macOS Apple Silicon:** `screen /dev/tty.usbmodem* 115200` (quit with `Ctrl-A` then `K`). +- **Linux x64:** `minicom -D /dev/ttyACM0 -b 115200`. + +``` +fav_num: 42.500000 +fav_num: 42.500000 +fav_num: 42.500000 +... +``` + +The program prints `42.500000` because `printf` with `%f` defaults to six decimal places. + +### Step 6: Flash Project 2 and confirm `fav_num: 42.525250` + +```bash +# macOS / Linux +./flash.sh 0x0011_double-floating-point-data-type/build/0x0011_double-floating-point-data-type.bin +``` +```powershell +# Windows +.\flash.ps1 -Bin 0x0011_double-floating-point-data-type\build\0x0011_double-floating-point-data-type.bin +``` + +**Or flash from the Binary Ninja console** (same form as Step 5, pointing at the Project 2 `.bin`): + +**macOS / Linux:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x0011_double-floating-point-data-type", "build", "0x0011_double-floating-point-data-type.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) +subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("flashing in the background; log:", log) +``` + +**Windows:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x0011_double-floating-point-data-type", "build", "0x0011_double-floating-point-data-type.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) +subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "flash.ps1"), "-Bin", bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT) +print("flashing in the background; log:", log) +``` + +Wait for `wrote 15324 bytes ...`. The serial monitor shows: + +``` +fav_num: 42.525250 +fav_num: 42.525250 +fav_num: 42.525250 +... +``` + +`42.52525` has a repeating binary fraction, so its 52 mantissa bits are not all zero. Remember that: it is why this value needs two words patched, and `42.5` needs only one. +--- + +## Part 2: Load the Raw `.bin` into Binary Ninja + +Start from a fresh Binary Ninja state. If you already have a `.bndb` for this lesson, **close it and start over**; a stale database keeps old names and patches. + +### Step 7: Bring the raw `.bin` into Binary Ninja + +A raw `.bin` has no headers, so Binary Ninja cannot know where it belongs or what architecture it is. You must supply both. If you just double-click the `.bin`, Binary Ninja may load it at address `0x0` with a guessed architecture, and every address in this lesson will be wrong. + +1. Choose `File -> Open with Options...` (do **not** use plain `File -> Open`). +2. Select `0x000e_floating-point-data-type/build/0x000e_floating-point-data-type.bin`. +3. In the loader options, set: + - **Architecture:** `thumb2` (the ARMv7-M / ARMv8-M Thumb-2 architecture, which covers the Cortex-M33) + - **Platform:** `thumb2` + - **Base Address:** `0x10000000` (the XIP flash base) +4. Click **Open**. + +Binary Ninja analyzes the image and opens the linear view. + +**Verify the load before going further.** Press `G`, type `0x10000000`, and read the first two words: + +``` +0x10000000 0x20082000 initial stack pointer +0x10000004 0x1000015d reset vector (bit 0 = Thumb) +``` + +If you instead see data at `0x00000000`, or a vector word without bit 0 set, close the tab and repeat with `Open with Options`. The Cortex-M33 only executes Thumb-2, so `thumb2` is the only correct architecture. + +> **Console equivalent:** +> ```python +> load("0x000e_floating-point-data-type/build/0x000e_floating-point-data-type.bin", +> options={"loader.imageBase": 0x10000000, "loader.platform": "thumb2"}) +> ``` + +### Step 8: Save it as a Binary Ninja database (`.bndb`) + +Binary Ninja never writes back into the `.bin`. Your names, comments, types, and patches live in a separate **`.bndb`** database. Save one now, before you make any changes: + +1. Choose `File -> Save As...`. +2. Save it next to the image as `0x000e_floating-point-data-type.bndb`. +3. From now on, save with `File -> Save` (`Cmd+S` on macOS, `Ctrl+S` on Windows/Linux) whenever you rename or patch. + +The two files have different roles: + +| File | Role | +| ---- | ---- | +| `0x000e_floating-point-data-type.bin` | the raw firmware image; Binary Ninja never modifies it | +| `0x000e_floating-point-data-type.bndb` | your analysis database: names, types, comments, and patches | + +When you come back later, **open the `.bndb`**, not the `.bin`; that restores all your work. If a database gets messy, delete the `.bndb` and re-import the `.bin` from Step 7 — the firmware is never at risk. You export the patched image out of this view later, in Step 19. + +### Step 9: The views you will use + +- **Linear view:** the disassembly listing. You navigate, read, and patch here. +- **Graph view:** the control-flow graph of the current function. +- **Decompiler (HLIL):** the pseudo-C decompilation. +- **Hex view:** raw bytes, used for patching. +- **Function list:** the sidebar list of every detected function. + +Navigation: `G` go to address, `N` rename, `Y` set type or signature, `;` add a comment. Breakpoints are set from the GUI through the GDB MI adapter — see Step 13. + +> **macOS function keys:** the top-row `F` keys are usually mapped to system functions. Every step here uses menu paths that work without them. + +--- + +## Part 3: Dynamic — Break at `main` and Hack Live (Project 1) + +### Step 10: Start OpenOCD as a live debug server + +Make sure no other OpenOCD is running; a forgotten server holds port `3333`. + +**macOS / Linux:** + +```bash +ps aux | grep -i openocd +``` + +**Windows (PowerShell):** + +```powershell +Get-Process | Where-Object { $_.ProcessName -like '*openocd*' } +``` + +Stop any leftover server gracefully: + +**macOS / Linux:** + +```bash +pkill -TERM -f openocd +``` + +**Windows (PowerShell):** + +```powershell +Get-Process openocd -ErrorAction SilentlyContinue | Stop-Process +``` + +Start the server **parked at `main`**: + +**macOS Apple Silicon / Linux x64:** + +```bash +BP_ADDR=0x10000234 ./debug-server.sh +``` + +**Windows x64 (PowerShell):** + +```powershell +$env:BP_ADDR="0x10000234"; .\debug-server.ps1 +``` + +**Or start it from the Binary Ninja console**, freeing the probe first and launching the server in the background so the console returns immediately: + +**macOS Apple Silicon / Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # stop any running server first +log = os.path.join(root, "openocd.log") +p = subprocess.Popen([os.path.join(root, "debug-server.sh")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("OpenOCD started (pid", p.pid, "); log:", log) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # stop any running server first +log = os.path.join(root, "openocd.log") +p = subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "debug-server.ps1")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT) +print("OpenOCD started (pid", p.pid, "); log:", log) +``` + +`Popen` returns in a few milliseconds; the server keeps running in the background. Check `openocd.log` for `Listening on port 3333`, then connect in Step 11. + +Wait for: + +``` +Info : [rp2350.dap.core0] Examination succeed +Startup breakpoint at 0x10000234 (2-byte hardware execute, one-shot). +Info : starting gdb server for rp2350.dap.core0 on 3333 +Info : Listening on port 3333 for gdb connections +``` + +> **`BP_ADDR` parks the core at `main` before any client connects.** The script arms a 2-byte hardware breakpoint and then does the startup `reset run`, so the core runs from the vector table and stops at your address with no debugger attached yet. When Binary Ninja connects a moment later, the first thing it reads is already the truth: `Stopped at 0x10000234`. This is the whole reason the lab works cleanly — you never have to drive a reset from outside the GUI. +> +> Use the address you actually want to stop at: +> +> | What you want to stop at | Project 1 `0x000e` | Project 2 `0x0011` | Command | +> | --- | --- | --- | --- | +> | `main` (once per reset) | `0x10000234` | `0x10000238` | `BP_ADDR=0x10000234 ./debug-server.sh` | +> | The **loop** — the `printf` call, hit every iteration | `0x10000244` | `0x1000024a` | `BP_ADDR=0x10000244 ./debug-server.sh` | +> +> ```bash +> BP_ADDR=0x10000234 ./debug-server.sh # park at main +> BP_ADDR=0x10000244 ./debug-server.sh # park in the loop instead +> ``` +> +> ```powershell +> $env:BP_ADDR="0x10000234"; .\debug-server.ps1 # park at main +> $env:BP_ADDR="0x10000244"; .\debug-server.ps1 # park in the loop +> ``` +> +> **Note the loop address differs from Week 4 and between the two projects.** In Project 1 the `bl __wrap_printf` sits at `0x10000244`; in Project 2 it sits at `0x1000024a`, because Project 2 loads the pair with `ldrd` first. Both were verified against the Release `.elf` with `arm-none-eabi-objdump` and confirmed live on hardware. +> +> **This startup stop is single-use.** OpenOCD flushes breakpoints when a client connects, so this one is gone once Binary Ninja attaches — fine for `main`, which only runs once per reset. Every breakpoint after that is set from the Binary Ninja GUI (Step 13) and is repeatable. To stop at `main` again, restart the server with `BP_ADDR` and reconnect. + +> **Exactly one core.** The line must say `core0` and must **not** mention `core1`. Core1 is never started by this firmware; exposing it makes Binary Ninja read core1's reset-state registers, which are not real addresses, and OpenOCD floods the log with `Failed to read memory at 0xf0000000`. The scripts already use `USE_CORE=0`; do not change it. + +> **Windows driver note:** the Debug Probe must use the **WinUSB** driver. If OpenOCD reports `unable to open CMSIS-DAP device`, install it with [Zadig](https://zadig.akeo.ie/) (select `Debug Probe (CMSIS-DAP)` -> WinUSB). + +### Step 11: Connect Binary Ninja to the GDB server + +1. Make sure the image is open and analyzed (Part 2) and the server from Step 10 is running (parked at `main`). +2. Choose `Debugger -> Connect to Remote Process`. +3. In the **adapter** dropdown, select **GDB MI**. +4. In the **connect** settings group, set **IP Address** to `127.0.0.1` and **Port** to `3333`. +5. Set **Full GDB Executable Path** to the `arm-none-eabi-gdb` from the **Arm GNU Toolchain 14.2.rel1**. It ships for all three hosts, and the Raspberry Pi Pico VS Code extension installs that same 14.2.rel1 toolchain (including `arm-none-eabi-gdb`) on all of them: + + | OS | `arm-none-eabi-gdb` path | + | -- | ------------------------ | + | macOS Apple Silicon | `/Applications/ArmGNUToolchain/14.2.rel1/arm-none-eabi/bin/arm-none-eabi-gdb` (or the Pico extension's `~/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb`) | + | Windows x64 | `%USERPROFILE%\.pico-sdk\toolchain\14_2_Rel1\bin\arm-none-eabi-gdb.exe` (Pico extension), or `C:\Program Files (x86)\Arm GNU Toolchain arm-none-eabi\14.2 rel1\bin\arm-none-eabi-gdb.exe` | + | Linux x64 | `~/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb` (Pico extension), or the `bin/` directory of the extracted `arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi` tarball | +6. Click **Accept**. + +> **Use the GDB MI adapter.** It launches a real `arm-none-eabi-gdb --interpreter=mi2` and lets Binary Ninja drive it, so breakpoints and stepping go through real GDB — which sends the correct 2-byte breakpoint length and handles step-over itself. Verified working end to end: connect, GUI breakpoints (**Add Hardware Breakpoint...**, hardware execute), **Step Into** / **Step Over**, and register edits. Stops are reported as `Breakpoint` (not `SingleStep`). +> +> **Do NOT have any breakpoints set in Binary Ninja before you connect.** With the GDB MI adapter, attaching while Binary Ninja already has a breakpoint **hangs the session**. Start the server parked with `BP_ADDR` (Step 10), connect, and only add hardware breakpoints *after* the connection is up. This is a Binary Ninja bug; it is the single most common GDB MI failure. +> +> **The GDB executable path matters.** Use the **14.2.rel1** build on every OS (Windows, macOS, Linux). The 13.3.rel1 build did **not** connect in testing. +> +> **This step is temporary.** Vector35 plans to ship a GDB binary with the GDB MI adapter ([Vector35/debugger#929](https://github.com/Vector35/debugger/issues/929), milestone *Langara*). Once that lands, Binary Ninja provides GDB itself and you will not need to set **Full GDB Executable Path** at all. +> +> **Do not pick Corellium.** Binary Ninja's adapter dropdown also lists **Corellium**, which is for Corellium's virtual devices and expects an API token, not a local OpenOCD server. It is not the adapter for this lab. The dropdown is a combo box, so an accidental arrow-key press can land on it — always read the label back and confirm it says **GDB MI** before clicking **Accept**. + +> **The adapter and port are not saved in the `.bndb`.** Every time you relaunch Binary Ninja you must re-select **GDB MI**, re-enter port `3333`, and re-set the GDB path. + +> **Watch for an off-screen error dialog.** When a connection fails, Binary Ninja pops a `Binary Ninja critical alert` window that can be positioned mostly outside the main window, which makes it look like nothing happened. If the connect seems to do nothing, check your other display. + +The target keeps running. Open the **Registers** tab (bug icon) and confirm you see live values. `pc` inside `0x10003xxx` and `sp` just below `0x20082000` are healthy. + +> **If `pc` is `0x00000088`, `0x000000ec`, or `sp` is `0xf0000000`, the session is bad.** Restart the server, then restart Binary Ninja (a server restart while attached leaves Binary Ninja in a stale session), and connect again. + +### Step 12: Find `main` without relying on its address + +`main` moves between these programs (`0x10000234` vs `0x10000238`), so we do not guess it. We follow the one fixed path to it. Press `G` and go to `0x10000000`: + +``` +0x10000000 0x20082000 initial stack pointer (top of SRAM) +0x10000004 0x1000015d reset vector +``` + +Bit 0 of a vector is the Thumb bit, so `0x1000015d` means "start at `0x1000015c`". That is `_reset_handler`. Follow the reset path to `0x10000186`, `platform_entry`: + +```asm +10000186: 4914 ldr r1, [pc, #80] ; @ 0x100001d8 +10000188: 4788 blx r1 ; runtime_init +1000018a: 4914 ldr r1, [pc, #80] ; @ 0x100001dc +1000018c: 4788 blx r1 ; main <-- the fixed anchor +1000018e: 4914 ldr r1, [pc, #80] ; @ 0x100001e0 +10000190: 4788 blx r1 ; exit +10000192: be00 bkpt 0x0000 +``` + +**The middle `blx` at `0x1000018c` is the call to `main`.** `platform_entry` is byte-identical in both projects, so `0x1000018c` catches `main` no matter where the linker placed it. The literal pool at `0x100001dc` holds `main | 1`; clearing bit 0 gives `0x10000234` for Project 1 and `0x10000238` for Project 2. + +### Step 13: Set a hardware breakpoint in the GUI + +With the **GDB MI** adapter, Binary Ninja sets breakpoints through real GDB, which sends the correct 2-byte length, so you set them **in the UI**. There is no command port here. + +> **Why older drafts used the command port.** Binary Ninja's **GDB RSP** adapter is its own minimal RSP client and sends a **1-byte** breakpoint (`Z0,,1`); the Cortex-M33 FPB comparators need 2 bytes, so OpenOCD rejected it with `only breakpoints of two bytes length supported`. The old workaround was to arm breakpoints by hand over telnet. **The GDB MI adapter does not have this problem** — it drives real `arm-none-eabi-gdb`, which sends the right length. So everything below is done in the GUI. The command port still exists as a fallback (see the end of this step), but you do not need it. + +#### Where you can stop + +| You want to stop at | Address | How | Repeatable? | +| --- | --- | --- | --- | +| **`main`** | Project 1 `0x10000234`, Project 2 `0x10000238` | The server starts parked there with `BP_ADDR` (Step 10), so Binary Ninja is already stopped at `main` when it connects. | No — `main` runs once per reset. | +| **The loop** (`printf` call) | Project 1 `0x10000244`, Project 2 `0x1000024a` | Set a hardware breakpoint in the GUI, then click **Resume**. | Yes — fires on every iteration. | + +#### Set the loop breakpoint in the GUI + +1. Press `G`, type the loop address (`0x10000244` for Project 1, `0x1000024a` for Project 2), and press Enter. +2. Set a **hardware execution** breakpoint at that address, either way: + - `Debugger -> Add Hardware Breakpoint...` — a **hardware execute** (`HE`) breakpoint. **Use this one.** + - click the line and press `F2` (`Debugger -> Toggle Breakpoint`) — a **software** breakpoint. It will **not** work here: the code is in read-only flash, so GDB cannot install it and the core just keeps running. +3. Click **Resume**. The core is already running the loop, so the breakpoint fires on the next iteration. Binary Ninja stops with the PC at the loop address and reports it as a **Breakpoint** — verified: `Stopped (Breakpoint) at 0x10000244`. + +> **No breakpoints before you connect.** With GDB MI, a breakpoint set before the connection hangs the session (Step 11). Start parked with `BP_ADDR`, connect, *then* add breakpoints. + +#### Stepping + +With the target halted at the breakpoint, **Step Into** (`F7`) and **Step Over** (`F8`) run through real GDB and move the PC. Verified: `0x10000244 -> 0x100030ec -> 0x100030ee -> ...`. + +> **Step Over on the raw `.bin` steps *into* calls.** The raw image has no symbol for `__wrap_printf`, so **Step Over** at the `printf` call behaves like **Step Into**. When the lab needs to execute the call and then stop, it moves the breakpoint to the return site and clicks **Resume** instead (Step 14 shows this). + +> **Never use Binary Ninja's Restart button.** On RP2350 it resets and halts inside the boot ROM (`pc=0x88`, `sp=0xf0000000`). To reset cleanly, restart the server with `BP_ADDR` and reconnect. + +> **If you ever need the command port.** It is still there — `nc 127.0.0.1 4444`, and `bp 2 hw` still arms a breakpoint, `rbp ` / `rbp all` still remove them. It is the fallback if you switch back to the **GDB RSP** adapter, whose 1-byte breakpoints the GUI cannot set. With GDB MI you do not need it for this lab. + +### Step 14: HACK IT LIVE — change the printed double + +Project 1's `main` sets `r4 = 0`, loads the high word of the double into `r5`, and on every iteration copies them into the `r2:r3` argument pair before calling `printf`. We break on that call and change the value live: + +1. Press `G`, go to `0x10000244` (the `bl __wrap_printf`). +2. Set a **hardware execute** breakpoint there: `Debugger -> Add Hardware Breakpoint...`. (Do not use `F2` — that is a software breakpoint and will not work on read-only flash.) +3. Click **Resume** in Binary Ninja. The target is already running the loop, so the breakpoint fires on the next iteration. Binary Ninja stops with the program counter at `0x10000244`, `r2 = 0x00000000`, and `r3 = 0x40454000`. +4. Open the **Registers** widget (bug icon -> **Registers**). +5. The ABI passes the promoted `double` in `r2:r3` — `r2` is the low 32 bits, `r3` the high 32 bits. Together they are `0x40454000_00000000`, the IEEE-754 encoding of `42.5`. +6. **Set `r3` to `0x4058C000`** — the high word of `99.0` — from Binary Ninja's Python console (`Plugins -> Python Console`): + ```python + dbg.set_reg_value("r2", 0x00000000) # low word of the 99.0 double (unchanged) + dbg.set_reg_value("r3", 0x4058C000) # high word of the 99.0 double + ``` + `dbg.set_reg_value(name, value)` writes one register (returns `True` on success). You can also right-click a register in the **Registers** widget, press `E` (edit), type the hex value, and press Enter. The widget may not repaint, but the write reaches the target — you confirm it by the printed output in the next steps. +7. **Move the breakpoint past the call.** You want `printf` to run once and then stop, so move the breakpoint from `0x10000244` to the instruction *after* the call, `0x10000248` (the `b.n` that closes the loop): remove the breakpoint at `0x10000244` and set a hardware breakpoint at `0x10000248`. Two reasons not to just click **Step Over** here: a breakpoint left on the current PC re-traps the step, and Binary Ninja's **Step Over** steps *into* `__wrap_printf` on this raw `.bin` because the image carries no symbol for the call. Moving the breakpoint to the return site is deterministic. +8. Click **Resume** in Binary Ninja. The core executes `bl __wrap_printf` with `r2:r3 = 0x4058C000_00000000`, so this iteration prints `fav_num: 99.000000`, then stops at `0x10000248`. +9. Look at your serial monitor — the `screen` session on the Pico's USB serial port — and at the **Target** tab in Binary Ninja: + + ``` + fav_num: 99.000000 + ``` + +You changed a running program's output without touching the binary. + +### Step 14b: HACK THE STRING LIVE — change `fav_num:` to `myvalue:` + +The text `"fav_num: %f\r\n"` lives in flash (`.rodata`) at `0x100034a8`, and flash is **read-only at runtime** — a debugger write there does not stick. So instead of overwriting the text in place, redirect the pointer: at the `printf` call, `r0` holds the string address, so point `r0` at a replacement string you place in RAM. + +1. Arm the breakpoint at the `printf` call and hit it, exactly as in Step 14 steps 1-3. At the stop, `r0 = 0x100034a8`, `r2 = 0x00000000`, and `r3 = 0x40454000`. +2. Put the replacement string into free RAM at `0x20080000` from Binary Ninja's **Python console** (`Plugins -> Python Console`) — no command port needed: + ```python + dbg.write_memory(0x20080000, b"myvalue: %f\r\n\x00") + ``` + `dbg.write_memory(address, bytes)` is Binary Ninja's debugger memory-write API; it returns `True` on success. That writes the bytes `6d 79 76 61 6c 75 65 3a 20 25 66 0d 0a 00` = `"myvalue: %f\r\n\0"`. +3. Point `r0` at that string: + ```python + dbg.set_reg_value("r0", 0x20080000) + ``` + (Or right-click `r0` in the **Registers** widget, press `E`, type `0x20080000`, and press Enter.) +4. If you want the value hack too, set `r3` to `0x4058C000` as in Step 14. Then move the breakpoint past the call in the GUI (remove it at `0x10000244`, set one at `0x10000248`) and click **Resume**. The core runs `printf` with `r0` pointing at your RAM string and `r2:r3` holding `99.0`, so this iteration prints: + ``` + myvalue: 99.000000 + ``` + then stops at `0x10000248`. + +Like the value hack, this is **one iteration only**: the loop reloads `r0` (and `r2`/`r3`) from flash/literals on every pass, so the next line is `fav_num: 42.500000` again. The permanent version is the static patch in Step 18b. + +### Step 15: Why the hack reverts (and why we patch next) + +Press **Resume**. The loop branches back to `0x1000023e`, which copies `r4` and `r5` into `r2` and `r3` again. `r4` and `r5` were set once before the loop (`movs r4, #0`, `ldr r5, [pc, #12]`), so your `r3` edit is overwritten and the next line is `fav_num: 42.500000`. The live edit changed one iteration only. There is no variable in memory to change; the value is baked into the instruction/literal pool. To make `99.0` permanent we must patch the constant. That is the static pass. + +Press **Pause** to stop the output flood. + +### Step 15b: Kill the debugger and OpenOCD + +The live hack is done. Do this **before** the static pass. + +1. In the **Debugger** sidebar, click the **X** (**Kill**) (or **`Debugger -> Kill`**) to disconnect Binary Ninja. +2. **Kill does not stop the OpenOCD process** — `debug-server.sh` started it separately, and it keeps running and holding the probe. Stop it from the Binary Ninja console: + + **macOS / Linux:** + + ```python + import subprocess + subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # stop the debug server, free the probe + ``` + + **Windows:** + + ```python + import subprocess + subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # stop the debug server, free the probe + ``` + +3. Confirm nothing is left: `pgrep -fl openocd` (macOS/Linux) prints nothing. + +From a terminal it is the same: `pkill -TERM -f openocd`, or `Get-Process openocd | Stop-Process` on Windows. +--- + +## Part 4: Static — Resolve the Functions in Binary Ninja and Patch (Project 1) + +### Step 16: Resolve the functions in the Binary Ninja GUI + +We now name the functions in Binary Ninja using the ELF symbol map from Step 4. Binary Ninja loaded the raw `.bin` with **no symbols**, so every function shows as `sub_` — resolving means giving each one its real name and signature. + +Three keys do all the work: + +| Key | Binary Ninja action | Use it for | +| --- | --- | --- | +| `G` | Go to address | Jump to a function's address | +| `Y` | **Change Type** | Set the function's signature. The dialog shows the full prototype, so this sets the name *and* the type in one step. | +| `N` | Rename | Rename only, when you just want the name and not the type | + +For each function below: `G` to its address, then **`Y` (Change Type)** and type the prototype from the table. + +#### How to resolve a function in Binary Ninja (`Y`) + +`Y` is the **Change Type** key, and it is what actually resolves the function — it turns `void sub_10002f5c()` into `bool stdio_init_all(void)`. The Change Type dialog shows the full declaration (name and type), so typing the prototype sets both: + +1. `G` to the function's address. The cursor lands on the function. +2. Press **`Y`**. In the Change Type dialog, type the prototype from the table exactly — for example `bool stdio_init_all(void)` — and press Enter. + +The decompiler header then shows the real prototype, and calls to the function read cleanly instead of `sub_()`. `N` is only for renaming without touching the type; `Y` alone sets both the name and the type. + +If `Y` seems to do nothing, confirm the cursor is on the function, or right-click it and pick **Change Type...**. Binary Ninja parses what you type and silently keeps the old type if it does not parse, so glance at the header after each `Y`. + +#### Worked example: `main` + +1. Press `G`, type `0x10000234`, press Enter. The view jumps there; the cursor lands on `sub_10000234`. +2. Press **`Y`** (Change Type), type `int main(void)`, press Enter. That sets the name to `main` and the type to `int(void)`. + +> **Binary Ninja shows `int32_t` where Ghidra shows `int`.** After you set `int main(void)`, the decompiler header may read `int32_t main(void)`. That is the same type — on this platform `int` is 32 bits and Binary Ninja's parser normalises it to `int32_t`. Do not fight it; it is not an error. + +#### Worked example: `stdio_init_all` + +1. `G` -> `0x10002f5c`. +2. `Y` -> `bool stdio_init_all(void)`. + +It returns **`bool`**, not `void` — the ELF says `_Bool stdio_init_all(void)`. Our `main` ignores the return value, so the decompiler still reads cleanly. + +#### Worked example: `uart_init` + +1. `G` -> `0x10000e18`. +2. `Y` -> `uint uart_init(uart_inst_t *uart, uint baudrate)`. + +#### Worked example: `__wrap_printf` + +1. `G` -> `0x100030ec`. +2. `Y` -> `int __wrap_printf(const char *fmt, ...)`. Keep the `...` — `printf` is variadic. + +> **`printf` in our source is `__wrap_printf` in the binary.** The SDK links our `printf` calls to its `__wrap_printf` wrapper. + +#### Worked example: `_ftoa` (the double formatter) + +1. `G` -> `0x10001890`. +2. `Y` -> `unsigned int _ftoa(out_fct_type, char*, size_t, size_t, double, unsigned int, unsigned int, unsigned int)`. + +This is the function that actually turns the double into the `42.500000` text. It is why the `pico_double` `__aeabi_*` helpers are in the image at all. + +The rest of the chain is the same two keystrokes per function (`G`, then `Y`). This is **our code plus the library functions it actually calls** — not the whole SDK. `main` only calls `stdio_init_all` and `printf`, so we follow that chain down: `stdio_init_all` pulls in the stdio/UART setup, and `printf` lands in the SDK's `__wrap_printf`, which reaches the `pico_double` formatter. + +The call chain for this project: + +``` +main +├── stdio_init_all +│ └── stdio_uart_init ── gpio_set_function, uart_init, stdio_set_driver_enabled +│ └── uart_init ── clock_get_hz, busy_wait_us +└── __wrap_printf ── __wrap_vprintf + ├── vfctprintf ── _vsnprintf + │ ├── _ftoa / _etoa ── __wrap___aeabi_* (the pico_double helpers) + │ └── _ntoa_format / _out_rev + ├── stdio_out_chars_crlf + └── time_us_64 +``` + +**Project 1 — resolve every function in that chain:** + +| Address | Rename to (`N`) | Signature (`Y`) | +| ------- | --------------- | --------------- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | +| `0x100001e4` | `_init` | `void _init(void)` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | +| **`0x10000234`** | **`main`** | **`int main(void)`** | +| `0x10002ca8` | `vfctprintf` | `int vfctprintf(void (*)(char, void*), void*, const char*, va_list)` | +| `0x10002d04` | `exit` | `void exit(int)` | +| `0x10002d0c` | `runtime_init` | `void runtime_init(void)` | +| `0x10002d38` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | +| `0x10002e48` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | +| `0x10002f34` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | +| `0x10002f5c` | `stdio_init_all` | `bool stdio_init_all(void)` | +| `0x10003028` | `__wrap_vprintf` | `int __wrap_vprintf(const char*, va_list)` | +| `0x100030ec` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | +| `0x100032a8` | `stdio_uart_init` | `void stdio_uart_init(void)` | +| `0x100033e8` | `strlen` | `size_t strlen(const char*)` | +| `0x10000254` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | +| `0x10000e18` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | +| `0x10000da8` | `time_us_64` | `uint64_t time_us_64(void)` | + +**Project 1 — resolve the `pico_double` formatting helpers `printf` reaches:** + +| Address | Rename to (`N`) | Signature (`Y`) | +| ------- | --------------- | --------------- | +| `0x10001384` | `__wrap___aeabi_dadd` | `double __wrap___aeabi_dadd(double, double)` | +| `0x100013ac` | `__wrap___aeabi_dsub` | `double __wrap___aeabi_dsub(double, double)` | +| `0x100013d4` | `__wrap___aeabi_dmul` | `double __wrap___aeabi_dmul(double, double)` | +| `0x10001420` | `__wrap___aeabi_ddiv` | `double __wrap___aeabi_ddiv(double, double)` | +| `0x100014bc` | `__wrap___aeabi_i2d` | `double __wrap___aeabi_i2d(int)` | +| `0x100014e0` | `__wrap___aeabi_ui2d` | `double __wrap___aeabi_ui2d(unsigned)` | +| `0x10001504` | `__wrap___aeabi_d2iz` | `int __wrap___aeabi_d2iz(double)` | +| `0x10001528` | `__wrap___aeabi_d2uiz` | `unsigned __wrap___aeabi_d2uiz(double)` | +| `0x1000154c` | `__wrap___aeabi_dcmpun` | `int __wrap___aeabi_dcmpun(double, double)` | +| `0x10001570` | `__wrap___aeabi_dcmplt` | `int __wrap___aeabi_dcmplt(double, double)` | +| `0x10001598` | `__wrap___aeabi_dcmple` | `int __wrap___aeabi_dcmple(double, double)` | +| `0x100015c0` | `__wrap___aeabi_dcmpge` | `int __wrap___aeabi_dcmpge(double, double)` | +| `0x100015e8` | `__wrap___aeabi_dcmpgt` | `int __wrap___aeabi_dcmpgt(double, double)` | +| `0x1000160c` | `_out_rev` | `unsigned _out_rev(out_fct_type, char*, size_t, size_t, const char*, size_t, unsigned, unsigned)` | +| `0x100016a8` | `_ntoa_format` | `unsigned _ntoa_format(out_fct_type, char*, size_t, size_t, char*, size_t, bool, unsigned, unsigned, unsigned, unsigned)` | +| `0x1000187c` | `_out_char` | `void _out_char(char, void*, size_t, size_t)` | +| `0x10001890` | `_ftoa` | `unsigned int _ftoa(out_fct_type, char*, size_t, size_t, double, unsigned int, unsigned int, unsigned int)` | +| `0x10001d50` | `_etoa` | `unsigned int _etoa(out_fct_type, char*, size_t, size_t, double, unsigned int, unsigned int, unsigned int)` | +| `0x100022c4` | `_vsnprintf` | `int _vsnprintf(out_fct_type, char*, size_t, const char*, va_list)` | +| `0x10000dbc` | `busy_wait_us` | `void busy_wait_us(uint64_t)` | +| `0x10000fec` | `clock_get_hz` | `unsigned long clock_get_hz(clock_handle_t)` | + +> **A `void` return type may not stick — here is the fix.** Binary Ninja treats `void` as low-confidence, and its analysis can override it with an inferred type — most often `int32_t` on this 32-bit target. It is most visible on `_reset_handler` (a hand-written assembly entry that never returns normally), but it can happen to **any** function whose return type Binary Ninja thinks it can infer. +> +> Setting the full signature with `Y` reproduces the unwanted `int32_t`, and `fn.return_type = ...` fails too. What works is the **return-value** setter: +> +> ```python +> from binaryninja import ReturnValue, Type +> fn = bv.get_function_at(0x1000015c) +> if fn is not None: +> fn.return_value = ReturnValue(Type.void()) +> ``` +> +> That holds `_reset_handler` at `void` even after reanalysis. If it still will not stick, leave it — it does not affect the rest of the lesson. + +> **`__wrap_printf` is the real symbol.** `printf` in our source compiles to the SDK's `__wrap_printf` (which forwards to `__wrap_vprintf`). Rename it `printf` if you prefer the lesson's shorthand, but `__wrap_printf` is what the ELF says. +> +> **`stdio_init_all` returns `bool`, not `void`** — `_Bool stdio_init_all(void)` in the ELF. + +> **Shortcut — resolves name *and* type for every function.** Instead of doing `N` + `Y` by hand, paste this into Binary Ninja's Python console (`Plugins -> Python Console`). It sets each function's name and signature programmatically: +> +> ```python +> from binaryninja import Symbol, SymbolType +> # The raw .bin has no headers, so these SDK types don't exist. set_user_type() +> # re-parses each signature as C, so an undefined name raises +> # "SyntaxError: unknown type name '...'". Define them first. +> sdk = bv.parse_types_from_string(""" +> typedef unsigned int uint; +> typedef char* va_list; +> typedef unsigned long clock_handle_t; +> typedef void (*out_fct_type)(char, void*, size_t, size_t); +> struct stdio_driver; +> typedef struct stdio_driver stdio_driver_t; +> struct uart_inst; +> typedef struct uart_inst uart_inst_t; +> enum gpio_function { +> GPIO_FUNC_XIP = 0, GPIO_FUNC_SPI = 1, GPIO_FUNC_UART = 2, GPIO_FUNC_I2C = 3, +> GPIO_FUNC_PWM = 4, GPIO_FUNC_SIO = 5, GPIO_FUNC_PIO0 = 6, GPIO_FUNC_PIO1 = 7, +> GPIO_FUNC_GPCK = 8, GPIO_FUNC_USB = 9, GPIO_FUNC_NULL = 0x1f, +> }; +> typedef enum gpio_function gpio_function_t; +> """) +> for name, ty in sdk.types.items(): +> bv.define_user_type(name, ty) +> +> # address: (name, signature) +> funcs = { +> 0x1000015c: ("_reset_handler", "void _reset_handler(void)"), +> 0x10000186: ("platform_entry", "void platform_entry(void)"), +> 0x1000019a: ("data_cpy", "void data_cpy(void*, void*, void*)"), +> 0x100001e4: ("_init", "void _init(void)"), +> 0x10000210: ("frame_dummy", "void frame_dummy(void)"), +> 0x10000234: ("main", "int main(void)"), +> 0x10002ca8: ("vfctprintf", "int vfctprintf(void (*)(char, void*), void*, const char*, va_list)"), +> 0x10002d04: ("exit", "void exit(int)"), +> 0x10002d0c: ("runtime_init", "void runtime_init(void)"), +> 0x10002d38: ("stdio_out_chars_crlf", "void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)"), +> 0x10002e48: ("stdio_put_string", "int stdio_put_string(const char*, int, bool, bool)"), +> 0x10002f34: ("stdio_set_driver_enabled", "void stdio_set_driver_enabled(stdio_driver_t*, bool)"), +> 0x10002f5c: ("stdio_init_all", "bool stdio_init_all(void)"), +> 0x10003028: ("__wrap_vprintf", "int __wrap_vprintf(const char*, va_list)"), +> 0x100030ec: ("__wrap_printf", "int __wrap_printf(const char*, ...)"), +> 0x100032a8: ("stdio_uart_init", "void stdio_uart_init(void)"), +> 0x100033e8: ("strlen", "size_t strlen(const char*)"), +> 0x10000254: ("gpio_set_function", "void gpio_set_function(uint, gpio_function_t)"), +> 0x10000e18: ("uart_init", "uint uart_init(uart_inst_t*, uint)"), +> 0x10000da8: ("time_us_64", "uint64_t time_us_64(void)"), +> 0x10001384: ("__wrap___aeabi_dadd", "double __wrap___aeabi_dadd(double, double)"), +> 0x100013ac: ("__wrap___aeabi_dsub", "double __wrap___aeabi_dsub(double, double)"), +> 0x100013d4: ("__wrap___aeabi_dmul", "double __wrap___aeabi_dmul(double, double)"), +> 0x10001420: ("__wrap___aeabi_ddiv", "double __wrap___aeabi_ddiv(double, double)"), +> 0x100014bc: ("__wrap___aeabi_i2d", "double __wrap___aeabi_i2d(int)"), +> 0x100014e0: ("__wrap___aeabi_ui2d", "double __wrap___aeabi_ui2d(unsigned)"), +> 0x10001504: ("__wrap___aeabi_d2iz", "int __wrap___aeabi_d2iz(double)"), +> 0x10001528: ("__wrap___aeabi_d2uiz", "unsigned __wrap___aeabi_d2uiz(double)"), +> 0x1000154c: ("__wrap___aeabi_dcmpun", "int __wrap___aeabi_dcmpun(double, double)"), +> 0x10001570: ("__wrap___aeabi_dcmplt", "int __wrap___aeabi_dcmplt(double, double)"), +> 0x10001598: ("__wrap___aeabi_dcmple", "int __wrap___aeabi_dcmple(double, double)"), +> 0x100015c0: ("__wrap___aeabi_dcmpge", "int __wrap___aeabi_dcmpge(double, double)"), +> 0x100015e8: ("__wrap___aeabi_dcmpgt", "int __wrap___aeabi_dcmpgt(double, double)"), +> 0x1000160c: ("_out_rev", "unsigned _out_rev(out_fct_type, char*, size_t, size_t, const char*, size_t, unsigned, unsigned)"), +> 0x100016a8: ("_ntoa_format", "unsigned _ntoa_format(out_fct_type, char*, size_t, size_t, char*, size_t, bool, unsigned, unsigned, unsigned, unsigned)"), +> 0x1000187c: ("_out_char", "void _out_char(char, void*, size_t, size_t)"), +> 0x10001890: ("_ftoa", "unsigned int _ftoa(out_fct_type, char*, size_t, size_t, double, unsigned int, unsigned int, unsigned int)"), +> 0x10001d50: ("_etoa", "unsigned int _etoa(out_fct_type, char*, size_t, size_t, double, unsigned int, unsigned int, unsigned int)"), +> 0x100022c4: ("_vsnprintf", "int _vsnprintf(out_fct_type, char*, size_t, const char*, va_list)"), +> 0x10000dbc: ("busy_wait_us", "void busy_wait_us(uint64_t)"), +> 0x10000fec: ("clock_get_hz", "unsigned long clock_get_hz(clock_handle_t)"), +> } +> for addr, (name, sig) in funcs.items(): +> bv.define_user_symbol(Symbol(SymbolType.FunctionSymbol, addr, name)) +> f = bv.get_function_at(addr) +> if f is not None: +> f.set_user_type(sig) +> ``` +> +> SDK type names (`stdio_driver_t`, `gpio_function_t`, `uart_inst_t`, plus `uint`, `va_list`, `out_fct_type`, and `clock_handle_t`) are **not** in the raw `.bin`. `set_user_type` re-parses each signature as C, so an undefined name raises `SyntaxError: unknown type name '...'` and stops the loop — it is not harmless. The `sdk` block above defines them first (an opaque `struct`/`enum`/`typedef` is enough to parse). If you add a function that uses another SDK type, add a definition for it to that block too. + +### Step 17: Read `main` in the decompiler + +Open the **Decompiler** view on `main`. It reads: + +```c +int32_t main(void) +{ + stdio_init_all(); + do + { + __wrap_printf("fav_num: %f\r\n", 0, 0x40454000); + } while (true); +} +``` + +The trailing pair is the promoted `double`: `r2 = 0`, `r3 = 0x40454000`. Binary Ninja already knows the calling convention, so once `__wrap_printf` is typed `int __wrap_printf(const char*, ...)`, the pair is shown as data. Now make the hack permanent. + +### Step 18: Patch `0x40454000` to `0x4058C000` in the GUI + +Go to `0x1000024c`: + +```asm +1000024c: 40454000 .word 0x40454000 +``` + +That word is the high half of the `double` `42.5`. Its bytes, little-endian, are `00 40 45 40`. Change them to `00 c0 58 40`, the high half of `99.0`: + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x000e` | `0x1000024c` | `00 40 45 40` | `00 c0 58 40` | high word of `42.5` -> `99.0` | + +**Option A — Hex view:** + +1. Switch to the **Hex** view (`View -> Hex`). +2. Toggle the lock off so editing is enabled. +3. Go to `0x1000024c` and change `00 40 45 40` to `00 c0 58 40`. +4. Return to the linear view, right-click the function -> `Reanalyze`. + +**Option B — Python console:** + +```python +bv.write(0x1000024c, bytes.fromhex("00c05840")) +print(bv.read(0x1000024c, 4)) # -> b'\x00\xc0X@' +``` + +After reanalysis the literal reads `0x4058C000`, and the decompiler shows `__wrap_printf("fav_num: %f\r\n", 0, 0x4058c000)`. + +### Step 18b: Patch the string `fav_num:` to `myvalue:` in the GUI + +The format string `"fav_num: %f\r\n"` starts at `0x100034a8`. Its first eight bytes are `66 61 76 5f 6e 75 6d 3a` (`fav_num:`). Change them to `6d 79 76 61 6c 75 65 3a` (`myvalue:`), leaving the ` %f\r\n` tail untouched, so the line prints `myvalue: 99.000000`. + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x000e` | `0x100034a8` | `66 61 76 5f 6e 75 6d 3a` | `6d 79 76 61 6c 75 65 3a` | `fav_num:` -> `myvalue:` | + +**Option A — Hex view:** + +1. Switch to the **Hex** view (`View -> Hex`). +2. Go to `0x100034a8` and change the eight bytes `66 61 76 5f 6e 75 6d 3a` to `6d 79 76 61 6c 75 65 3a`. +3. Return to the linear view and reanalyze. + +**Option B — Python console:** + +```python +bv.write(0x100034a8, b"myvalue:") +print(bv.read(0x100034a8, 15)) # -> b'myvalue: %f\r\n\x00' +``` + +Keep the replacement exactly eight bytes. If you use a shorter string you must pad it, or `%f` shifts and `printf` reads the wrong argument. A longer string would overwrite the ` %f` tail. + +### Step 19: Export the patched `.bin` + +```python +import os +seg = next(s for s in bv.segments if s.data_length) # the loadable image segment +data = bv.read(seg.start, seg.data_length) # base + size come from the view itself +out = os.path.join(os.path.join(root, "0x000e_floating-point-data-type", "build"), "0x000e_floating-point-data-type-h.bin") +open(out, "wb").write(data) +print(len(data), out) # -> 15308 /.../build/0x000e_floating-point-data-type-h.bin +``` + +Where the two numbers come from — nothing is hardcoded: + +- **`seg.start`** is the image base Binary Ninja loaded the `.bin` at (`0x10000000`), the same value you pass to `uf2conv --base`. +- **`seg.data_length`** is the segment's size in the file (`0x3bcc` = 15308). Exactly one segment carries data (the image); every peripheral and synthetic segment has `data_length == 0`, so `next(...)` picks the image. +- Reading `seg.start` for `seg.data_length` bytes therefore grabs exactly the image. + +Two gotchas this avoids: + +- **No relative path.** Binary Ninja's Python console runs with a read-only working directory (inside the app bundle), so `open("0x000e_floating-point-data-type-h.bin", "wb")` fails with `OSError: [Errno 30] Read-only file system`. `root` (from `~/.embedded-hacking-repo`, Step 3) is the repo, so the file is written into the project's `build/` — no machine-specific path and no database needed. +- **Read the image, not the whole view.** `bv.read(bv.start, bv.length)` spans the entire mapped range, which is not the image. The segment's `data_length` is the image size. + +A different size means you exported a partial view. + +### Step 20: Convert to UF2 + +Run from the project directory: + +**macOS Apple Silicon / Linux x64:** + +```bash +python3 ../uf2conv.py 0x000e_floating-point-data-type-h.bin \ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +**Windows x64:** + +```cmd +python ..\uf2conv.py 0x000e_floating-point-data-type-h.bin ^ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +> **Or convert from the Binary Ninja console** — it is a normal Python interpreter, so you never have to leave the app. `chdir` to a writable directory first (the default one is read-only), then run the script: +> +> ```python +> import os, sys, runpy +> os.chdir(os.path.join(root, "0x000e_floating-point-data-type", "build")) # the project build dir (writable) +> sys.argv = ["uf2conv.py", "0x000e_floating-point-data-type-h.bin", +> "--base", "0x10000000", "--family", "0xe48bff59", "--output", "hacked.uf2"] +> runpy.run_path("../../uf2conv.py", run_name="__main__") # path to your uf2conv.py +> ``` +> +> This writes `hacked.uf2` next to the `.bin`, ready to drag onto the Pico. + +### Step 21: Flash and verify `fav_num: 99.000000` + +Hold **BOOTSEL**, plug in the Pico 2, and drag `hacked.uf2` onto the **`RP2350`** drive. Open the serial monitor: + +``` +myvalue: 99.000000 +myvalue: 99.000000 +myvalue: 99.000000 +... +``` + +**42.5 became 99.0, permanently, with one 32-bit word changed and no source code.** + +> **Faster: flash over the Debug Probe (no BOOTSEL).** The repo's `flash.sh` writes the raw `.bin` straight into XIP flash over SWD (`program 0x10000000 verify reset exit`), so you never touch BOOTSEL or a UF2. Run it from a terminal (`./flash.sh `), or from the Binary Ninja console **without freezing it** — use `subprocess.Popen`, which returns immediately, and send OpenOCD's output to a log file. (`subprocess.run` blocks the console until the flash finishes; do not use it here.) +> +> ```python +> import os, subprocess +> root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +> bin_path = os.path.join(os.path.join(root, "0x000e_floating-point-data-type", "build"), "0x000e_floating-point-data-type-h.bin") +> log = os.path.join(os.path.join(root, "0x000e_floating-point-data-type", "build"), "flash.log") +> subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +> p = subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], +> stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +> print("flashing in the background; log:", log) +> ``` +> +> The `pkill` frees the probe first; on Windows use `subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"])`. +> +> The console is free the moment this returns. Check it with `print(p.poll())` (`None` = still running, `0` = done) or read `flash.log` — success ends with `** Verified OK **`. +> +> The same non-blocking form without the script: +> +> ```python +> import os, subprocess +> ocd = os.path.expanduser("~/.pico-sdk/openocd/0.12.0+dev") +> bin_path = os.path.join(os.path.join(root, "0x000e_floating-point-data-type", "build"), "0x000e_floating-point-data-type-h.bin") +> log = os.path.join(os.path.join(root, "0x000e_floating-point-data-type", "build"), "flash.log") +> subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +> p = subprocess.Popen([f"{ocd}/openocd", "-s", f"{ocd}/scripts", +> "-f", "interface/cmsis-dap.cfg", "-f", "target/rp2350.cfg", +> "-c", "adapter speed 5000", +> "-c", f"program {bin_path} 0x10000000 verify reset exit"], +> stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +> print("flashing in the background; log:", log) +> ``` +> +> **The Debug Probe is single-owner.** If Binary Ninja is still attached (the `debug-server.sh` OpenOCD is running), the flash cannot grab the probe. Detach in Binary Ninja and stop that OpenOCD first: +> +> ```bash +> # macOS / Linux +> pkill -TERM -f openocd +> ``` +> ```powershell +> # Windows +> Get-Process openocd -ErrorAction SilentlyContinue | Stop-Process +> ``` +> +> Success looks like `Programming Finished` -> `Verified OK` -> `Resetting Target`. On Windows use `flash.ps1` (`.\flash.ps1 -Bin `) the same way. +--- + +## Part 5: Dynamic — Break at `main` and Hack Live (Project 2) + +### Step 22: Reflash Project 2 and reload Binary Ninja + +Part 4 left the Pico running the patched Project 1 image. Put the original Project 2 back and start a fresh session. + +1. Stop any running debug server so the flash script can use the probe: + + ```bash + # macOS / Linux + pkill -TERM -f openocd + ``` + ```powershell + # Windows + Get-Process openocd -ErrorAction SilentlyContinue | Stop-Process + ``` + +2. Flash the original Project 2 image: + + ```bash + # macOS / Linux + ./flash.sh 0x0011_double-floating-point-data-type/build/0x0011_double-floating-point-data-type.bin + ``` + ```powershell + # Windows + .\flash.ps1 -Bin 0x0011_double-floating-point-data-type\build\0x0011_double-floating-point-data-type.bin + ``` + + **Or do steps 1-2 from the Binary Ninja console** (the active view is still Project 1, so take the repo root from the marker file and point at the Project 2 `.bin`): + + **macOS / Linux:** + + ```python + import os, subprocess + root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) + bin_path = os.path.join(root, "0x0011_double-floating-point-data-type", "build", "0x0011_double-floating-point-data-type.bin") + log = os.path.join(os.path.dirname(bin_path), "flash.log") + subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first + subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) + print("flashing Project 2 in the background; log:", log) + ``` + + **Windows:** + + ```python + import os, subprocess + root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) + bin_path = os.path.join(root, "0x0011_double-floating-point-data-type", "build", "0x0011_double-floating-point-data-type.bin") + log = os.path.join(os.path.dirname(bin_path), "flash.log") + subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # free the probe first + subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "flash.ps1"), "-Bin", bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT) + print("flashing Project 2 in the background; log:", log) + ``` + +3. Start the debug server again (Step 10) and wait for `Listening on port 3333`. +4. Load Project 2 and save its database — see Step 22b. +5. Connect Binary Ninja again (Step 11): adapter **GDB MI**, IP `127.0.0.1`, port `3333`. + +Confirm the Pico prints `fav_num: 42.525250`. + +### Step 22b: Load Project 2 into Binary Ninja and save the database + +Exactly like Steps 7-8, but for Project 2. **Use `File -> Open with Options...`** (not plain `File -> Open`), select `0x0011_double-floating-point-data-type/build/0x0011_double-floating-point-data-type.bin`, and set: + +- **Architecture:** `thumb2` +- **Platform:** `thumb2` +- **Base Address:** `0x10000000` + +Click **Open**. Then press `G`, type `0x10000000`, and confirm the first two words: + +``` +0x10000000 0x20082000 initial stack pointer +0x10000004 0x1000015d reset vector (bit 0 = Thumb) +``` + +If you see data at `0x00000000`, close the tab and redo it with `Open with Options`. + +Save it with `File -> Save As...` as `0x0011_double-floating-point-data-type.bndb` (next to the `.bin`). From now on open the `.bndb`, not the `.bin`; save with `Cmd+S` / `Ctrl+S` after every rename or patch. + +> **Console equivalent:** +> ```python +> load("0x0011_double-floating-point-data-type/build/0x0011_double-floating-point-data-type.bin", +> options={"loader.imageBase": 0x10000000, "loader.platform": "thumb2"}) +> ``` + +Then resolve the functions for Project 2 the same way as Project 1 — Step 26. + +### Step 23: Break at `main` + +`main` is at `0x10000238` in this project. The GUI sets breakpoints fine (Step 13); the only caution is not to drive `reset run` from the port while Binary Ninja is attached (it desyncs Binary Ninja's view). Use `BP_ADDR`, which arms `main` before Binary Ninja connects: + +1. Stop the server (Ctrl-C), then start it parked at `main`: + ```bash + # macOS / Linux + BP_ADDR=0x10000238 ./debug-server.sh + ``` + ```powershell + # Windows + $env:BP_ADDR="0x10000238"; .\debug-server.ps1 + ``` + + **Or restart it from the Binary Ninja console** — kill any running server, then start it parked at `main`: + + **macOS / Linux:** + + ```python + import os, subprocess + root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() + log = os.path.join(root, "openocd.log") + subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # kill any running server first + subprocess.Popen([os.path.join(root, "debug-server.sh")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000238"), + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) + print("OpenOCD restarted parked at main; log:", log) + ``` + + **Windows:** + + ```python + import os, subprocess + root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() + log = os.path.join(root, "openocd.log") + subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # kill any running server first + subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "debug-server.ps1")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000238"), + stdout=open(log, "w"), stderr=subprocess.STDOUT) + print("OpenOCD restarted parked at main; log:", log) + ``` +2. Connect Binary Ninja (Step 11): adapter **GDB MI**, IP `127.0.0.1`, port `3333`. + +The target is already halted at `main` when Binary Ninja connects, and the sidebar reads `Stopped at 0x10000238`. + +> If you instead want to reach `main` on an already-connected session, you must Detach, send `reset run` from the port, then reconnect. Arming `main` and resetting while attached leaves the sidebar showing a stale address. + +`main` loads the whole 64-bit `double` from a single literal-pool address, then loops: copy the pair into `r2:r3` and call `printf`. The whole thing is one function: + +```asm +10000238: b538 push {r3, r4, r5, lr} +1000023a: a506 add r5, pc, #24 ; adr r5, 0x10000254 +1000023c: e9d5 4500 ldrd r4, r5, [r5] ; r4 = low word, r5 = high word +10000240: f002 fe90 bl 0x10002f64 ; stdio_init_all +10000244: 4622 mov r2, r4 +10000246: 462b mov r3, r5 +10000248: 4801 ldr r0, [pc, #4] ; -> 0x10000250 = 0x100034b0 (format string) +1000024a: f002 ff53 bl 0x100030f4 ; __wrap_printf +1000024e: e7f9 b.n 0x10000244 +10000250: 100034b0 .word 0x100034b0 +10000254: 645a1cac .word 0x645a1cac +10000258: 4045433b .word 0x4045433b +``` + +Notice the `ldrd r4, r5, [r5]` — a single 64-bit load from the literal pool at `0x10000254`, which fills **both** halves of the double at once. That is the structural difference from Project 1, where the low half was a register zero (`movs r4, #0`) and only the high half lived in the pool. + +Look at the **Registers** widget at `0x1000024a`: `r2 = 0x645A1CAC` (low) and `r3 = 0x4045433B` (high). Together that is `0x4045433B_645A1CAC`, the IEEE-754 encoding of `42.52525`. + +### Step 24: Inspect the double argument live + +The `double` crosses the ABI in two registers. At the `printf` call the pair is exactly the literal-pool word pair: + +| Register | Value | Role | +| -------- | ----- | ---- | +| `r2` | `0x645A1CAC` | low 32 bits of `42.52525` | +| `r3` | `0x4045433B` | high 32 bits of `42.52525` | + +Step Over through `0x10000244` (`mov r2, r4`) and `0x10000246` (`mov r3, r5`) and watch `r2`/`r3` populate from `r4`/`r5`. This is C's variadic rule in action: `printf`'s `%lf` receives a 64-bit `double`, and on this target a `double` argument travels in `r2:r3`. + +> **Why `r2:r3`, not `r0:r1`?** The first variadic argument goes after the named format pointer, so `printf(fmt, value)` puts `fmt` in `r0` and the promoted `double` in `r2:r3`. That leaves `r1` unused here, which is why the format string pointer is `r0` and the number is `r2:r3`. + +### Step 25: HACK IT LIVE — change the printed double + +1. Press `G`, go to `0x1000024a` (the `bl __wrap_printf`). +2. Set a **hardware execute** breakpoint at `0x1000024a` in the GUI (`Debugger -> Add Hardware Breakpoint...`; not `F2`). Note `0x1000024a` — Project 2's loop sits at a different address than Project 1's. +3. Click **Resume** in Binary Ninja. The target is already looping, so the breakpoint fires on the next pass. Binary Ninja stops with `r2 = 0x645A1CAC`, `r3 = 0x4045433B`. +4. Overwrite both halves with the encoding of `99.99` (`0x4058FF5C_28F5C28F`): + ```python + dbg.set_reg_value("r2", 0x28F5C28F) # low word of the 99.99 double + dbg.set_reg_value("r3", 0x4058FF5C) # high word of the 99.99 double + ``` + (Or right-click each register in the **Registers** widget, press `E`, type the hex value, and press Enter.) +5. **Move the breakpoint past the call.** `0x1000024e` is the instruction right after the `bl __wrap_printf`. Remove the breakpoint at `0x1000024a` and set a hardware breakpoint at `0x1000024e`, then click **Resume**. The core runs `printf` with `r2:r3 = 0x4058FF5C_28F5C28F` and stops at `0x1000024e`. (Not **Step Over** — it steps into the call on this symbol-less `.bin`, and a breakpoint left on the current PC re-traps the step; Step 13 explains both.) +6. Look at your serial monitor and the **Target** tab: + + ``` + fav_num: 99.990000 + ``` + +Press **Resume** and the next iteration prints `fav_num: 42.525250` again, because the loop reloads `r2`/`r3` from `r4`/`r5` each pass. The live hack is temporary; the static patch makes it permanent. + +### Step 25b: HACK THE STRING LIVE — change `fav_num:` to `myvalue:` + +Same idea as Project 1, different addresses. Here the format string is at `0x100034b0` and the `printf` call is at `0x1000024a`. + +1. Hit the breakpoint at `0x1000024a` as in Step 25. At the stop, `r0 = 0x100034b0`, `r2 = 0x645A1CAC`, `r3 = 0x4045433B`. +2. Write the replacement string to free RAM at `0x20080000` from the **Python console** (`dbg.write_memory` — no command port needed): + ```python + dbg.write_memory(0x20080000, b"myvalue: %lf\r\n\x00") + ``` + Bytes `6d 79 76 61 6c 75 65 3a 20 25 6c 66 0d 0a 00` = `"myvalue: %lf\r\n\0"`. +3. Point `r0` at that string: + ```python + dbg.set_reg_value("r0", 0x20080000) + ``` + (Or right-click `r0` in the **Registers** widget, press `E`, type `0x20080000`, and press Enter.) +4. If you want the value hack too, set `r2 = 0x28F5C28F` and `r3 = 0x4058FF5C` as in Step 25. Then move the breakpoint past the call in the GUI (remove it at `0x1000024a`, set one at `0x1000024e`) and click **Resume**. This iteration prints: + ``` + myvalue: 99.990000 + ``` + then stops at `0x1000024e`. One iteration only — the loop reloads `r0` (and `r2`/`r3`) each pass. The permanent version is the static patch in Step 28b. + +### Step 25c: Kill the debugger and OpenOCD + +Same as Step 15b: click the **X** (**Kill**) in the **Debugger** sidebar (or **`Debugger -> Kill`**), then stop OpenOCD from the Binary Ninja console: + +**macOS / Linux:** + +```python +import subprocess +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # stop the debug server, free the probe +``` + +**Windows:** + +```python +import subprocess +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # stop the debug server, free the probe +``` +--- + +## Part 6: Static — Resolve the Functions and Patch (Project 2) + +### Step 26: Resolve the functions in the Binary Ninja GUI + +Same two keys as Step 16 — `G` to the address, then `Y` (Change Type) to set the prototype — using the Project 2 ELF symbol map from Step 4. + +The mechanics are identical to Step 16, so here are the worked examples for the functions that are specific to this project. + +#### `main` + +1. `G` -> `0x10000238`. +2. `Y` -> `int main(void)` (Binary Ninja shows `int32_t main(void)` — the same 32-bit `int`). + +#### `stdio_uart_init` + +1. `G` -> `0x100032b0`. +2. `Y` -> `void stdio_uart_init(void)`. + +#### `_ftoa` and `_etoa` + +Same formatters as Project 1, eight bytes higher: `_ftoa` at `0x10001898` (`unsigned int _ftoa(out_fct_type, char*, size_t, size_t, double, unsigned int, unsigned int, unsigned int)`) and `_etoa` at `0x10001d58` (same prototype). + +#### `stdio_init_all` and `__wrap_printf` + +Same as Project 1, different addresses: `stdio_init_all` at `0x10002f64` (`bool stdio_init_all(void)`), and `__wrap_printf` at `0x100030f4` (`int __wrap_printf(const char *fmt, ...)`). + +Then work down the table the same way. + +Same idea as Project 1: **our code plus what it calls**, not the whole SDK. The call chain here is identical to Project 1 — because `%lf` and `%f` both route into the same `pico_double` formatter: + +``` +main +├── stdio_init_all +│ └── stdio_uart_init ── gpio_set_function, uart_init, stdio_set_driver_enabled +│ └── uart_init ── clock_get_hz, busy_wait_us +└── __wrap_printf ── __wrap_vprintf + ├── vfctprintf ── _vsnprintf + │ ├── _ftoa / _etoa ── __wrap___aeabi_* (the pico_double helpers) + │ └── _ntoa_format / _out_rev + ├── stdio_out_chars_crlf + └── time_us_64 +``` + +One thing in this project has **no separate call**, because the compiler emitted it as a single instruction: the 64-bit literal load is the `ldrd r4, r5, [r5]` you see inside `main`. There is no helper function to rename for it — it is two `.word`s in the pool at `0x10000254`. + +**Project 2 — resolve every function in that chain:** + +| Address | Rename to (`N`) | Signature (`Y`) | +| ------- | --------------- | --------------- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | +| `0x100001e4` | `_init` | `void _init(void)` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | +| **`0x10000238`** | **`main`** | **`int main(void)`** | +| `0x10002cb0` | `vfctprintf` | `int vfctprintf(void (*)(char, void*), void*, const char*, va_list)` | +| `0x10002d0c` | `exit` | `void exit(int)` | +| `0x10002d14` | `runtime_init` | `void runtime_init(void)` | +| `0x10002d40` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | +| `0x10002e50` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | +| `0x10002f3c` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | +| `0x10002f64` | `stdio_init_all` | `bool stdio_init_all(void)` | +| `0x10003030` | `__wrap_vprintf` | `int __wrap_vprintf(const char*, va_list)` | +| `0x100030f4` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | +| `0x100032b0` | `stdio_uart_init` | `void stdio_uart_init(void)` | +| `0x100033f0` | `strlen` | `size_t strlen(const char*)` | +| `0x1000025c` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | +| `0x10000e20` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | +| `0x10000db0` | `time_us_64` | `uint64_t time_us_64(void)` | + +**Project 2 — resolve the `pico_double` formatting helpers `printf` reaches:** + +| Address | Rename to (`N`) | Signature (`Y`) | +| ------- | --------------- | --------------- | +| `0x1000138c` | `__wrap___aeabi_dadd` | `double __wrap___aeabi_dadd(double, double)` | +| `0x100013b4` | `__wrap___aeabi_dsub` | `double __wrap___aeabi_dsub(double, double)` | +| `0x100013dc` | `__wrap___aeabi_dmul` | `double __wrap___aeabi_dmul(double, double)` | +| `0x10001428` | `__wrap___aeabi_ddiv` | `double __wrap___aeabi_ddiv(double, double)` | +| `0x100014c4` | `__wrap___aeabi_i2d` | `double __wrap___aeabi_i2d(int)` | +| `0x100014e8` | `__wrap___aeabi_ui2d` | `double __wrap___aeabi_ui2d(unsigned)` | +| `0x1000150c` | `__wrap___aeabi_d2iz` | `int __wrap___aeabi_d2iz(double)` | +| `0x10001530` | `__wrap___aeabi_d2uiz` | `unsigned __wrap___aeabi_d2uiz(double)` | +| `0x10001554` | `__wrap___aeabi_dcmpun` | `int __wrap___aeabi_dcmpun(double, double)` | +| `0x10001578` | `__wrap___aeabi_dcmplt` | `int __wrap___aeabi_dcmplt(double, double)` | +| `0x100015a0` | `__wrap___aeabi_dcmple` | `int __wrap___aeabi_dcmple(double, double)` | +| `0x100015c8` | `__wrap___aeabi_dcmpge` | `int __wrap___aeabi_dcmpge(double, double)` | +| `0x100015f0` | `__wrap___aeabi_dcmpgt` | `int __wrap___aeabi_dcmpgt(double, double)` | +| `0x10001614` | `_out_rev` | `unsigned _out_rev(out_fct_type, char*, size_t, size_t, const char*, size_t, unsigned, unsigned)` | +| `0x100016b0` | `_ntoa_format` | `unsigned _ntoa_format(out_fct_type, char*, size_t, size_t, char*, size_t, bool, unsigned, unsigned, unsigned, unsigned)` | +| `0x10001884` | `_out_char` | `void _out_char(char, void*, size_t, size_t)` | +| `0x10001898` | `_ftoa` | `unsigned int _ftoa(out_fct_type, char*, size_t, size_t, double, unsigned int, unsigned int, unsigned int)` | +| `0x10001d58` | `_etoa` | `unsigned int _etoa(out_fct_type, char*, size_t, size_t, double, unsigned int, unsigned int, unsigned int)` | +| `0x100022cc` | `_vsnprintf` | `int _vsnprintf(out_fct_type, char*, size_t, const char*, va_list)` | +| `0x10000dc4` | `busy_wait_us` | `void busy_wait_us(uint64_t)` | +| `0x10000ff4` | `clock_get_hz` | `unsigned long clock_get_hz(clock_handle_t)` | + +Python console shortcut (resolves name **and** type): + +```python +from binaryninja import Symbol, SymbolType +# The raw .bin has no headers, so these SDK types don't exist. set_user_type() +# re-parses each signature as C, so an undefined name raises +# "SyntaxError: unknown type name '...'". Define them first. +sdk = bv.parse_types_from_string(""" +typedef unsigned int uint; +typedef char* va_list; +typedef unsigned long clock_handle_t; +typedef void (*out_fct_type)(char, void*, size_t, size_t); +struct stdio_driver; +typedef struct stdio_driver stdio_driver_t; +struct uart_inst; +typedef struct uart_inst uart_inst_t; +enum gpio_function { + GPIO_FUNC_XIP = 0, GPIO_FUNC_SPI = 1, GPIO_FUNC_UART = 2, GPIO_FUNC_I2C = 3, + GPIO_FUNC_PWM = 4, GPIO_FUNC_SIO = 5, GPIO_FUNC_PIO0 = 6, GPIO_FUNC_PIO1 = 7, + GPIO_FUNC_GPCK = 8, GPIO_FUNC_USB = 9, GPIO_FUNC_NULL = 0x1f, +}; +typedef enum gpio_function gpio_function_t; +""") +for name, ty in sdk.types.items(): + bv.define_user_type(name, ty) + +# address: (name, signature) +funcs = { + 0x1000015c: ("_reset_handler", "void _reset_handler(void)"), + 0x10000186: ("platform_entry", "void platform_entry(void)"), + 0x1000019a: ("data_cpy", "void data_cpy(void*, void*, void*)"), + 0x100001e4: ("_init", "void _init(void)"), + 0x10000210: ("frame_dummy", "void frame_dummy(void)"), + 0x10000238: ("main", "int main(void)"), + 0x10002cb0: ("vfctprintf", "int vfctprintf(void (*)(char, void*), void*, const char*, va_list)"), + 0x10002d0c: ("exit", "void exit(int)"), + 0x10002d14: ("runtime_init", "void runtime_init(void)"), + 0x10002d40: ("stdio_out_chars_crlf", "void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)"), + 0x10002e50: ("stdio_put_string", "int stdio_put_string(const char*, int, bool, bool)"), + 0x10002f3c: ("stdio_set_driver_enabled", "void stdio_set_driver_enabled(stdio_driver_t*, bool)"), + 0x10002f64: ("stdio_init_all", "bool stdio_init_all(void)"), + 0x10003030: ("__wrap_vprintf", "int __wrap_vprintf(const char*, va_list)"), + 0x100030f4: ("__wrap_printf", "int __wrap_printf(const char*, ...)"), + 0x100032b0: ("stdio_uart_init", "void stdio_uart_init(void)"), + 0x100033f0: ("strlen", "size_t strlen(const char*)"), + 0x1000025c: ("gpio_set_function", "void gpio_set_function(uint, gpio_function_t)"), + 0x10000e20: ("uart_init", "uint uart_init(uart_inst_t*, uint)"), + 0x10000db0: ("time_us_64", "uint64_t time_us_64(void)"), + 0x1000138c: ("__wrap___aeabi_dadd", "double __wrap___aeabi_dadd(double, double)"), + 0x100013b4: ("__wrap___aeabi_dsub", "double __wrap___aeabi_dsub(double, double)"), + 0x100013dc: ("__wrap___aeabi_dmul", "double __wrap___aeabi_dmul(double, double)"), + 0x10001428: ("__wrap___aeabi_ddiv", "double __wrap___aeabi_ddiv(double, double)"), + 0x100014c4: ("__wrap___aeabi_i2d", "double __wrap___aeabi_i2d(int)"), + 0x100014e8: ("__wrap___aeabi_ui2d", "double __wrap___aeabi_ui2d(unsigned)"), + 0x1000150c: ("__wrap___aeabi_d2iz", "int __wrap___aeabi_d2iz(double)"), + 0x10001530: ("__wrap___aeabi_d2uiz", "unsigned __wrap___aeabi_d2uiz(double)"), + 0x10001554: ("__wrap___aeabi_dcmpun", "int __wrap___aeabi_dcmpun(double, double)"), + 0x10001578: ("__wrap___aeabi_dcmplt", "int __wrap___aeabi_dcmplt(double, double)"), + 0x100015a0: ("__wrap___aeabi_dcmple", "int __wrap___aeabi_dcmple(double, double)"), + 0x100015c8: ("__wrap___aeabi_dcmpge", "int __wrap___aeabi_dcmpge(double, double)"), + 0x100015f0: ("__wrap___aeabi_dcmpgt", "int __wrap___aeabi_dcmpgt(double, double)"), + 0x10001614: ("_out_rev", "unsigned _out_rev(out_fct_type, char*, size_t, size_t, const char*, size_t, unsigned, unsigned)"), + 0x100016b0: ("_ntoa_format", "unsigned _ntoa_format(out_fct_type, char*, size_t, size_t, char*, size_t, bool, unsigned, unsigned, unsigned, unsigned)"), + 0x10001884: ("_out_char", "void _out_char(char, void*, size_t, size_t)"), + 0x10001898: ("_ftoa", "unsigned int _ftoa(out_fct_type, char*, size_t, size_t, double, unsigned int, unsigned int, unsigned int)"), + 0x10001d58: ("_etoa", "unsigned int _etoa(out_fct_type, char*, size_t, size_t, double, unsigned int, unsigned int, unsigned int)"), + 0x100022cc: ("_vsnprintf", "int _vsnprintf(out_fct_type, char*, size_t, const char*, va_list)"), + 0x10000dc4: ("busy_wait_us", "void busy_wait_us(uint64_t)"), + 0x10000ff4: ("clock_get_hz", "unsigned long clock_get_hz(clock_handle_t)"), +} +for addr, (name, sig) in funcs.items(): + bv.define_user_symbol(Symbol(SymbolType.FunctionSymbol, addr, name)) + f = bv.get_function_at(addr) + if f is not None: + f.set_user_type(sig) +``` + +The decompiler now shows `main` loading the `double` pair and looping. We make two changes: + +- **Change the printed value from `42.52525` to `99.99`** by patching **both** literal words. +- **Rename the label** `fav_num:` to `myvalue:` by patching the format string. + +### Step 27: Patch 1 — the low word `0x645A1CAC` to `0x28F5C28F` + +`99.99` is `0x4058FF5C_28F5C28F`, so the low word changes from `0x645A1CAC` to `0x28F5C28F`. At `0x10000254` the stored bytes are `ac 1c 5a 64`; change them to `8f c2 f5 28`: + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x0011` | `0x10000254` | `ac 1c 5a 64` | `8f c2 f5 28` | low word of `42.52525` -> `99.99` | + +**Hex view:** lock off, go to `0x10000254`, change `ac 1c 5a 64` to `8f c2 f5 28`, reanalyze. **Or the Python console:** + +```python +bv.write(0x10000254, bytes.fromhex("8fc2f528")) +print(bv.read(0x10000254, 4)) # -> b'\x8f\xc2\xf5(' +``` + +### Step 28: Patch 2 — the high word `0x4045433B` to `0x4058FF5C` + +At `0x10000258` the stored bytes are `3b 43 45 40`; change them to `5c ff 58 40`: + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x0011` | `0x10000258` | `3b 43 45 40` | `5c ff 58 40` | high word of `42.52525` -> `99.99` | + +**Hex view:** go to `0x10000258`, change `3b 43 45 40` to `5c ff 58 40`, reanalyze. **Or the Python console:** + +```python +bv.write(0x10000258, bytes.fromhex("5cff5840")) +for addr in (0x10000254, 0x10000258): + print(hex(addr), bv.read(addr, 4).hex()) +# -> 0x10000254 8fc2f528 +# -> 0x10000258 5cff5840 +``` + +> **Both words are required.** `42.52525` has a repeating binary fraction, so its low word is non-zero (`0x645A1CAC`). Patching only the high word leaves the low 20 mantissa bits from `0.52525`, and `printf` prints a wrong hybrid. Compare Project 1, where `42.5` is exact and the low word was already `0x00000000`, so one word sufficed. + +### Step 28b: Patch the string `fav_num:` to `myvalue:` + +The format string starts at `0x100034b0`; change its first eight bytes `66 61 76 5f 6e 75 6d 3a` (`fav_num:`) to `6d 79 76 61 6c 75 65 3a` (`myvalue:`): + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x0011` | `0x100034b0` | `66 61 76 5f 6e 75 6d 3a` | `6d 79 76 61 6c 75 65 3a` | `fav_num:` -> `myvalue:` | + +```python +bv.write(0x100034b0, b"myvalue:") +print(bv.read(0x100034b0, 15)) # -> b'myvalue: %lf\r\n\x00' +``` + +Exactly eight bytes, same rule as Project 1: a shorter string must be padded, a longer one overwrites the ` %lf` tail. + +### Step 29: Export, convert, and flash + +```python +import os +seg = next(s for s in bv.segments if s.data_length) # the loadable image segment +data = bv.read(seg.start, seg.data_length) # base + size from the view itself +out = os.path.join(os.path.join(root, "0x0011_double-floating-point-data-type", "build"), "0x0011_double-floating-point-data-type-h.bin") +open(out, "wb").write(data) +print(len(data), out) # -> 15324 /.../build/0x0011_double-floating-point-data-type-h.bin +``` + +Same as Project 1: `seg.start` is the load base and `seg.data_length` is the image size (here `0x3bdc` = 15324) — both read from the view, and no relative path (the console's CWD is read-only). + +**macOS Apple Silicon / Linux x64:** + +```bash +python3 ../uf2conv.py 0x0011_double-floating-point-data-type-h.bin \ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +**Windows x64:** + +```cmd +python ..\uf2conv.py 0x0011_double-floating-point-data-type-h.bin ^ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +Or run the conversion from the Binary Ninja console, exactly as in Step 20 (`os.chdir` to the build dir, then `runpy.run_path("../../uf2conv.py", run_name="__main__")` with `sys.argv` set to the arguments above). + +Hold **BOOTSEL**, plug in the Pico 2, drag `hacked.uf2` onto the **`RP2350`** drive. Or flash the `.bin` over the Debug Probe with SWD — no BOOTSEL — from the console, exactly as in Step 21 (stop any running OpenOCD first, and use `Popen`, not `run`, so the console is not blocked): + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +bin_path = os.path.join(os.path.join(root, "0x0011_double-floating-point-data-type", "build"), "0x0011_double-floating-point-data-type-h.bin") +log = os.path.join(os.path.join(root, "0x0011_double-floating-point-data-type", "build"), "flash.log") +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +p = subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("flashing in the background; log:", log) +``` + +### Step 30: Verify + +Open the serial monitor: + +``` +myvalue: 99.990000 +myvalue: 99.990000 +myvalue: 99.990000 +... +``` + +**We changed the printed value and relabeled the line, with ten bytes and no source code.** `42.52525` became `99.99` because both halves of the double moved together. +--- + +## Cheatsheet + +### Binary Ninja GUI actions + +| Action | How | +| ------ | --- | +| Go to address | `G` | +| Rename function/symbol | `N` | +| Set type or signature | `Y` | +| Add comment | `;` | +| Open Hex view | `View -> Hex` | +| Enable hex editing | Toggle the lock in the status bar | +| Reanalyze after a patch | Right-click function -> `Reanalyze` | +| Edit a register live | `dbg.set_reg_value("r3", 0x4058C000)` in the Python console (or right-click the register, press `E`, type hex, Enter) | +| Write a RAM string live | `dbg.write_memory(0x20080000, b"myvalue: %f\r\n\x00")` | +| Set a breakpoint | `Debugger -> Add Hardware Breakpoint...` (hardware execute). Do **not** use `F2` — software breakpoints cannot be written to read-only flash. | +| Move a breakpoint | Remove it and set it at the new address in the GUI (command-port fallback: `rbp ` then `bp 2 hw`) | +| Confirm what is armed | The **Breakpoints** widget lists it (command-port fallback: `mdw 0xE0002000 8`, each armed breakpoint shows as ``) | +| Apply the ELF symbol map | Paste the Python snippet from Step 16 / 26 into the Python Console | + +### OpenOCD server and reset + +The server runs with `gdb_breakpoint_override hard` so that flash-writes are never attempted. Breakpoints in this lab are set in the Binary Ninja GUI through the **GDB MI** adapter (Step 13). The command-port rows below are the fallback if you use the **GDB RSP** adapter instead. + +| Action | Command | +| ------ | ------- | +| Connect to the OpenOCD prompt (fallback) | `nc 127.0.0.1 4444` (or `telnet 127.0.0.1 4444`) | +| Reset and run (command port) | `reset run` | +| Check core state (command port) | `targets` | +| Set a breakpoint in the GUI | `Debugger -> Add Hardware Breakpoint...` (hardware execute; `F2` software breakpoints do not work on flash) | +| (fallback) Add a breakpoint without the GUI | `bp 2 hw` | +| Remove one breakpoint | `rbp ` — **address only, no length, no `hw`** | +| Remove every breakpoint | `rbp all` | +| Start the server parked at `main` | macOS/Linux: `BP_ADDR=0x10000234 ./debug-server.sh` (Project 2: `0x10000238`) — Windows: `$env:BP_ADDR="0x10000234"; .\debug-server.ps1` (one-shot) | +| Start the server parked in the loop | macOS/Linux: `BP_ADDR=0x10000244 ./debug-server.sh` — Windows: `$env:BP_ADDR="0x10000244"; .\debug-server.ps1` (`0x1000024a` for Project 2) | +| Break on the loop in a running target | set a hardware breakpoint in the GUI at the loop address, then **Resume** — repeatable | +| Make Binary Ninja stepping work | `rp2350.dap.core0 configure -rtos none` (already in the scripts) | +| Step without re-trapping | move the breakpoint off the current PC first, then **Step Into**/**Step Over** | +| Reset without desyncing Binary Ninja | **Detach**, `reset run` on the port, reconnect — never `reset run` while attached | + +### Every address and byte we changed + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x000e` | `0x1000024c` | `00 40 45 40` | `00 c0 58 40` | high word of the double: `42.5` -> `99.0` | +| `0x0011` | `0x10000254` | `ac 1c 5a 64` | `8f c2 f5 28` | low word of the double: `42.52525` -> `99.99` | +| `0x0011` | `0x10000258` | `3b 43 45 40` | `5c ff 58 40` | high word of the double: `42.52525` -> `99.99` | +| `0x000e` | `0x100034a8` | `66 61 76 5f 6e 75 6d 3a` | `6d 79 76 61 6c 75 65 3a` | string prints `myvalue:` instead of `fav_num:` | +| `0x0011` | `0x100034b0` | `66 61 76 5f 6e 75 6d 3a` | `6d 79 76 61 6c 75 65 3a` | string prints `myvalue:` instead of `fav_num:` | + +### IEEE 754 Quick Reference for the Values in This Lesson + +| Value | Double Hex | High Word (`r3`) | Low Word (`r2`) | +| ----- | ---------- | ---------------- | --------------- | +| `42.5` | `0x4045400000000000` | `0x40454000` | `0x00000000` | +| `42.52525` | `0x4045433B645A1CAC` | `0x4045433B` | `0x645A1CAC` | +| `99.0` | `0x4058C00000000000` | `0x4058C000` | `0x00000000` | +| `99.99` | `0x4058FF5C28F5C28F` | `0x4058FF5C` | `0x28F5C28F` | + +### Raw image facts + +| Item | Value | +| ---- | ----- | +| Build type | `Release` | +| Load base address | `0x10000000` | +| Project 1 size | `15308` bytes (`0x3bcc`) | +| Project 2 size | `15324` bytes (`0x3bdc`) | +| Fixed `main` anchor (both projects) | `0x1000018c` (reset handler middle `blx`) | +| `main`, Project 1 | `0x10000234` | +| `main`, Project 2 | `0x10000238` | +| `printf` call / return, Project 1 | `0x10000244` / `0x10000248` | +| `printf` call / return, Project 2 | `0x1000024a` / `0x1000024e` | +| `double` argument registers | `r2` (low) : `r3` (high) | +| RP2350 UF2 family ID | `0xe48bff59` | + +--- + +## Troubleshooting + +### Binary Ninja hangs or crashes when you connect (macOS 27) + +Three different causes have been seen on this setup; check them in this order. + +- **A breakpoint set before connecting.** With the **GDB MI** adapter, if the binary view already has a breakpoint, the session hangs. Start parked with `BP_ADDR`, connect, then add breakpoints (see the next entry). +- **The wrong GDB executable.** Point **Full GDB Executable Path** at the **14.2.rel1** toolchain (Step 11). The 13.3.rel1 build did **not** connect in testing. +- **The LLDB adapter.** A crash report with `libdebuggercore.dylib -> std::terminate() -> abort()` and `liblldb` in the stack is the **LLDB** adapter, not GDB MI. Avoid LLDB on this setup. + +**Use GDB MI**, with the 14.2.rel1 path above. If it still fails, fall back to plain `arm-none-eabi-gdb` against the same server — the addresses and register values are identical to the GUI steps. + +If Binary Ninja hangs, force-quit it; the connect dialog has no working Cancel. The static steps (resolve, patch, export, flash) never touch the debugger and always work. + +### The GUI refuses to set a breakpoint (GDB RSP adapter only) + +If you are on the **GDB RSP** adapter, the GUI cannot set breakpoints on this target. That adapter is Binary Ninja's own minimal RSP client and sends a **1-byte** breakpoint (`Z0,,1`); the Cortex-M33 comparators need 2 bytes, so OpenOCD answers `only breakpoints of two bytes length supported`. It affects every address, both `Toggle Breakpoint` and `Add Hardware Breakpoint`, and the dialog's **Size** field is disabled. `gdb_breakpoint_override` makes no difference. + +**Fix: use the GDB MI adapter** (Step 11). It drives real GDB, which sends the correct length, so GUI breakpoints just work. If you must stay on GDB RSP, arm breakpoints from the command port after connecting (`bp 2 hw`) — but the lab uses GDB MI and does not need that. + +### GDB MI hangs when you connect (a breakpoint already existed) + +With the **GDB MI** adapter, if Binary Ninja already has a breakpoint set when you connect, the session **hangs**. This is a Binary Ninja bug. The working order is: + +1. Start the server parked, e.g. `BP_ADDR=0x10000234 ./debug-server.sh` (Windows: `$env:BP_ADDR="0x10000234"; .\debug-server.ps1`). +2. Connect with the **GDB MI** adapter. +3. Only *then* set hardware breakpoints in the UI. + +Never have a breakpoint in the binary view before the GDB MI connection. If it hangs, quit Binary Ninja, restart the server with `BP_ADDR`, and connect again before adding any breakpoints. + +### Step Into / Step Over does nothing (PC never moves) + +Two causes have been seen on this target. + +1. **A breakpoint on the current PC re-traps the step.** OpenOCD's step-over-breakpoint logic fails with `Duplicate Breakpoint address` and the PC stays put. Fix: move the breakpoint off the current PC (in the GUI), then step. +2. **The `hwthread` RTOS (GDB RSP adapter only).** With the **GDB RSP** adapter, OpenOCD can log `fake step thread 0` and reply without stepping, because the RP2350 config's `-rtos hwthread` makes the current thread id 1 while Binary Ninja sends thread id 0. Fix: `rp2350.dap.core0 configure -rtos none` (the launcher scripts already pass this). **GDB MI does not hit this.** + +To tell them apart, turn on OpenOCD logging (`log_output /tmp/ocd.log`, then `debug_level 3` on the command port) and look for `fake step` versus `Duplicate Breakpoint`. + +### `zsh: bad CPU type in executable: cmake` + +An Intel `x86_64` tool is on your `PATH` on Apple Silicon. Run Step 2: `export PATH="/opt/homebrew/bin:$PATH"`, then `hash -r`. Add it to `~/.zshrc` to make it permanent. + +### My addresses do not match this guide + +You probably built `Debug`. This lesson is a `Release` build. Re-run Step 3 with `-DCMAKE_BUILD_TYPE=Release`. A `Debug` build moves the SDK functions and lays the `pico_double` helpers out differently, so Project 2's `main` is not at `0x10000238`. + +### A breakpoint never fires + +First, confirm you actually set one, and that it is a **hardware** breakpoint. With the **GDB MI** adapter, `Debugger -> Add Hardware Breakpoint...` (hardware execute) should land in the **Breakpoints** widget. If nothing lands, or the core keeps running, you probably used `F2` (`Toggle Breakpoint`) — that is a software breakpoint and cannot be written to read-only flash, so it never installs. Also check you are on **GDB MI**, not **GDB RSP** (the GDB RSP adapter cannot set breakpoints on this target at all). + +Then check the order and the state: + +- **Arm it only after Binary Ninja is connected.** OpenOCD flushes every breakpoint when a client attaches, so anything armed earlier is gone. This also applies to `BP_ADDR` on the startup command line. +- **Verify it is armed:** `mdw 0xE0002000 8`. You should see your address with the low bit set (`0x10000234` -> `0x10000235`). All zeros means nothing is armed — re-read this first, because it distinguishes "not armed" from "armed but never reached". +- **Is the core running?** `poll` on the command port should not report a halt. If it is stopped, click **Resume**. +- **Does the address get reached again?** `main` runs once per reset, so use `BP_ADDR` at startup (Step 10) rather than `reset run` while attached. Loop addresses such as `0x10000244` fire on the next pass with no reset — arm them and click **Resume** in Binary Ninja. +- **With GDB MI the stop is reported as `Breakpoint`** and appears in the **Breakpoints** widget, because GDB really did set it. + +### I edit `r2`/`r3` (or another register) and it reverts + +Both `main`s reload the argument pair at the top of every loop iteration — `mov r2, r4` / `mov r3, r5` run right before the `printf` call. So your edit is only live for the instant between the write and the next pass; then the pair is reloaded from `r4`/`r5` (Project 1) or from the literal pool via `ldrd` (Project 2). The edit sticks only if the core is **genuinely stopped** at the breakpoint and stays stopped. + +If it keeps reverting, the core is running, which almost always means the breakpoint is not installed — usually because it is a **software** breakpoint (`F2`) that cannot be written to read-only flash. Use `Debugger -> Add Hardware Breakpoint...` (hardware execute). + +> **The Registers widget is a snapshot, not a live view.** Binary Ninja reads the registers at each stop and shows that snapshot; it does not poll the target, and there is no "refresh registers" command. So a value changed outside Binary Ninja will not appear until the next stop. + +### The serial capture is garbage on macOS + +Reading `/dev/cu.usbmodem*` with a bare `read()` returns garbage. Set **raw termios at 115200** first: clear canonical/echo flags, set `CLOCAL|CREAD`, and `B115200` on input and output. `screen /dev/cu.usbmodem* 115200` does all of this for you; a script must call `tcsetattr` itself. Once set, the capture reads clean `fav_num: 42.500000` lines. + +### It worked for a second, then stopped (Binary Ninja's view desyncs) + +This is the most common failure, and it has one main cause: **driving the core from the OpenOCD command port while Binary Ninja is connected.** + +- If you send `reset run` from the port while attached, the core resets, runs, and halts at your breakpoint — but Binary Ninja never receives the stop event. Its sidebar keeps showing the *previous* location, so **Step** and **Resume** act on a stale PC and appear to do nothing. +- If the OpenOCD process dies (or you restart it) while attached, Binary Ninja keeps believing it is connected: the sidebar stays, but the menu shows **Pause** enabled and **Resume**/**Step** disabled because Binary Ninja last saw the target *running*. + +Recovery: **Detach, then reconnect.** If Detach does nothing (the connection is already dead), restart Binary Ninja — its menu still shows a session that no longer exists. + +Prevention: +- Stop at `main` with `BP_ADDR` on a fresh server start, not with `reset run` while attached. +- For loop addresses, set the breakpoint in the GUI and click **Resume**. Let Binary Ninja be the thing that starts the core. +- If you must reset, **Detach first**, `reset run`, then reconnect. +- Never leave a breakpoint on the PC you are about to step or resume from. + +### The target "blows past" `main` and stops at `0x10003214` instead + +`0x10003214` is inside `stdio_uart_out_flush`: + +```asm +10003210: 4b02 ldr r3, [pc, #8] ; @ 0x1000321c +10003212: 681a ldr r2, [r3] +10003214: 6993 ldr r3, [r2, #24] ; the core sits here while the UART drains +10003216: 071b lsls r3, r3, #28 +10003218: d4fc bmi.n 0x10003214 +1000321a: 4770 bx lr +1000321c: 20000850 .word 0x20000850 +``` + +That is the UART transmit-FIFO drain loop inside `printf`, so the core is running `main`'s loop and simply spends nearly all its time there. The breakpoint at `main` did not fire because `main`'s entry runs exactly **once per reset**. If you arm the breakpoint after the reset, or set it while the target is already running and just resume, the core is already past `main` and will never re-execute it. Either arm the breakpoint **before** resetting, or break inside the loop at the `printf` call, which fires every iteration. + +**`0x10003214` is not a function.** It is one instruction inside `stdio_uart_out_flush`, which starts at `0x10003210`. If Binary Ninja has created a function at `0x10003214` (for example because the debugger stopped at that PC), the decompiler shows garbage. Delete that bogus function (right-click it -> `Delete Function`, or put the cursor on it and press `U` to undefine) and reanalyze. The real function is `stdio_uart_out_flush` at `0x10003210`. (In Project 2 the same drain loop is at `0x1000321c`.) + +### The console floods with `Failed to read memory at 0xf0000000` + +Core1 is exposed. The scripts must run with `USE_CORE=0`. Stop the server, confirm only `core0` is reported, restart, then restart Binary Ninja. + +### `Connect to Remote Process` is greyed out and Pause does nothing + +Binary Ninja is in a stale session, usually because the debug server restarted while attached. Quit and reopen Binary Ninja (or the `.bndb`) and connect again. + +### The decompiler still shows the old value after patching + +Right-click the function and choose `Reanalyze`. + +### Project 2 prints a wrong, hybrid number after patching + +You patched only one of the two literal words. `42.52525` has a non-zero low word, so `0x10000254` **and** `0x10000258` must both change. Project 1's `42.5` is the opposite case: its low word is `0x00000000`, so only `0x1000024c` changes. + +### The double does not print as `99.99` after patching + +Confirm you wrote the bytes little-endian. `0x28F5C28F` is stored `8f c2 f5 28`, and `0x4058FF5C` is stored `5c ff 58 40`. If you typed the words in big-endian order the value is nonsense. The Python form `bytes.fromhex("8fc2f528")` is already in file order. + +--- + +## Fallback: do the dynamic steps with GDB (macOS 27) + +If Binary Ninja's debugger crashes on attach on macOS 27 (see Troubleshooting), you can still do the live hack with the ARM GDB from the toolchain, against the same OpenOCD server. The addresses and register values are identical to the GUI steps. + +Start the debug server (Step 10), then in a new terminal: + +``` +arm-none-eabi-gdb +``` + +At the `(gdb)` prompt: + +``` +set architecture armv8-m.main +target extended-remote :3333 +hbreak *0x10000244 +continue +``` + +Do **not** run `monitor reset run` before `hbreak`. `0x10000244` is inside `main`'s loop, so the breakpoint fires on the next iteration with no reset. If you reset first, the core runs `main` and you will not catch it. + +GDB stops at the `printf` call. Confirm the pair, change it, and let it run: + +``` +info registers pc r2 r3 # pc = 0x10000244, r2 = 0x00000000, r3 = 0x40454000 +set $r3 = 0x4058C000 +stepi +continue +``` + +The serial monitor prints `fav_num: 99.000000` for the iteration you changed — the same temporary live hack as editing `r3` in the Binary Ninja Registers widget. When you are done, press `Ctrl-C`, then `detach` and `quit`. + +**If you specifically want to stop at `main` (`0x10000234`),** remember its entry runs only once per reset, so the breakpoint must be armed *before* the reset: + +``` +monitor reset halt +hbreak *0x10000234 +continue +``` + +If you instead set it while the target is running and just `continue`, you will "blow past" `main` and catch the core inside `printf` — in this build at `0x10003214`, the `stdio_uart_out_flush` UART-drain loop. + +Project 2 is the same with the other call site and pair: + +``` +hbreak *0x1000024a +continue +info registers pc r2 r3 # pc = 0x1000024a, r2 = 0x645A1CAC, r3 = 0x4045433B +set $r2 = 0x28F5C28F +set $r3 = 0x4058FF5C +stepi +``` + +`hbreak` sets a hardware breakpoint, which is required for read-only flash. It works from plain GDB because GDB sends the 2-byte length the Cortex-M33 comparators need. Binary Ninja's **GDB MI** adapter goes through the same GDB, so its GUI breakpoints work too; the old **GDB RSP** adapter was the one that sent a 1-byte length and could not set breakpoints here. + +## Glossary + +| Term | Definition | +| ---- | ---------- | +| **`.bss`** | Section for uninitialized global variables; zeroed by startup code | +| **`.data`** | Section for initialized global variables; copied from flash to SRAM at boot | +| **`.elf`** | Linked image with the symbol table; the ground truth for addresses and names | +| **`.rodata`** | Read-only section for constants and string literals; stays in flash | +| **Bias** | Constant added to an IEEE 754 exponent (`127` for float, `1023` for double) | +| **Double** | 64-bit IEEE 754 floating-point type (1 sign, 11 exponent, 52 mantissa) | +| **Float** | 32-bit IEEE 754 floating-point type (1 sign, 8 exponent, 23 mantissa) | +| **GPIO** | General Purpose Input/Output — controllable pins on the microcontroller | +| **Hardware breakpoint** | A breakpoint serviced by the CPU comparators, required for read-only flash | +| **IEEE 754** | The standard that defines binary floating-point encoding | +| **Inlining** | The optimizer replacing a function call with the function body | +| **Literal pool** | A block of 32-bit constants that Thumb-2 code reaches with PC-relative `ldr`/`ldrd` | +| **Mantissa** | The fractional significand bits (23 for float, 52 for double) | +| **MMIO** | Memory-mapped I/O — hardware registers accessed as memory addresses | +| **Promotion** | C's automatic `float` -> `double` conversion for variadic arguments | +| **Register pair** | Two 32-bit registers (`r2:r3`) that together hold a 64-bit value | +| **SIO** | Single-cycle I/O — the fast GPIO block in the RP2350, at `0xd0000000` | +| **Thumb bit** | Bit 0 of a Cortex-M function pointer; selects Thumb instruction mode | +| **UF2** | USB Flashing Format — the file format the Pico 2 bootloader accepts | +| **Vector table** | The first words of flash: initial stack pointer and exception vectors | + +--- + +**Remember:** the ELF tells you what every address is, and the `.bin` is what you actually patch. Prove the behavior dynamically by reading `r2:r3`, resolve the names from the ELF, then patch the constant bytes — one word for a clean fraction like `42.5`, two words for a repeating one like `42.52525` — and flash. diff --git a/WEEK05/WEEK05-BN.pdf b/WEEK05/WEEK05-BN.pdf new file mode 100644 index 0000000..730a29f Binary files /dev/null and b/WEEK05/WEEK05-BN.pdf differ diff --git a/WEEK05/WEEK05-SLIDES.pdf b/WEEK05/WEEK05-SLIDES.pdf new file mode 100644 index 0000000..d6078b0 Binary files /dev/null and b/WEEK05/WEEK05-SLIDES.pdf differ diff --git a/WEEK05/WEEK05.md b/WEEK05/WEEK05.md new file mode 100644 index 0000000..c66ab20 --- /dev/null +++ b/WEEK05/WEEK05.md @@ -0,0 +1,1529 @@ +# Week 5: Integers and Floats in Embedded Systems: Debugging and Hacking Integers and Floats w/ Intermediate GPIO Output Assembler Analysis + +*** +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: + +- Understand how integers and floating-point numbers are stored in memory +- Know the difference between signed and unsigned integers (`uint8_t` vs `int8_t`) +- Understand how floats and doubles are represented using IEEE 754 encoding +- Use inline assembly to control GPIO pins directly at the hardware level +- Debug numeric data types using GDB and the OpenOCD debugger +- Hack integer values by modifying registers at runtime +- Hack floating-point values by understanding and manipulating their binary representation +- Reconstruct 64-bit doubles from two 32-bit registers +--- + +## Part 1: Understanding Integer Data Types + +### What is an Integer? + +An **integer** is a whole number without any decimal point. Think of it like counting apples: you can have 0 apples, 1 apple, 42 apples, but you can't have 3.5 apples (that would be a fraction!). + +In C programming for embedded systems, we have special integer types that tell the compiler exactly how much memory to use: + +``` ++-----------------------------------------------------------------+ +| Integer Types - Different Sizes for Different Needs | +| | +| uint8_t: 1 byte (0 to 255) - like a small box | +| int8_t: 1 byte (-128 to 127) - can hold negatives! | +| uint16_t: 2 bytes (0 to 65,535) - medium box | +| uint32_t: 4 bytes (0 to 4 billion) - big box | +| | ++-----------------------------------------------------------------+ +``` + +### Signed vs Unsigned Integers + +The difference between `uint8_t` and `int8_t` is whether the number can be **negative**: + +| Type | Prefix | Range | Use Case | +| --------- | ------ | ----------- | ----------------------------- | +| `uint8_t` | `u` | 0 to 255 | Ages, counts, always positive | +| `int8_t` | none | -128 to 127 | Temperature, can be negative | + +#### The Integer Variables + +Let's say a program declares two integer variables that demonstrate the difference between **signed** and **unsigned** types: + +```c +uint8_t age = 43; +int8_t range = -42; +``` + +The variable `age` is a `uint8_t` - an **unsigned** 8-bit integer that can only hold values from `0` to `255`. Since age is always a positive number, unsigned is the right choice. The variable `range` is an `int8_t` - a **signed** 8-bit integer that can hold values from `-128` to `127`. The signed type allows it to represent negative numbers like `-42`. Under the hood, negative values are stored using **two's complement** encoding: the CPU flips all the bits of `42` (`0x2A`) and adds `1`, producing `0xD6`, which is how `-42` lives in a single byte of memory. + +--- + +## Part 2: Understanding Floating-Point Data Types + +### What is a Float? + +A **float** is a number that can have a decimal point. Unlike integers which can only hold whole numbers like `42`, a float can hold values like `42.5`, `3.14`, or `-0.001`. In C, the `float` type uses **32 bits (4 bytes)** to store a number using the **IEEE 754** standard. + +``` ++-----------------------------------------------------------------+ +| IEEE 754 Single-Precision (32-bit float) | +| | +| +------+----------+---------------------------+ | +| | Sign | Exponent | Mantissa (Fraction) | | +| | 1bit | 8 bits | 23 bits | | +| +------+----------+---------------------------+ | +| | +| Value = (-1)^sign * 2^(exponent-127) * 1.mantissa | +| | +| Example: 42.5 | +| Sign: 0 (positive) | +| Exponent: 10000100 (132 - 127 = 5) | +| Mantissa: 01010100000000000000000 | +| Full: 0 10000100 01010100000000000000000 | +| Hex: 0x422A0000 | +| | ++-----------------------------------------------------------------+ +``` + +### How to Compute This by Hand (42.5 -> IEEE 754) + +Use this exact process any time you need to encode a decimal float manually. + +1. Determine the sign bit. + - `42.5` is positive, so `sign = 0`. + +2. Convert the number to binary. + - Integer part: `42 = 101010 (base 2)` + - $42 = 32 + 8 + 2 = 2^5 + 2^3 + 2^1$ + - In 6-bit binary: `101010` + - Fractional part: use repeated multiply-by-2 on the fraction. + - Start with `0.5` + - $0.5 \times 2 = 1.0 \implies$ integer part is `1` (this is the first binary fractional bit) + - Remaining fractional part is now `0.0`, so we stop. + - Therefore `0.5 = 0.1 (base 2)`. + - Combined fixed-point binary: `42.5 = 101010.1 (base 2)` + +3. Normalize to scientific form ($1.\text{mantissa} \times 2^n$) - Where the 5 comes from! + - **Why is the exponent 5?** Look at the powers of 2 for the integer part (42): + - $2^4 = 16$ + - $2^5 = 32$ + - $2^6 = 64$ + - Because $32 \le 42 < 64$ (that is, $2^5 \le 42 < 2^6$), the largest power of 2 contained within 42 is $2^{\mathbf{5}}$. This mathematical bound guarantees that when normalized, the exponent **must be 5**. + - Now count the binary point shifts to bring the number into $1.xxxx$ form: + +``` +Original position: 1 0 1 0 1 0 . 1 x 2^0 (value: 42.5) +Shift 1 place left: 1 0 1 0 1 . 0 1 x 2^1 +Shift 2 places left: 1 0 1 0 . 1 0 1 x 2^2 +Shift 3 places left: 1 0 1 . 0 1 0 1 x 2^3 +Shift 4 places left: 1 0 . 1 0 1 0 1 x 2^4 +Shift 5 places left: 1 . 0 1 0 1 0 1 x 2^5 + ^ + Binary point is now right after the first '1'! +``` + + - We shifted the binary point **exactly 5 places to the left**, so the true exponent is $n = \mathbf{5}$: + $$101010.1_2 = 1.010101_2 \times 2^{\mathbf{5}}$$ + +4. Compute the stored exponent (bias 127 for float, 1023 for double). + - For a 32-bit `float`: + $$\text{stored exponent} = n + 127 = 5 + 127 = 132 = 10000100_2$$ + - For a 64-bit `double`: + $$\text{stored exponent} = n + 1023 = 5 + 1023 = 1028 = 10000000100_2$$ + + > Tip: **Why 127?** The exponent field is 8 bits wide, giving $2^8 = 256$ total values. Half of that range should represent negative exponents and half positive. The midpoint is $(2^8 / 2) - 1 = 127$. So a stored exponent of `127` means a real exponent of **0**, values below `127` are negative exponents, and values above `127` are positive exponents. Doubles use an 11-bit exponent field so their midpoint (bias) is $( 2^{11} / 2) - 1 = 1023$ instead. + +5. Build the mantissa (fraction bits). + - Take bits after the leading `1.` from `1.010101` -> `010101` + - Pad with zeros to 23 bits (for 32-bit float): + - `01010100000000000000000` + +6. Assemble all fields. + - `sign | exponent | mantissa` + - `0 | 10000100 | 01010100000000000000000` + - Full 32-bit pattern: + - `01000010001010100000000000000000` + +7. Convert the 32-bit binary to hex (group by 4 bits). + - `0100 0010 0010 1010 0000 0000 0000 0000` + - `4 2 2 A 0 0 0 0` + - Final result: `0x422A0000` + +Quick decode check (reverse direction, fully expanded): + +Given the 32-bit pattern: + +- `0 | 10000100 | 01010100000000000000000` + +Decode it field by field back to decimal: + +1. Sign bit + - Sign bit is `0` -> number is positive: `(+1)`. + +2. Exponent field + - Exponent bits are `10000100`. + - Convert to decimal: $10000100_2 = 128 + 4 = 132$. + - Float bias is `127`, so subtract the bias to recover the true exponent: + $$132 - 127 = \mathbf{5}$$ + - Recovering **5** tells us the significand was scaled by $2^5$. + +3. Mantissa field + - Stored mantissa bits are `01010100000000000000000`. + - IEEE 754 normal numbers restore the implicit leading `1.`, so significand becomes: + $$1.010101_2$$ + +4. Rebuild the value (undo the normalization) + - Formula: $\text{value} = (+1) \times 1.010101_2 \times 2^5$. + - Multiplying by $2^5$ shifts the binary point **5 places to the right**: + +``` +Start: 1 . 0 1 0 1 0 1 x 2^5 +Shift 1 place right: 1 0 . 1 0 1 0 1 x 2^4 +Shift 2 places right: 1 0 1 . 0 1 0 1 x 2^3 +Shift 3 places right: 1 0 1 0 . 1 0 1 x 2^2 +Shift 4 places right: 1 0 1 0 1 . 0 1 x 2^1 +Shift 5 places right: 1 0 1 0 1 0 . 1 x 2^0 = 101010.1 +``` + + - Resulting fixed-point binary: $101010.1_2$. + +5. Convert $101010.1_2$ to decimal + - Integer part: $101010_2 = 32 + 8 + 2 = 42$ + - Fraction part: $.1_2 = 1/2 = 0.5$ + - Total: $42 + 0.5 = \mathbf{42.5} \checkmark$ + +So the decoded value is exactly `42.5`. + +### Float vs Integer - Key Differences + +| Property | Integer (`uint8_t`) | Float (`float`) | +| -------------- | ---------------------- | --------------------------- | +| **Size** | 1 byte | 4 bytes | +| **Precision** | Exact | ~7 decimal digits | +| **Range** | 0 to 255 | 3.4 10^38 | +| **Encoding** | Direct binary | IEEE 754 (sign/exp/mantissa)| +| **printf** | `%d` | `%f` | + +### Our Floating-Point Program + +Let's look at a simple program that uses a `float` variable: + +**File: `0x000e_floating-point-data-type.c`** + +```c +#include +#include "pico/stdlib.h" + +int main(void) { + float fav_num = 42.5; + + stdio_init_all(); + + while (true) + printf("fav_num: %f\r\n", fav_num); +} +``` + +> Note: `fav_num` is declared inside `main`, so by C rules it is an automatic (stack) variable. In optimized embedded builds, the compiler may avoid creating a real stack slot and instead materialize the value from read-only constant storage (typically `.rodata` and/or an ARM literal pool). +> +> An ARM **literal pool** is a small table of constants that the assembler places near code in memory. Instead of encoding a large immediate value directly in an instruction, the CPU executes a load instruction (such as `ldr`) that reads the constant from that nearby table. That is why Ghidra can show constant loads rather than a classic stack local. + +**What this code does:** + +1. Declares a `float` variable `fav_num` and initializes it to `42.5` +2. Initializes the serial output +3. Prints `fav_num` forever in a loop using the `%f` format specifier + +> Tip: **Why `%f` instead of `%d`?** The `%d` format specifier tells `printf` to expect an integer. The `%f` specifier tells it to expect a floating-point number. Using the wrong one would print garbage! + +### Step 1: Flash the Binary to Your Pico 2 + +1. Hold the BOOTSEL button on your Pico 2 +2. Plug in the USB cable (while holding BOOTSEL) +3. Release BOOTSEL - a drive called "RPI-RP2" appears +4. Drag and drop `0x000e_floating-point-data-type.uf2` onto the drive +5. The Pico will reboot and start running! + +### Step 2: Verify It's Working + +Open your serial monitor (PuTTY) and you should see: + +**You should see:** + +``` +fav_num: 42.500000 +fav_num: 42.500000 +fav_num: 42.500000 +... +``` + +The program is printing `42.500000` because `printf` with `%f` defaults to 6 decimal places. + +--- + +## Part 2.5: Setting Up Ghidra for Float Analysis + +### Step 3: Start Ghidra + +**Open a terminal and type:** + +```cmd +ghidraRun +``` + +Ghidra will open. Now we need to create a new project. + +### Step 4: Create a New Project + +1. Click **File** -> **New Project** +2. Select **Non-Shared Project** +3. Click **Next** +4. Enter Project Name: `0x000e_floating-point-data-type` +5. Click **Finish** + +### Step 5: Import the Binary + +1. Open your file explorer +2. Navigate to the `Embedded-Hacking` folder +3. Find `0x000e_floating-point-data-type.bin` +4. Select Cortex M Little Endian 32 +5. Select Options and set up the .text and offset 10000000 +6. **Drag and drop** the `.bin` file into Ghidra's project window + +### Step 6: Configure the Binary Format + +A dialog appears. The file is identified as a "BIN" (raw binary without debug symbols). + +**Click the three dots (...) next to "Language" and:** + +1. Search for "Cortex" +2. Select **ARM Cortex 32 little endian default** +3. Click **OK** + +**Click the "Options..." button and:** + +1. Change **Block Name** to `.text` +2. Change **Base Address** to `10000000` (the XIP address!) +3. Click **OK** + +### Step 7: Open and Analyze + +1. Double-click on the file in the project window +2. A dialog asks "Analyze now?" - Click **Yes** +3. Use default analysis options and click **Analyze** + +Wait for analysis to complete (watch the progress bar in the bottom right). + +--- + +## Part 2.6: Navigating and Resolving Functions + +### Step 8: Find the Functions + +Look at the **Symbol Tree** panel on the left. Expand **Functions**. + +You'll see function names like: + +- `FUN_1000019a` +- `FUN_10000210` +- `FUN_10000234` + +These are auto-generated names because we imported a raw binary without symbols! + +### Step 9: Resolve Known Functions + +From our previous chapters, we know what some of these functions are: + +| Ghidra Name | Actual Name | How We Know | +| -------------- | ------------- | -------------------------- | +| `FUN_1000019a` | `data_cpy` | From Week 3 boot analysis | +| `FUN_10000210` | `frame_dummy` | From Week 3 boot analysis | +| `FUN_10000234` | `main` | This is where our code is! | + +### Step 10: Update Main's Signature + +For `main`, let's also fix the return type: + +1. Right-click on `main` in the Decompile window +2. Select **Edit Function Signature** +3. Change to: `int main(void)` +4. Click **OK** + +--- + +## Part 2.7: Analyzing the Main Function + +### Step 11: Examine Main in Ghidra + +Click on `main` (or `FUN_10000234`). Look at the **Decompile** window: + +You'll see something like: + +```c +int main(void) + +{ + undefined4 uVar1; + undefined4 extraout_r1; + undefined4 uVar2; + undefined4 extraout_r1_00; + + FUN_10002f5c(); + uVar1 = DAT_1000024c; + uVar2 = extraout_r1; + do { + FUN_100030ec(DAT_10000250,uVar2,0,uVar1); + uVar2 = extraout_r1_00; + } while( true ); +} +``` + +### Step 12: Resolve stdio_init_all + +1. Click on `FUN_10002f5c` +2. Right-click -> **Edit Function Signature** +3. Change to: `bool stdio_init_all(void)` +4. Click **OK** + +### Step 13: Resolve printf + +1. Click on `FUN_100030ec` +2. Right-click -> **Edit Function Signature** +3. Change to: `int printf(char *format,...)` +4. Check the **Varargs** checkbox (printf takes variable arguments!) +5. Click **OK** + +### Step 14: Understand the Float Encoding + +Look at the decompiled code after resolving functions: + +```c +int main(void) + +{ + undefined4 uVar1; + undefined4 extraout_r1; + undefined4 uVar2; + undefined4 extraout_r1_00; + + stdio_init_all(); + uVar1 = DAT_1000024c; + uVar2 = extraout_r1; + do { + printf(DAT_10000250,uVar2,0,uVar1); + uVar2 = extraout_r1_00; + } while( true ); +} +``` + +**Where's `float fav_num = 42.5`?** It's been optimized into an immediate value! + +The compiler replaced our float variable with constants passed directly to `printf`. But wait - we see **two** values: `0x0`, in `r2` and `DAT_1000024c` or `0x40454000`, in `r3`. That's because `printf` with `%f` always receives a **double** (64-bit), not a `float` (32-bit). The C standard requires that `float` arguments to variadic functions like `printf` are **promoted to `double`**. + +A 64-bit double is passed in two 32-bit registers: + +| Register | Value | Role | +| -------- | ------------ | ------------ | +| `r2` | `0x00000000` | Low 32 bits | +| `r3` | `0x40454000` | High 32 bits | + +Together they form `0x40454000_00000000` - the IEEE 754 **double-precision** encoding of `42.5`. + +### Step 15: Verify the Double Encoding + +We need to decode `0x4045400000000000` field by field. The two registers give us the full 64-bit value: + +``` +r3 (high 32 bits): 0x40454000 = 0100 0000 0100 0101 0100 0000 0000 0000 +r2 (low 32 bits): 0x00000000 = 0000 0000 0000 0000 0000 0000 0000 0000 +``` + +Laid out as a single 64-bit value with every bit numbered: + +``` +Bit: 63 62-52 (11b) 51-32 (20b from r3) 31-0 (32b from r2) ++---+-------------+---------------------+-------------------------+ +| 0 | 10000000100 | 01010100000000000000| 00000000...00000000 | ++---+-------------+---------------------+-------------------------+ +Sign Exponent Mantissa (High 20) Mantissa (Low 32) + (from r3 bits 19-0) (from r2, all zero) +``` + +**Step-by-step field extraction:** + +**1. Sign bit** + +In IEEE 754, the **sign bit** is the very first (leftmost) bit of the 64-bit double. In the full 64-bit layout we call it **bit 63**: + +``` +64-bit double: [bit 63] [bit 62 ... bit 0] + ^ + sign bit +``` + +But we don't have a single 64-bit register - we have **two** 32-bit registers. The high register `r3` holds bits 63-32 of the double. So bit 63 of the double is the same physical bit as **bit 31 of r3** (the topmost bit of r3): + +``` +r3 holds bits 63-32 of the double +r2 holds bits 31-0 of the double +``` + +Now let's check it. IEEE 754 uses a simple rule for the sign bit: + +| Sign bit | Meaning | +|----------|----------| +| `0` | Positive | +| `1` | Negative | + +``` +r3 = 0x40454000 = 0100 0000 0100 0101 0100 0000 0000 0000 + ^ + r3 bit 31 = 0 -> sign = 0 -> Positive number +``` + +The topmost bit of r3 is `0`, so the number is **positive**. If that bit were `1` instead (e.g. `0xC0454000`), the number would be negative (`-42.5`). + +**2. Exponent - bits 62-52 of the 64-bit value = bits 30-20 of r3** + +Extract bits 30-20 from `0x40454000`: + +``` +0x40454000 in binary: 0 10000000100 01010100000000000000 + sign exponent mantissa (top 20 bits) +``` + +Exponent bits: `10000000100` + +Convert to decimal: $2^{10} + 2^{2} = 1024 + 4 = 1028$ + +But `1028` is **not** the actual power of 2 yet. IEEE 754 stores exponents with a **bias** - a fixed number that gets added during encoding so that the stored value is always positive (no sign bit needed for the exponent). For doubles, the bias is **1023**. + +> Tip: **Why 1023?** The exponent field is 11 bits wide, giving $2^{11} = 2048$ total values. Half of that range should represent negative exponents and half positive. The midpoint is $(2^{11} / 2) - 1 = 1023$. So a stored exponent of `1023` means a real exponent of **0**, values below `1023` are negative exponents, and values above `1023` are positive exponents. + +To recover the real exponent, we subtract the bias: + +$$\text{real exponent} = \text{stored exponent} - \text{bias}$$ + +$$\text{real exponent} = 1028 - 1023 = \mathbf{5}$$ + +This confirms the number is scaled by $2^5 = 32$. When reconstructing the value, multiplying the significand $1.010101_2$ by $2^5$ shifts the binary point 5 places to the right, recovering $101010.1_2 = 42.5$. + +**3. Mantissa - bits 51-0 of the 64-bit value** + +- **High 20 bits of mantissa** (bits 51-32) = bits 19-0 of r3: + +``` +r3 bits 19-0: 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +``` + +- **Low 32 bits of mantissa** (bits 31-0) = all of r2: + +``` +r2 = 0x00000000 -> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +``` + +Full 52-bit mantissa: + +``` +0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + <- top 20 bits from r3 -> <- bottom 32 bits from r2 (all zero) -> +``` + +IEEE 754 always prepends an **implied leading `1`**, so the actual value represented is: + +``` +1.010101 00000... (the 1. is implicit, not stored) +``` + +**4. Reconstruct the value** + +$$1.010101\text{ (base 2)} \times 2^5$$ + +Shift the binary point 5 places right: + +$$101010.1\text{ (base 2)}$$ + +Now convert each bit position to decimal: + +| Bit position | Power of 2 | Value | +|---|---|---| +| `1` (bit 5) | $2^5$ | 32 | +| `0` (bit 4) | $2^4$ | 0 | +| `1` (bit 3) | $2^3$ | 8 | +| `0` (bit 2) | $2^2$ | 0 | +| `1` (bit 1) | $2^1$ | 2 | +| `0` (bit 0) | $2^0$ | 0 | +| `1` (bit -1) | $2^{-1}$ | 0.5 | + +$$32 + 8 + 2 + 0.5 = \mathbf{42.5}$$ + +### Step 16: Examine the Assembly + +Look at the **Listing** window (assembly view). Find the main function: + +``` + ************************************************************* + * FUNCTION + ************************************************************* + int __stdcall main (void ) + int r0:4 + main+1 XREF[1,1]: 1000018c (c) , 1000018a (*) + main + 10000234 38 b5 push {r3,r4,r5,lr} + 10000236 02 f0 91 fe bl stdio_init_all bool stdio_init_all(void) + 1000023a 00 24 movs r4,#0x0 + 1000023c 03 4d ldr r5,[DAT_1000024c ] = 40454000h + LAB_1000023e XREF[1]: 10000248 (j) + 1000023e 22 46 mov r2,r4 + 10000240 2b 46 mov r3,r5 + 10000242 03 48 ldr r0=>s_fav_num:_%f_100034a8 ,[DAT_10000250 ] = "fav_num: %f\r\n" + = 100034A8h + 10000244 02 f0 52 ff bl printf int printf(char * format, ...) + 10000248 f9 e7 b LAB_1000023e + 1000024a 00 ?? 00h + 1000024b bf ?? BFh + DAT_1000024c XREF[1]: main:1000023c (R) + 1000024c 00 40 45 40 undefine 40454000h + DAT_10000250 XREF[1]: main:10000242 (R) + 10000250 a8 34 00 10 undefine 100034A8h * -> 100034a8 +``` + +> **Key Insight:** The `mov.w r2, #0x0` loads the low 32 bits (all zeros) and `ldr r3, [DAT_...]` loads the high 32 bits (`0x40454000`) of the double. Together, `r2:r3` = `0x40454000_00000000` = `42.5` as a double. + +### Step 17: Find the Format String + +In the Listing view, click on the data reference to find the format string: + +``` + s_fav_num:_%f_100034a8 XREF[1]: main:10000242 (*) + 100034a8 66 61 76 ds "fav_num: %f\r\n" + 5f 6e 75 + 6d 3a 20 +``` + +This confirms `printf` is called with the format string `"fav_num: %f\r\n"` and the double-precision value of `42.5`. + +--- + +## Part 2.8: Patching the Float - Changing 42.5 to 99.0 + +### Step 18: Calculate the New IEEE 754 Encoding + +We want to change `42.5` to `99.0`. First, we need to figure out the double-precision encoding of `99.0`: + +**Step A - Convert the integer part (99) to binary:** + +| Division | Quotient | Remainder | +|---------------|----------|-----------| +| 99 2 | 49 | **1** | +| 49 2 | 24 | **1** | +| 24 2 | 12 | **0** | +| 12 2 | 6 | **0** | +| 6 2 | 3 | **0** | +| 3 2 | 1 | **1** | +| 1 2 | 0 | **1** | + +Read remainders bottom-to-top: 99 (base 10) = 1100011 (base 2) + +**Step B - Convert the fractional part (.0) to binary:** + +There is no fractional part - `.0` is exactly zero, so the fractional binary is just `0`. + +**Step C - Combine:** + +$$99.0\text{ (base 10)} = 1100011.0\text{ (base 2)}$$ + +**Step D - Normalize to IEEE 754 form** (move the binary point so there's exactly one `1` before it): + +$$1100011.0\text{ (base 2)} = 1.100011\text{ (base 2)} \times 2^6$$ + +We shifted the binary point 6 places left, so the exponent is **6**. + +**Step E - Extract the IEEE 754 fields:** + +1. **Sign:** `0` (positive) +2. **Exponent:** $6 + 1023 = 1029 = 10000000101\text{ (base 2)}$ +3. **Mantissa:** `1000110000000000...` (everything after the `1.`, padded with zeros to 52 bits) +4. **Full double:** `0x4058C00000000000` + +| Register | Old Value | New Value | +| -------- | ------------ | ------------ | +| `r2` | `0x00000000` | `0x00000000` | +| `r3` | `0x40454000` | `0x4058C000` | + +Since `r2` stays `0x00000000`, we only need to patch the high word loaded into `r3`. + +### Step 19: Find the Value to Patch + +Look in the Listing view for the data that loads the high word of the double: + +``` + 1000024c 00 40 45 40 undefined4 40454000h +``` + +This is the 32-bit constant that gets loaded into `r3` - the high word of our double `42.5`. + +### Step 20: Patch the Constant + +1. Click on Window -> Bytes +2. Click on the Pencil icon to enable byte editing +3. At address `1000024c`, overwrite `00 40 45 40` with `00 C0 58 40` (little-endian for `0x40454000 -> 0x4058C000`) +4. Press Enter + +This changes the high word from `0x40454000` (42.5 as double) to `0x4058C000` (99.0 as double). + +--- + +## Part 2.9: Export and Test the Hacked Binary + +### Step 21: Export the Patched Binary + +1. Click **File** -> **Export Program** +2. Set **Format** to **Raw Bytes** +3. Navigate to your build directory +4. Name the file `0x000e_floating-point-data-type-h.bin` +5. Click **OK** + +### Step 22: Convert to UF2 Format + +**Open a terminal and navigate to your project directory:** + +```cmd +cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x000e_floating-point-data-type +``` + +**Run the conversion command:** + +```cmd +python ..\uf2conv.py build\0x000e_floating-point-data-type-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +### Step 23: Flash the Hacked Binary + +1. Hold BOOTSEL and plug in your Pico 2 +2. Drag and drop `hacked.uf2` onto the RPI-RP2 drive +3. Open your serial monitor + +**You should see:** + +``` +fav_num: 99.000000 +fav_num: 99.000000 +fav_num: 99.000000 +... +``` + + **BOOM! We hacked the float!** The value changed from `42.5` to `99.0`! + +--- + +## Part 3: Understanding Double-Precision Floating-Point Data Types + +### What is a Double? + +A **double** (short for "double-precision floating-point") is like a `float` but with **twice the precision**. While a `float` uses 32 bits, a `double` uses **64 bits (8 bytes)**, giving it roughly **15-16 significant decimal digits** of precision compared to a float's ~7. + +``` ++-----------------------------------------------------------------+ +| IEEE 754 Double-Precision (64-bit double) | +| | +| +------+-----------+--------------------------------------+ | +| | Sign | Exponent | Mantissa (Fraction) | | +| | 1bit | 11 bits | 52 bits | | +| +------+-----------+--------------------------------------+ | +| | +| Value = (-1)^sign * 2^(exponent-1023) * 1.mantissa | +| | +| Example: 42.52525 | +| Sign: 0 (positive) | +| Exponent: 10000000100 (1028 - 1023 = 5) | +| Mantissa: 0101010000110011101101100100010110100001110010101100 | +| Hex: 0x4045433B645A1CAC | +| | ++-----------------------------------------------------------------+ +``` + +### Float vs Double - Key Differences + +| Property | Float (`float`) | Double (`double`) | +| --------------- | ---------------------- | --------------------------- | +| **Size** | 4 bytes (32 bits) | 8 bytes (64 bits) | +| **Precision** | ~7 decimal digits | ~15 decimal digits | +| **Exponent** | 8 bits (bias 127) | 11 bits (bias 1023) | +| **Mantissa** | 23 bits | 52 bits | +| **Range** | 3.4 10^38 | 1.8 10^308 | +| **printf** | `%f` | `%lf` | +| **ARM passing** | Promoted to double | Native in `r2:r3` | + +> Tip: **Why does precision matter?** With a `float`, the value `42.52525` might be stored as `42.525249` due to rounding. A `double` can represent it as `42.525250` with much higher fidelity. For scientific or financial applications, that extra precision is critical! + +### Our Double-Precision Program + +Let's look at a program that uses a `double` variable: + +**File: `0x0011_double-floating-point-data-type.c`** + +```c +#include +#include "pico/stdlib.h" + +int main(void) { + double fav_num = 42.52525; + + stdio_init_all(); + + while (true) + printf("fav_num: %lf\r\n", fav_num); +} +``` + +**What this code does:** + +1. Declares a `double` variable `fav_num` and initializes it to `42.52525` +2. Initializes the serial output +3. Prints `fav_num` forever in a loop using the `%lf` format specifier + +> Tip: **`%lf` vs `%f`:** While `printf` actually treats `%f` and `%lf` identically (both expect a `double`), using `%lf` makes your intent clear - you're explicitly working with a `double`, not a `float`. It's good practice to match the format specifier to your variable type. + +### Step 1: Flash the Binary to Your Pico 2 + +1. Hold the BOOTSEL button on your Pico 2 +2. Plug in the USB cable (while holding BOOTSEL) +3. Release BOOTSEL - a drive called "RPI-RP2" appears +4. Drag and drop `0x0011_double-floating-point-data-type.uf2` onto the drive +5. The Pico will reboot and start running! + +### Step 2: Verify It's Working + +Open your serial monitor (PuTTY) and you should see: + +**You should see:** + +``` +fav_num: 42.525250 +fav_num: 42.525250 +fav_num: 42.525250 +... +``` + +The program is printing `42.525250` because `printf` with `%lf` defaults to 6 decimal places. + +--- + +## Part 3.5: Setting Up Ghidra for Double Analysis + +### Step 3: Start Ghidra + +**Open a terminal and type:** + +```cmd +ghidraRun +``` + +Ghidra will open. Now we need to create a new project. + +### Step 4: Create a New Project + +1. Click **File** -> **New Project** +2. Select **Non-Shared Project** +3. Click **Next** +4. Enter Project Name: `0x0011_double-floating-point-data-type` +5. Click **Finish** + +### Step 5: Import the Binary + +1. Open your file explorer +2. Navigate to the `Embedded-Hacking` folder +3. Find `0x0011_double-floating-point-data-type.bin` +4. Select Cortex M Little Endian 32 +5. Select Options and set up the .text and offset 10000000 +6. **Drag and drop** the `.bin` file into Ghidra's project window + +### Step 6: Configure the Binary Format + +A dialog appears. The file is identified as a "BIN" (raw binary without debug symbols). + +**Click the three dots (...) next to "Language" and:** + +1. Search for "Cortex" +2. Select **ARM Cortex 32 little endian default** +3. Click **OK** + +**Click the "Options..." button and:** + +1. Change **Block Name** to `.text` +2. Change **Base Address** to `10000000` (the XIP address!) +3. Click **OK** + +### Step 7: Open and Analyze + +1. Double-click on the file in the project window +2. A dialog asks "Analyze now?" - Click **Yes** +3. Use default analysis options and click **Analyze** + +Wait for analysis to complete (watch the progress bar in the bottom right). + +--- + +## Part 3.6: Navigating and Resolving Functions + +### Step 8: Find the Functions + +Look at the **Symbol Tree** panel on the left. Expand **Functions**. + +You'll see function names like: + +- `FUN_1000019a` +- `FUN_10000210` +- `FUN_10000238` + +These are auto-generated names because we imported a raw binary without symbols! + +### Step 9: Resolve Known Functions + +From our previous chapters, we know what some of these functions are: + +| Ghidra Name | Actual Name | How We Know | +| -------------- | ------------- | -------------------------- | +| `FUN_1000019a` | `data_cpy` | From Week 3 boot analysis | +| `FUN_10000210` | `frame_dummy` | From Week 3 boot analysis | +| `FUN_10000238` | `main` | This is where our code is! | + +### Step 10: Update Main's Signature + +For `main`, let's also fix the return type: + +1. Right-click on `main` in the Decompile window +2. Select **Edit Function Signature** +3. Change to: `int main(void)` +4. Click **OK** + +--- + +## Part 3.7: Analyzing the Main Function + +### Step 11: Examine Main in Ghidra + +Click on `main` (or `FUN_10000234`). Look at the **Decompile** window: + +You'll see something like: + +```c +int main(void) + +{ + undefined4 uVar1; + undefined4 uVar2; + undefined4 extraout_r1; + undefined4 uVar3; + undefined4 extraout_r1_00; + + uVar2 = DAT_10000258; + uVar1 = DAT_10000254; + FUN_10002f64(); + uVar3 = extraout_r1; + do { + FUN_100030f4(DAT_10000250,uVar3,uVar1,uVar2); + uVar3 = extraout_r1_00; + } while( true ); +} +``` + +### Step 12: Resolve stdio_init_all + +1. Click on `FUN_10002f64` +2. Right-click -> **Edit Function Signature** +3. Change to: `bool stdio_init_all(void)` +4. Click **OK** + +### Step 13: Resolve printf + +1. Click on `FUN_100030f4` +2. Right-click -> **Edit Function Signature** +3. Change to: `int printf(char *format,...)` +4. Check the **Varargs** checkbox (printf takes variable arguments!) +5. Click **OK** + +### Step 14: Understand the Double Encoding + +Look at the decompiled code after resolving functions: + +```c +int main(void) + +{ + undefined4 uVar1; + undefined4 uVar2; + undefined4 extraout_r1; + undefined4 uVar3; + undefined4 extraout_r1_00; + + uVar2 = DAT_10000258; + uVar1 = DAT_10000254; + stdio_init_all(); + uVar3 = extraout_r1; + do { + printf(DAT_10000250,uVar3,uVar1,uVar2); + uVar3 = extraout_r1_00; + } while( true ); +} +``` + +**Where's `double fav_num = 42.52525`?** It's been optimized into immediate values! + +This time we see **two** non-zero values: `0x645a1cac` and `0x4045433b`. Unlike the float example where the low word was `0x0`, a double with a fractional part like `42.52525` needs **all 52 mantissa bits** - so both halves carry data. + +A 64-bit double is passed in two 32-bit registers: + +| Register | Value | Role | +| -------- | ------------ | ------------ | +| `r2` | `0x645A1CAC` | Low 32 bits | +| `r3` | `0x4045433B` | High 32 bits | + +Together they form `0x4045433B645A1CAC` - the IEEE 754 **double-precision** encoding of `42.52525`. + +> **Key Difference from Float:** In the float example, `r2` was `0x00000000` because `42.5` has a clean fractional part. But `42.52525` has a repeating binary fraction, so the low 32 bits are non-zero (`0x645A1CAC`). This means **both** registers matter when patching doubles with complex fractional values! + +### Step 15: Verify the Double Encoding + +We need to decode `0x4045433B645A1CAC` field by field. The two registers give us the full 64-bit value: + +``` +r3 (high 32 bits): 0x4045433B = 0100 0000 0100 0101 0100 0011 0011 1011 +r2 (low 32 bits): 0x645A1CAC = 0110 0100 0101 1010 0001 1100 1010 1100 +``` + +Laid out as a single 64-bit value with every bit numbered: + +``` +Bit: 63 62-52 (11b) 51-32 (20b from r3) 31-0 (32b from r2) ++---+-------------+---------------------+-------------------------+ +| 0 | 10000000100 | 01010100001100111011| 01100100...10101100 | ++---+-------------+---------------------+-------------------------+ +Sign Exponent Mantissa (High 20) Mantissa (Low 32) + (from r3 bits 19-0) (from r2) +``` + +**Step-by-step field extraction:** + +**1. Sign bit** + +The sign bit is bit 63 of the 64-bit double, which is bit 31 of r3 (the high register holds bits 63-32): + +``` +r3 = 0x4045433B = 0100 0000 0100 0101 0100 0011 0011 1011 + ^ + r3 bit 31 = 0 -> sign = 0 -> Positive number ✓ +``` + +**2. Exponent - bits 62-52 = bits 30-20 of r3** + +Extract bits 30-20 from `0x4045433B`: + +``` +0x4045433B in binary: 0 10000000100 01010100001100111011 + sign exponent mantissa (top 20 bits) +``` + +Exponent bits: `10000000100` + +Convert to decimal: $2^{10} + 2^{2} = 1024 + 4 = 1028$ + +Subtract the bias (the bias is 1023 for all 64-bit doubles): + +$$\text{real exponent} = 1028 - 1023 = \mathbf{5}$$ + +#### Deep Dive: Where Did the Exponent 5 Come From? + +If you are wondering why the real exponent is **5**, let's walk through the foundational math step-by-step using `42.5`. This shows both how decimal `42.5` converts into IEEE 754 format, and how that converted value breaks down step-by-step back to `42.5`. + +##### 1. Forward Conversion: Decimal 42.5 to IEEE 754 + +**Step A: The Power-of-2 Bounding Rule (Why 5?)** +Why is the exponent 5 and not 4 or 6? Look at the powers of 2 around the integer part (42): +- $2^4 = 16$ +- $2^5 = 32$ +- $2^6 = 64$ + +Because $32 \le 42 < 64$ (that is, $2^{\mathbf{5}} \le 42 < 2^6$), the largest power of 2 contained within 42 is $2^{\mathbf{5}}$. This mathematical bound guarantees that when normalized, the exponent **must be 5**. + +**Step B: Convert 42.5 to Fixed-Point Binary** +- Integer part (42): + - $42 - 32 = 10 \implies 2^5$ bit is `1` + - $10 < 16 \implies 2^4$ bit is `0` + - $10 - 8 = 2 \implies 2^3$ bit is `1` + - $2 < 4 \implies 2^2$ bit is `0` + - $2 - 2 = 0 \implies 2^1$ bit is `1` + - $0 \implies 2^0$ bit is `0` + - Result: $42_{10} = 101010_2$ +- Fractional part (0.5): + - $0.5 \times 2 = 1.0 \implies$ integer part `1`, remainder `0.0` + - Result: $0.5_{10} = 0.1_2$ +- Combined fixed-point binary: + $$42.5_{10} = 101010.1_2$$ + +**Step C: Normalize to Scientific Notation (Counting the 5 Shifts Left)** +IEEE 754 requires every normal non-zero number to be represented in scientific form: +$$1.\text{mantissa} \times 2^n$$ +We start with $101010.1$ and shift the binary point to the left until exactly one non-zero bit (`1`) remains before the point: + +``` +Original position: 1 0 1 0 1 0 . 1 x 2^0 (value: 42.5) +Shift 1 place left: 1 0 1 0 1 . 0 1 x 2^1 +Shift 2 places left: 1 0 1 0 . 1 0 1 x 2^2 +Shift 3 places left: 1 0 1 . 0 1 0 1 x 2^3 +Shift 4 places left: 1 0 . 1 0 1 0 1 x 2^4 +Shift 5 places left: 1 . 0 1 0 1 0 1 x 2^5 + ^ + Point is now immediately after the first 1! +``` + +Notice we shifted the binary point **exactly 5 places to the left**. That shift count is our true exponent: +$$101010.1_2 = 1.010101_2 \times 2^{\mathbf{5}}$$ +**That is exactly where the 5 comes from!** + +**Step D: Add the Exponent Bias** +IEEE 754 adds a fixed bias so exponents are stored as unsigned positive integers: +- For `float` (bias 127): $\text{stored exponent} = 5 + 127 = 132 = 10000100_2$ +- For `double` (bias 1023): $\text{stored exponent} = 5 + 1023 = 1028 = 10000000100_2$ + +**Step E: Assemble the 64-Bit Double Pattern for 42.5** +- Sign (1 bit): `0` (positive) +- Exponent (11 bits): `10000000100` (1028) +- Mantissa (52 bits): `010101` followed by 46 zeros (the leading `1.` is implied) +- Register distribution: + - `r3` (high 32 bits): `0x40454000` + - `r2` (low 32 bits): `0x00000000` + +##### 2. Reverse Breakdown: IEEE 754 Double Back to Decimal 42.5 + +Now take `r3 = 0x40454000` and `r2 = 0x00000000` and break it down step-by-step back to `42.5`: + +**Step A: Recover the Real Exponent 5** +- Extract the 11 exponent bits from `r3` (bits 30-20): + $$\text{Stored exponent} = 10000000100_2 = 1024 + 4 = 1028$$ +- Subtract the 1023 bias: + $$\text{Real Exponent} = 1028 - 1023 = \mathbf{5}$$ +Subtracting 1023 recovers our shift count of **5**, telling us the significand was scaled by $2^{\mathbf{5}}$. + +**Step B: Restore the Significand (Reattach Implicit 1)** +- Stored mantissa bits from `r3`: `01010100000...` +- Reattach the implicit leading `1.`: + $$\text{Significand} = 1.010101_2$$ + +**Step C: Multiply by $2^5$ (Shift Binary Point 5 Places Right)** +$$\text{Value} = 1.010101_2 \times 2^{\mathbf{5}}$$ +Multiplying by $2^5$ shifts the binary point **5 places to the right**: + +``` +Start: 1 . 0 1 0 1 0 1 x 2^5 +Shift 1 place right: 1 0 . 1 0 1 0 1 x 2^4 +Shift 2 places right: 1 0 1 . 0 1 0 1 x 2^3 +Shift 3 places right: 1 0 1 0 . 1 0 1 x 2^2 +Shift 4 places right: 1 0 1 0 1 . 0 1 x 2^1 +Shift 5 places right: 1 0 1 0 1 0 . 1 x 2^0 = 101010.1 +``` + +The un-normalized fixed-point binary representation is: +$$101010.1_2$$ + +**Step D: Convert Fixed-Point Binary to Decimal** +- Integer part (`101010`): + - $1 \times 2^5 = 32$ + - $0 \times 2^4 = 0$ + - $1 \times 2^3 = 8$ + - $0 \times 2^2 = 0$ + - $1 \times 2^1 = 2$ + - $0 \times 2^0 = 0$ + - Sum: $32 + 8 + 2 = 42$ +- Fractional part (`.1`): + - $1 \times 2^{-1} = 0.5$ +- Final Total: + $$42 + 0.5 = \mathbf{42.5} \checkmark$$ + +##### 3. Why 42.52525 Shares the Exact Same Exponent 5 + +Now return to `42.52525` in our binary (`r3 = 0x4045433B`): +- The integer part of `42.52525` is still **42**. +- Since $32 \le 42.52525 < 64$ ($2^{\mathbf{5}} \le 42.52525 < 2^6$), the largest power of 2 fitting in the number is still $2^{\mathbf{5}}$. +- Normalizing $101010.10000110011101..._2$ to $1.xxxx...$ still requires shifting the binary point **5 places to the left**: + $$1.0101010000110011101..._2 \times 2^{\mathbf{5}}$$ +- Therefore, the stored exponent in `r3` is identically $5 + 1023 = 1028$ (`10000000100`), producing the exact same `0x404...` in the upper 12 bits of `r3`! +- The only difference between `42.5` (`0x40454000_00000000`) and `42.52525` (`0x4045433B_645A1CAC`) is in the remaining mantissa bits that capture the fractional difference between `0.5` and `0.52525`. + +**3. Mantissa - bits 51-0** + +Unlike the `42.5` example where r2 was all zeros, **both registers contribute non-zero bits** here: + +- **High 20 bits of mantissa** (bits 51-32) = bits 19-0 of r3: + +``` +r3 bits 19-0: 0 1 0 1 0 1 0 0 0 0 1 1 0 0 1 1 1 0 1 1 +``` + +- **Low 32 bits of mantissa** (bits 31-0) = all of r2: + +``` +r2 = 0x645A1CAC -> 0 1 1 0 0 1 0 0 0 1 0 1 1 0 1 0 0 0 0 1 1 1 0 0 1 0 1 0 1 1 0 0 +``` + +Full 52-bit mantissa: + +``` +0 1 0 1 0 1 0 0 0 0 1 1 0 0 1 1 1 0 1 1 | 0 1 1 0 0 1 0 0 0 1 0 1 1 0 1 0 0 0 0 1 1 1 0 0 1 0 1 0 1 1 0 0 + <- top 20 bits from r3 -> <- bottom 32 bits from r2 -> +``` + +IEEE 754 always prepends an **implied leading `1`**, so the actual value represented is: + +``` +1.0101010000110011101101100100010110100001110010101100 (the 1. is implicit, not stored) +``` + +**4. Reconstruct the value** + +$$1.0101010000110011101101100100...\text{ (base 2)} \times 2^5$$ + +Shift the binary point 5 places right: + +$$101010.10000110011101101100100010110100001110010101100\text{ (base 2)}$$ + +**Integer part** (`101010`): + +| Bit position | Power of 2 | Value | +|---|---|---| +| `1` (bit 5) | $2^5$ | 32 | +| `0` (bit 4) | $2^4$ | 0 | +| `1` (bit 3) | $2^3$ | 8 | +| `0` (bit 2) | $2^2$ | 0 | +| `1` (bit 1) | $2^1$ | 2 | +| `0` (bit 0) | $2^0$ | 0 | + +$$32 + 8 + 2 = \mathbf{42}$$ + +**Fractional part** (`.10000110011101101...`): + +| Bit position | Power of 2 | Decimal value | +|---|---|---| +| `1` (bit -1) | $2^{-1}$ | 0.5 | +| `0` (bit -2) | $2^{-2}$ | 0 | +| `0` (bit -3) | $2^{-3}$ | 0 | +| `0` (bit -4) | $2^{-4}$ | 0 | +| `0` (bit -5) | $2^{-5}$ | 0 | +| `1` (bit -6) | $2^{-6}$ | 0.015625 | +| `1` (bit -7) | $2^{-7}$ | 0.0078125 | +| `0` (bit -8) | $2^{-8}$ | 0 | +| `0` (bit -9) | $2^{-9}$ | 0 | +| `1` (bit -10) | $2^{-10}$ | 0.0009765625 | +| `1` (bit -11) | $2^{-11}$ | 0.00048828125 | +| `1` (bit -12) | $2^{-12}$ | 0.000244140625 | +| ... | ... | *(remaining 35 bits add smaller and smaller fractions)* | + +First 12 fractional bits sum: $0.5 + 0.015625 + 0.0078125 + 0.0009765625 + 0.00048828125 + 0.000244140625 \approx 0.5251$ + +The remaining 35 fractional bits refine this to $\approx 0.52525$. This is because `0.52525` is a **repeating fraction** in binary - it can never be represented with a finite number of bits, so double precision stores the closest possible 52-bit approximation. + +$$42 + 0.52525 = \mathbf{42.52525} \checkmark$$ + +### Step 16: Examine the Assembly + +Look at the **Listing** window (assembly view). Find the main function: + +``` + ************************************************************* + * FUNCTION + ************************************************************* + int __stdcall main (void ) + int r0:4 + main+1 XREF[1,1]: 1000018c (c) , 1000018a (*) + main + 10000238 38 b5 push {r3,r4,r5,lr} + 1000023a 06 a5 adr r5,[0x10000254 ] + 1000023c d5 e9 00 45 ldrd r4,r5,[r5,#0x0 ]=>DAT_10000254 = 645A1CACh + = 4045433Bh + 10000240 02 f0 90 fe bl stdio_init_all bool stdio_init_all(void) + LAB_10000244 XREF[1]: 1000024e (j) + 10000244 22 46 mov r2,r4 + 10000246 2b 46 mov r3,r5 + 10000248 01 48 ldr r0=>s_fav_num:_%lf_100034b0 ,[DAT_10000250 ] = "fav_num: %lf\r\n" + = 100034B0h + 1000024a 02 f0 53 ff bl printf int printf(char * format, ...) + 1000024e f9 e7 b LAB_10000244 + DAT_10000250 XREF[1]: main:10000248 (R) + 10000250 b0 34 00 10 undefine 100034B0h ? -> 100034b0 + DAT_10000254 XREF[1]: main:1000023c (R) + 10000254 ac 1c 5a 64 undefine 645A1CACh + DAT_10000258 XREF[1]: main:1000023c (R) + 10000258 3b 43 45 40 undefine 4045433Bh +``` + +> **Key Insight:** Notice that **both** `r2` and `r3` are loaded from data constants using `ldr`. Compare this to the float example where `r2` was loaded with `mov.w r2, #0x0`. Because `42.52525` requires all 52 mantissa bits, neither word can be zero - the compiler must store both halves as separate data constants. + +### Step 17: Find the Format String + +In the Listing view, click on the data reference to find the format string: + +``` + s_fav_num:_%lf_100034b0 XREF[1]: main:10000248 (*) + 100034b0 66 61 76 ds "fav_num: %lf\r\n" + 5f 6e 75 + 6d 3a 20 +``` + +This confirms `printf` is called with the format string `"fav_num: %lf\r\n"` and the double-precision value of `42.52525`. + +--- + +## Part 3.8: Patching the Double - Changing 42.52525 to 99.99 + +### Step 18: Calculate the New IEEE 754 Encoding + +We want to change `42.52525` to `99.99`. First, we need to figure out the double-precision encoding of `99.99`: + +1. $99.99 = 1.5623... \times 2^6 = 1.100011111111...\text{ (base 2)} \times 2^6$ +2. **Sign:** `0` (positive) +3. **Exponent:** $6 + 1023 = 1029 = 10000000101\text{ (base 2)}$ +4. **Mantissa:** `1000111111010111000010100011110101110000101000111... (base 2)` +5. **Full double:** `0x4058FF5C28F5C28F` + +| Register | Old Value | New Value | +| -------- | ------------ | ------------ | +| `r2` | `0x645A1CAC` | `0x28F5C28F` | +| `r3` | `0x4045433B` | `0x4058FF5C` | + +Unlike the float example, **both** registers change! The value `99.99` has a repeating binary fraction, so both the high and low words are different. + +### Step 19: Find the Values to Patch + +Look in the Listing view for the two data constants: + +**Low word (loaded into `r2`):** +``` + 10000254 ac 1c 5a 64 undefined4 645A1CACh +``` + +**High word (loaded into `r3`):** +``` + 10000258 3b 43 45 40 undefined4 4045433Bh +``` + +### Step 20: Patch Both Constants + +**Patch the low word:** + +1. Click on the data at address `10000254` containing `645A1CAC` +2. Open the Bytes window and enable byte editing (Pencil icon) +3. Overwrite bytes `ac 1c 5a 64` with `8f c2 f5 28` (little-endian for `0x645A1CAC -> 0x28F5C28F`) +4. Press Enter + +**Patch the high word:** + +1. Click on the data at address `10000258` containing `4045433B` +2. Keep byte editing enabled in the Bytes window +3. Overwrite bytes `3b 43 45 40` with `5c ff 58 40` (little-endian for `0x4045433B -> 0x4058FF5C`) +4. Press Enter + +This changes the full 64-bit double from `0x4045433B645A1CAC` (42.52525) to `0x4058FF5C28F5C28F` (99.99). + +> **Key Difference from Float Patching:** When we patched the float `42.5`, we only needed to change one word (the high word in `r3`) because the low word was all zeros. With `42.52525 -> 99.99`, **both** words change. Always check whether the low word is non-zero before patching! + +--- + +## Part 3.9: Export and Test the Hacked Binary + +### Step 21: Export the Patched Binary + +1. Click **File** -> **Export Program** +2. Set **Format** to **Raw Bytes** +3. Navigate to your build directory +4. Name the file `0x0011_double-floating-point-data-type-h.bin` +5. Click **OK** + +### Step 22: Convert to UF2 Format + +**Open a terminal and navigate to your project directory:** + +```cmd +cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0011_double-floating-point-data-type +``` + +**Run the conversion command:** + +```cmd +python ..\uf2conv.py build\0x0011_double-floating-point-data-type-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +### Step 23: Flash the Hacked Binary + +1. Hold BOOTSEL and plug in your Pico 2 +2. Drag and drop `hacked.uf2` onto the RPI-RP2 drive +3. Open your serial monitor + +**You should see:** + +``` +fav_num: 99.990000 +fav_num: 99.990000 +fav_num: 99.990000 +... +``` + + **BOOM! We hacked the double!** The value changed from `42.52525` to `99.99`! + +--- + +## Part 3.95: Summary - Float and Double Analysis + +### What We Accomplished + +1. **Learned about IEEE 754** - How floating-point numbers are encoded in 32-bit (float) and 64-bit (double) formats +2. **Discovered float-to-double promotion** - `printf` with `%f` always receives a `double`, even when you pass a `float` +3. **Decoded register pairs** - 64-bit doubles are split across `r2` (low) and `r3` (high) +4. **Patched a float value** - Changed `42.5` to `99.0` by modifying only the high word +5. **Patched a double value** - Changed `42.52525` to `99.99` by modifying **both** words +6. **Understood the key difference** - Clean fractions (like `42.5`) have a zero low word; complex fractions (like `42.52525`) require patching both words + +### IEEE 754 Quick Reference for Common Values + +| Value | Double Hex | High Word (r3) | Low Word (r2) | +| -------- | ------------------------ | --------------- | -------------- | +| 42.0 | `0x4045000000000000` | `0x40450000` | `0x00000000` | +| 42.5 | `0x4045400000000000` | `0x40454000` | `0x00000000` | +| 42.52525 | `0x4045433B645A1CAC` | `0x4045433B` | `0x645A1CAC` | +| 43.0 | `0x4045800000000000` | `0x40458000` | `0x00000000` | +| 99.0 | `0x4058C00000000000` | `0x4058C000` | `0x00000000` | +| 99.99 | `0x4058FF5C28F5C28F` | `0x4058FF5C` | `0x28F5C28F` | +| 100.0 | `0x4059000000000000` | `0x40590000` | `0x00000000` | +| 3.14 | `0x40091EB851EB851F` | `0x40091EB8` | `0x51EB851F` | + +### The Float/Double Patching Workflow + +``` ++-----------------------------------------------------------------+ +| 1. Identify the float/double value in the decompiled view | +| - Look for hex constants like 0x40454000 or 0x4045433B | ++-----------------------------------------------------------------+ +| 2. Determine if it's float (32-bit) or double (64-bit) | +| - printf promotes floats to doubles! | +| - Check if value spans r2:r3 (double) or just r0 (float) | ++-----------------------------------------------------------------+ +| 3. Check if the low word (r2) is zero or non-zero | +| - Zero low word = only patch the high word | +| - Non-zero low word = patch BOTH words | ++-----------------------------------------------------------------+ +| 4. Calculate the new IEEE 754 encoding | +| - Convert your desired value to IEEE 754 | +| - Split into high/low words | ++-----------------------------------------------------------------+ +| 5. Patch the constant(s) in Ghidra | +| - Edit bytes in the Bytes window (Pencil mode) | +| - Replace the old encoding with the new one | ++-----------------------------------------------------------------+ +| 6. Export -> Convert to UF2 -> Flash -> Verify | +| - Same workflow as integer patching | ++-----------------------------------------------------------------+ +``` + +> Tip: **Key takeaway:** Hacking doubles is the same process as hacking floats - find the IEEE 754 constant, calculate the new encoding, patch it. The only extra step is checking whether the **low word** (`r2`) is also non-zero. Clean values like `42.5` only need one patch; messy fractions like `42.52525` need two! + +--- + +--- + +## Key Takeaways + +1. **Integers have fixed sizes** - `uint8_t` is 1 byte (0-255), `int8_t` is 1 byte (-128 to 127). The `u` prefix means unsigned. + +2. **IEEE 754 encodes floats in binary** - Sign bit, exponent (with bias), and mantissa form the encoding for both 32-bit floats and 64-bit doubles. + +3. **printf promotes floats to doubles** - Even when you pass a `float`, `printf` receives a 64-bit `double` due to C's variadic function rules. + +4. **64-bit values span two registers** - On ARM Cortex-M33, doubles use `r2` (low 32 bits) and `r3` (high 32 bits). + +5. **Clean fractions have zero low words** - Values like `42.5` have `0x00000000` in the low word; complex fractions like `42.52525` have non-zero low words. + +6. **Inline assembly controls hardware directly** - The `mcrr` coprocessor instruction talks to the GPIO block without any SDK overhead. + +7. **Binary patching works on any data type** - Integers, floats, and doubles can all be patched in Ghidra using the same workflow. + +--- + +## Glossary + +| Term | Definition | +| ----------------------- | ------------------------------------------------------------------------------ | +| **Bias** | Constant added to the exponent in IEEE 754 (127 for float, 1023 for double) | +| **Double** | 64-bit floating-point type following IEEE 754 double-precision format | +| **Exponent** | Part of IEEE 754 encoding that determines the magnitude of the number | +| **Float** | 32-bit floating-point type following IEEE 754 single-precision format | +| **FUNCSEL** | Function Select - register field that assigns a GPIO pin's function (e.g., SIO)| +| **GPIO** | General Purpose Input/Output - controllable pins on a microcontroller | +| **IEEE 754** | International standard for floating-point arithmetic and binary encoding | +| **Inline Assembly** | Assembly code embedded directly within C source using `__asm volatile` | +| **int8_t** | Signed 8-bit integer type (-128 to 127) | +| **IO_BANK0** | Register block at `0x40028000` that controls GPIO pin function selection | +| **Mantissa** | Fractional part of IEEE 754 encoding (23 bits for float, 52 bits for double) | +| **mcrr** | ARM coprocessor register transfer instruction used for GPIO control | +| **PADS_BANK0** | Register block at `0x40038000` that controls GPIO pad electrical properties | +| **Promotion** | Automatic conversion of a smaller type to a larger type (float -> double) | +| **Register Pair** | Two 32-bit registers (r2:r3) used together to hold a 64-bit value | +| **UF2** | USB Flashing Format - file format for Pico 2 firmware | +| **uint8_t** | Unsigned 8-bit integer type (0 to 255) | + +--- + +## Additional Resources + +### GPIO Coprocessor Reference + +The RP2350 GPIO coprocessor instructions: + +| Instruction | Description | +| --------------------------- | ---------------------------- | +| `mcrr p0, #4, Rt, Rt2, c0` | Set/clear GPIO output | +| `mcrr p0, #4, Rt, Rt2, c4` | Set/clear GPIO output enable | + +### RP2350 Memory Map Quick Reference + +| Address | Description | +| ------------ | ------------------------ | +| `0x10000000` | XIP Flash (code) | +| `0x20000000` | SRAM (data) | +| `0x40028000` | IO_BANK0 (GPIO control) | +| `0x40038000` | PADS_BANK0 (pad control) | +| `0xd0000000` | SIO (single-cycle I/O) | + +### IEEE 754 Encoding Formula + +``` ++-----------------------------------------------------------------+ +| Float (32-bit): [1 sign] [8 exponent] [23 mantissa] | +| Double (64-bit): [1 sign] [11 exponent] [52 mantissa] | +| | +| Value = (-1)^sign * 2^(exponent - bias) * (1 + mantissa) | +| | +| Float bias: 127 | +| Double bias: 1023 | ++-----------------------------------------------------------------+ +``` + +--- + +**Remember:** Every binary you encounter in the real world can be analyzed and understood using these same techniques. Whether it's an integer, a float, or a double - it's all just bits waiting to be decoded. Practice makes perfect! + +Happy hacking! + diff --git a/WEEK05/WEEK05.pdf b/WEEK05/WEEK05.pdf new file mode 100644 index 0000000..a0cc5b1 Binary files /dev/null and b/WEEK05/WEEK05.pdf differ diff --git a/WEEK05/WEEK05a.md b/WEEK05/WEEK05a.md new file mode 100644 index 0000000..d454f5b --- /dev/null +++ b/WEEK05/WEEK05a.md @@ -0,0 +1,611 @@ +# Week 5a: Deep-Dive Hardware Architecture: RP2350 PADS_BANK0, IO_BANK0, SIO, and the GPIO Coprocessor (GPIOC) + +*** +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: + +- Master the physical silicon reality of General Purpose Input/Output (GPIO) on the RP2350 microcontroller from Chapter 9 of the datasheet. +- Understand why microcontroller pins are never simple wires, dissecting the split between the internal 1.1V digital core domain ($V_{\text{core}} / \text{DVDD}$) and the external 3.3V analog pad ring domain ($V_{\text{io}} / \text{IOVDD}$). +- Decouple the two independent hardware layers: electrical pad configuration (`PADS_BANK0` at `0x40038000`) versus digital crossbar multiplexing (`IO_BANK0` at `0x40028000`). +- Dissect every bitfield of `PADS_BANK0`: Output Disable (`OD`), Input Enable (`IE`), Pad Isolation (`ISO`), Drive Strength ($2/4/8/12\text{ mA}$), Pull-Up (`PUE`), Pull-Down (`PDE`), Schmitt Trigger (`SCHMITT`), and Slew Rate (`SLEWFAST`). +- Discover why unconfigured RP2350 pins are physically isolated by default (`ISO = 1`) and why digital input buffers are disconnected (`IE = 0`) to prevent catastrophic CMOS shoot-through leakage current. +- Master RP2350 **Bus Keeper Mode**, activated by setting both `PUE = 1` and `PDE = 1` to weakly latch floating line states without power dissipation. +- Navigate the digital crossbar switchboard in `IO_BANK0`: function selection (`FUNCSEL 0..31`), peripheral routing (UART, SPI, I2C, PWM, SIO, PIO), and signal overrides (`OUTOVER`, `OEOVER`, `INOVER`, `IRQOVER`). +- Analyze edge-sensitive and level-sensitive hardware interrupt generation across `proc0`, `proc1`, and `dormant_wake`. +- Understand the hardware Single-Cycle I/O (`SIO` at `0xd0000000`) block and contrast standard MMIO load/store operations against the RP2350 **GPIO Coprocessor (GPIOC)** on ARM Cortex-M33 Coprocessor Port `p0` (Section 3.6.1 and Section 9.9). +- Trace Thumb-2 coprocessor instructions (`mcrr p0`, `mcr p0`, `mrrc p0`) that set, clear, toggle, and configure GPIO output-enables in a single clock cycle with zero memory bus arbitration. +- Deconstruct the assembly implementation in `0x000b_integer-data-type.c`, performing a cycle-accurate trace of `asm_init_gpio_range()` and `asm_blink_pin()`. +- Eliminate the disassembler "hardware MMIO blind spot" using PyCortexMDebug in GDB for live dynamic peripheral struct inspection. +- Import CMSIS-SVD hardware definitions into Ghidra using SVD-Loader-Ghidra to reconstruct clear, readable decompilation from raw binary firmware. + +--- + +## Part 1: Why Microcontroller Pins Are Not Simple Wires + +### The Silicon Reality: Core Logic vs. The Pad Ring + +In software, a developer writes `gpio_put(16, 1)` and visualizes a direct wire connecting a CPU register bit to an external copper pin. In silicon engineering, this concept is completely false. + +A modern microcontroller die like the RP2350 is divided into two radically distinct physical and electrical domains: + +1. **The Internal Digital Core Domain ($1.1\text{ V}$ nominal $\text{DVDD}$):** + The central silicon die contains the dual ARM Cortex-M33 processors, the bus fabric, static RAM, cache controllers, and digital peripheral state machines (UART, SPI, PIO). These circuits are fabricated using ultra-thin, low-voltage nanoscale transistors designed exclusively for high switching speeds ($150\text{ MHz}$) and ultra-low power consumption. These microscopic gates cannot tolerate voltages above $1.21\text{ V}$. Connecting an external $3.3\text{ V}$ signal directly to a core gate would instantly cause dielectric oxide breakdown, permanently melting the transistor. + +2. **The External Pad Ring Domain ($1.8\text{ V} - 3.3\text{ V}$ nominal $\text{IOVDD}$):** + Surrounding the perimeter of the die is the **Pad Ring**. The pad ring consists of specialized, high-voltage analog and electrical interface cells ("pad cells") fabricated with thick-oxide transistors. These cells translate internal $1.1\text{ V}$ digital logic signals up to external $3.3\text{ V}$ board voltages, provide electrostatic discharge (ESD) protection diodes, supply high output drive currents (up to $12\text{ mA}$) to charge external capacitive PCB traces, and filter noisy incoming analog edges. +``` ++-----------------------------------------------------------------+ +| RP2350 GPIO HARDWARE SUBSYSTEM ARCHITECTURE | +| | +| ARM Cortex-M33 Core / DMA / Interconnect | +| +---------------------------------------------------------+ | +| | Coprocessor Port p0 (GPIOC) / SIO (0xd0000000) | | +| | - 1-cycle atomic bit set/clear/toggle/oe (mcrr/mcr) | | +| | - 32-bit MMIO registers: GPIO_OUT, GPIO_OE, GPIO_IN | | +| +----------------------------+----------------------------+ | +| | | +| On-Chip Peripherals | | +| +----------------------------+----------------------------+ | +| | UART0/1, SPI0/1, I2C0/1, PWM, PIO0/1/2, CLK_GP, HSTX | | +| +----------------------------+----------------------------+ | +| | | +| v | +| Digital Routing Domain (clk_sys / 150 MHz) | +| +---------------------------------------------------------+ | +| | IO_BANK0 (0x40028000) - Digital Crossbar Switchboard | | +| | - FUNCSEL (bits 4:0): Selects function (0..31) | | +| | - Signal Overrides: OUTOVER, OEOVER, INOVER, IRQOVER | | +| | - Edge/Level Interrupt Detection (PROC0/1/DORMANT) | | +| +----------------------------+----------------------------+ | +| | | +| v | +| Physical / Electrical Domain (IOVDD 1.8V - 3.3V) | +| +---------------------------------------------------------+ | +| | PADS_BANK0 (0x40038000) - Die Perimeter Pad Ring | | +| | - ISO (bit 8): Pad Isolation Latch (frozen on reset!) | | +| | - OD / IE (bits 7,6): Output Disable / Input Enable | | +| | - DRIVE (bits 5:4): 2mA / 4mA / 8mA / 12mA Strength | | +| | - PUE / PDE (bits 3,2): Pull-Up / Pull-Down / Keeper | | +| | - SCHMITT / SLEWFAST (bits 1,0): Hysteresis & Slew | | +| +----------------------------+----------------------------+ | +| | | +| v | +| Physical Bond Pad & Pin | ++-----------------------------------------------------------------+ +``` +### The Physical Hazards of Uncontrolled I/O + +Why can't a microcontroller provide a single "GPIO register" where bit $n$ directly drives pin $n$? Because every physical pin faces severe physical and electrical constraints: + +- **CMOS Shoot-Through Current:** When a CMOS digital input buffer encounters a voltage near mid-rail (e.g., $1.65\text{ V}$ on a $3.3\text{ V}$ rail), both the upper P-channel and lower N-channel MOSFETs turn partially ON simultaneously. This creates a low-resistance path directly from `IOVDD` to ground, dissipating substantial leakage current and generating heat. To prevent this on unused or analog pins (such as ADC inputs), the chip must provide a hardware switch to completely disconnect the input buffer (`IE = 0`). +- **Electrostatic Discharge (ESD):** External human touch or inductive spikes can inject thousands of volts into a pin. Pad cells integrate dual clamping diodes to shunt overvoltage spikes safely into power and ground rails. +- **Power Domain Collapse during Deep Sleep:** When the RP2350 enters low-power dormant sleep, the $1.1\text{ V}$ core voltage can be completely powered down by the Power Manager (`POWMAN`). If the pad ring were directly connected to the core, the unpowered core outputs would float, causing external actuators, transceivers, or power supplies to turn on uncontrollably. The pad ring must therefore integrate **Pad Isolation Latches (`ISO`)** that freeze the external electrical state even when the CPU and digital core no longer exist! + +To solve these orthogonal challenges, the RP2350 divides GPIO into a clean three-tier hardware hierarchy: +- **`PADS_BANK0` (`0x40038000`):** Governs the electrical pad cells along the silicon die perimeter. +- **`IO_BANK0` (`0x40028000`):** Governs digital routing, multiplexing, and signal overrides. +- **`SIO` (`0xd0000000`) & Coprocessor Port `p0`:** Governs single-cycle processor execution. + +--- + +## Part 2: PADS_BANK0 Deep Dive: The Electrical Pad Ring + +### Physical Placement and Register Map + +The User Bank Pad Control registers reside at base address **`0x40038000`** (`PADS_BANK0_BASE`). + +According to **Chapter 9.11.3 (Table 851)** of the RP2350 Datasheet: +- Offset `0x00`: **`VOLTAGE_SELECT`** (Bank-wide voltage threshold: `0 = 3.3V`, `1 = 1.8V`). +- Offset `0x04`: **`GPIO0`** pad control register. +- Offset `0x08`: **`GPIO1`** pad control register. +- ... +- Offset `0x04 + (4 * x)`: **`GPIOx`** pad control register. + +Each 32-bit register controls the analog and physical circuitry of exactly one pin. +``` ++-----------------------------------------------------------------+ +| ANATOMY OF A SINGLE RP2350 PAD CELL | +| | +| IO_BANK0 Digital Signal PADS_BANK0 Controls | +| +-----------------------+ +-------------------+ | +| | OUTTOPAD (Data) | | DRIVE (2/4/8/12mA)| | +| | OETOPAD (Enable) | | SLEWFAST (0/1) | | +| +-----------+-----------+ | ISO (Isolation) | | +| | | OD (Out Disable) | | +| v +---------+---------+ | +| [ISO Latch (bit 8)] | | +| | | | +| v v | +| +---------------+ +-------------------+ | +| | Output Buffer |<----------------| High-Voltage CMOS | | +| | Driver Stage | | Push-Pull MOSFETs | | +| +-------+-------+ +---------+---------+ | +| | | | +| +-----------------+-----------------+ | +| | | +| +-----------------------------+---------------------------+ | +| | PHYSICAL PAD (External Pin) | | +| | - ESD Protection Diodes to IOVDD and GND | | +| | - Switchable 50k Pull-Up (PUE) to IOVDD | | +| | - Switchable 50k Pull-Down (PDE) to GND | | +| | - Bus Keeper: PUE=1 + PDE=1 (Weak State Retention) | | +| +-----------------------------+---------------------------+ | +| | | +| v | +| [Schmitt Trigger Filter] | +| | | +| v | +| [Input Enable Gate (IE)] | +| | | +| v | +| INFROMPAD to IO_BANK0 | ++-----------------------------------------------------------------+ +``` +### The PADS_BANK0 Bitfield Breakdown + +Every 32-bit `GPIOx` register in `PADS_BANK0` has the exact bitfield structure defined in **Table 853**: +``` ++-----------------------------------------------------------------+ +| PADS_BANK0: GPIOx REGISTER BITFIELDS (Offset: 0x04 + 4*x) | +| | +| 31 9 8 7 6 5 4 3 2 1 0 | +| +---------------+---+---+---+-------+---+---+---+---+ | +| | Reserved |ISO|OD |IE | DRIVE |PUE|PDE|SCH|SLW| | +| +---------------+---+---+---+-------+---+---+---+---+ | +| | +| Bit 8: ISO - Pad isolation latch (1 = isolated/frozen) | +| Bit 7: OD - Output disable (1 = output driver off) | +| Bit 6: IE - Input enable (1 = digital input buffer on)| +| Bits 5:4 DRIVE - Drive strength (00=2mA, 01=4mA, 10=8mA, | +| 11=12mA) | +| Bit 3: PUE - Pull-up resistor enable (~50 kOhm) | +| Bit 2: PDE - Pull-down resistor enable (~50 kOhm) | +| PUE & PDE == 1 - Bus Keeper Mode (weakly holds last state) | +| Bit 1: SCHMITT - Schmitt trigger hysteresis filter | +| Bit 0: SLEWFAST - Slew rate control (0 = Slow, 1 = Fast) | ++-----------------------------------------------------------------+ +``` +Let us examine each bitfield with silicon-level precision: + +#### 1. ISO (Bit 8) - Pad Isolation Control +- **Reset Value:** `0x1` (Isolated!). +- **Function:** When `ISO = 1`, an analog transmission gate isolates the pad from the switched core domain. The output driver state, output enable, and pull resistors remain frozen at their last latched values. +- **The Startup Gotcha:** Upon microcontroller power-up or reset, `ISO` resets to `1` across all pads! Before any pin can be driven or read by software, **the application software must explicitly clear the `ISO` bit**. If you attempt to configure `IO_BANK0` or `SIO` while `ISO = 1`, the external pin will remain totally unresponsive! + +#### 2. OD (Bit 7) - Output Disable +- **Reset Value:** `0x0`. +- **Function:** Master hardware output disable. When `OD = 1`, the pad's output driver transistors are completely turned OFF, forcing the pin into high-impedance (High-Z) mode. +- **Priority:** `OD` has absolute silicon priority over any output-enable signal emitted by internal peripherals (UART, SPI, PWM, SIO). Even if the UART hardware attempts to transmit, setting `OD = 1` silences the pin. + +#### 3. IE (Bit 6) - Input Enable +- **Reset Value:** `0x0` (Disabled!). +- **Function:** Master hardware gate for the digital input buffer. +- **The Analog Protection Rule:** When a pin is used as an analog input (e.g., ADC pins GPIO 26..29), or when a pin is left disconnected/floating, `IE` must remain `0`. Disabling the input buffer prevents floating voltages from causing CMOS shoot-through currents across internal transistor pairs. +- **The Input Gotcha:** If you want to read digital input from a button or serial line using SIO (`sio_hw->gpio_in`), you **must explicitly set `IE = 1`** in `PADS_BANK0`. If `IE = 0`, reading `GPIO_IN` will always return `0`, regardless of the voltage applied to the physical pin! + +#### 4. DRIVE (Bits 5:4) - Output Drive Strength +- **Reset Value:** `0x1` ($4\text{ mA}$). +- **Options:** `00 = 2mA`, `01 = 4mA`, `10 = 8mA`, `11 = 12mA`. +- **Silicon Implementation:** Inside the pad cell, the output push-pull stage consists of parallel arrays of PMOS (pull-up) and NMOS (pull-down) transistors. Selecting $2\text{ mA}$ enables a single transistor pair; selecting $12\text{ mA}$ switches on all parallel pairs, dramatically lowering the driver's output resistance ($R_{\text{on}}$). +- **Engineering Trade-off:** High drive strength ($12\text{ mA}$) charges external trace capacitance rapidly, enabling high-speed $50\text{ MHz}+$ SPI clocks. However, it generates sharp current transients ($dI/dt$), leading to ground bounce, ringing, and electromagnetic interference (EMI). Low drive strength ($2\text{ mA}$) softens transitions, producing clean, low-noise waveforms on sensitive sensor buses. + +#### 5. PUE (Bit 3) & PDE (Bit 2) - Pull-Up and Pull-Down Resistors +- **Reset Value:** `PUE = 0`, `PDE = 1` (Pads reset with weak pull-downs active!). +- **Electrical Specification:** Nominal internal resistance of approximately $50\text{ k}\Omega$ to `IOVDD` (PUE) or ground (PDE). +- **Bus Keeper Mode (Section 9.6.1):** What happens if software sets both `PUE = 1` and `PDE = 1` simultaneously? On older microcontrollers, this would create an illegal cross-conduction voltage divider dissipating power. On the RP2350, setting both bits activates **Bus Keeper Mode**! In this mode: + - If the pad's voltage is High, the pad pulls weakly Up. + - If the pad's voltage is Low, the pad pulls weakly Down. + When an external device stops driving a shared bus (e.g., a bidirectional data line in tri-state), the Bus Keeper holds the last valid logic level indefinitely, preventing the line from floating into mid-rail states without drawing active power! + +#### 6. SCHMITT (Bit 1) - Schmitt Trigger Enable +- **Reset Value:** `0x1` (Enabled). +- **Function:** Adds voltage hysteresis to the input comparator ($V_{\text{IH}} \approx 2.0\text{ V}$, $V_{\text{IL}} \approx 0.8\text{ V}$). If an incoming digital signal has a slow rise time or is corrupted by high-frequency noise, the Schmitt trigger prevents false multi-triggering and oscillation at the digital threshold. + +#### 7. SLEWFAST (Bit 0) - Slew Rate Control +- **Reset Value:** `0x0` (Slow). +- **Function:** `0 = Slow`, `1 = Fast`. Slow slew rate actively limits the edge rate ($dV/dt$) of the output buffer, minimizing radiated emissions and transmission-line reflections on unterminated PCB traces. Fast slew rate is required for high-frequency interfaces like QSPI, HSTX, or high-speed UART. + +--- + +## Part 3: IO_BANK0 Deep Dive: The Digital Crossbar Switchboard + +### The Multiplexer Matrix (`0x40028000`) + +Once a signal passes through the electrical pad ring, it enters the digital core domain. Here sits **`IO_BANK0`**, located at base address **`0x40028000`** (`IO_BANK0_BASE`). + +`IO_BANK0` functions as an enormous digital crossbar switchboard. The RP2350 contains dozens of peripheral controllers: +- Two SPI controllers (`SPI0`, `SPI1`) +- Two UART controllers (`UART0`, `UART1`) +- Two I2C controllers (`I2C0`, `I2C1`) +- Up to 12 dual-channel PWM slices (`PWM0` through `PWM11`) +- Three Programmable I/O blocks (`PIO0`, `PIO1`, `PIO2`) +- Single-Cycle I/O (`SIO`) +- General Purpose Clock outputs (`CLK_GP`) +- High-Speed Serial Transmitter (`HSTX`) + +None of these peripherals own any dedicated physical pins! Instead, every peripheral signal is routed through `IO_BANK0`, which selects which peripheral connects to which pad. + +### Register Structure per Pin + +In `IO_BANK0`, every GPIO pin occupies an **8-byte stride** containing two 32-bit registers (Section 9.11.1, Table 649): + +$$\text{GPIOx\_STATUS Offset} = 0\text{x}000 + (8 \times x)$$ +$$\text{GPIOx\_CTRL Offset} = 0\text{x}004 + (8 \times x)$$ + +For example, for GPIO 16: +$$\text{GPIO16\_CTRL Offset} = 0\text{x}004 + (8 \times 16) = 0\text{x}004 + 0\text{x}080 = 0\text{x}084$$ +$$\text{Physical Address} = 0\text{x}40028000 + 0\text{x}084 = 0\text{x}40028084$$ + +### The GPIOx_CTRL Register: Function Selection & Overrides +``` ++-----------------------------------------------------------------+ +| IO_BANK0: GPIOx_CTRL REGISTER BITFIELDS (Offset: 0x04+8*x)| +| | +| 31 30 29 28 27 18 17 16 15 14 13 12 11 5 4 0 | +| +-----+-----+---------+-----+-----+-----+---------+---------+ | +| |Rsvd |IRQO |Reserved |INOV |OEOV |OUTOV|Reserved | FUNCSEL | | +| +-----+-----+---------+-----+-----+-----+---------+---------+ | +| | +| Bits 4:0 FUNCSEL - Function Select (0..31, 5=SIO, 31=NULL) | +| Bits 13:12 OUTOVER - Output signal override (0=norm, 1=invert, | +| 2=force low, 3=force high) | +| Bits 15:14 OEOVER - Output enable override (0=norm, 1=invert, | +| 2=force disable, 3=force enable) | +| Bits 17:16 INOVER - Input signal override (0=norm, 1=invert, | +| 2=force low, 3=force high) | +| Bits 29:28 IRQOVER - Interrupt override (0=norm, 1=invert, | +| 2=force low, 3=force high) | ++-----------------------------------------------------------------+ +``` +According to **Table 651**, the bitfields of `GPIOx_CTRL` provide ultimate routing and logic control: + +#### 1. FUNCSEL (Bits 4:0) - Function Select +A 5-bit multiplexer index determining which peripheral controls the pin: +- `0x00`: JTAG +- `0x01`: SPI +- `0x02`: UART +- `0x03`: I2C +- `0x04`: PWM +- **`0x05`: SIO (Single-Cycle I/O - CPU / Coprocessor Software Control)** +- `0x06`: PIO0 +- `0x07`: PIO1 +- `0x08`: PIO2 +- `0x09`: Clock / Auxiliary +- `0x1f` (`31`): **NULL / Disconnected** (Reset Value!) + +Upon reset, `FUNCSEL` is set to `0x1f` (NULL). The pin is completely disconnected from all internal peripherals. To control a pin via CPU software or inline assembly, you must write `5` to `FUNCSEL`. + +#### 2. Signal Overrides (OUTOVER, OEOVER, INOVER, IRQOVER) +The RP2350 allows hardware hackers and defensive engineers to intercept, invert, or force signals between peripherals and pads without reconfiguring peripheral registers: + +| Override Field | Bits | Values & Behavior | +|:---|:---|:---| +| **`OUTOVER`** | `13:12` | `0 = Normal` (Peripheral drives data)
`1 = Invert` (Invert peripheral data)
`2 = Force Low` (Drive 0 to pad)
`3 = Force High` (Drive 1 to pad) | +| **`OEOVER`** | `15:14` | `0 = Normal` (Peripheral drives OE)
`1 = Invert` (Invert OE)
`2 = Force Disable` (Disable output driver)
`3 = Force Enable` (Enable output driver) | +| **`INOVER`** | `17:16` | `0 = Normal` (Pad drives peripheral)
`1 = Invert` (Invert pad data to peripheral)
`2 = Force Low` (Peripheral sees 0)
`3 = Force High` (Peripheral sees 1) | +| **`IRQOVER`** | `29:28` | `0 = Normal` (Pad drives interrupt)
`1 = Invert` (Invert interrupt logic)
`2 = Force Low` (Deassert IRQ)
`3 = Force High` (Assert IRQ) | + +*Security/Hacking Implication:* If a target firmware runs a proprietary UART protocol on GPIO 0/1, an analyst can invert the transmitted serial stream in hardware by writing `0x1` to `OUTOVER`, without the target firmware ever detecting that its serial framing has been altered! + +### The GPIOx_STATUS Register: Real-Time Silicon Probing + +The read-only `GPIOx_STATUS` register (+`0x000`) provides a live digital probe inside the chip (Table 650): +- **`INFROMPAD` (Bit 17):** The raw digital logic level coming from the pad cell (after Schmitt trigger, before any `INOVER` inversion). +- **`OETOPAD` (Bit 13):** The effective Output Enable signal being transmitted to the pad cell after all overrides. +- **`OUTTOPAD` (Bit 9):** The effective Output Data signal being transmitted to the pad cell after all overrides. +- **`IRQTOPROC` (Bit 26):** The interrupt status signal sent to the CPU NVIC. + +--- + +## Part 4: SIO vs. The RP2350 GPIO Coprocessor (GPIOC) + +### The Single-Cycle I/O (SIO) Architecture + +When a pin's `FUNCSEL` is set to `5` in `IO_BANK0`, it connects to the **SIO (Single-Cycle I/O)** block, located at base address **`0xd0000000`** (`SIO_BASE`). + +SIO provides fast, non-blocking registers that allow the ARM CPU to manipulate GPIOs in 1 clock cycle: +- `GPIO_OUT` (`0xd0000010`): Direct 32-bit output value. +- `GPIO_OUT_SET` (`0xd0000014`): Atomic bit set (writing a `1` sets the corresponding pin High). +- `GPIO_OUT_CLR` (`0xd0000018`): Atomic bit clear (writing a `1` clears the corresponding pin Low). +- `GPIO_OUT_XOR` (`0xd000001c`): Atomic bit toggle (writing a `1` inverts the corresponding pin). +- `GPIO_OE` (`0xd0000020`), `GPIO_OE_SET`, `GPIO_OE_CLR`, `GPIO_OE_XOR`: Output Enable registers. +- `GPIO_IN` (`0xd0000004`): Direct 32-bit digital input read. + +### The Memory Bus Overhead Bottleneck + +While SIO registers respond in a single clock cycle, interacting with them via standard ARM memory-mapped instructions (`ldr`, `str`) suffers from severe software overhead: + +1. **Address Materialization:** Cortex-M33 instructions cannot embed a 32-bit address like `0xd0000014` directly in a single 16-bit Thumb opcode. The compiler must generate an `ldr r0, =0xd0000014` instruction, pulling the 32-bit address from a Flash literal pool (taking 2 cycles plus cache/wait-states). +2. **Register Pressure:** The CPU must allocate a general-purpose register (`r0`) to hold the address and a second register (`r1`) to hold the bitmask (`1 << pin`). +3. **Bus Arbitration:** Even though SIO is single-cycle, the `str` instruction still arbitrates across the internal AHB-Lite bus matrix. +``` ++-----------------------------------------------------------------+ +| SIO MMIO ACCESS VS. GPIOC COPROCESSOR PORT P0 | +| | +| Standard SIO Access (3 to 5 Instructions / Bus Bottleneck) | +| +---------------------------------------------------------+ | +| | ldr r0, =0xd0000000 ; Load 32-bit SIO base | | +| | movs r1, #1 ; Load bitmask | | +| | lsls r1, r1, #16 ; Shift to pin 16 | | +| | str r1, [r0, #0x14] ; Write to GPIO_OUT_SET | | +| | => Requires register allocation, memory bus arbitration| | +| +---------------------------------------------------------+ | +| | +| RP2350 GPIOC Coprocessor Access (1 Instruction / Zero Bus) | +| +---------------------------------------------------------+ | +| | mcrr p0, #4, r4, r5, c0 ; gpioc_bit_out_put(pin,val) | | +| | - r4 = pin number (16) | | +| | - r5 = value (1 = High, 0 = Low) | | +| | => 1 CPU clock cycle! Zero bus traffic! Zero address! | | +| +---------------------------------------------------------+ | ++-----------------------------------------------------------------+ +``` +### The Silicon Breakthrough: GPIOC on Coprocessor Port p0 + +To eliminate this overhead entirely, Raspberry Pi engineered a custom hardware **GPIO Coprocessor (GPIOC)** directly into each ARM Cortex-M33 core on **Coprocessor Port `p0`** (Datasheet Section 3.6.1 and Section 9.9). + +Instead of issuing memory load and store instructions across the system bus, the ARM core uses native ARMv8-M coprocessor instructions (`mcr`, `mcrr`, `mrc`, `mrrc`): +``` ++-----------------------------------------------------------------+ +| COMPLETE RP2350 GPIO SIGNAL FLOW PATH | +| | +| [ Cortex-M33 CPU ] | +| | | +| (Coprocessor p0 / SIO) | +| v | +| [ SIO Block (0xd0000000) ] | +| | (Raw Out Data & Out Enable) | +| v | +| [ IO_BANK0 Crossbar (0x40028000) ] | +| | - Mux: FUNCSEL = 5 (Select SIO) | +| | - Apply OUTOVER / OEOVER overrides | +| v | +| [ Voltage Level Shifter ] (1.1V DVDD Core -> 3.3V IOVDD Pad) | +| v | +| [ PADS_BANK0 (0x40038000) ] | +| | - Check ISO latch (Must be cleared = 0) | +| | - Check OD bit (Must be cleared = 0) | +| | - Apply DRIVE strength (2/4/8/12mA) and SLEWFAST | +| v | +| [ Physical Bond Pad & Package Pin ] | +| | | +| |------> External Circuit (e.g. LED, Scope, Target) | +| | | +| [ Schmitt Trigger Input Buffer ] | +| | | +| [ PADS_BANK0 IE Gate ] (Must be set = 1 to pass) | +| v | +| [ Voltage Level Shifter ] (3.3V IOVDD Pad -> 1.1V DVDD Core) | +| v | +| [ IO_BANK0 INFROMPAD ] | +| | - Apply INOVER override | +| | - Route to Edge/Level Interrupt Detector | +| v | +| [ SIO GPIO_IN Register / Peripheral RX ] | ++-----------------------------------------------------------------+ +``` +According to **Section 3.6.1**, the GPIOC instruction set provides instantaneous, zero-latency GPIO control: + +#### 1. Single-Bit Output Enable (gpioc_bit_oe_put) +$$\text{Opcode: } \texttt{mcrr p0, #4, Rt, Rt2, c4}$$ +- `Rt`: General-purpose register holding the pin number ($0..47$). +- `Rt2`: General-purpose register holding the 1-bit value ($1 = \text{Output}, 0 = \text{Input}$). +- **Operation:** Atomically sets or clears the output enable for pin `Rt` in **1 single clock cycle**! No 32-bit addresses, no bit-shifts (`1 << pin`), and zero bus traffic! + +#### 2. Single-Bit Output Data (gpioc_bit_out_put) +$$\text{Opcode: } \texttt{mcrr p0, #4, Rt, Rt2, c0}$$ +- `Rt`: Pin number ($0..47$). +- `Rt2`: Value ($1 = \text{High}, 0 = \text{Low}$). +- **Operation:** Atomically drives pin `Rt` High or Low in 1 clock cycle. + +#### 3. Single-Pin Direct Toggling / Setting / Clearing +- Toggle pin `Rt`: `mcr p0, #5, Rt, c0, c0` (`gpioc_bit_out_xor`) +- Set pin `Rt` High: `mcr p0, #6, Rt, c0, c0` (`gpioc_bit_out_set`) +- Clear pin `Rt` Low: `mcr p0, #7, Rt, c0, c0` (`gpioc_bit_out_clr`) + +#### 4. 64-Bit Simultaneous Sampling +$$\text{Opcode: } \texttt{mrrc p0, #0, Rt, Rt2, c0}$$ +- Reads back all 48 GPIO pins simultaneously into register pair `Rt:Rt2` in a single clock cycle! + +--- + +## Part 5: Deconstructing 0x000b_integer-data-type.c + +Now let us examine the production firmware in `0x000b_integer-data-type/0x000b_integer-data-type.c`. This program configures pins 16, 17, and 18 entirely through raw assembly, manipulating `PADS_BANK0`, `IO_BANK0`, and the `GPIOC` coprocessor. + +### Dissecting asm_init_gpio_range() + +```c +static void asm_init_gpio_range(void) { + __asm volatile ( + "ldr r3, =0x40038000\n" // address of PADS_BANK0_BASE + "ldr r2, =0x40028004\n" // address of IO_BANK0 GPIO0.ctrl + "movs r0, #16\n" // GPIO16 (start pin) + "init_loop:\n" // loop start + "lsls r1, r0, #2\n" // pin * 4 (pad offset) + "adds r4, r3, r1\n" // PADS base + offset + "ldr r5, [r4]\n" // load current config + "bic r5, r5, #0x180\n" // clear OD+ISO + "orr r5, r5, #0x40\n" // set IE + "str r5, [r4]\n" // store updated config + "lsls r1, r0, #3\n" // pin * 8 (ctrl offset) + "adds r4, r2, r1\n" // IO_BANK0 base + offset + "ldr r5, [r4]\n" // load current config + "bic r5, r5, #0x1f\n" // clear FUNCSEL bits [4:0] + "orr r5, r5, #5\n" // set FUNCSEL = 5 (SIO) + "str r5, [r4]\n" // store updated config + "mov r4, r0\n" // pin + "movs r5, #1\n" // bit 1; used for OUT/OE writes + "mcrr p0, #4, r4, r5, c4\n" // gpioc_bit_oe_put(pin,1) + "adds r0, r0, #1\n" // increment pin + "cmp r0, #20\n" // stop after pin 18 + "blt init_loop\n" // loop until r0 == 20 + ); +} +``` + +### Line-by-Line Machine Code and Silicon Execution Trace + +1. **`ldr r3, =0x40038000`:** Loads `PADS_BANK0_BASE` into `r3`. +2. **`ldr r2, =0x40028004`:** Loads the address of `IO_BANK0: GPIO0_CTRL` into `r2`. +3. **`movs r0, #16`:** Sets loop counter starting at GPIO 16. +4. **`lsls r1, r0, #2`:** Multiplies pin number by 4 ($16 \times 4 = 64 = 0\text{x}40$). +5. **`adds r4, r3, r1`:** Calculates the pad register address ($0\text{x}40038000 + 0\text{x}40 = 0\text{x}40038040$). +6. **`ldr r5, [r4]`:** Reads current pad configuration. +7. **`bic r5, r5, #0x180`:** Clears bits 7 and 8 ($0\text{x}180 = 0001\,1000\,0000_2$): + - **Bit 7 (`OD`):** Cleared to `0` $\rightarrow$ Output driver enabled! + - **Bit 8 (`ISO`):** Cleared to `0` $\rightarrow$ Pad isolation latch de-asserted! The pad is connected to internal core logic! +8. **`orr r5, r5, #0x40`:** Sets bit 6 ($0\text{x}40 = 0100\,0000_2$): + - **Bit 6 (`IE`):** Set to `1` $\rightarrow$ Input buffer enabled! +9. **`str r5, [r4]`:** Writes new configuration back to `PADS_BANK0`. The physical pad cell is now electrically active! +10. **`lsls r1, r0, #3`:** Multiplies pin number by 8 ($16 \times 8 = 128 = 0\text{x}80$). +11. **`adds r4, r2, r1`:** Calculates `GPIO16_CTRL` address ($0\text{x}40028004 + 0\text{x}80 = 0\text{x}40028084$). +12. **`ldr r5, [r4]`:** Reads current control register. +13. **`bic r5, r5, #0x1f`:** Clears bits 4:0 (`FUNCSEL`), wiping out the reset value of `31` (NULL). +14. **`orr r5, r5, #5`:** Sets `FUNCSEL = 5` $\rightarrow$ Connects pin 16 to the SIO block! +15. **`str r5, [r4]`:** Writes back to `IO_BANK0`. The digital routing switchboard is now connected! +16. **`mcrr p0, #4, r4, r5, c4`:** Calls `gpioc_bit_oe_put(16, 1)`. The GPIO coprocessor on port `p0` sets the Output Enable bit in SIO in 1 single clock cycle! + +### Dissecting asm_blink_pin() + +```c +static void asm_blink_pin(uint8_t pin) { + __asm volatile ( + "mov r4, %0\n" + "movs r5, #0x01\n" + "mcrr p0, #4, r4, r5, c0\n" // Drive High + : : "r"(pin) : "r4", "r5" + ); + sleep_ms(500); + __asm volatile ( + "mov r4, %0\n" + "movs r5, #0\n" + "mcrr p0, #4, r4, r5, c0\n" // Drive Low + : : "r"(pin) : "r4", "r5" + ); + sleep_ms(500); +} +``` + +- When `mcrr p0, #4, r4, r5, c0` executes with `r5 = 1`, the coprocessor drives the output bit High in 1 cycle. +- The high signal flows from SIO $\rightarrow$ `IO_BANK0` multiplexer $\rightarrow$ Voltage Level Shifter ($1.1\text{V} \rightarrow 3.3\text{V}$) $\rightarrow$ `PADS_BANK0` push-pull MOSFET driver $\rightarrow$ Physical Pin 16 $\rightarrow$ LED illuminates! +- When `mcrr p0, #4, r4, r5, c0` executes with `r5 = 0`, the coprocessor drives the output bit Low in 1 cycle, turning off the LED. + +--- + +## Part 6: Live GDB and Ghidra Dynamic & Static Reverse Engineering + +### The Hardware MMIO Blind Spot + +When a reverse engineer opens a stripped binary or analyzes a firmware dump, disassemblers do not possess semantic knowledge of microcontroller memory maps. + +In standard GDB disassembly, you encounter instructions like: +```text +0x10000320 <+16>: ldr r3, [pc, #48] ; =0x40038000 +0x10000322 <+18>: ldr r2, [pc, #48] ; =0x40028004 +0x1000032c <+28>: bic r5, r5, #384 ; 0x180 +0x10000336 <+38>: orr r5, r5, #5 +0x1000033c <+44>: mcrr p0, #4, r4, r5, c4 +``` + +To an unassisted analyst, `0x40038000` is an anonymous memory address, `0x180` is an arbitrary bitmask, and `mcrr p0` looks like an invalid or proprietary instruction. This is the **Hardware MMIO Blind Spot**. + +### Dynamic Hardware Awareness with PyCortexMDebug in GDB + +To eliminate this blind spot in live debugging, we use **PyCortexMDebug**. PyCortexMDebug ingests the official ARM CMSIS System View Description (`.svd`) file for the RP2350 (`rp2350.svd`). + +Once loaded into GDB over OpenOCD and SWD: + +```text +(gdb) svd-load /path/to/rp2350.svd +(gdb) svd PADS_BANK0 +PADS_BANK0 @ 0x40038000: + VOLTAGE_SELECT = 0x00000000 + GPIO0 = 0x00000104 [ISO=1, DRIVE=4mA, PDE=1, SCHMITT=1] + GPIO16 = 0x00000054 [ISO=0, OD=0, IE=1, DRIVE=4mA, PDE=1, SCHMITT=1] +``` + +Notice the difference! In an instant: +- We verify that `GPIO0` has `ISO=1` (pad is isolated). +- We verify that after `asm_init_gpio_range()` runs, `GPIO16` has `ISO=0`, `OD=0`, and `IE=1`! + +You can also read the live hardware status of the digital crossbar switchboard: +```text +(gdb) svd IO_BANK0 GPIO16_CTRL +IO_BANK0 -> GPIO16_CTRL @ 0x40028084: + FUNCSEL = 0x05 (SIO) + OUTOVER = 0x00 (NORMAL) + OEOVER = 0x00 (NORMAL) + INOVER = 0x00 (NORMAL) +``` + +GDB confirms that GPIO 16 is actively routed to the SIO block! + +### Static Reversing with SVD-Loader-Ghidra + +In static analysis with Ghidra, decompiled code for raw MMIO looks like: +```c +*(uint32_t *)(uVar3 + 0x40) = *(uint32_t *)(uVar3 + 0x40) & 0xfffffe7f | 0x40; +*(uint32_t *)(uVar2 + 0x80) = *(uint32_t *)(uVar2 + 0x80) & 0xffffffe0 | 5; +``` + +This decompilation is obscure. By running **SVD-Loader-Ghidra**: +1. Ghidra creates memory-mapped peripheral blocks labeled `PADS_BANK0` and `IO_BANK0`. +2. Ghidra generates C structure definitions matching the RP2350 hardware layout. +3. The decompilation transforms into clean, readable peripheral code: + +```c +pads_bank0->GPIO[16] = (pads_bank0->GPIO[16] & ~0x180) | 0x40; +io_bank0->GPIO[16].CTRL = (io_bank0->GPIO[16].CTRL & ~0x1f) | FUNCSEL_SIO; +``` + +--- + +## Part 7: Summary & Hardware Register Master Cheat Sheet + +### Architectural Summary + +1. **Hardware Split:** RP2350 enforces a strict division between electrical physics (`PADS_BANK0`) and digital logic (`IO_BANK0`). +2. **PADS_BANK0 (`0x40038000`):** Manages analog pad cells. Controls `ISO` (isolation latch), `OD` (output disable), `IE` (input enable), `DRIVE` ($2/4/8/12\text{ mA}$), `PUE`/`PDE` (pulls and Bus Keeper), `SCHMITT`, and `SLEWFAST`. +3. **IO_BANK0 (`0x40028000`):** Manages digital crossbar routing. Controls `FUNCSEL` (functions 0..31; $5 = \text{SIO}$), logic overrides (`OUTOVER`, `OEOVER`, `INOVER`, `IRQOVER`), and edge/level interrupt generation. +4. **SIO (`0xd0000000`):** Single-Cycle I/O memory-mapped registers for 32-bit atomic bit set, clear, toggle, and read. +5. **GPIOC (Coprocessor Port `p0`):** Native ARMv8-M coprocessor hardware enabling 1-cycle single-bit output and output-enable manipulation (`mcrr p0, #4`), single-pin toggling (`mcr p0, #5`), and 64-bit sampling (`mrrc p0, #0`) with zero memory bus overhead. + +### Hardware Register Reference Table + +| Subsystem | Base Address | Register / Offset | Key Bitfields & Silicon Function | +|:---|:---|:---|:---| +| **PADS_BANK0** | `0x40038000` | `VOLTAGE_SELECT` (`+0x00`) | Bit 0: Bank voltage select (`0 = 3.3V`, `1 = 1.8V`) | +| **PADS_BANK0** | `0x40038000` | `GPIOx` (`+0x04 + 4*x`) | Bit 8: `ISO` (Isolation Latch, reset=1)
Bit 7: `OD` (Output Disable)
Bit 6: `IE` (Input Enable, reset=0)
Bits 5:4: `DRIVE` (`00=2mA`, `01=4mA`, `10=8mA`, `11=12mA`)
Bit 3: `PUE` (Pull-Up Enable)
Bit 2: `PDE` (Pull-Down Enable)
Bit 1: `SCHMITT` (Hysteresis Enable)
Bit 0: `SLEWFAST` (Slew Rate Control) | +| **IO_BANK0** | `0x40028000` | `GPIOx_STATUS` (`+0x000 + 8*x`) | Bit 17: `INFROMPAD` (Raw input level)
Bit 13: `OETOPAD` (Effective Output Enable)
Bit 9: `OUTTOPAD` (Effective Output Data)
Bit 26: `IRQTOPROC` (Interrupt status) | +| **IO_BANK0** | `0x40028000` | `GPIOx_CTRL` (`+0x004 + 8*x`) | Bits 4:0: `FUNCSEL` (`0=JTAG`, `1=SPI`, `2=UART`, `3=I2C`, `4=PWM`, `5=SIO`, `6=PIO0`, `31=NULL`)
Bits 13:12: `OUTOVER`
Bits 15:14: `OEOVER`
Bits 17:16: `INOVER`
Bits 29:28: `IRQOVER` | +| **SIO** | `0xd0000000` | `GPIO_OUT` (`+0x10`) | Bits 31:0: Output data values | +| **SIO** | `0xd0000000` | `GPIO_OUT_SET` (`+0x14`) | Bits 31:0: Atomic bit set High | +| **SIO** | `0xd0000000` | `GPIO_OUT_CLR` (`+0x18`) | Bits 31:0: Atomic bit clear Low | +| **SIO** | `0xd0000000` | `GPIO_OUT_XOR` (`+0x1c`) | Bits 31:0: Atomic bit toggle | +| **SIO** | `0xd0000000` | `GPIO_OE` (`+0x20`) | Bits 31:0: Output enable ($1=\text{Out}, 0=\text{In}$) | +| **SIO** | `0xd0000000` | `GPIO_IN` (`+0x04`) | Bits 31:0: Direct input sample (Requires `IE=1`) | +| **GPIOC** | Port `p0` | `mcrr p0, #4, Rt, Rt2, c4` | Single-pin atomic output enable write (`gpioc_bit_oe_put`) | +| **GPIOC** | Port `p0` | `mcrr p0, #4, Rt, Rt2, c0` | Single-pin atomic output data write (`gpioc_bit_out_put`) | +| **GPIOC** | Port `p0` | `mcr p0, #5, Rt, c0, c0` | Single-pin atomic output toggle (`gpioc_bit_out_xor`) | +| **GPIOC** | Port `p0` | `mcr p0, #6, Rt, c0, c0` | Single-pin atomic output set (`gpioc_bit_out_set`) | +| **GPIOC** | Port `p0` | `mcr p0, #7, Rt, c0, c0` | Single-pin atomic output clear (`gpioc_bit_out_clr`) | +| **GPIOC** | Port `p0` | `mrrc p0, #0, Rt, Rt2, c0` | 64-bit dual-word simultaneous GPIO sample | + +*** diff --git a/WEEK05/WEEK05a.pdf b/WEEK05/WEEK05a.pdf new file mode 100644 index 0000000..605c7d6 Binary files /dev/null and b/WEEK05/WEEK05a.pdf differ diff --git a/WEEK05/float_hex_converter.py b/WEEK05/float_hex_converter.py new file mode 100644 index 0000000..e2241e9 --- /dev/null +++ b/WEEK05/float_hex_converter.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 +# MIT License +# +# Copyright (c) 2026 Kevin Thomas +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Author: Kevin Thomas +# Email: kevin@mytechnotalent.com +# GitHub: https://github.com/mytechnotalent +# File: float_hex_converter.py +# Desc: Convert and explain IEEE 754 float/hex operations step-by-step. +# Created: 2026 + +"""Convert and explain IEEE 754 float/hex operations step-by-step.""" + +import argparse +import struct +import sys + + +def _exp_str(e_val: int, bias: int, is_64: bool) -> str: + """ + Get accurate true exponent string accounting for IEEE 754 edge cases. + + Parameters + ---------- + e_val : int + The stored exponent value. + bias : int + The exponent bias (127 or 1023). + is_64 : bool + True if 64-bit precision. + + Returns + ------- + str + The formatted true exponent explanation string. + """ + if e_val == 0: + return f"0 (Zero/Subnormal, True Exp: {1 - bias})" + if e_val == (2047 if is_64 else 255): + return f"{e_val} (Inf/NaN flag)" + return f"{e_val} - {bias} = {e_val - bias}" + + +def _print_encode_steps(val: float, b: int) -> None: + """ + Print the math steps for encoding a float to hex. + + Parameters + ---------- + val : float + The float value to encode. + b : int + The bit size (32 or 64). + + Returns + ------- + None + """ + f1, f2, bias = (" None: + """ + Print the math steps for decoding a hex string to float. + + Parameters + ---------- + hex_str : str + The hex string to decode. + + Returns + ------- + None + """ + c = hex_str.lower() + if c.startswith("0x"): + c = c[2:] + b, f1, f2, bias = (64, " 8 else (32, " bool: + """ + Check if a string is a hexadecimal representation. + + Parameters + ---------- + s : str + Candidate string, with or without a 0x prefix. + + Returns + ------- + bool + True if every character is a hexadecimal digit. + """ + cleaned = s.lower() + if cleaned.startswith("0x"): + cleaned = cleaned[2:] + if not cleaned: + return False + return all(c in "0123456789abcdef" for c in cleaned) + + +def _process_conversion(val_str: str) -> None: + """ + Execute the conversion and print the output. + + Parameters + ---------- + val_str : str + The raw input string to process. + + Returns + ------- + None + """ + cleaned = val_str.strip() + if cleaned.lower().startswith("0x") or (len(cleaned) >= 8 and _is_hex(cleaned)): + _print_decode_steps(cleaned) + else: + val = float(cleaned) + _print_encode_steps(val, 32) + _print_encode_steps(val, 64) + + +def main() -> int: + """ + Execute the conversion pipeline based on CLI arguments. + + Parameters + ---------- + None + + Returns + ------- + int + Zero on successful conversion, otherwise non-zero. + """ + parser = argparse.ArgumentParser(description="Float/Hex step converter.") + parser.add_argument("val", help="Hex (0x...) or Float value to convert.") + args = parser.parse_args() + try: + _process_conversion(args.val) + return 0 + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/WEEK05/slides/WEEK05-IMG00.svg b/WEEK05/slides/WEEK05-IMG00.svg new file mode 100644 index 0000000..77db88a --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 05 + + +Integers and Floats in Embedded Systems: +Debugging and Hacking Integers and Floats +w/ Intermediate GPIO Output Analysis + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK05/slides/WEEK05-IMG01.svg b/WEEK05/slides/WEEK05-IMG01.svg new file mode 100644 index 0000000..1659bfa --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG01.svg @@ -0,0 +1,77 @@ + + + + + +Integer Data Types +Fixed-Size Types for Embedded Systems + + + +uint8_t +Unsigned 8-bit +1 byte +Range: +0 to 255 +Ages, counts, always positive + + + +int8_t +Signed 8-bit +1 byte +Range: +-128 to 127 +Temperature, can be negative + + + +uint16_t +Unsigned 16-bit +2 bytes +Range: +0 to 65,535 +Sensor readings, medium values + + + +uint32_t +Unsigned 32-bit +4 bytes +Range: +0 to ~4 billion +Addresses, timestamps + + + +Code Example + +uint8_t age = 43; +// unsigned, 0-255 +int8_t range = -42; +// signed, -128 to 127 + + + +Key Insight +The +u +prefix means +unsigned +(no negatives) +Without +u += signed (allows negatives) +Choose the smallest type that fits your data + \ No newline at end of file diff --git a/WEEK05/slides/WEEK05-IMG02.svg b/WEEK05/slides/WEEK05-IMG02.svg new file mode 100644 index 0000000..229f5e9 --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG02.svg @@ -0,0 +1,79 @@ + + + + + +Two's Complement +How Negative Numbers are Stored + + + +Encoding -42 as int8_t + + +Step 1: Start +42 = 0x2A + + +Step 2: Flip +~0x2A = 0xD5 + + +Step 3: Add 1 +0xD5+1 = 0xD6 + +Binary: +00101010 +-> +11010101 +-> +11010110 + +Result: -42 stored as 0xD6 in memory +Top bit = 1 means negative + + + +Signed vs Unsigned: Same Bits! + +Hex +Binary +uint8_t +int8_t + + + +0x2A +00101010 +42 +42 + +0xD6 +11010110 +214 +-42 + +Same byte 0xD6 = 214 unsigned, -42 signed + + + +GDB Verification + + +(gdb) +x/1xb &range +0x200003e7: +0xd6 +// -42 in memory + \ No newline at end of file diff --git a/WEEK05/slides/WEEK05-IMG03.svg b/WEEK05/slides/WEEK05-IMG03.svg new file mode 100644 index 0000000..497a13d --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG03.svg @@ -0,0 +1,75 @@ + + + + + +Inline Assembly GPIO +Direct Hardware Control via ASM + + + +GPIO Init Loop (pins 16-19) + + +1. Config Pad +PADS_BANK0 +Clear OD+ISO, set IE +0x40038000 + + +2. Set Function +IO_BANK0 +FUNCSEL = 5 (SIO) +0x40028004 + + +3. Enable Out +GPIO Coprocessor +mcrr p0,#4,r4,r5,c4 +Output Enable + +Loop: r0 = 16 to 19 +Red, Green, Blue, Yellow LEDs +Each pin: pad config + function select + OE + + + +Blink Loop + + +mcrr p0,#4,r4,r5,c0 + +r4 = pin, r5 = value +c0 = output value register +r5=1 ON, r5=0 OFF + + + +Pin Cycling + + +pin++; +if (pin > 18) pin=16; + +Cycles: 16 -> 17 -> 18 -> 16 +Red -> Green -> Blue -> repeat + + + +Why Inline Assembly? +gpio_put(16,1) calls +mcrr +underneath +Inline ASM shows what the SDK does at hardware level + \ No newline at end of file diff --git a/WEEK05/slides/WEEK05-IMG04.svg b/WEEK05/slides/WEEK05-IMG04.svg new file mode 100644 index 0000000..ecfa467 --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG04.svg @@ -0,0 +1,83 @@ + + + + + +IEEE 754 Float +32-bit Single Precision Encoding + + + +Float Bit Layout (32 bits) + + +S + + +Exponent (8) + + +Mantissa (23) + +1 bit +sign + +8 bits +bias=127 + +23 bits ++implicit 1 + + + +Decode Formula +Value = (-1)^sign × 2^(exp-127) × (1 + mantissa) +Sign determines +/- Exponent scales with bias 127 Mantissa adds precision +Special cases: exp=0 or 255 (denorm, inf, NaN) + + + +Decode Example: 42.5 + +Sign Bit: 0 +Positive number + +Exponent: 10000100 += 132 decimal + +Bias subtraction: 132 - 127 = 5 + +Mantissa: 01010100...0 += 1.010101 + +Combine: 1.010101 × 2^5 = 42.5 + +Hex: 0x422A0000 + +Binary: 01000010001010100000000000000000 + + + +32-bit Storage Comparison + +Integer: +Exact values +Range: -2.1B to +2.1B +No decimals + +Float: +Approximate values +Range: ±10^38 +~7 sig digits + \ No newline at end of file diff --git a/WEEK05/slides/WEEK05-IMG05.svg b/WEEK05/slides/WEEK05-IMG05.svg new file mode 100644 index 0000000..90e591f --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG05.svg @@ -0,0 +1,76 @@ + + + + + +Float in Ghidra +Analyzing 42.5 in the Binary + + + +Decompiled main() + + +int main(void) { +stdio_init_all(); +uVar1 = DAT_1000024c; +do { +printf(fmt,0,uVar1); +} while(true); + + + +Key Discovery + +printf with %f always +receives a +double +(64-bit) + +C promotes float to double +for variadic functions! + + +42.5 sent as double + + + +Register Pair r2:r3 + + +r2 (low): +0x00000000 + + +r3 (high): +0x40454000 + +Combined: 0x4045400000000000 += 42.5 + + + +Assembly View + + +1000023a +00 24 +movs r4, #0x0 +// r2 = 0 + +1000023c +03 4d +ldr r5,[DAT...] +// r3 = 0x40454000 + \ No newline at end of file diff --git a/WEEK05/slides/WEEK05-IMG06.svg b/WEEK05/slides/WEEK05-IMG06.svg new file mode 100644 index 0000000..3c82e9e --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG06.svg @@ -0,0 +1,87 @@ + + + + + +Patching Float +Changing 42.5 to 99.0 in Ghidra + + + +Calculate 99.0 as Double + +99 decimal = +1100011 +binary + +Normalize: +1.100011 x 2^6 + +Sign: +0 +Exp: +6+1023 = 1029 + +Mantissa: +100011000...0 + +Full double: 0x4058C00000000000 + + + +Before (42.5) + + +r2: +0x00000000 + + +r3: +0x40454000 + +Output: fav_num: 42.500000 + + +After (99.0) + + +r2: +0x00000000 +same! + + +r3: +0x4058C000 +changed + +Output: fav_num: 99.000000 + + + +Patch in Ghidra + + +1. Window: Bytes +Open byte editor + + +2. Find 00404540 +High word of 42.5 + + +3. Patch 00C05840 +High word of 99.0 + +Only one word to patch (low word is 0) + \ No newline at end of file diff --git a/WEEK05/slides/WEEK05-IMG07.svg b/WEEK05/slides/WEEK05-IMG07.svg new file mode 100644 index 0000000..d67d52e --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG07.svg @@ -0,0 +1,75 @@ + + + + + +Double Precision +IEEE 754 64-bit Floating Point + + + +64-Bit Layout + + +Sign +1 bit + + +Exponent +11 bits (bias 1023) + + +Mantissa (Fraction) +52 bits + +Formula: (-1)^S x 1.Mantissa x 2^(Exp-1023) + + + +Encoding 42.52525 + +Sign: +0 (positive) + +Exponent: +5 + 1023 = 1028 += 0x404 (hex) + +Mantissa: +0101010000110011... + +Full 64-bit hex: +0x4045433B645A1CAC + + + +Float (32-bit) + +Size: 4 bytes +Exp: 8 bits +Mantissa: 23 bits +Precision: ~7 digits +Bias: 127 +1 register (ARM) + + +Double (64-bit) + +Size: 8 bytes +Exp: 11 bits +Mantissa: 52 bits +Precision: ~15 digits +Bias: 1023 +2 registers (r2:r3) + \ No newline at end of file diff --git a/WEEK05/slides/WEEK05-IMG08.svg b/WEEK05/slides/WEEK05-IMG08.svg new file mode 100644 index 0000000..ad7749c --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG08.svg @@ -0,0 +1,71 @@ + + + + + +Double in Ghidra +Analyzing 42.52525 in memory + + + +Decompiled main() + + +int main(void) { +double fav_num + = 42.52525; +stdio_init_all(); +} + + + +Key Difference + +Float (42.5): +r2 = 0x00000000 (zero!) +r3 = 0x40454000 + +Double (42.52525): +r2 = 0x645A1CAC (non-zero!) + + + +Register Pair r2:r3 + + +r3 (HIGH): +0x4045433B + + +r2 (LOW): +0x645A1CAC + +Combined: 0x4045433B645A1CAC = 42.52525 + + + +Assembly (ldrd) + + +ldrd r2,r3,[PC,#0x10] +; Loads BOTH words at once +; r2 gets low word +; r3 gets high word + + +ldrd = Load +Register +Doubleword +ARM Cortex-M33 + \ No newline at end of file diff --git a/WEEK05/slides/WEEK05-IMG09.svg b/WEEK05/slides/WEEK05-IMG09.svg new file mode 100644 index 0000000..fcf359f --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG09.svg @@ -0,0 +1,88 @@ + + + + + +Patching Double +Changing 42.52525 to 99.99 + + + +99.99 as IEEE 754 Double + +Sign: +0 +Exp: +6 + 1023 = 1029 + +Result: +0x4058FF5C28F5C28F + +r3 (HIGH) = 0x4058FF5C +r2 (LOW) = 0x28F5C28F + + + +Before (42.52525) + + +r3: +0x4045433B + + +r2: +0x645A1CAC + +printf: 42.525250 +Both words non-zero + + +After (99.99) + + +r3: +0x4058FF5C +changed + + +r2: +0x28F5C28F +changed + +printf: 99.990000 +BOTH words changed! + + + +Float Patch + +Words changed: +1 +r2 (low): +stays 0x0 +r3 (high): +patched +Easier to patch + + +Double Patch + +Words changed: +2 +r2 (low): +patched +r3 (high): +patched +Both words must change + \ No newline at end of file diff --git a/WEEK05/slides/WEEK05-IMG10.svg b/WEEK05/slides/WEEK05-IMG10.svg new file mode 100644 index 0000000..ce71556 --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG10.svg @@ -0,0 +1,101 @@ + + + + + +IEEE 754 & Data Types +Data Types and IEEE 754 Reference + + + +IEEE 754 Hex Values + + +Value +Float (32b) +Double (64b) + + + + +42.0 +0x42280000 +0x4045000000000000 + + +42.5 +0x422A0000 +0x4045400000000000 + + +99.0 +0x42C60000 +0x4058C00000000000 + + +99.99 +0x42C7F5C3 +0x4058FF5C28F5C28F + + +3.14 +0x4048F5C3 +0x40091EB851EB851F + + +100.0 +0x42C80000 +0x4059000000000000 + +Tip: float low word often 0x0; double low word usually non-zero + + + +Patching Workflow + + +1. Identify +float / double + + +2. Check r2 +zero = float + + +3. Calculate +new hex value + + +4. Patch +in byte editor + + +5. Export +UF2 + test + + + +Integer Types + +uint8_t: 0-255 +int8_t: -128 to 127 +Two's complement for signed + + +Key Insight + +Float: patch 1 word +Double: patch 2 words +Check r2 to detect type + \ No newline at end of file diff --git a/WEEK06/WEEK06-BN.md b/WEEK06/WEEK06-BN.md new file mode 100644 index 0000000..5b3d3db --- /dev/null +++ b/WEEK06/WEEK06-BN.md @@ -0,0 +1,1394 @@ +# Week 6-BN: Binary Ninja Personal — Read, Hack, and Patch a Static Variable and a GPIO Input (Raw `.bin`) + +*** + +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +- Build the lesson project with `Release` and get both an `.elf` and a raw `.bin` +- Dump the **ELF symbol map** with `arm-none-eabi-nm` and use it as ground truth +- Load the raw `.bin` into Binary Ninja at `0x10000000` +- Read how a **static local** keeps a fixed RAM address while a **regular local** is inlined to a constant +- **Break at `main`** on live silicon, even though `main` moves between programs +- **Hack a running target live** by editing a register in Binary Ninja's Registers widget +- **Resolve the functions in the Binary Ninja GUI** using the ELF symbol map +- **Patch** the value byte, invert the button/LED logic, and (optionally) rename the printed label +- **Export** the patched image, convert it to UF2, and flash it + +--- + +## How This Guide Works + +The build produces two files for the project: + +| File | What it is | How we use it | +| ---- | ---------- | ------------- | +| `.elf` | The linked image with a full symbol table | Ground truth for every function address and name | +| `.bin` | The raw flash image, no headers, no symbols | The image we load into Binary Ninja and reverse | + +The `.bin` is built **from** the `.elf`, so the ELF tells you exactly what is at every address. We use the ELF symbol map to resolve functions in Binary Ninja, and we reverse-engineer the raw `.bin` the way a real extracted firmware image is reversed. + +> **Build `Release`, not `Debug`.** Every address in this guide matches the Week 6 lesson, and the Week 6 lesson is a `Release` build. `Release` optimizes the code the same way the original lesson was built: it **inlines** the `static` helper `demo_static_variable` (and the SDK's `gpio_set_dir` / `gpio_get` / `gpio_put` / `gpio_pull_up` helpers) into `main`, and it folds the regular local `regular_fav_num` down to a bare `movs r1, #42` constant. If you build `Debug`, the SDK function addresses move and the helper stays a separate call, so nothing lines up. Always build `Release` for this lesson. + +The order is **dynamic first, static second**: + +1. Break on the live target and prove what the code does. +2. Hack it live in the debugger and watch the output change. +3. Resolve the functions in Binary Ninja using the ELF symbol map. +4. Patch the bytes, export, convert, and flash. + +| Project | Prints | Also does | The hacks | +| ------- | ------ | --------- | --------- | +| `0x0014_static-variables` | `regular_fav_num: 42`, then `static_fav_num:` incrementing | reads button GPIO 15, drives LED GPIO 16 | change `42` to `43`, invert the button/LED logic, (optional) rename the label | + +> **Addresses come from your build.** Every address here is from the `Release` build produced in Step 3. Confirm against your own `.elf` with the command in Step 4. + +> **`static_fav_num` is the star of this week.** `regular_fav_num` is an automatic (stack) variable that the compiler bakes into a constant; `static_fav_num` is a function-local `static` that the linker parks at a **fixed RAM address** (`0x200005a8`) in `.data`. That fixed address is why it persists across loop iterations, and why you can read and patch it like a global. + +--- + +## Part 1: Build, Flash, and Get the Symbol Map + +### Step 1: Install the toolchain + +**Windows x64** + +- Install the **Raspberry Pi Pico** extension in VS Code. It installs the ARM GNU toolchain, CMake, Ninja, and the Pico SDK. +- Install **Binary Ninja Personal** and complete its license activation. +- Install **PuTTY** for the serial monitor. + +**macOS Apple Silicon** + +```bash +brew install cmake ninja +``` + +- Install **Binary Ninja Personal** and complete its license activation. +- Install the **Arm GNU Toolchain**, or let the VS Code Pico extension manage it. + +**Linux x64** + +```bash +sudo apt install cmake ninja-build gcc-arm-none-eabi libnewlib-arm-none-eabi git python3 openocd minicom +``` + +- Install **Binary Ninja Personal** and complete its license activation. + +### Step 2: Verify your tools are the right architecture (do not skip this) + +On **macOS Apple Silicon**, the most common failure is an Intel `x86_64` tool on your `PATH`: + +``` +zsh: bad CPU type in executable: cmake +``` + +You may have **two Homebrews**: the arm64 one at `/opt/homebrew` and the Intel one at `/usr/local`. If `/usr/local/bin` wins, every `brew` tool is x86_64. Check: + +```bash +file "$(which cmake)" +file "$(which ninja)" +file "$(which arm-none-eabi-gdb)" +file "$(which arm-none-eabi-nm)" +file "$(which openocd)" +file "$(which telnet)" +``` + +All must report `arm64`. If any is `x86_64`, put the Apple Silicon prefix first for the session and check again: + +```bash +export PATH="/opt/homebrew/bin:$PATH" +hash -r +file "$(which cmake)" +``` + +To make it permanent, add that `export` to `~/.zshrc`. Do not use Rosetta as a fix; OpenOCD and GDB are exactly the kind of programs where a translation layer produces failures that look like debugger bugs. + +**`telnet` is special — and optional.** The GDB MI workflow does not need it; it is only used by the command-port fallback. macOS no longer ships `telnet`, and the Homebrew build is often the Intel one, so `telnet 127.0.0.1 4444` fails with `bad CPU type in executable`. Your `brew` command itself may also be the Intel one: if `brew install telnet` fails with `.../portable-ruby/.../ruby: Bad CPU type in executable`, you are running the Intel Homebrew. Call the Apple Silicon Homebrew explicitly: + +```bash +/opt/homebrew/bin/brew install telnet +``` + +If you would rather not install anything, macOS ships an arm64 `nc`, which can connect to the same OpenOCD port: + +```bash +nc 127.0.0.1 4444 +``` + +**Windows x64** and **Linux x64** do not have this problem. Skip to Step 3. + +### Step 3: Build the project with `Release` + +Run this inside `0x0014_static-variables/`: + +```bash +cmake -B build -G Ninja -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 -DCMAKE_BUILD_TYPE=Release +cmake --build build +``` + +**Point Binary Ninja at this repository (once).** Every console snippet below reads the repo root from `~/.embedded-hacking-repo`, so Binary Ninja never needs a database open and nothing is hardcoded. From the repo root, run once: + +**macOS / Linux:** + +```bash +pwd > ~/.embedded-hacking-repo +``` + +**Windows (PowerShell):** + +```powershell +(Get-Location).Path | Set-Content "$env:USERPROFILE\.embedded-hacking-repo" +``` + +**Then build from the Binary Ninja console**, so the whole build -> patch -> flash loop stays inside Binary Ninja. The console inherits a minimal `PATH` — on macOS just `/usr/bin:/bin:/usr/sbin:/sbin` — so it does not see Homebrew; add your package manager's `bin` first, then run plain `cmake`. + +**macOS Apple Silicon:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +os.environ["PATH"] = "/opt/homebrew/bin:" + os.environ["PATH"] # the console's PATH omits Homebrew +proj = os.path.join(root, "0x0014_static-variables") +subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) +subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +**Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +proj = os.path.join(root, "0x0014_static-variables") +subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) +subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +proj = os.path.join(root, "0x0014_static-variables") +subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) +subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +The build directory now contains the pair we need: + +- `0x0014_static-variables/build/0x0014_static-variables.elf` and `.bin` — the `.bin` is **15516** bytes (`0x3c9c`) + +If the ARM toolchain is not on your `PATH`, add `-DPICO_TOOLCHAIN_PATH=...`: + +| OS | Typical toolchain path | +| -- | ---------------------- | +| Windows x64 | `C:/Program Files/Arm GNU Toolchain arm-none-eabi/14.2 rel1/bin` | +| macOS Apple Silicon | `~/.pico-sdk/toolchain/14_2_Rel1/bin` | +| Linux x64 | `/usr` | + +> **This guide's toolchain lives at `~/.pico-sdk/toolchain/14_2_Rel1/bin`.** All of `arm-none-eabi-nm`, `arm-none-eabi-objdump`, and `arm-none-eabi-gdb` resolved in this document come from there. If your install is elsewhere, `which arm-none-eabi-nm` tells you where to point. + +### Step 4: Dump the ELF symbol map + +This is the ground truth for the whole lesson. Run `arm-none-eabi-nm` on the ELF and keep the output in a terminal or a text file: + +**macOS Apple Silicon / Linux x64:** + +```bash +arm-none-eabi-nm -n --defined-only build/0x0014_static-variables.elf | grep -E ' [Tt] ' +``` + +**Windows x64:** + +```powershell +arm-none-eabi-nm -n --defined-only build\0x0014_static-variables.elf | Select-String ' [Tt] ' +``` + +Each line is `address type name`. The `T`/`t` type is a function. Here are the functions this lesson uses. The signatures come from the ELF's DWARF debug info queried with `arm-none-eabi-gdb -batch -ex "ptype "`, so they are exact. + +**Our code and the startup chain:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | reset entry | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | calls `runtime_init`, `main`, `exit` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | copies `.data` from flash to SRAM | +| `0x100001e4` | `_init` | `void _init(void)` | runs `.init_array` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | C runtime boilerplate | +| `0x10000234` | `main` | `int main(void)` | the lesson function (`demo_static_variable` inlined) | + +**The GPIO functions `main` calls (or that are inlined into it):** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x1000029c` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | SDK GPIO function select (UART pins) | +| `0x100002d8` | `gpio_set_pulls` | `void gpio_set_pulls(uint, bool, bool)` | SDK pull config (`gpio_pull_up` inlined) | +| `0x10000300` | `gpio_init` | `void gpio_init(uint)` | SDK GPIO init | + +**The stdio/UART and printf chain `main` reaches:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x10000e60` | `time_us_64` | `uint64_t time_us_64(void)` | SDK microsecond clock | +| `0x10000ed0` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | SDK UART init | +| `0x10002dbc` | `exit` | `void exit(int)` | C runtime exit | +| `0x10002dc4` | `runtime_init` | `void runtime_init(void)` | SDK runtime init | +| `0x10002df0` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | CRLF output driver | +| `0x10002f00` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | buffered string output | +| `0x10002fec` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | enable the UART driver | +| `0x10003014` | `stdio_init_all` | `bool stdio_init_all(void)` | SDK serial init | +| `0x100030e0` | `__wrap_vprintf` | `int __wrap_vprintf(const char*, va_list)` | printf core | +| `0x100031a4` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | the `printf` wrapper | +| `0x10003360` | `stdio_uart_init` | `void stdio_uart_init(void)` | SDK UART stdio init | +| `0x100034a0` | `strlen` | `size_t strlen(const char*)` | C runtime string length | + +**SDK helpers the `printf` and `uart_init` paths reach:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x10002d60` | `vfctprintf` | `int vfctprintf(void (*)(char, void*), void*, const char*, va_list)` | printf format engine | +| `0x1000237c` | `_vsnprintf` | `int _vsnprintf(out_fct_type, char*, size_t, const char*, va_list)` | the format dispatcher | +| `0x10001760` | `_ntoa_format` | `unsigned _ntoa_format(out_fct_type, char*, size_t, size_t, char*, size_t, bool, unsigned, unsigned, unsigned, unsigned)` | number formatter | +| `0x100016c4` | `_out_rev` | `unsigned _out_rev(out_fct_type, char*, size_t, size_t, const char*, size_t, unsigned, unsigned)` | reversed-digit output | +| `0x10001934` | `_out_char` | `void _out_char(char, void*, size_t, size_t)` | single-char sink | +| `0x10000e74` | `busy_wait_us` | `void busy_wait_us(uint64_t)` | UART timing loop | +| `0x100010a4` | `clock_get_hz` | `unsigned long clock_get_hz(clock_handle_t)` | UART clock lookup | + +Two things in this project have **no symbol of their own**, because the compiler inlined them into `main`: + +- `demo_static_variable` — the `static` helper in our own source is inlined, so there is no `demo_static_variable` address to rename. You see its body directly inside `main`. +- `init_gpio`, `gpio_set_dir`, `gpio_get`, `gpio_put`, and `gpio_pull_up` — these are `static inline` in the SDK headers, so they compile to the `mcrr`/SIO writes and the `ldr`/`ubfx` reads you see in `main` rather than to calls. `gpio_pull_up(15)` becomes the direct call to `gpio_set_pulls` at `0x1000024e`. + +> **`static_fav_num` is a `t` symbol, not a function.** `arm-none-eabi-nm -n` lists `200005a8 t static_fav_num.0`. That lowercase `t` is a **local data** symbol: the linker named the function-local static `static_fav_num.0` and parked it at RAM address `0x200005a8`. There is no pool entry for `regular_fav_num` because it is an automatic (stack) variable — and here the compiler never gave it a stack slot at all, folding it into a constant. + +### Step 5: Flash and confirm the output + +A `.bin` has no headers, so OpenOCD must be told the base address `0x10000000`. From the repository root: + +**macOS Apple Silicon / Linux x64:** + +```bash +./flash.sh 0x0014_static-variables/build/0x0014_static-variables.bin +``` + +**Windows x64 (PowerShell):** + +```powershell +.\flash.ps1 -Bin 0x0014_static-variables\build\0x0014_static-variables.bin +``` + +**Or flash from the Binary Ninja console** (the console reads the repo root from the marker file, so it works with no database open): + +**macOS Apple Silicon / Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x0014_static-variables", "build", "0x0014_static-variables.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("flashing in the background; log:", log) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x0014_static-variables", "build", "0x0014_static-variables.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # free the probe first +subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "flash.ps1"), "-Bin", bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT) +print("flashing in the background; log:", log) +``` + +Wait for `wrote 15516 bytes ...` and `** Verified OK **`. Open a serial monitor at **115200** baud: + +- **Windows x64:** PuTTY -> Connection type **Serial**, the Pico's COM port, speed `115200`. +- **macOS Apple Silicon:** `screen /dev/tty.usbmodem* 115200` (quit with `Ctrl-A` then `K`). +- **Linux x64:** `minicom -D /dev/ttyACM0 -b 115200`. + +``` +regular_fav_num: 42 +static_fav_num: 42 +regular_fav_num: 42 +static_fav_num: 43 +regular_fav_num: 42 +static_fav_num: 44 +... +``` + +`regular_fav_num` stays at `42` every pass (it is recreated as `42` each loop), while `static_fav_num` keeps climbing (it persists at `0x200005a8`). Wire a push button from GPIO 15 to GND and an LED from GPIO 16 (through a resistor) to GND. With the stock image, the LED is **off** when the button is released and **on** when you press it — the pull-up holds GPIO 15 high when released, and the code inverts that with `eor.w r3, r3, #1`. We will flip that behavior in Step 18b. + +--- + +## Part 2: Load the Raw `.bin` into Binary Ninja + +Start from a fresh Binary Ninja state. If you already have a `.bndb` for this lesson, **close it and start over**; a stale database keeps old names and patches. + +### Step 6: Bring the raw `.bin` into Binary Ninja + +A raw `.bin` has no headers, so Binary Ninja cannot know where it belongs or what architecture it is. You must supply both. If you just double-click the `.bin`, Binary Ninja may load it at address `0x0` with a guessed architecture, and every address in this lesson will be wrong. + +1. Choose `File -> Open with Options...` (do **not** use plain `File -> Open`). +2. Select `0x0014_static-variables/build/0x0014_static-variables.bin`. +3. In the loader options, set: + - **Architecture:** `thumb2` (the ARMv7-M / ARMv8-M Thumb-2 architecture, which covers the Cortex-M33) + - **Platform:** `thumb2` + - **Base Address:** `0x10000000` (the XIP flash base) +4. Click **Open**. + +Binary Ninja analyzes the image and opens the linear view. + +**Verify the load before going further.** Press `G`, type `0x10000000`, and read the first two words: + +``` +0x10000000 0x20082000 initial stack pointer +0x10000004 0x1000015d reset vector (bit 0 = Thumb) +``` + +If you instead see data at `0x00000000`, or a vector word without bit 0 set, close the tab and repeat with `Open with Options`. The Cortex-M33 only executes Thumb-2, so `thumb2` is the only correct architecture. + +> **Console equivalent:** +> ```python +> import os +> root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +> load(os.path.join(root, "0x0014_static-variables", "build", "0x0014_static-variables.bin"), +> options={"loader.imageBase": 0x10000000, "loader.platform": "thumb2"}) +> ``` + +### Step 7: Save it as a Binary Ninja database (`.bndb`) + +Binary Ninja never writes back into the `.bin`. Your names, comments, types, and patches live in a separate **`.bndb`** database. Save one now, before you make any changes: + +1. Choose `File -> Save As...`. +2. Save it next to the image as `0x0014_static-variables.bndb`. +3. From now on, save with `File -> Save` (`Cmd+S` on macOS, `Ctrl+S` on Windows/Linux) whenever you rename or patch. + +The two files have different roles: + +| File | Role | +| ---- | ---- | +| `0x0014_static-variables.bin` | the raw firmware image; Binary Ninja never modifies it | +| `0x0014_static-variables.bndb` | your analysis database: names, types, comments, and patches | + +When you come back later, **open the `.bndb`**, not the `.bin`; that restores all your work. If a database gets messy, delete the `.bndb` and re-import the `.bin` from Step 6 — the firmware is never at risk. You export the patched image out of this view later, in Step 19. + +### Step 8: The views you will use + +- **Linear view:** the disassembly listing. You navigate, read, and patch here. +- **Graph view:** the control-flow graph of the current function. +- **Decompiler (HLIL):** the pseudo-C decompilation. +- **Hex view:** raw bytes, used for patching. +- **Function list:** the sidebar list of every detected function. + +Navigation: `G` go to address, `N` rename, `Y` set type or signature, `;` add a comment. Breakpoints are set from the GUI through the GDB MI adapter — see Step 12. + +> **macOS function keys:** the top-row `F` keys are usually mapped to system functions. Every step here uses menu paths that work without them. + +--- + +## Part 3: Dynamic — Break at `main` and Hack Live + +### Step 9: Start OpenOCD as a live debug server + +Make sure no other OpenOCD is running; a forgotten server holds port `3333`. + +**macOS / Linux:** + +```bash +ps aux | grep -i openocd +``` + +**Windows (PowerShell):** + +```powershell +Get-Process | Where-Object { $_.ProcessName -like '*openocd*' } +``` + +Stop any leftover server gracefully: + +**macOS / Linux:** + +```bash +pkill -TERM -f openocd +``` + +**Windows (PowerShell):** + +```powershell +Get-Process openocd -ErrorAction SilentlyContinue | Stop-Process +``` + +Start the server **parked at `main`**: + +**macOS Apple Silicon / Linux x64:** + +```bash +BP_ADDR=0x10000234 ./debug-server.sh +``` + +**Windows x64 (PowerShell):** + +```powershell +$env:BP_ADDR="0x10000234"; .\debug-server.ps1 +``` + +**Or start it from the Binary Ninja console**, freeing the probe first and launching the server in the background so the console returns immediately: + +**macOS Apple Silicon / Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # stop any running server first +log = os.path.join(root, "openocd.log") +p = subprocess.Popen([os.path.join(root, "debug-server.sh")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("OpenOCD started (pid", p.pid, "); log:", log) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # stop any running server first +log = os.path.join(root, "openocd.log") +p = subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "debug-server.ps1")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT) +print("OpenOCD started (pid", p.pid, "); log:", log) +``` + +`Popen` returns in a few milliseconds; the server keeps running in the background. Check `openocd.log` for `Listening on port 3333`, then connect in Step 10. + +Wait for: + +``` +Info : [rp2350.dap.core0] Examination succeed +Startup breakpoint at 0x10000234 (2-byte hardware execute, one-shot). +Info : starting gdb server for rp2350.dap.core0 on 3333 +Info : Listening on port 3333 for gdb connections +``` + +> **`BP_ADDR` parks the core at `main` before any client connects.** The script arms a 2-byte hardware breakpoint and then does the startup `reset run`, so the core runs from the vector table and stops at your address with no debugger attached yet. When Binary Ninja connects a moment later, the first thing it reads is already the truth: `Stopped at 0x10000234`. This is the whole reason the lab works cleanly — you never have to drive a reset from outside the GUI. +> +> Use the address you actually want to stop at: +> +> | What you want to stop at | Address | Command | +> | --- | --- | --- | +> | `main` (once per reset) | `0x10000234` | `BP_ADDR=0x10000234 ./debug-server.sh` | +> | The **loop** — the first `printf` call, hit every iteration | `0x10000268` | `BP_ADDR=0x10000268 ./debug-server.sh` | +> +> ```bash +> BP_ADDR=0x10000234 ./debug-server.sh # park at main +> BP_ADDR=0x10000268 ./debug-server.sh # park in the loop instead +> ``` +> +> ```powershell +> $env:BP_ADDR="0x10000234"; .\debug-server.ps1 # park at main +> $env:BP_ADDR="0x10000268"; .\debug-server.ps1 # park in the loop +> ``` +> +> **This startup stop is single-use.** OpenOCD flushes breakpoints when a client connects, so this one is gone once Binary Ninja attaches — fine for `main`, which only runs once per reset. Every breakpoint after that is set from the Binary Ninja GUI (Step 12) and is repeatable. To stop at `main` again, restart the server with `BP_ADDR` and reconnect. + +> **Exactly one core.** The line must say `core0` and must **not** mention `core1`. Core1 is never started by this firmware; exposing it makes Binary Ninja read core1's reset-state registers, which are not real addresses, and OpenOCD floods the log with `Failed to read memory at 0xf0000000`. The scripts already use `USE_CORE=0`; do not change it. + +> **Windows driver note:** the Debug Probe must use the **WinUSB** driver. If OpenOCD reports `unable to open CMSIS-DAP device`, install it with [Zadig](https://zadig.akeo.ie/) (select `Debug Probe (CMSIS-DAP)` -> WinUSB). + +### Step 10: Connect Binary Ninja to the GDB server + +1. Make sure the image is open and analyzed (Part 2) and the server from Step 9 is running (parked at `main`). +2. Choose `Debugger -> Connect to Remote Process`. +3. In the **adapter** dropdown, select **GDB MI**. +4. In the **connect** settings group, set **IP Address** to `127.0.0.1` and **Port** to `3333`. +5. Set **Full GDB Executable Path** to the `arm-none-eabi-gdb` from the **Arm GNU Toolchain 14.2.rel1**. It ships for all three hosts, and the Raspberry Pi Pico VS Code extension installs that same 14.2.rel1 toolchain (including `arm-none-eabi-gdb`) on all of them: + + | OS | `arm-none-eabi-gdb` path | + | -- | ------------------------ | + | macOS Apple Silicon | `/Applications/ArmGNUToolchain/14.2.rel1/arm-none-eabi/bin/arm-none-eabi-gdb` (or the Pico extension's `~/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb`) | + | Windows x64 | `%USERPROFILE%\.pico-sdk\toolchain\14_2_Rel1\bin\arm-none-eabi-gdb.exe` (Pico extension), or `C:\Program Files (x86)\Arm GNU Toolchain arm-none-eabi\14.2 rel1\bin\arm-none-eabi-gdb.exe` | + | Linux x64 | `~/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb` (Pico extension), or the `bin/` directory of the extracted `arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi` tarball | +6. Click **Accept**. + +> **Use the GDB MI adapter.** It launches a real `arm-none-eabi-gdb --interpreter=mi2` and lets Binary Ninja drive it, so breakpoints and stepping go through real GDB — which sends the correct 2-byte breakpoint length and handles step-over itself. Verified working end to end: connect, GUI breakpoints (**Add Hardware Breakpoint...**, hardware execute), **Step Into** / **Step Over**, and register edits. Stops are reported as `Breakpoint` (not `SingleStep`). +> +> **Do NOT have any breakpoints set in Binary Ninja before you connect.** With the GDB MI adapter, attaching while Binary Ninja already has a breakpoint **hangs the session**. Start the server parked with `BP_ADDR` (Step 9), connect, and only add hardware breakpoints *after* the connection is up. This is a Binary Ninja bug; it is the single most common GDB MI failure. +> +> **The GDB executable path matters.** Use the **14.2.rel1** build on every OS (Windows, macOS, Linux). The 13.3.rel1 build did **not** connect in testing. +> +> **This step is temporary.** Vector35 plans to ship a GDB binary with the GDB MI adapter ([Vector35/debugger#929](https://github.com/Vector35/debugger/issues/929), milestone *Langara*). Once that lands, Binary Ninja provides GDB itself and you will not need to set **Full GDB Executable Path** at all. +> +> **Do not pick Corellium.** Binary Ninja's adapter dropdown also lists **Corellium**, which is for Corellium's virtual devices and expects an API token, not a local OpenOCD server. It is not the adapter for this lab. The dropdown is a combo box, so an accidental arrow-key press can land on it — always read the label back and confirm it says **GDB MI** before clicking **Accept**. + +> **The adapter and port are not saved in the `.bndb`.** Every time you relaunch Binary Ninja you must re-select **GDB MI**, re-enter port `3333`, and re-set the GDB path. + +> **Watch for an off-screen error dialog.** When a connection fails, Binary Ninja pops a `Binary Ninja critical alert` window that can be positioned mostly outside the main window, which makes it look like nothing happened. If the connect seems to do nothing, check your other display. + +The target keeps running. Open the **Registers** tab (bug icon) and confirm you see live values. `pc` inside `0x10003xxx` and `sp` just below `0x20082000` are healthy. + +> **If `pc` is `0x00000088`, `0x000000ec`, or `sp` is `0xf0000000`, the session is bad.** Restart the server, then restart Binary Ninja (a server restart while attached leaves Binary Ninja in a stale session), and connect again. + +### Step 11: Find `main` without relying on its address + +`main` can move between programs, so we do not guess it. We follow the one fixed path to it. Press `G` and go to `0x10000000`: + +``` +0x10000000 0x20082000 initial stack pointer (top of SRAM) +0x10000004 0x1000015d reset vector +``` + +Bit 0 of a vector is the Thumb bit, so `0x1000015d` means "start at `0x1000015c`". That is `_reset_handler`. Follow the reset path to `0x10000186`, `platform_entry`: + +```asm +10000186: 4914 ldr r1, [pc, #80] ; @ 0x100001d8 +10000188: 4788 blx r1 ; runtime_init +1000018a: 4914 ldr r1, [pc, #80] ; @ 0x100001dc +1000018c: 4788 blx r1 ; main <-- the fixed anchor +1000018e: 4914 ldr r1, [pc, #80] ; @ 0x100001e0 +10000190: 4788 blx r1 ; exit +10000192: be00 bkpt 0x0000 +10000194: e7fd b.n 0x10000192 +``` + +**The middle `blx` at `0x1000018c` is the call to `main`.** `platform_entry` is byte-identical in every project, so `0x1000018c` catches `main` no matter where the linker placed it. The literal pool at `0x100001dc` holds `main | 1`; clearing bit 0 gives `0x10000234`. + +### Step 12: Set a hardware breakpoint in the GUI + +With the **GDB MI** adapter, Binary Ninja sets breakpoints through real GDB, which sends the correct 2-byte length, so you set them **in the UI**. There is no command port here. + +> **Why older drafts used the command port.** Binary Ninja's **GDB RSP** adapter is its own minimal RSP client and sends a **1-byte** breakpoint (`Z0,,1`); the Cortex-M33 FPB comparators need 2 bytes, so OpenOCD rejected it with `only breakpoints of two bytes length supported`. The old workaround was to arm breakpoints by hand over telnet. **The GDB MI adapter does not have this problem** — it drives real `arm-none-eabi-gdb`, which sends the right length. So everything below is done in the GUI. The command port still exists as a fallback (see the end of this step), but you do not need it. + +#### Where you can stop + +| You want to stop at | Address | How | Repeatable? | +| --- | --- | --- | --- | +| **`main`** | `0x10000234` | The server starts parked there with `BP_ADDR=0x10000234` (Step 9), so Binary Ninja is already stopped at `main` when it connects. | No — `main` runs once per reset. | +| **The regular `printf` call** | `0x10000268` | Set a hardware breakpoint in the GUI, then click **Resume**. | Yes — fires on every iteration. | +| **The static `printf` call** | `0x10000270` | Set a hardware breakpoint in the GUI, then click **Resume**. | Yes — fires on every iteration. | + +#### Set the loop breakpoint in the GUI + +1. Press `G`, type the loop address (`0x10000268`), and press Enter. +2. Set a **hardware execution** breakpoint at that address, either way: + - `Debugger -> Add Hardware Breakpoint...` — a **hardware execute** (`HE`) breakpoint. **Use this one.** + - click the line and press `F2` (`Debugger -> Toggle Breakpoint`) — a **software** breakpoint. It will **not** work here: the code is in read-only flash, so GDB cannot install it and the core just keeps running. +3. Click **Resume**. The core is already running the loop, so the breakpoint fires on the next iteration. Binary Ninja stops with the PC at the loop address and reports it as a **Breakpoint** — verified: `Stopped (Breakpoint) at 0x10000268`. + +> **No breakpoints before you connect.** With GDB MI, a breakpoint set before the connection hangs the session (Step 10). Start parked with `BP_ADDR`, connect, *then* add breakpoints. + +#### Stepping + +With the target halted at the breakpoint, **Step Into** (`F7`) and **Step Over** (`F8`) run through real GDB and move the PC. Verified: `0x10000268 -> 0x100031a4 -> 0x100031a6 -> ...`. + +> **Step Over on the raw `.bin` steps *into* calls.** The raw image has no symbol for `__wrap_printf`, so **Step Over** at the `printf` call behaves like **Step Into**. When the lab needs to execute the call and then stop, it moves the breakpoint to the return site and clicks **Resume** instead (Step 13 shows this). + +> **Never use Binary Ninja's Restart button.** On RP2350 it resets and halts inside the boot ROM (`pc=0x88`, `sp=0xf0000000`). To reset cleanly, restart the server with `BP_ADDR` and reconnect. + +> **If you ever need the command port.** It is still there — `nc 127.0.0.1 4444`, and `bp 2 hw` still arms a breakpoint, `rbp ` / `rbp all` still remove them. It is the fallback if you switch back to the **GDB RSP** adapter, whose 1-byte breakpoints the GUI cannot set. With GDB MI you do not need it for this lab. + +### Step 13: HACK IT LIVE — change the printed value + +`main` loads the constant `0x2a` (42) into `r1` and calls `printf` for the regular variable on every iteration. We break on that call and change it live: + +1. Press `G`, go to `0x10000268` (the first `bl __wrap_printf`). +2. Set a **hardware execute** breakpoint there: `Debugger -> Add Hardware Breakpoint...`. (Do not use `F2` — that is a software breakpoint and will not work on read-only flash.) +3. Click **Resume** in Binary Ninja. The target is already running the loop, so the breakpoint fires on the next iteration. Binary Ninja stops with the program counter at `0x10000268` and `r1 = 0x2a`. +4. Open the **Registers** widget (bug icon -> **Registers**). +5. Find `r1`. Its value is `0x2a` (42), loaded by the `movs r1, #42` at `0x10000264`. +6. **Set `r1` to `0x2b` (43).** From Binary Ninja's Python console (`Plugins -> Python Console`): + ```python + dbg.set_reg_value("r1", 0x2b) + ``` + `dbg.set_reg_value(name, value)` writes one register (returns `True` on success). You can also right-click `r1` in the **Registers** widget, press `E` (edit), type `2b`, and press Enter. The widget may not repaint the value, but the write reaches the target — you confirm it by the printed output in the next steps. +7. **Move the breakpoint past the call.** You want `printf` to run once and then stop, so move the breakpoint from `0x10000268` to the instruction *after* the call, `0x1000026c` (the `ldrb r1, [r4]` that begins the static-variable half of the loop): remove the breakpoint at `0x10000268` and set a hardware breakpoint at `0x1000026c`. Two reasons not to just click **Step Over** here: a breakpoint left on the current PC re-traps the step, and Binary Ninja's **Step Over** steps *into* `__wrap_printf` on this raw `.bin` because the image carries no symbol for the call. Moving the breakpoint to the return site is deterministic. +8. Click **Resume** in Binary Ninja. The core executes `bl __wrap_printf` with `r1 = 0x2b`, so this iteration prints `regular_fav_num: 43`, then stops at `0x1000026c`. +9. Look at your serial monitor — the `screen` session on the Pico's USB serial port — and at the **Target** tab in Binary Ninja: + + ``` + regular_fav_num: 43 + ``` + +You changed a running program's output without touching the binary. + +### Step 13b: HACK THE STRING LIVE — change `regular_fav_num:` to `patched_fav_num:` (optional) + +The text `"regular_fav_num: %d\r\n"` lives in flash (`.rodata`) at `0x10003560`, and flash is **read-only at runtime** — a debugger write there does not stick. So instead of overwriting the text in place, redirect the pointer: at the `printf` call, `r0` holds the string address, so point `r0` at a replacement string you place in RAM. + +1. Arm the breakpoint at the `printf` call and hit it, exactly as in Step 13 steps 1-3. At the stop, `r0 = 0x10003560` and `r1 = 0x2a`. +2. Put the replacement string into free RAM at `0x20080000` from Binary Ninja's **Python console** (`Plugins -> Python Console`) — no command port needed: + ```python + dbg.write_memory(0x20080000, b"patched_fav_num: %d\r\n\x00") + ``` + `dbg.write_memory(address, bytes)` is Binary Ninja's debugger memory-write API; it returns `True` on success. That writes `patched_fav_num: %d\r\n\0`. +3. Point `r0` at that string: + ```python + dbg.set_reg_value("r0", 0x20080000) + ``` + (Or right-click `r0` in the **Registers** widget, press `E`, type `0x20080000`, and press Enter.) +4. If you want the value hack too, set `r1` to `0x2b` as in Step 13. Then move the breakpoint past the call in the GUI (remove it at `0x10000268`, set one at `0x1000026c`) and click **Resume**. The core runs `printf` with `r0` pointing at your RAM string and `r1 = 0x2b`, so this iteration prints: + ``` + patched_fav_num: 43 + ``` + then stops at `0x1000026c`. + +Like the value hack, this is **one iteration only**: the loop reloads `r0` (and `r1`) from flash on every pass, so the next line is `regular_fav_num: 42` again. The permanent version is the static patch in Step 18c. + +### Step 14: Why the hack reverts (and why we patch next) + +Press **Resume**. The loop branches back to `0x10000264`, which reloads `movs r1, #42`, so the next line is `regular_fav_num: 42`. The live edit changed one iteration only. There is no stack variable in memory to change; the value is baked into the instruction. To make `regular_fav_num: 43` permanent we must patch the instruction. That is the static pass. + +Press **Pause** to stop the output flood. + +### Step 15: Kill the debugger and OpenOCD + +The live hack is done. Do this **before** the static pass. + +1. In the **Debugger** sidebar, click the **X** (**Kill**) (or **`Debugger -> Kill`**) to disconnect Binary Ninja. +2. **Kill does not stop the OpenOCD process** — `debug-server.sh` started it separately, and it keeps running and holding the probe. Stop it from the Binary Ninja console: + + **macOS / Linux:** + + ```python + import subprocess + subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # stop the debug server, free the probe + ``` + + **Windows:** + + ```python + import subprocess + subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # stop the debug server, free the probe + ``` + +3. Confirm nothing is left: `pgrep -fl openocd` (macOS/Linux) prints nothing. + +From a terminal it is the same: `pkill -TERM -f openocd`, or `Get-Process openocd | Stop-Process` on Windows. + +--- + +## Part 4: Static — Resolve the Functions in Binary Ninja and Patch + +### Step 16: Resolve the functions in the Binary Ninja GUI + +We now name the functions in Binary Ninja using the ELF symbol map from Step 4. Binary Ninja loaded the raw `.bin` with **no symbols**, so every function shows as `sub_` — resolving means giving each one its real name and signature. + +Three keys do all the work: + +| Key | Binary Ninja action | Use it for | +| --- | --- | --- | +| `G` | Go to address | Jump to a function's address | +| `Y` | **Change Type** | Set the function's signature. The dialog shows the full prototype, so this sets the name *and* the type in one step. | +| `N` | Rename | Rename only, when you just want the name and not the type | + +For each function below: `G` to its address, then **`Y` (Change Type)** and type the prototype from the table. + +#### How to resolve a function in Binary Ninja (`Y`) + +`Y` is the **Change Type** key, and it is what actually resolves the function — it turns `void sub_10003014()` into `bool stdio_init_all(void)`. The Change Type dialog shows the full declaration (name and type), so typing the prototype sets both: + +1. `G` to the function's address. The cursor lands on the function. +2. Press **`Y`**. In the Change Type dialog, type the prototype from the table exactly — for example `bool stdio_init_all(void)` — and press Enter. + +The decompiler header then shows the real prototype, and calls to the function read cleanly instead of `sub_()`. `N` is only for renaming without touching the type; `Y` alone sets both the name and the type. + +If `Y` seems to do nothing, confirm the cursor is on the function, or right-click it and pick **Change Type...**. Binary Ninja parses what you type and silently keeps the old type if it does not parse, so glance at the header after each `Y`. + +#### Worked example: `main` + +1. Press `G`, type `0x10000234`, press Enter. The view jumps there; the cursor lands on `sub_10000234`. +2. Press **`Y`** (Change Type), type `int main(void)`, press Enter. That sets the name to `main` and the type to `int(void)`. + +> **Binary Ninja shows `int32_t` where Ghidra shows `int`.** After you set `int main(void)`, the decompiler header may read `int32_t main(void)`. That is the same type — on this platform `int` is 32 bits and Binary Ninja's parser normalises it to `int32_t`. Do not fight it; it is not an error. + +#### Worked example: `gpio_init` + +1. `G` -> `0x10000300`. +2. `Y` -> `void gpio_init(uint gpio)`. + +#### Worked example: `gpio_set_pulls` + +1. `G` -> `0x100002d8`. +2. `Y` -> `void gpio_set_pulls(uint gpio, bool up, bool down)`. + +This is what `gpio_pull_up(15)` in our source compiles to: the SDK's `static inline` `gpio_pull_up` disappears, and `main` calls `gpio_set_pulls(15, true, false)` directly at `0x1000024e`. + +#### Worked example: `gpio_set_function` + +1. `G` -> `0x1000029c`. +2. `Y` -> `void gpio_set_function(uint gpio, gpio_function_t fn)`. + +#### Worked example: `__wrap_printf` + +1. `G` -> `0x100031a4`. +2. `Y` -> `int __wrap_printf(const char *fmt, ...)`. Keep the `...` — `printf` is variadic. + +> **`printf` in our source is `__wrap_printf` in the binary.** The SDK links our `printf` calls to its `__wrap_printf` wrapper. + +The rest of the chain is the same two keystrokes per function (`G`, then `Y`). This is **our code plus the library functions it actually calls** — not the whole SDK. `main` calls `stdio_init_all`, `gpio_init`, `gpio_set_pulls`, and `printf`, so we follow that chain down: `stdio_init_all` pulls in the stdio/UART setup, `printf` lands in the SDK's `__wrap_printf`, and `uart_init` reaches `clock_get_hz` and `busy_wait_us`. + +The call chain for this project: + +``` +main +├── stdio_init_all +│ └── stdio_uart_init ── gpio_set_function, uart_init, stdio_set_driver_enabled +│ └── uart_init ── clock_get_hz, busy_wait_us +├── gpio_init +├── gpio_set_pulls (gpio_pull_up inlined) +├── __wrap_printf ── __wrap_vprintf +│ ├── vfctprintf ── _vsnprintf +│ │ └── _ntoa_format / _out_rev / _out_char +│ ├── stdio_out_chars_crlf +│ └── time_us_64 +└── gpio_set_dir / gpio_get / gpio_put (inlined; no calls) +``` + +**Resolve every function in that chain:** + +| Address | Rename to (`N`) | Signature (`Y`) | +| ------- | --------------- | --------------- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | +| `0x100001e4` | `_init` | `void _init(void)` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | +| **`0x10000234`** | **`main`** | **`int main(void)`** | +| `0x1000029c` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | +| `0x100002d8` | `gpio_set_pulls` | `void gpio_set_pulls(uint, bool, bool)` | +| `0x10000300` | `gpio_init` | `void gpio_init(uint)` | +| `0x10000e60` | `time_us_64` | `uint64_t time_us_64(void)` | +| `0x10000ed0` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | +| `0x10002dbc` | `exit` | `void exit(int)` | +| `0x10002dc4` | `runtime_init` | `void runtime_init(void)` | +| `0x10002df0` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | +| `0x10002f00` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | +| `0x10002fec` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | +| `0x10003014` | `stdio_init_all` | `bool stdio_init_all(void)` | +| `0x100030e0` | `__wrap_vprintf` | `int __wrap_vprintf(const char*, va_list)` | +| `0x100031a4` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | +| `0x10003360` | `stdio_uart_init` | `void stdio_uart_init(void)` | +| `0x100034a0` | `strlen` | `size_t strlen(const char*)` | + +**Resolve the SDK helpers the `printf` and `uart_init` paths reach:** + +| Address | Rename to (`N`) | Signature (`Y`) | +| ------- | --------------- | --------------- | +| `0x10002d60` | `vfctprintf` | `int vfctprintf(void (*)(char, void*), void*, const char*, va_list)` | +| `0x1000237c` | `_vsnprintf` | `int _vsnprintf(out_fct_type, char*, size_t, const char*, va_list)` | +| `0x10001760` | `_ntoa_format` | `unsigned _ntoa_format(out_fct_type, char*, size_t, size_t, char*, size_t, bool, unsigned, unsigned, unsigned, unsigned)` | +| `0x100016c4` | `_out_rev` | `unsigned _out_rev(out_fct_type, char*, size_t, size_t, const char*, size_t, unsigned, unsigned)` | +| `0x10001934` | `_out_char` | `void _out_char(char, void*, size_t, size_t)` | +| `0x10000e74` | `busy_wait_us` | `void busy_wait_us(uint64_t)` | +| `0x100010a4` | `clock_get_hz` | `unsigned long clock_get_hz(clock_handle_t)` | + +> **A `void` return type may not stick — here is the fix.** Binary Ninja treats `void` as low-confidence, and its analysis can override it with an inferred type — most often `int32_t` on this 32-bit target. It is most visible on `_reset_handler` (a hand-written assembly entry that never returns normally), but it can happen to **any** function whose return type Binary Ninja thinks it can infer. +> +> Setting the full signature with `Y` reproduces the unwanted `int32_t`, and `fn.return_type = ...` fails too. What works is the **return-value** setter: +> +> ```python +> from binaryninja import ReturnValue, Type +> fn = bv.get_function_at(0x1000015c) +> if fn is not None: +> fn.return_value = ReturnValue(Type.void()) +> ``` +> +> That holds `_reset_handler` at `void` even after reanalysis. If it still will not stick, leave it — it does not affect the rest of the lesson. + +> **`__wrap_printf` is the real symbol.** `printf` in our source compiles to the SDK's `__wrap_printf` (which forwards to `__wrap_vprintf`). Rename it `printf` if you prefer the lesson's shorthand, but `__wrap_printf` is what the ELF says. +> +> **`stdio_init_all` returns `bool`, not `void`** — `_Bool stdio_init_all(void)` in the ELF. + +> **Shortcut — resolves name *and* type for every function.** Instead of doing `N` + `Y` by hand, paste this into Binary Ninja's Python console (`Plugins -> Python Console`). It sets each function's name and signature programmatically: +> +> ```python +> from binaryninja import Symbol, SymbolType +> # The raw .bin has no headers, so these SDK types don't exist. set_user_type() +> # re-parses each signature as C, so an undefined name raises +> # "SyntaxError: unknown type name '...'". Define them first. +> sdk = bv.parse_types_from_string(""" +> typedef unsigned int uint; +> typedef char* va_list; +> typedef unsigned long clock_handle_t; +> typedef void (*out_fct_type)(char, void*, size_t, size_t); +> struct stdio_driver; +> typedef struct stdio_driver stdio_driver_t; +> struct uart_inst; +> typedef struct uart_inst uart_inst_t; +> enum gpio_function { +> GPIO_FUNC_XIP = 0, GPIO_FUNC_SPI = 1, GPIO_FUNC_UART = 2, GPIO_FUNC_I2C = 3, +> GPIO_FUNC_PWM = 4, GPIO_FUNC_SIO = 5, GPIO_FUNC_PIO0 = 6, GPIO_FUNC_PIO1 = 7, +> GPIO_FUNC_GPCK = 8, GPIO_FUNC_USB = 9, GPIO_FUNC_NULL = 0x1f, +> }; +> typedef enum gpio_function gpio_function_t; +> """) +> for name, ty in sdk.types.items(): +> bv.define_user_type(name, ty) +> +> # address: (name, signature) +> funcs = { +> 0x1000015c: ("_reset_handler", "void _reset_handler(void)"), +> 0x10000186: ("platform_entry", "void platform_entry(void)"), +> 0x1000019a: ("data_cpy", "void data_cpy(void*, void*, void*)"), +> 0x100001e4: ("_init", "void _init(void)"), +> 0x10000210: ("frame_dummy", "void frame_dummy(void)"), +> 0x10000234: ("main", "int main(void)"), +> 0x1000029c: ("gpio_set_function", "void gpio_set_function(uint, gpio_function_t)"), +> 0x100002d8: ("gpio_set_pulls", "void gpio_set_pulls(uint, bool, bool)"), +> 0x10000300: ("gpio_init", "void gpio_init(uint)"), +> 0x10000e60: ("time_us_64", "uint64_t time_us_64(void)"), +> 0x10000ed0: ("uart_init", "uint uart_init(uart_inst_t*, uint)"), +> 0x10002dbc: ("exit", "void exit(int)"), +> 0x10002dc4: ("runtime_init", "void runtime_init(void)"), +> 0x10002df0: ("stdio_out_chars_crlf", "void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)"), +> 0x10002f00: ("stdio_put_string", "int stdio_put_string(const char*, int, bool, bool)"), +> 0x10002fec: ("stdio_set_driver_enabled", "void stdio_set_driver_enabled(stdio_driver_t*, bool)"), +> 0x10003014: ("stdio_init_all", "bool stdio_init_all(void)"), +> 0x100030e0: ("__wrap_vprintf", "int __wrap_vprintf(const char*, va_list)"), +> 0x100031a4: ("__wrap_printf", "int __wrap_printf(const char*, ...)"), +> 0x10003360: ("stdio_uart_init", "void stdio_uart_init(void)"), +> 0x100034a0: ("strlen", "size_t strlen(const char*)"), +> 0x10002d60: ("vfctprintf", "int vfctprintf(void (*)(char, void*), void*, const char*, va_list)"), +> 0x1000237c: ("_vsnprintf", "int _vsnprintf(out_fct_type, char*, size_t, const char*, va_list)"), +> 0x10001760: ("_ntoa_format", "unsigned _ntoa_format(out_fct_type, char*, size_t, size_t, char*, size_t, bool, unsigned, unsigned, unsigned, unsigned)"), +> 0x100016c4: ("_out_rev", "unsigned _out_rev(out_fct_type, char*, size_t, size_t, const char*, size_t, unsigned, unsigned)"), +> 0x10001934: ("_out_char", "void _out_char(char, void*, size_t, size_t)"), +> 0x10000e74: ("busy_wait_us", "void busy_wait_us(uint64_t)"), +> 0x100010a4: ("clock_get_hz", "unsigned long clock_get_hz(clock_handle_t)"), +> } +> for addr, (name, sig) in funcs.items(): +> bv.define_user_symbol(Symbol(SymbolType.FunctionSymbol, addr, name)) +> f = bv.get_function_at(addr) +> if f is not None: +> f.set_user_type(sig) +> ``` +> +> SDK type names (`stdio_driver_t`, `gpio_function_t`, `uart_inst_t`, plus `uint`, `va_list`, `out_fct_type`, and `clock_handle_t`) are **not** in the raw `.bin`. `set_user_type` re-parses each signature as C, so an undefined name raises `SyntaxError: unknown type name '...'` and stops the loop — it is not harmless. The `sdk` block above defines them first (an opaque `struct`/`enum`/`typedef` is enough to parse). If you add a function that uses another SDK type, add a definition for it to that block too. + +### Step 17: Read `main` in the decompiler + +Open the **Decompiler** view on `main`. Once the functions above are typed, it reads roughly: + +```c +int32_t main(void) +{ + stdio_init_all(); + gpio_init(0xf); + gpio_set_dir(0xf, GPIO_IN); // inlined: mcrr 0, 4, r0, r3, cr4 + gpio_set_pulls(0xf, true, false); // gpio_pull_up(15) + gpio_init(0x10); + gpio_set_dir(0x10, GPIO_OUT); // inlined + do + { + __wrap_printf("regular_fav_num: %d\r\n", 0x2a); + __wrap_printf("static_fav_num: %d\r\n", *(uint8_t*)0x200005a8); + *(uint8_t*)0x200005a8 = *(uint8_t*)0x200005a8 + 1; // static_fav_num++ + gpio_put(0x10, gpio_get(0xf) ^ 1); // inlined SIO read/write + } while (true); +} +``` + +The `0x2a` is the value we edited live; `0x200005a8` is the fixed RAM address of `static_fav_num`. The `gpio_get(0xf) ^ 1` is the `eor.w r3, r3, #1` we will patch in Step 18b. Now make the hacks permanent. + +### Step 18: Patch 1 — change `regular_fav_num` from 42 to 43 + +Go to `0x10000264`: + +```asm +10000264: 212a movs r1, #42 ; 0x2a +``` + +The halfword is `0x212a`, stored little-endian as `2a 21`. The immediate is the low byte, so the byte at the instruction's own address is `0x2a`. Change it to `0x2b` (43). + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x0014` | `0x10000264` | `2a` | `2b` | `movs r1, #42` -> `#43`, prints `regular_fav_num: 43` | + +**Option A — Hex view:** + +1. Switch to the **Hex** view (`View -> Hex`). +2. Toggle the lock off so editing is enabled. +3. Go to `0x10000264` and change the byte `2A` to `2B`. +4. Return to the linear view, right-click the function -> `Reanalyze`. + +**Option B — Python console:** + +```python +bv.write(0x10000264, b"\x2b") +print(hex(bv.read(0x10000264, 1)[0])) # -> 0x2b +``` + +After reanalysis the instruction reads `movs r1, #43`. + +### Step 18b: Patch 2 — invert the button/LED logic + +The stock code inverts the raw button bit with `eor.w r3, r3, #1` at `0x10000286`. Its four bytes are `83 f0 01 03`; the immediate `#1` is the **third** byte, at `0x10000288`. Change `01` to `00` so the XOR becomes `#0` (a no-op) and the LED follows the raw pin state instead: + +```asm +10000286: f083 0301 eor.w r3, r3, #1 ; immediate byte at 0x10000288 +``` + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x0014` | `0x10000288` | `01` | `00` | `eor.w r3, r3, #1` -> `#0`, inverts the LED behavior | + +**Option A — Hex view:** + +1. Switch to the **Hex** view (`View -> Hex`). +2. Go to `0x10000288` and change the byte `01` to `00`. +3. Return to the linear view and reanalyze. + +**Option B — Python console:** + +```python +bv.write(0x10000288, b"\x00") +print(bv.read(0x10000286, 4).hex()) # -> 83f00003 +``` + +Now the logic is permanently changed: + +- Button released (GPIO 15 reads `1`): `1 XOR 0 = 1` -> LED **ON** +- Button pressed (GPIO 15 reads `0`): `0 XOR 0 = 0` -> LED **OFF** + +This is the **opposite** of the original behavior. The `gpio_get` and `gpio_put` are inlined, so the only byte that controls the inversion is this immediate. + +### Step 18c: Patch 3 — rename the printed label (optional) + +The format string `"regular_fav_num: %d\r\n"` starts at `0x10003560`. Its first 16 bytes are `72 65 67 75 6c 61 72 5f 66 61 76 5f 6e 75 6d 3a` (`regular_fav_num:`). Change them to `70 61 74 63 68 65 64 5f 66 61 76 5f 6e 75 6d 3a` (`patched_fav_num:`), leaving the ` %d\r\n` tail untouched, so the line prints `patched_fav_num: 43`. + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x0014` | `0x10003560` | `72 65 67 75 6c 61 72 5f 66 61 76 5f 6e 75 6d 3a` | `70 61 74 63 68 65 64 5f 66 61 76 5f 6e 75 6d 3a` | `regular_fav_num:` -> `patched_fav_num:` | + +**Option A — Hex view:** + +1. Switch to the **Hex** view (`View -> Hex`). +2. Go to `0x10003560` and change the sixteen bytes `72 65 67 75 6c 61 72 5f 66 61 76 5f 6e 75 6d 3a` to `70 61 74 63 68 65 64 5f 66 61 76 5f 6e 75 6d 3a`. +3. Return to the linear view and reanalyze. + +**Option B — Python console:** + +```python +bv.write(0x10003560, b"patched_fav_num:") +print(bv.read(0x10003560, 22)) # -> b'patched_fav_num: %d\r\n\x00' +``` + +Keep the replacement exactly sixteen bytes — the same length as `regular_fav_num:`. If you use a shorter string you must pad it, or `%d` shifts and `printf` reads the wrong argument. A longer string would overwrite the ` %d` tail. + +### Step 19: Export the patched `.bin` + +```python +import os +seg = next(s for s in bv.segments if s.data_length) # the loadable image segment +data = bv.read(seg.start, seg.data_length) # base + size come from the view itself +out = os.path.join(os.path.join(root, "0x0014_static-variables", "build"), "0x0014_static-variables-h.bin") +open(out, "wb").write(data) +print(len(data), out) # -> 15516 /.../build/0x0014_static-variables-h.bin +``` + +Where the two numbers come from — nothing is hardcoded: + +- **`seg.start`** is the image base Binary Ninja loaded the `.bin` at (`0x10000000`), the same value you pass to `uf2conv --base`. +- **`seg.data_length`** is the segment's size in the file (`0x3c9c` = 15516). Exactly one segment carries data (the image); every peripheral and synthetic segment has `data_length == 0`, so `next(...)` picks the image. +- Reading `seg.start` for `seg.data_length` bytes therefore grabs exactly the image. + +Two gotchas this avoids: + +- **No relative path.** Binary Ninja's Python console runs with a read-only working directory (inside the app bundle), so `open("0x0014_static-variables-h.bin", "wb")` fails with `OSError: [Errno 30] Read-only file system`. `root` (from `~/.embedded-hacking-repo`, Step 3) is the repo, so the file is written into the project's `build/` — no machine-specific path and no database needed. +- **Read the image, not the whole view.** `bv.read(bv.start, bv.length)` spans the entire mapped range, which is not the image. The segment's `data_length` is the image size. + +A different size means you exported a partial view. + +### Step 20: Convert to UF2 + +Run from the project directory: + +**macOS Apple Silicon / Linux x64:** + +```bash +python3 ../uf2conv.py 0x0014_static-variables-h.bin \ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +**Windows x64:** + +```cmd +python ..\uf2conv.py 0x0014_static-variables-h.bin ^ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +> **Or convert from the Binary Ninja console** — it is a normal Python interpreter, so you never have to leave the app. `chdir` to a writable directory first (the default one is read-only), then run the script: +> +> ```python +> import os, sys, runpy +> os.chdir(os.path.join(root, "0x0014_static-variables", "build")) # the project build dir (writable) +> sys.argv = ["uf2conv.py", "0x0014_static-variables-h.bin", +> "--base", "0x10000000", "--family", "0xe48bff59", "--output", "hacked.uf2"] +> runpy.run_path("../../uf2conv.py", run_name="__main__") # path to your uf2conv.py +> ``` +> +> This writes `hacked.uf2` next to the `.bin`, ready to drag onto the Pico. + +### Step 21: Flash and verify + +Hold **BOOTSEL**, plug in the Pico 2, and drag `hacked.uf2` onto the **`RP2350`** drive. Open the serial monitor: + +``` +patched_fav_num: 43 +static_fav_num: 42 +patched_fav_num: 43 +static_fav_num: 43 +patched_fav_num: 43 +static_fav_num: 44 +... +``` + +and the **LED is on by default** (button released) and turns **off** when you press the button — the opposite of the stock image. + +**The regular value is now 43, the label reads `patched_fav_num:`, and the button/LED logic is inverted — with eighteen bytes changed and no source code.** (`static_fav_num` keeps incrementing from `42`, unchanged: we left the static variable itself alone so you can watch it persist.) + +> **Faster: flash over the Debug Probe (no BOOTSEL).** The repo's `flash.sh` writes the raw `.bin` straight into XIP flash over SWD (`program 0x10000000 verify reset exit`), so you never touch BOOTSEL or a UF2. Run it from a terminal (`./flash.sh `), or from the Binary Ninja console **without freezing it** — use `subprocess.Popen`, which returns immediately, and send OpenOCD's output to a log file. (`subprocess.run` blocks the console until the flash finishes; do not use it here.) +> +> ```python +> import os, subprocess +> root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +> bin_path = os.path.join(os.path.join(root, "0x0014_static-variables", "build"), "0x0014_static-variables-h.bin") +> log = os.path.join(os.path.join(root, "0x0014_static-variables", "build"), "flash.log") +> subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +> p = subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], +> stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +> print("flashing in the background; log:", log) +> ``` +> +> The `pkill` frees the probe first; on Windows use `subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"])`. +> +> The console is free the moment this returns. Check it with `print(p.poll())` (`None` = still running, `0` = done) or read `flash.log` — success ends with `** Verified OK **`. +> +> The same non-blocking form without the script: +> +> ```python +> import os, subprocess +> ocd = os.path.expanduser("~/.pico-sdk/openocd/0.12.0+dev") +> bin_path = os.path.join(os.path.join(root, "0x0014_static-variables", "build"), "0x0014_static-variables-h.bin") +> log = os.path.join(os.path.join(root, "0x0014_static-variables", "build"), "flash.log") +> subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +> p = subprocess.Popen([f"{ocd}/openocd", "-s", f"{ocd}/scripts", +> "-f", "interface/cmsis-dap.cfg", "-f", "target/rp2350.cfg", +> "-c", "adapter speed 5000", +> "-c", f"program {bin_path} 0x10000000 verify reset exit"], +> stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +> print("flashing in the background; log:", log) +> ``` +> +> **The Debug Probe is single-owner.** If Binary Ninja is still attached (the `debug-server.sh` OpenOCD is running), the flash cannot grab the probe. Detach in Binary Ninja and stop that OpenOCD first: +> +> ```bash +> # macOS / Linux +> pkill -TERM -f openocd +> ``` +> ```powershell +> # Windows +> Get-Process openocd -ErrorAction SilentlyContinue | Stop-Process +> ``` +> +> Success looks like `Programming Finished` -> `Verified OK` -> `Resetting Target`. On Windows use `flash.ps1` (`.\flash.ps1 -Bin `) the same way. + +--- + +## Cheatsheet + +### Binary Ninja GUI actions + +| Action | How | +| ------ | --- | +| Go to address | `G` | +| Rename function/symbol | `N` | +| Set type or signature | `Y` | +| Add comment | `;` | +| Open Hex view | `View -> Hex` | +| Enable hex editing | Toggle the lock in the status bar | +| Reanalyze after a patch | Right-click function -> `Reanalyze` | +| Edit a register live | `dbg.set_reg_value("r1", 0x2b)` in the Python console (or right-click the register, press `E`, type hex, Enter) | +| Write a RAM string live | `dbg.write_memory(0x20080000, b"patched_fav_num: %d\r\n\x00")` | +| Set a breakpoint | `Debugger -> Add Hardware Breakpoint...` (hardware execute). Do **not** use `F2` — software breakpoints cannot be written to read-only flash. | +| Move a breakpoint | Remove it and set it at the new address in the GUI (command-port fallback: `rbp ` then `bp 2 hw`) | +| Confirm what is armed | The **Breakpoints** widget lists it (command-port fallback: `mdw 0xE0002000 8`, each armed breakpoint shows as ``) | +| Apply the ELF symbol map | Paste the Python snippet from Step 16 into the Python Console | + +### OpenOCD server and reset + +The server runs with `gdb_breakpoint_override hard` so that flash-writes are never attempted. Breakpoints in this lab are set in the Binary Ninja GUI through the **GDB MI** adapter (Step 12). The command-port rows below are the fallback if you use the **GDB RSP** adapter instead. + +| Action | Command | +| ------ | ------- | +| Connect to the OpenOCD prompt (fallback) | `nc 127.0.0.1 4444` (or `telnet 127.0.0.1 4444`) | +| Reset and run (command port) | `reset run` | +| Check core state (command port) | `targets` | +| Set a breakpoint in the GUI | `Debugger -> Add Hardware Breakpoint...` (hardware execute; `F2` software breakpoints do not work on flash) | +| (fallback) Add a breakpoint without the GUI | `bp 2 hw` | +| Remove one breakpoint | `rbp ` — **address only, no length, no `hw`** | +| Remove every breakpoint | `rbp all` | +| Start the server parked at `main` | macOS/Linux: `BP_ADDR=0x10000234 ./debug-server.sh` — Windows: `$env:BP_ADDR="0x10000234"; .\debug-server.ps1` (one-shot) | +| Start the server parked in the loop | macOS/Linux: `BP_ADDR=0x10000268 ./debug-server.sh` — Windows: `$env:BP_ADDR="0x10000268"; .\debug-server.ps1` | +| Break on the loop in a running target | set a hardware breakpoint in the GUI at the loop address, then **Resume** — repeatable | +| Make Binary Ninja stepping work | `rp2350.dap.core0 configure -rtos none` (already in the scripts) | +| Step without re-trapping | move the breakpoint off the current PC first, then **Step Into**/**Step Over** | +| Reset without desyncing Binary Ninja | **Detach**, `reset run` on the port, reconnect — never `reset run` while attached | + +### Every address and byte we changed + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x0014` | `0x10000264` | `2a` | `2b` | `movs r1, #42` -> `#43`, prints `regular_fav_num: 43` | +| `0x0014` | `0x10000288` | `01` | `00` | `eor.w r3, r3, #1` -> `#0`, inverts the button/LED logic | +| `0x0014` | `0x10003560` | `72 65 67 75 6c 61 72 5f 66 61 76 5f 6e 75 6d 3a` | `70 61 74 63 68 65 64 5f 66 61 76 5f 6e 75 6d 3a` | string prints `patched_fav_num:` instead of `regular_fav_num:` | + +### The static-variable memory map + +| Item | Address | Notes | +| ---- | ------- | ----- | +| `static_fav_num` | `0x200005a8` | RAM `.data`, fixed for the life of the program; initial value `42` copied from flash at boot | +| Literal-pool word 1 | `0x10000290` | `0x200005a8` — the RAM address `r4` holds | +| Literal-pool word 2 | `0x10000294` | `0x10003560` — pointer to `"regular_fav_num: %d\r\n"` | +| Literal-pool word 3 | `0x10000298` | `0x10003578` — pointer to `"static_fav_num: %d\r\n"` | +| `regular_fav_num` | (no address) | inlined to `movs r1, #42`; never given a stack slot | + +### Raw image facts + +| Item | Value | +| ---- | ----- | +| Build type | `Release` | +| Load base address | `0x10000000` | +| Project size | `15516` bytes (`0x3c9c`) | +| Fixed `main` anchor | `0x1000018c` (reset handler middle `blx`) | +| `main` | `0x10000234` | +| `printf` call / return, `regular_fav_num` | `0x10000268` / `0x1000026c` | +| `printf` call, `static_fav_num` | `0x10000270` | +| `static_fav_num` RAM address | `0x200005a8` | +| `regular_fav_num` format string | `0x10003560` | +| `static_fav_num` format string | `0x10003578` | +| RP2350 UF2 family ID | `0xe48bff59` | + +--- + +## Troubleshooting + +### Binary Ninja hangs or crashes when you connect (macOS 27) + +Three different causes have been seen on this setup; check them in this order. + +- **A breakpoint set before connecting.** With the **GDB MI** adapter, if the binary view already has a breakpoint, the session hangs. Start parked with `BP_ADDR`, connect, then add breakpoints (see the next entry). +- **The wrong GDB executable.** Point **Full GDB Executable Path** at the **14.2.rel1** toolchain (Step 10). The 13.3.rel1 build did **not** connect in testing. +- **The LLDB adapter.** A crash report with `libdebuggercore.dylib -> std::terminate() -> abort()` and `liblldb` in the stack is the **LLDB** adapter, not GDB MI. Avoid LLDB on this setup. + +**Use GDB MI**, with the 14.2.rel1 path above. If it still fails, fall back to plain `arm-none-eabi-gdb` against the same server — the addresses and register values are identical to the GUI steps. + +If Binary Ninja hangs, force-quit it; the connect dialog has no working Cancel. The static steps (resolve, patch, export, flash) never touch the debugger and always work. + +### The GUI refuses to set a breakpoint (GDB RSP adapter only) + +If you are on the **GDB RSP** adapter, the GUI cannot set breakpoints on this target. That adapter is Binary Ninja's own minimal RSP client and sends a **1-byte** breakpoint (`Z0,,1`); the Cortex-M33 comparators need 2 bytes, so OpenOCD answers `only breakpoints of two bytes length supported`. It affects every address, both `Toggle Breakpoint` and `Add Hardware Breakpoint`, and the dialog's **Size** field is disabled. `gdb_breakpoint_override` makes no difference. + +**Fix: use the GDB MI adapter** (Step 10). It drives real GDB, which sends the correct length, so GUI breakpoints just work. If you must stay on GDB RSP, arm breakpoints from the command port after connecting (`bp 2 hw`) — but the lab uses GDB MI and does not need that. + +### GDB MI hangs when you connect (a breakpoint already existed) + +With the **GDB MI** adapter, if Binary Ninja already has a breakpoint set when you connect, the session **hangs**. This is a Binary Ninja bug. The working order is: + +1. Start the server parked, e.g. `BP_ADDR=0x10000234 ./debug-server.sh` (Windows: `$env:BP_ADDR="0x10000234"; .\debug-server.ps1`). +2. Connect with the **GDB MI** adapter. +3. Only *then* set hardware breakpoints in the UI. + +Never have a breakpoint in the binary view before the GDB MI connection. If it hangs, quit Binary Ninja, restart the server with `BP_ADDR`, and connect again before adding any breakpoints. + +### Step Into / Step Over does nothing (PC never moves) + +Two causes have been seen on this target. + +1. **A breakpoint on the current PC re-traps the step.** OpenOCD's step-over-breakpoint logic fails with `Duplicate Breakpoint address` and the PC stays put. Fix: move the breakpoint off the current PC (in the GUI), then step. +2. **The `hwthread` RTOS (GDB RSP adapter only).** With the **GDB RSP** adapter, OpenOCD can log `fake step thread 0` and reply without stepping, because the RP2350 config's `-rtos hwthread` makes the current thread id 1 while Binary Ninja sends thread id 0. Fix: `rp2350.dap.core0 configure -rtos none` (the launcher scripts already pass this). **GDB MI does not hit this.** + +To tell them apart, turn on OpenOCD logging (`log_output /tmp/ocd.log`, then `debug_level 3` on the command port) and look for `fake step` versus `Duplicate Breakpoint`. + +### `zsh: bad CPU type in executable: cmake` + +An Intel `x86_64` tool is on your `PATH` on Apple Silicon. Run Step 2: `export PATH="/opt/homebrew/bin:$PATH"`, then `hash -r`. Add it to `~/.zshrc` to make it permanent. + +### My addresses do not match this guide + +You probably built `Debug`. This lesson is a `Release` build. Re-run Step 3 with `-DCMAKE_BUILD_TYPE=Release`. A `Debug` build moves the SDK functions and keeps `demo_static_variable` separate, so `main` is not at `0x10000234`. + +### A breakpoint never fires + +First, confirm you actually set one, and that it is a **hardware** breakpoint. With the **GDB MI** adapter, `Debugger -> Add Hardware Breakpoint...` (hardware execute) should land in the **Breakpoints** widget. If nothing lands, or the core keeps running, you probably used `F2` (`Toggle Breakpoint`) — that is a software breakpoint and cannot be written to read-only flash, so it never installs. Also check you are on **GDB MI**, not **GDB RSP** (the GDB RSP adapter cannot set breakpoints on this target at all). + +Then check the order and the state: + +- **Arm it only after Binary Ninja is connected.** OpenOCD flushes every breakpoint when a client attaches, so anything armed earlier is gone. This also applies to `BP_ADDR` on the startup command line. +- **Verify it is armed:** `mdw 0xE0002000 8`. You should see your address with the low bit set (`0x10000234` -> `0x10000235`). All zeros means nothing is armed — re-read this first, because it distinguishes "not armed" from "armed but never reached". +- **Is the core running?** `poll` on the command port should not report a halt. If it is stopped, click **Resume**. +- **Does the address get reached again?** `main` runs once per reset, so use `BP_ADDR` at startup (Step 9) rather than `reset run` while attached. Loop addresses such as `0x10000268` fire on the next pass with no reset — arm them and click **Resume** in Binary Ninja. +- **With GDB MI the stop is reported as `Breakpoint`** and appears in the **Breakpoints** widget, because GDB really did set it. + +### I edit `r1` (or another register) and it reverts + +`main` reloads the value at the top of every loop iteration — `movs r1, #42` at `0x10000264` runs right before the `printf` at `0x10000268`. So `r1` is only `0x2b` for the instant between your edit and the next pass; then it is `0x2a` again. The edit sticks only if the core is **genuinely stopped** at the breakpoint and stays stopped. + +If it keeps reverting, the core is running, which almost always means the breakpoint is not installed — usually because it is a **software** breakpoint (`F2`) that cannot be written to read-only flash. Use `Debugger -> Add Hardware Breakpoint...` (hardware execute). + +> **The Registers widget is a snapshot, not a live view.** Binary Ninja reads the registers at each stop and shows that snapshot; it does not poll the target, and there is no "refresh registers" command. So a value changed outside Binary Ninja will not appear until the next stop. + +### The static variable shows a wrong value in GDB / Binary Ninja + +`static_fav_num` is a function-local `static`, so it does not appear in the global symbol table as `static_fav_num`; `arm-none-eabi-nm` names it `static_fav_num.0`. A 4-byte read at `0x200005a8` returns `0x0000122a`, whose low byte `0x2a` (42) is the variable — the upper bytes belong to whatever is in adjacent RAM. Read **one byte** (`x/1ub 0x200005a8`) to see `42`, and never add a `*` (that would treat the value as a pointer). In Binary Ninja, define a `uint8_t` at `0x200005a8` if you want the decompiler to show the byte cleanly. + +### The serial capture is garbage on macOS + +Reading `/dev/cu.usbmodem*` with a bare `read()` returns garbage. Set **raw termios at 115200** first: clear canonical/echo flags, set `CLOCAL|CREAD`, and `B115200` on input and output. `screen /dev/cu.usbmodem* 115200` does all of this for you; a script must call `tcsetattr` itself. Once set, the capture reads clean `regular_fav_num: 42` lines. + +### It worked for a second, then stopped (Binary Ninja's view desyncs) + +This is the most common failure, and it has one main cause: **driving the core from the OpenOCD command port while Binary Ninja is connected.** + +- If you send `reset run` from the port while attached, the core resets, runs, and halts at your breakpoint — but Binary Ninja never receives the stop event. Its sidebar keeps showing the *previous* location, so **Step** and **Resume** act on a stale PC and appear to do nothing. +- If the OpenOCD process dies (or you restart it) while attached, Binary Ninja keeps believing it is connected: the sidebar stays, but the menu shows **Pause** enabled and **Resume**/**Step** disabled because Binary Ninja last saw the target *running*. + +Recovery: **Detach, then reconnect.** If Detach does nothing (the connection is already dead), restart Binary Ninja — its menu still shows a session that no longer exists. + +Prevention: + +- Stop at `main` with `BP_ADDR` on a fresh server start, not with `reset run` while attached. +- For loop addresses, set the breakpoint in the GUI and click **Resume**. Let Binary Ninja be the thing that starts the core. +- If you must reset, **Detach first**, `reset run`, then reconnect. +- Never leave a breakpoint on the PC you are about to step or resume from. + +### The target "blows past" `main` and stops at `0x100032cc` instead + +`0x100032cc` is inside `stdio_uart_out_flush`: + +```asm +100032c8: 4b02 ldr r3, [pc, #8] ; @ 0x100032d4 +100032ca: 681a ldr r2, [r3] +100032cc: 6993 ldr r3, [r2, #24] ; the core sits here while the UART drains +100032ce: 071b lsls r3, r3, #28 +100032d0: d4fc bmi.n 0x100032cc +100032d2: 4770 bx lr +100032d4: 20000850 .word 0x20000850 +``` + +That is the UART transmit-FIFO drain loop inside `printf`, so the core is running `main`'s loop and simply spends nearly all its time there. The breakpoint at `main` did not fire because `main`'s entry runs exactly **once per reset**. If you arm the breakpoint after the reset, or set it while the target is already running and just resume, the core is already past `main` and will never re-execute it. Either arm the breakpoint **before** resetting, or break inside the loop at `0x10000268`, which fires every iteration. + +**`0x100032cc` is not a function.** It is one instruction inside `stdio_uart_out_flush`, which starts at `0x100032c8`. If Binary Ninja has created a function at `0x100032cc` (for example because the debugger stopped at that PC), the decompiler shows garbage. Delete that bogus function (right-click it -> `Delete Function`, or put the cursor on it and press `U` to undefine) and reanalyze. The real function is `stdio_uart_out_flush` at `0x100032c8`. + +### The console floods with `Failed to read memory at 0xf0000000` + +Core1 is exposed. The scripts must run with `USE_CORE=0`. Stop the server, confirm only `core0` is reported, restart, then restart Binary Ninja. + +### `Connect to Remote Process` is greyed out and Pause does nothing + +Binary Ninja is in a stale session, usually because the debug server restarted while attached. Quit and reopen Binary Ninja (or the `.bndb`) and connect again. + +### The decompiler still shows the old value after patching + +Right-click the function and choose `Reanalyze`. + +### The LED does not change after patching + +You patched the wrong byte. The inversion immediate is the **third** byte of the 4-byte `eor.w` instruction: the instruction is at `0x10000286`, so the byte to change is `0x10000288`. Confirm it now reads `00`, not `01`. + +--- + +## Fallback: do the dynamic steps with GDB (macOS 27) + +If Binary Ninja's debugger crashes on attach on macOS 27 (see Troubleshooting), you can still do the live hack with the ARM GDB from the toolchain, against the same OpenOCD server. The addresses and register values are identical to the GUI steps. + +Start the debug server (Step 9), then in a new terminal: + +``` +arm-none-eabi-gdb +``` + +At the `(gdb)` prompt: + +``` +set architecture armv8-m.main +target extended-remote :3333 +hbreak *0x10000268 +continue +``` + +Do **not** run `monitor reset run` before `hbreak`. `0x10000268` is inside `main`'s loop, so the breakpoint fires on the next iteration with no reset. If you reset first, the core runs `main` and you will not catch it. + +GDB stops at the `printf` call. Confirm the value, change it, and let it run: + +``` +info registers pc r1 # pc = 0x10000268, r1 = 0x2a +set $r1 = 0x2b +stepi +continue +``` + +The serial monitor prints `regular_fav_num: 43` for the iteration you changed — the same temporary live hack as editing `r1` in the Binary Ninja Registers widget. When you are done, press `Ctrl-C`, then `detach` and `quit`. + +**If you specifically want to stop at `main` (`0x10000234`),** remember its entry runs only once per reset, so the breakpoint must be armed *before* the reset: + +``` +monitor reset halt +hbreak *0x10000234 +continue +``` + +If you instead set it while the target is running and just `continue`, you will "blow past" `main` and catch the core inside `printf` — in this build at `0x100032cc`, the `stdio_uart_out_flush` UART-drain loop. + +`hbreak` sets a hardware breakpoint, which is required for read-only flash. It works from plain GDB because GDB sends the 2-byte length the Cortex-M33 comparators need. Binary Ninja's **GDB MI** adapter goes through the same GDB, so its GUI breakpoints work too; the old **GDB RSP** adapter was the one that sent a 1-byte length and could not set breakpoints here. + +## Glossary + +| Term | Definition | +| ---- | ---------- | +| **`.bss`** | Section for uninitialized (or zero-initialized) static/global variables; zeroed by startup code | +| **`.data`** | Section for initialized static/global variables; copied from flash to SRAM at boot | +| **`.elf`** | Linked image with the symbol table; the ground truth for addresses and names | +| **`.rodata`** | Read-only section for constants and string literals; stays in flash | +| **Automatic variable** | A local variable created and destroyed with its block; lives on the stack (or is optimized away) | +| **`eor` / XOR** | Exclusive OR — flips bits where the operands differ | +| **GPIO** | General Purpose Input/Output — controllable pins on the microcontroller | +| **Hardware breakpoint** | A breakpoint serviced by the CPU comparators, required for read-only flash | +| **Inlining** | The optimizer replacing a function call with the function body; why `demo_static_variable` disappears | +| **Literal pool** | A block of 32-bit constants that Thumb-2 code reaches with PC-relative `ldr` | +| **MMIO** | Memory-mapped I/O — hardware registers accessed as memory addresses | +| **Pull-up / pull-down** | A resistor that holds an input pin at a defined level when nothing drives it | +| **SIO** | Single-cycle I/O — the fast GPIO block in the RP2350, at `0xd0000000` | +| **Static variable** | A variable with static storage duration; persists for the whole program and keeps a fixed address | +| **Ternary operator** | `condition ? value_if_true : value_if_false` | +| **Thumb bit** | Bit 0 of a Cortex-M function pointer; selects Thumb instruction mode | +| **`ubfx`** | Unsigned Bit Field Extract — pulls a bit field out of a register | +| **UF2** | USB Flashing Format — the file format the Pico 2 bootloader accepts | +| **Vector table** | The first words of flash: initial stack pointer and exception vectors | + +--- + +**Remember:** the ELF tells you what every address is, and the `.bin` is what you actually patch. Prove the behavior dynamically, read `r1` at the `printf` call, resolve the names from the ELF, then patch the bytes — the constant `42`, the inversion immediate, and (optionally) the label — and flash. diff --git a/WEEK06/WEEK06-BN.pdf b/WEEK06/WEEK06-BN.pdf new file mode 100644 index 0000000..c11c48e Binary files /dev/null and b/WEEK06/WEEK06-BN.pdf differ diff --git a/WEEK06/WEEK06-SLIDES.pdf b/WEEK06/WEEK06-SLIDES.pdf new file mode 100644 index 0000000..ae53838 Binary files /dev/null and b/WEEK06/WEEK06-SLIDES.pdf differ diff --git a/WEEK06/WEEK06.md b/WEEK06/WEEK06.md new file mode 100644 index 0000000..be5411f --- /dev/null +++ b/WEEK06/WEEK06.md @@ -0,0 +1,1300 @@ +# Week 6: Static Variables in Embedded Systems: Debugging and Hacking Static Variables w/ GPIO Input Basics + +*** +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: + +- Understand the difference between regular (automatic) variables and static variables +- Know where different types of variables are stored (stack vs static storage) +- Configure GPIO pins as inputs and use internal pull-up resistors +- Read button states using `gpio_get()` and control LEDs based on input +- Use GDB to examine how the compiler handles static vs automatic variables +- Identify compiler optimizations by stepping through assembly +- Hack variable values and invert GPIO input/output logic using a hex editor +- Convert patched binaries to UF2 format for flashing + +--- + +## Part 1: Understanding Static Variables + +### What is a Static Variable? + +A **static variable** is a special kind of variable that "remembers" its value between function calls or loop iterations. Unlike regular variables that get created and destroyed each time, static variables **persist** for the entire lifetime of your program. + +Think of it like this: + +- **Regular variable:** Like writing on a whiteboard that gets erased after each class +- **Static variable:** Like writing in a notebook that you keep forever + +``` ++-----------------------------------------------------------------+ +| Regular vs Static Variables | +| | +| REGULAR (automatic): | +| +------------------------------------------------------------+ | +| | Loop 1: Create -> Set to 42 -> Increment to 43 -> Destroy | +| | Loop 2: Create -> Set to 42 -> Increment to 43 -> Destroy | +| | Loop 3: Create -> Set to 42 -> Increment to 43 -> Destroy | +| | Result: Always appears as 42! | +| +------------------------------------------------------------+ | +| | +| STATIC: | +| +------------------------------------------------------------+ | +| | Loop 1: Already exists -> Read 42 -> Increment -> Store 43 | +| | Loop 2: Already exists -> Read 43 -> Increment -> Store 44 | +| | Loop 3: Already exists -> Read 44 -> Increment -> Store 45 | +| | Result: Keeps incrementing! | +| +------------------------------------------------------------+ | +| | ++-----------------------------------------------------------------+ +``` + +### The `static` Keyword + +In C, you declare a static variable by adding the `static` keyword: + +```c +uint8_t regular_fav_num = 42; // Regular - recreated each time +static uint8_t static_fav_num = 42; // Static - persists forever +``` + +### Where Do Variables Live in Memory? + +Different types of variables are stored in different memory locations: + +| Variable Type | Storage Location | Lifetime | Example | +| ----------------- | ---------------- | ------------------------- | ------------------------------------ | +| Automatic (local) | Stack | Until function/block ends | `uint8_t x = 5;` | +| Static | Static Storage | Entire program lifetime | `static uint8_t x = 5;` | +| Global | Static Storage | Entire program lifetime | `uint8_t x = 5;` (outside functions) | +| Dynamic (heap) | Heap | Until `free()` is called | `malloc(sizeof(int))` | + +### Stack vs Static Storage vs Heap + +``` ++-----------------------------------------------------------------+ +| Memory Layout | +| | +| +-------------------+ High Address (0x20082000) | +| | STACK | ?? Automatic/local variables | +| | (grows down) | Created/destroyed per function | +| +-------------------+ | +| | | | +| | (free space) | | +| | | | +| +-------------------+ | +| | HEAP | ?? Dynamic allocation (malloc/free) | +| | (grows up) | | +| +-------------------+ | +| | .bss section | ?? Uninitialized static/global vars | +| +-------------------+ | +| | .data section | ?? Initialized static/global vars | +| +-------------------+ Low Address (0x20000000) | +| | ++-----------------------------------------------------------------+ +``` + +**Key Point:** Static variables are NOT on the heap! They live in a fixed location in the `.data` section (if initialized) or `.bss` section (if uninitialized). This is different from heap memory which is dynamically allocated at runtime. + +### What Happens with Overflow? + +Since `static_fav_num` is a `uint8_t` (unsigned 8-bit), it can only hold values 0-255. What happens when it reaches 255 and we add 1? + +``` +255 + 1 = 256... but that doesn't fit in 8 bits! +Binary: 11111111 + 1 = 100000000 (9 bits) +The 9th bit is lost, so we get: 00000000 = 0 +``` + +This is called **overflow** or **wrap-around**. The value "wraps" back to 0 and starts counting again! + +--- + +## Part 2: Understanding GPIO Inputs + +### Input vs Output + +So far, we've used GPIO pins as **outputs** to control LEDs. Now we'll learn to use them as **inputs** to read button states! + +``` ++-----------------------------------------------------------------+ +| GPIO Direction | +| | +| OUTPUT (what we've done before): | +| +---------+ | +| | Pico | -------? LED | +| | GPIO 16 | (We control the LED) | +| +---------+ | +| | +| INPUT (new this week): | +| +---------+ | +| | Pico | ?------- Button | +| | GPIO 15 | (We read the button state) | +| +---------+ | +| | ++-----------------------------------------------------------------+ +``` + +### The Floating Input Problem + +When a GPIO pin is set as an input but nothing is connected, it's called a **floating input**. The voltage on the pin is undefined and can randomly read as HIGH (1) or LOW (0) due to electrical noise. + +``` ++-----------------------------------------------------------------+ +| Floating Input = Random Values! | +| | +| GPIO Pin (no connection): | +| Reading 1: HIGH | +| Reading 2: LOW | +| Reading 3: HIGH | +| Reading 4: HIGH | +| Reading 5: LOW | +| (Completely unpredictable!) | +| | ++-----------------------------------------------------------------+ +``` + +### Pull-Up and Pull-Down Resistors + +To solve the floating input problem, we use **pull resistors**: + +| Resistor Type | Default State | When Button Pressed | +| ------------- | ------------- | ------------------- | +| **Pull-Up** | HIGH (1) | LOW (0) | +| **Pull-Down** | LOW (0) | HIGH (1) | + +The Pico 2 has **internal** pull resistors that you can enable with software - no external components needed! + +``` ++-----------------------------------------------------------------+ +| Pull-Up Resistor (what we're using) | +| | +| 3.3V | +| | | +| + (internal pull-up resistor) | +| | | +| +------? GPIO 15 (reads HIGH normally) | +| | | +| +-+-+ | +| |BTN| ?? Button connects GPIO to GND when pressed | +| +-+-+ | +| | | +| GND | +| | +| Button NOT pressed: GPIO reads 1 (HIGH) | +| Button PRESSED: GPIO reads 0 (LOW) | +| | ++-----------------------------------------------------------------+ +``` + +### GPIO Input Functions + +| Function | Purpose | +| ---------------------------- | --------------------------------------- | +| `gpio_init(pin)` | Initialize a GPIO pin for use | +| `gpio_set_dir(pin, GPIO_IN)` | Set pin as INPUT | +| `gpio_pull_up(pin)` | Enable internal pull-up resistor | +| `gpio_pull_down(pin)` | Enable internal pull-down resistor | +| `gpio_get(pin)` | Read the current state (returns 0 or 1) | + +### The Ternary Operator + +The code uses a **ternary operator** to control the LED based on button state: + +```c +gpio_put(LED_GPIO, pressed ? 0 : 1); +``` + +This is a compact if-else statement: + +- If `pressed` is **true (1)**: output `0` (LED OFF... wait, that seems backwards!) +- If `pressed` is **false (0)**: output `1` (LED ON) + +**Why is it inverted?** Because of the pull-up resistor! + +- Button **released** -> GPIO reads `1` -> `pressed = 1` -> output `0` -> LED OFF +- Button **pressed** -> GPIO reads `0` -> `pressed = 0` -> output `1` -> LED ON + +A clearer way to write this: +```c +gpio_put(LED_GPIO, !gpio_get(BUTTON_GPIO)); +``` + +--- + +## Part 3: Understanding Compiler Optimizations + +### Why Does Code Disappear? + +When you compile code, the compiler tries to make it faster and smaller. This is called **optimization**. Sometimes the compiler removes code that it thinks has no effect! + +**Example from our code:** +```c +while (true) { + uint8_t regular_fav_num = 42; // Created + regular_fav_num++; // Incremented to 43 + // But then it's destroyed and recreated as 42 next loop! +} +``` + +The compiler sees that incrementing `regular_fav_num` has no lasting effect (because it's recreated as 42 each loop), so it may **optimize away** the increment operation entirely! + +### Function Inlining + +Sometimes the compiler **inlines** functions, meaning it replaces a function call with the function's code directly. + +**Original code:** +```c +gpio_pull_up(BUTTON_GPIO); +``` + +**What the compiler might do:** +```c +// Instead of calling gpio_pull_up, it calls the underlying function: +gpio_set_pulls(BUTTON_GPIO, true, false); +``` + +This is why when you look for `gpio_pull_up` in the binary, you might find `gpio_set_pulls` instead! + +--- + +## Part 4: Setting Up Your Environment + +### Prerequisites + +Before we start, make sure you have: + +1. A Raspberry Pi Pico 2 board +2. A Raspberry Pi Pico Debug Probe +3. OpenOCD installed and configured +4. GDB (`arm-none-eabi-gdb`) installed +5. Python installed (for UF2 conversion) +6. A serial monitor (PuTTY, minicom, or screen) +7. A push button connected to GPIO 15 +8. An LED connected to GPIO 16 (or use the breadboard LED) +9. A hex editor (HxD, ImHex, or similar) +10. The sample project: `0x0014_static-variables` + +### Hardware Setup + +Connect your button like this: + +- One side of button -> GPIO 15 +- Other side of button -> GND + +The internal pull-up resistor provides the 3.3V connection, so you only need to connect to GND! + +``` ++-----------------------------------------------------------------+ +| Breadboard Wiring | +| | +| Pico 2 | +| +----------+ | +| | | | +| | GPIO 15 |--------+ | +| | | | | +| | GPIO 16 |--------+---? LED (with resistor to GND) | +| | | | | +| | GND |--------+---+ | +| | | | | | +| +----------+ +-+-+ | | +| |BTN|-+ | +| +---+ | +| | ++-----------------------------------------------------------------+ +``` + +### Project Structure + +``` +Embedded-Hacking/ ++-- 0x0014_static-variables/ +| +-- build/ +| | +-- 0x0014_static-variables.uf2 +| | +-- 0x0014_static-variables.elf +| +-- 0x0014_static-variables.c ++-- uf2conv.py +``` + +--- + +## Part 5: Hands-On Tutorial - Static Variables and GPIO Input + +### Step 1: Review the Source Code + +Let's examine the static variables code: + +**File: `0x0014_static-variables.c`** + +```c +#include +#include "pico/stdlib.h" + +int main(void) { + stdio_init_all(); + + const uint BUTTON_GPIO = 15; + const uint LED_GPIO = 16; + bool pressed = 0; + + gpio_init(BUTTON_GPIO); + gpio_set_dir(BUTTON_GPIO, GPIO_IN); + gpio_pull_up(BUTTON_GPIO); + + gpio_init(LED_GPIO); + gpio_set_dir(LED_GPIO, GPIO_OUT); + + while (true) { + uint8_t regular_fav_num = 42; + static uint8_t static_fav_num = 42; + + printf("regular_fav_num: %d\r\n", regular_fav_num); + printf("static_fav_num: %d\r\n", static_fav_num); + + regular_fav_num++; + static_fav_num++; + + pressed = gpio_get(BUTTON_GPIO); + gpio_put(LED_GPIO, pressed ? 0 : 1); + } +} +``` + +**What this code does:** + +1. **Line 6-8:** Defines constants for button (GPIO 15) and LED (GPIO 16) pins +2. **Line 10-12:** Sets up GPIO 15 as input with internal pull-up resistor +3. **Line 14-15:** Sets up GPIO 16 as output for the LED +4. **Line 18-19:** Creates two variables: + - `regular_fav_num` - a normal local variable (recreated each loop) + - `static_fav_num` - a static variable (persists across loops) +5. **Line 21-22:** Prints both values to the serial terminal +6. **Line 24-25:** Increments both values +7. **Line 27-28:** Reads button and controls LED accordingly + +### Step 2: Flash the Binary to Your Pico 2 + +1. Hold the BOOTSEL button on your Pico 2 +2. Plug in the USB cable (while holding BOOTSEL) +3. Release BOOTSEL - a drive called "RPI-RP2" appears +4. Drag and drop `0x0014_static-variables.uf2` onto the drive +5. The Pico will reboot and start running! + +### Step 3: Open Your Serial Monitor + +Open PuTTY, minicom, or screen and connect to your Pico's serial port. + +**You should see output like this:** + +``` +... +regular_fav_num: 42 +static_fav_num: 42 +regular_fav_num: 42 +static_fav_num: 43 +regular_fav_num: 42 +static_fav_num: 44 +regular_fav_num: 42 +static_fav_num: 45 +... +``` + +**Notice the difference:** + +- `regular_fav_num` stays at 42 every time (it's recreated each loop) +- `static_fav_num` increases each time (it persists and remembers its value) + +### Step 4: Test the Button + +Now test the button behavior: + +- **Button NOT pressed:** LED should be OFF +- **Button PRESSED:** LED should turn ON + +That may feel backwards at first glance, but it matches the pull-up + ternary logic in the code. We'll hack this later to make the behavior more intuitive. + +### Step 5: Watch for Overflow + +Keep the program running and watch `static_fav_num`. After 255, you'll see: + +``` +static_fav_num: 254 +static_fav_num: 255 +static_fav_num: 0 ?? Wrapped around! +static_fav_num: 1 +static_fav_num: 2 +... +``` + +This demonstrates unsigned integer overflow! + +--- + +## Part 6: Debugging with GDB (Dynamic Analysis) + +> ? **REVIEW:** This setup is identical to previous weeks. If you need a refresher on OpenOCD and GDB connection, refer back to Week 3 Part 6. + +### Starting the Debug Session + +**Terminal 1 - Start OpenOCD:** + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +**Terminal 2 - Start GDB:** + +```cmd +arm-none-eabi-gdb build\0x0014_static-variables.elf +``` + +**Connect to target:** + +```gdb +(gdb) target extended-remote :3333 +(gdb) monitor reset halt +``` + +### Step 6: Examine Main Function + +Let's examine the main function at its entry point. First, disassemble from the start: + +``` +x/38i 0x10000234 +``` + +You should see output like: + +``` +(gdb) x/38i 0x10000234 + 0x10000234
: push {r4, lr} + 0x10000236 : bl 0x10003014 + 0x1000023a : movs r0, #15 + 0x1000023c : bl 0x10000300 + 0x10000240 : movs r0, #15 + 0x10000242 : mov.w r3, #0 + 0x10000246 : mcrr 0, 4, r0, r3, cr4 + 0x1000024a : movs r2, #0 + 0x1000024c : movs r1, #1 + 0x1000024e : bl 0x100002d8 + 0x10000252 : movs r0, #16 + 0x10000254 : bl 0x10000300 + 0x10000258 : movs r3, #16 + 0x1000025a : mov.w r2, #1 + 0x1000025e : mcrr 0, 4, r3, r2, cr4 + 0x10000262 : ldr r4, [pc, #44] @ (0x10000290 ) + 0x10000264 : movs r1, #42 @ 0x2a + 0x10000266 : ldr r0, [pc, #44] @ (0x10000294 ) + 0x10000268 : bl 0x100031a4 <__wrap_printf> + 0x1000026c : ldrb r1, [r4, #0] + 0x1000026e : ldr r0, [pc, #40] @ (0x10000298 ) + 0x10000270 : bl 0x100031a4 <__wrap_printf> + 0x10000274 : mov.w r1, #3489660928 @ 0xd0000000 + 0x10000278 : ldrb r3, [r4, #0] + 0x1000027a : movs r2, #16 + 0x1000027c : adds r3, #1 + 0x1000027e : strb r3, [r4, #0] + 0x10000280 : ldr r3, [r1, #4] + 0x10000282 : ubfx r3, r3, #15, #1 + 0x10000286 : eor.w r3, r3, #1 + 0x1000028a : mcrr 0, 4, r2, r3, cr0 + 0x1000028e : b.n 0x10000264 + 0x10000290 : lsls r0, r5, #22 + 0x10000292 : movs r0, #0 + 0x10000294 : adds r5, #96 @ 0x60 + 0x10000296 : asrs r0, r0, #32 + 0x10000298 : adds r5, #120 @ 0x78 + 0x1000029a : asrs r0, r0, #32 +``` + +### Step 7: Set a Breakpoint at Main + +``` +b *0x10000234 +c +``` + +### Step 8: Examine the Static Variable Location + +Static variables live at fixed RAM addresses. But how do we find that address? Look at `0x10000262` in the disassembly from Step 6: + +``` +(gdb) x/16i 0x10000234 +=> 0x10000234
: push {r4, lr} + 0x10000236 : bl 0x10003014 + 0x1000023a : movs r0, #15 + 0x1000023c : bl 0x10000300 + 0x10000240 : movs r0, #15 + 0x10000242 : mov.w r3, #0 + 0x10000246 : mcrr 0, 4, r0, r3, cr4 + 0x1000024a : movs r2, #0 + 0x1000024c : movs r1, #1 + 0x1000024e : bl 0x100002d8 + 0x10000252 : movs r0, #16 + 0x10000254 : bl 0x10000300 + 0x10000258 : movs r3, #16 + 0x1000025a : mov.w r2, #1 + 0x1000025e : mcrr 0, 4, r3, r2, cr4 + 0x10000262 : ldr r4, [pc, #44] @ (0x10000290 ) +``` + +This loads `r4` from the **literal pool** at address `0x10000290`. The literal pool stores constants that are too large for immediate encoding - in this case, a 32-bit RAM address. Let's examine what's stored there: + +```gdb +(gdb) x/1wx 0x10000290 +0x10000290 : 0x200005a8 +``` + +That's `0x200005a8` - the RAM address of `static_fav_num`! The compiler placed this address in the literal pool because it can't encode a full 32-bit address in a single Thumb instruction. + +> **Common confusion three things that trip people up here:** +> +> **1. GDB shows `` instead of ``.** +> `static_fav_num` is a function-local static, so it does not appear in GDB's global symbol table. GDB labels the address with the nearest *named* global symbol it can find in this case an SDK-internal spin-lock variable that happens to be nearby. The address `0x200005a8` is still correct; the label is just GDB's best guess at a name. +```gdb +(gdb) x/x 0x200005a8 +0x200005a8 : 0x0000122a +``` +> +> **2. `x/x 0x200005a8` shows `0x0000122a`, not `0x0000002a` (42).** +> `x/x` reads a **4-byte word** (32 bits). `static_fav_num` is a `uint8_t` only **1 byte**. The low byte of `0x0000122a` is `0x2a` = 42. That *is* the variable. The upper bytes belong to whatever happens to be in adjacent RAM. To read exactly one byte and see `42`, use: +```gdb +(gdb) x/1ub 0x200005a8 +0x200005a8 : 42 +``` +> +> **3. `x/x *0x200005a8` shows junk at address `0x122a`.** +> The `*` is a pointer dereference. GDB first reads the 4-byte word at `0x200005a8` (= `0x0000122a`), then treats that *value* as a new address and reads from `0x0000122a`. You are asking GDB to follow a pointer that was never meant to be a pointer the result is meaningless. Drop the `*`: use `x/1ub 0x200005a8` to read the variable's value directly. + +> Tip: **Why did the disassembly at `0x10000290` show `lsls r0, r5, #22` instead?** Because `x/i` (disassemble) interprets raw data as instructions. The bytes `A8 05 00 20` at that address are the little-endian encoding of `0x200005A8`, but GDB's disassembler doesn't know it's data - it tries to decode it as a Thumb instruction. Using `x/wx` (examine as word) shows the actual value. + +### Step 9: Step Through the Loop + +The loop body starts at `0x10000264`. Set a breakpoint there and continue: + +```gdb +(gdb) b *0x10000264 +(gdb) c +(gdb) x/i $pc +=> 0x10000264 : movs r1, #42 @ 0x2a +``` + +GDB will stop at `movs r1, #42` that is the compiler's constant for `regular_fav_num`. Let's set a breakpoint and continue at `0x10000278`. + +```gdb +(gdb) b *0x10000278 +(gdb) c +(gdb) x/i $pc +=> 0x10000278 : ldrb r3, [r4, #0] +``` + +This is where the static variable is actually manipulated: + +```gdb +(gdb) x/4i $pc +=> 0x10000278 : ldrb r3, [r4, #0] <- load static_fav_num from RAM into r3 + 0x1000027a : movs r2, #16 <- load LED pin number (16) into r2 for later + 0x1000027c : adds r3, #1 <- increment r3 by 1 + 0x1000027e : strb r3, [r4, #0] <- store incremented value back to RAM +``` + +Confirm what `r4` actually contains right now: + +```gdb +(gdb) x/x $r4 +0x200005a8 : 0x2a +``` + +`r4` is `0x200005a8`, a RAM address. The value at that address is `0x2a` (42 in decimal), which is the current value of `static_fav_num`. After `adds r3, #1` and `strb r3, [r4, #0]` execute, examining `r4` again will show `0x0000002b` (43). + +**What is the literal pool?** + +A Thumb `ldr` instruction is only 16 or 32 bits wide. There is no room inside those bits to encode a full 32-bit constant like `0x200005a8`. To solve this, the compiler collects all such large constants and writes them into a small block of **raw data** embedded directly in the flash image this block is called a **literal pool** (also called a constant pool). + +**There is not one literal pool per flash, there is (at least) one per function.** Every function that references large constants gets its own pool appended to its code. A large function can have several pools scattered through it wherever the compiler decides to emit them. Each pool is purely data; the CPU must never try to execute it. + +**Where exactly is a literal pool placed?** Always **after an unconditional branch**, never before or in the middle of a straight-line code path. This is by design: the CPU falls through code sequentially, so the pool must be unreachable by fall-through. In our case the pool sits at `0x10000290`, directly after the `b.n 0x10000264` branch at `0x1000028e` that closes the `while (true)` loop: + +``` +0x1000028e: b.n 0x10000264 <- unconditional branch back to loop top +0x10000290: [literal pool] <- CPU never reaches here by fall-through +``` + +**Why does the pool have to be nearby?** The `ldr rN, [pc, #offset]` instruction encodes the offset in a limited number of bits. In standard Thumb the maximum reach is **1020 bytes** from the instruction. If the function is long enough that the end is out of range, the assembler emits an intermediate pool mid-function after the next unconditional branch it can find. This is in the ARM v8-M Architecture Reference Manual p. 672. + +**How the load works in our function:** At `0x10000262` the compiler emits: + +``` +ldr r4, [pc, #44] @ (0x10000290) +``` + +On ARM, `PC` reads as the address of the instruction **plus 4** during execution. So the effective address is `0x10000262 + 4 + 44 = 0x10000290`. That word in the pool contains `0x200005a8`, the RAM address of `static_fav_num`. The pool is also where the `printf` format-string pointers live (the other `ldr` instructions you saw in the disassembly). + +``` +Flash (read-only) ++-------------------------------------------+ RAM +| 0x10000262: ldr r4, [pc, #44] | +------------------+ +| ...loop body... | | 0x200005a8: 42 | <- static_fav_num +| 0x1000028e: b.n 0x10000264 (end of loop) | +------------------+ ++------- literal pool (data, not code) -----+ ^ +| 0x10000290: 0x200005a8 --------------------+----------------+ +| 0x10000294: ptr to "regular_fav_num: %d" | r4 holds this address +| 0x10000298: ptr to "static_fav_num: %d" | ++------- next function ---------------------+ +| 0x1000029c: push {r4} | ++-------------------------------------------+ +``` + +You can see this directly. Run `x/10i 0x1000028e` in GDB and you will get exactly this: + +```gdb +(gdb) x/10i 0x1000028e + (gdb) x/10i 0x1000028e + 0x1000028e : b.n 0x10000264 <- unconditional branch, loop top + 0x10000290 : lsls r0, r5, #22 <-+ + 0x10000292 : movs r0, #0 <-+-- word 1: 0x200005a8 (fixed RAM addr static_fav_num) + 0x10000294 : adds r5, #96 @ 0x60 <-+ + 0x10000296 : asrs r0, r0, #32 <-+-- word 2: 0x10004b60 (flash addr regular_fav_num text) + 0x10000298 : adds r5, #120 <-+ + 0x1000029a : asrs r0, r0, #32 <-+-- word 3: 0x10004b78 (flash addr static_fav_num text) +``` + +`0x10000290` - `0x1000029b` is the literal pool 12 bytes, 3 32-bit words. GDB's `x/i` (disassemble) has no idea those bytes are data, so it blindly decodes them as Thumb instructions and produces nonsense (`lsls r0, r5, #22`, etc.). The giveaway that you have entered pool territory is always the same: **garbled, implausible instructions immediately after an unconditional branch**. Use `x/wx` to read those addresses as data and you get the real values: + +```gdb +(gdb) x/3wx 0x10000290 +0x10000290 : 0x200005a8 0x10003560 0x10003578 +``` + +```gdb +(gdb) x/1ub 0x200005a8 +0x200005a8 : 42 +``` +- `0x200005a8` RAM address of `static_fav_num`, lives in the **`.data` section** (initialized static RAM, `0x20000000``0x200xxxxx`). It is `.data` and not `.bss` because the variable has an explicit initializer (`= 42`). `.bss` is for variables that are zero-initialized or have no initializer at all. + +```gdb +(gdb) x/s 0x10003560 +0x10003560: "regular_fav_num: %d\r\n" +``` +- `0x10003560` flash address of the `"regular_fav_num: %d\r\n"` string literal, lives in the **`.rodata` section** (read-only data) inside flash (`0x10000000`+). String literals are constants baked into the binary at compile time they never change and never move, so they stay in flash. + +```gdb +(gdb) x/s 0x10003578 +0x10003578: "static_fav_num: %d\r\n" +``` + +- `0x10003578` flash address of the `"static_fav_num: %d\r\n"` string literal, also in **`.rodata`** in flash for the same reason. + +So the pool contains addresses into three different regions: RAM `.data` (the static variable), and flash `.rodata` (both format strings). Only the RAM address needed to be in the pool, the flash addresses could in principle be reached other ways, but they are also too large to encode as 16-bit immediates, so they go in the pool too. + +**Why `.data` and not `.bss`?** The distinction matters: + +| Section | What goes here | Example declarations | Initial value stored in flash? | +|---------|---------------|----------------------|-------------------------------| +| `.data` | Any static-duration variable with a **non-zero initializer** | `static uint8_t x = 42;` (inside a function) | Yes the initial value lives in flash; startup copies it to RAM | +| `.data` | Same rule applies to **global** variables with non-zero initializers | `uint8_t g = 10;` (outside all functions) | Yes same startup copy | +| `.bss` | Any static-duration variable with **no initializer or zero initializer** | `static uint8_t x;` or `static uint8_t x = 0;` | No startup just zeroes the RAM range; no flash copy needed | +| `.bss` | Same rule for **global** variables that are zero/uninitialized | `uint8_t g;` or `uint8_t g = 0;` (outside functions) | No same zero-fill at startup | + +The rule is not about `static` specifically it is about **lifetime and initial value**. Any variable whose lifetime spans the whole program (static locals, globals) goes into `.data` or `.bss`. The `static` keyword inside a function just forces that lifetime. A global variable without `static` has the same lifetime and follows the same rule. + +`static_fav_num = 42` has an explicit non-zero initializer, so the linker puts it in `.data`. The startup code copies the initial value `42` from flash into RAM address `0x200005a8` before `main()` runs. If it had been `static uint8_t static_fav_num;` (no initializer), the linker would put it in `.bss` instead and the startup code would zero that RAM region no flash copy needed. + +**Why is there no pool entry for the address of `regular_fav_num`?** + +Because `regular_fav_num` is an **automatic (stack) variable** it has no fixed address. Automatic variables are allocated on the call stack at runtime, relative to the current stack pointer (`sp`). Their address changes every time the function runs and is different on every call. There is no constant 32-bit address to put in a pool. + +In this specific case the compiler went even further: it **never gave `regular_fav_num` a stack slot at all**. The compiler saw that the value is always `42` when printed, so it baked the constant `42` directly into the `movs r1, #42` instruction. The variable lives only in a register at the moment it is needed — no stack space reserved, no pool entry, no load-store cycle. + +**You can prove this with three GDB checks:** + +**Proof 1 `info locals` shows the value came from a register, not a stack slot:** + +```gdb +(gdb) b *0x10000264 +(gdb) c +(gdb) info locals +regular_fav_num = 42 '*' +static_fav_num = 42 '*' +BUTTON_GPIO = 15 +LED_GPIO = 16 +pressed = +``` + +The `'*'` suffix after `42` means GDB retrieved the value from a **register** (specifically `r1`, which holds `42` at this point), not from a stack slot. The variable has no fixed memory address — GDB can report its current value only because the right register happens to hold it right now. Notice that `pressed` is the one showing `` — the compiler removed it from tracking entirely because its value is never needed after the ternary expression. + +**Proof 2 The function prologue allocates no stack space for locals:** + +```gdb +(gdb) x/2i 0x10000234 + 0x10000234
: push {r4, lr} + 0x10000236 : bl 0x10003014 +``` + +The very first instruction is `push {r4, lr}`, that saves `r4` (a callee-saved register) and `lr` (return address). There is **no** `sub sp, #N` instruction after it. On ARM, allocating stack space for local variables requires subtracting from `sp` to reserve room. If `regular_fav_num` lived on the stack, there would be a `sub sp, #4` (or similar) here. There isn't, so no stack slot was ever created. + +**Proof 3 The loop body contains no store to the stack for `regular_fav_num`:** + +```gdb +(gdb) x/6i 0x10000264 +=> 0x10000264 : movs r1, #42 @ 0x2a + // constant 42 baked directly into instruction + 0x10000266 : ldr r0, [pc, #44] @ (0x10000294 ) + // load format string address from pool + 0x10000268 : bl 0x100031a4 <__wrap_printf> + // call printf + 0x1000026c : ldrb r1, [r4, #0] + // load static_fav_num from RAM + 0x1000026e : ldr r0, [pc, #40] @ (0x10000298 ) + // load format string address from pool + 0x10000270 : bl 0x100031a4 <__wrap_printf> + call printf +``` + +For `regular_fav_num`, the value `42` goes straight into `r1` via `movs`, it is never written to memory and never read back from memory. There is no `strb r1, [sp, #N]` storing it to the stack, and no `ldrb r1, [sp, #N]` loading it back. The variable exists only in the source code. In the binary it is just a number in an instruction encoding. + +`static_fav_num` is the exact opposite. It lives in the `.data` section of RAM, a fixed, known address (`0x200005a8`) that is set at link time and never changes for the life of the program. That fixed address is exactly the kind of value that cannot fit in a 16-bit Thumb instruction, so the linker writes it into the literal pool and the CPU fetches it with `ldr r4, [pc, #44]`. + +> **IMPORTANT:** Static variables are **not** on the heap. The heap is for dynamic memory (`malloc`/`free`), memory whose lifetime is controlled explicitly at runtime. Static and global variables live in the `.data` section (if initialized) or `.bss` section (if zero/uninitialized), a region of RAM with a fixed layout determined at link time, not at runtime. + +``` +Memory regions for our two variables: ++------------------------------------------+ +| FLASH (read-only, 0x10000000+) | +| - Code (instructions) | +| - Literal pool (our 3 words) | +| - String literals ("regular_fav_num...")| ++------------------------------------------+ +| RAM .data section (0x20000000+) | +| - static_fav_num @ 0x200005a8 <- pool | ++------------------------------------------+ +| STACK (grows down from 0x20082000) | +| - regular_fav_num (if not optimized) | +| frame-relative, no fixed address | ++------------------------------------------+ +| HEAP (grows up, between stack and .bss) | +| - malloc/free only, nothing here today | ++------------------------------------------+ +``` + +The pool ends at `0x1000029b`. At `0x1000029d` a new function begins, specifically `gpio_set_function`. The first two instructions, `push {r4}` (save caller's `r4` to the stack) and `mov.w r4, #256` (load a working constant), are a standard SDK function prologue, nothing to do with `main`. The `ldr r3, [pc, #48]`i r/. at `0x100002a3` (`gpio_set_function+6`) is that next function loading from **its own** literal pool further ahead in flash — proof that every function manages its own pool. + +### Step 10: Examine Register Values + +After breaking at `0x10000264`, check the registers: + +```gdb +(gdb) i r +``` + +What you will actually see: + +- `r4` = `0x200005a8` — the static variable's fixed RAM address. This was loaded from the literal pool in the function prologue and never changes across loop iterations. +- `r1` = leftover value from the previous `printf` call (e.g. `0x40039044`). The breakpoint is stopped **at** `movs r1, #42` — that instruction has not executed yet, so `r1` does not yet hold `42`. +- `r3` = leftover from prior operations (e.g. `0x10`). It will be used later in the loop body to load, increment, and store `static_fav_num`, but not yet. +- `pc` = `0x10000264` — the loop top, pointing at the `movs r1, #42` instruction. + +You can confirm `r4` holds the right address and value: + +```gdb +(gdb) x/1db $r4 +0x200005a8 : 42 +``` + +The `` label is just the nearest named global symbol in the SDK that GDB finds at that address — the actual variable stored there is our `static_fav_num`. + +### Step 11: Watch the Static Variable Change + +Now that we know the static variable lives at `0x200005a8`, examine it directly: + +```gdb +(gdb) x/1db 0x200005a8 +0x200005a8 : 42 +``` + +Step through a full loop iteration (back to `0x10000264`) and re-examine: + +```gdb +(gdb) c +(gdb) x/1db 0x200005a8 +0x200005a8 : 43 +``` + +The value incremented from 42 to 43! Each loop iteration, the `adds r3, #1` at `0x1000027c` bumps it by 1, and `strb r3, [r4, #0]` at `0x1000027e` writes it back to RAM. + +### Step 12: Examine GPIO State + +Read the GPIO input register to see the button state: + +```gdb +(gdb) x/1wx 0xd0000004 +0xd0000004: 0x00008003 +``` + +The SIO GPIO input register at `0xd0000004` shows the current state of all GPIO pins. Bit 15 corresponds to our button on GPIO 15. To extract just bit 15: + +```gdb +(gdb) p/x (*(unsigned int *)0xd0000004 >> 15) & 1 +$1 = 0x1 +``` + +- Returns `1` when button is **not pressed** (pull-up holds it HIGH) +- Returns `0` when button is **pressed** (connected to GND) + +TRY IT! + +--- + +## Part 7: Understanding the Assembly + +Now that we've explored the binary in GDB, let's make sense of the key patterns. + +### Step 13: Analyze the Regular Variable + +In GDB, examine the code at the start of the loop: + +```gdb +(gdb) x/5i 0x10000262 + 0x10000262 : ldr r4, [pc, #44] @ (0x10000290 ) +=> 0x10000264 : movs r1, #42 @ 0x2a + 0x10000266 : ldr r0, [pc, #44] @ (0x10000294 ) + 0x10000268 : bl 0x100031a4 <__wrap_printf> + 0x1000026c : ldrb r1, [r4, #0] +``` + +Look for this instruction: + +``` +0x10000264 : movs r1, #42 @ 0x2a +``` + +This loads the value `0x2a` (42 in decimal) directly into register `r1` for the first `printf` call. + +**Key insight:** The compiler **never allocated stack space** for `regular_fav_num`. Since it is always `42` when printed, the compiler bakes the constant directly into the `movs r1, #42` instruction. The `regular_fav_num++` after the print is also removed because it has no observable effect — the variable is recreated as `42` on the next loop iteration anyway. + +### Step 14: Analyze the Static Variable + +Examine the static variable operations in the second half of the loop body: + +```gdb +(gdb) x/10i 0x10000274 + 0x10000274 : mov.w r1, #3489660928 @ 0xd0000000 + 0x10000278 : ldrb r3, [r4, #0] + 0x1000027a : movs r2, #16 + 0x1000027c : adds r3, #1 + 0x1000027e : strb r3, [r4, #0] + 0x10000280 : ldr r3, [r1, #4] + 0x10000282 : ubfx r3, r3, #15, #1 + 0x10000286 : eor.w r3, r3, #1 + 0x1000028a : mcrr 0, 4, r2, r3, cr0 + 0x1000028e : b.n 0x10000264 +``` + +Look for the load-increment-store pattern using `r4` (which holds the static variable's RAM address): + +``` + ... + 0x10000278 : ldrb r3, [r4, #0] + 0x1000027a : movs r2, #16 + 0x1000027c : adds r3, #1 + 0x1000027e : strb r3, [r4, #0] + ... +``` + +Note that `r4` was loaded earlier at `0x10000262` via `ldr r4, [pc, #44]` - this pulled the static variable's RAM address (`0x200005a8`) from the literal pool at `0x10000290`. + +**Key insight:** The static variable lives at a **fixed RAM address** (`0x200005a8`). It's loaded, incremented, and stored back. The regular variable, by contrast, never gets a stack slot — the compiler holds it in a register and bakes the constant `42` directly into the instruction, so there is nothing to load or store. + +Verify the static variable value which should be `43`: + +```gdb +(gdb) x/1db 0x200005a8 +0x200005a8 : 43 +``` + +### Step 15: Analyze the GPIO Logic + +Examine the GPIO input/output code: + +```gdb +(gdb) x/10i 0x10000274 + 0x10000274 : mov.w r1, #3489660928 @ 0xd0000000 + 0x10000278 : ldrb r3, [r4, #0] + 0x1000027a : movs r2, #16 + 0x1000027c : adds r3, #1 + 0x1000027e : strb r3, [r4, #0] + 0x10000280 : ldr r3, [r1, #4] + 0x10000282 : ubfx r3, r3, #15, #1 + 0x10000286 : eor.w r3, r3, #1 + 0x1000028a : mcrr 0, 4, r2, r3, cr0 + 0x1000028e : b.n 0x10000264 +``` + +**Breaking this down:** + +| Address | Instruction | Purpose | +| -------------- | -------------------------- | ---------------------------------------------------- | +| `0x10000274` | `mov.w r1, #0xd0000000` | Load SIO (Single-cycle I/O) base address into `r1` | +| `0x10000278` | `ldrb r3, [r4, #0]` | Load `static_fav_num` from RAM into `r3` | +| `0x1000027a` | `movs r2, #16` | Load LED pin number (16) into `r2` for later | +| `0x1000027c` | `adds r3, #1` | Increment `static_fav_num` by 1 | +| `0x1000027e` | `strb r3, [r4, #0]` | Store incremented value back to RAM | +| `0x10000280` | `ldr r3, [r1, #4]` | Read GPIO input state (SIO_GPIO_IN at offset `0x04`) | +| `0x10000282` | `ubfx r3, r3, #15, #1` | Extract bit 15 (GPIO 15 = button) | +| `0x10000286` | `eor.w r3, r3, #1` | XOR with 1 to invert (implements `? 0 : 1`) | +| `0x1000028a` | `mcrr 0, 4, r2, r3, cr0` | Write `r3` (button) and `r2` (pin 16) to GPIO output | +| `0x1000028e` | `b.n 0x10000264` | Loop back to start (`while (true)`) | + +> Tip: **Notice how the compiler interleaves the static variable increment with the GPIO logic.** It loads the SIO base address (`r1`) *before* doing the increment, and sets up `r2 = 16` (LED pin) in between. This is called **instruction scheduling** - the compiler reorders instructions to avoid pipeline stalls while waiting for memory reads. + +### Step 16: Find the Infinite Loop + +The last instruction at `0x1000028e` is already covered in the table above: + +``` + 0x1000028e : b.n 0x10000264 +``` + +This is an **unconditional branch** back to `0x10000264` (the `movs r1, #42` at the top of the loop) - this is the `while (true)` in our code! There is no `pop` or `bx lr` to return from `main` because the loop never exits. + +--- + +## Part 8: Hacking the Binary with a Hex Editor + +Now for the fun part - we'll patch the `.bin` file directly using a hex editor! + +> Tip: **Why a hex editor?** GDB **cannot write to flash memory** - the `0x10000000+` address range where program instructions live. Trying `set *(char *)0x10000264 = 0x2b` in GDB gives `Writing to flash memory forbidden in this context`. To make **permanent** patches that survive a power cycle, we edit the `.bin` file directly with a hex editor and re-flash it. + +### Step 17: Open the Binary in a Hex Editor + +1. Open **HxD** (or your preferred hex editor: ImHex, 010 Editor, etc.) +2. Click **File** -> **Open** +3. Navigate to `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0014_static-variables\build\` +4. Open `0x0014_static-variables.bin` + +### Step 18: Calculate the File Offset + +The binary is loaded at base address `0x10000000`. To find the file offset of any address: + +``` +file_offset = address - 0x10000000 +``` + +For example: + +- Address `0x10000264` -> file offset `0x264` (612 in decimal) +- Address `0x10000286` -> file offset `0x286` (646 in decimal) + +### Step 19: Hack #1 - Change regular_fav_num from 42 to 43 + +From our GDB analysis, we know the instruction at `0x10000264` is: + +``` +movs r1, #0x2a -> bytes: 2a 21 +``` + +To change the value from 42 (`0x2a`) to 43 (`0x2b`): + +1. In HxD, open `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0014_static-variables\build\0x0014_static-variables.bin` +2. Press **Ctrl+G** (Go to offset) +3. Enter offset: `264` +4. You should see the byte `2A` at this position +5. Change `2A` to `2B` +6. The instruction is now `movs r1, #0x2b` (43 in decimal) + +> ?? **How Thumb encoding works:** In `movs r1, #imm8`, the immediate value is the first byte, and the opcode `21` is the second byte. So the bytes `2a 21` encode `movs r1, #0x2a`. + +### Step 20: Hack #2 - Invert the Button Logic + +#### Understand the Encoding + +From GDB, we found the `eor.w r3, r3, #1` instruction at `0x10000286` that inverts the button value. Examine the exact bytes: + +```gdb +(gdb) x/4bx 0x10000286 +0x10000286 : 0x83 0xf0 0x01 0x03 +``` + +This is the 32-bit Thumb-2 encoding of `eor.w r3, r3, #1`. The bytes break down as: + +``` ++-----------------------------------------------------------------+ +| eor.w r3, r3, #1 -> bytes: 83 F0 01 03 | +| | +| Byte 0: 0x83 -?? | +| Byte 1: 0xF0 -+ First halfword (opcode + source register) | +| Byte 2: 0x01 ---- Immediate value (#1) ?? CHANGE THIS | +| Byte 3: 0x03 ---- Destination register (r3) | +| | ++-----------------------------------------------------------------+ +``` + +To change `eor.w r3, r3, #1` to `eor.w r3, r3, #0` (making XOR do nothing): + +The file offset is `0x10000286 - 0x10000000 = 0x286`. The immediate byte is the 3rd byte of the instruction, so: `0x286 + 2 = 0x288`. + +To change `eor.w r3, r3, #1` to `eor.w r3, r3, #0`: + +1. In HxD, press **Ctrl+G** (Go to offset) +2. Enter offset: `288` (the third byte of the 4-byte instruction) +3. You should see the byte `01` at this position +4. Change `01` to `00` + +> ?? **Why offset `0x288` and not `0x286`?** The immediate value `#1` is in the **third byte** of the 4-byte instruction. The instruction starts at file offset `0x286`, so the immediate byte is at `0x286 + 2 = 0x288`. + +Now the logic is permanently changed: + +- Button released (input = 1): `1 XOR 0 = 1` -> LED **ON** +- Button pressed (input = 0): `0 XOR 0 = 0` -> LED **OFF** + +This is the **opposite** of the original behavior! + +### Step 21: Save the Patched Binary + +1. Click **File** -> **Save As** +2. Save as `0x0014_static-variables-h.bin` in the build directory +3. Close the hex editor + +--- + +## Part 9: Converting and Flashing the Hacked Binary + +### Step 22: Convert to UF2 Format + +Open a terminal and navigate to your project directory: + +```cmd +cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0014_static-variables +``` + +Run the conversion command: + +```cmd +python ..\uf2conv.py build\0x0014_static-variables-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +**What this command means:** + +- `uf2conv.py` = the conversion script (in the parent `Embedded-Hacking` directory) +- `--base 0x10000000` = the XIP base address where code runs from +- `--family 0xe48bff59` = the RP2350 family ID +- `--output build\hacked.uf2` = the output filename + +### Step 23: Flash the Hacked Binary + +1. Hold BOOTSEL and plug in your Pico 2 +2. Drag and drop `hacked.uf2` onto the RPI-RP2 drive +3. Open your serial monitor + +### Step 24: Verify the Hacks + +**Check the serial output:** +``` +regular_fav_num: 43 ?? Changed from 42! +static_fav_num: 42 +regular_fav_num: 43 +static_fav_num: 43 +... +``` + +**Check the LED behavior:** + +- LED should now be **ON by default** (when button is NOT pressed) +- LED should turn **OFF** when you press the button + + **BOOM! We successfully:** + +1. Changed the printed value from 42 to 43 +2. Inverted the LED/button logic + +--- + +## Part 10: Summary and Review + +### What We Accomplished + +1. **Learned about static variables** - How they persist across function calls and loop iterations +2. **Understood memory layout** - Stack vs static storage vs heap +3. **Configured GPIO inputs** - Using pull-up resistors and reading button states +4. **Analyzed compiled code in GDB** - Saw how the compiler optimizes code +5. **Discovered function inlining** - `gpio_pull_up` became `gpio_set_pulls` +6. **Hacked variable values** - Changed 42 to 43 using a hex editor +7. **Inverted GPIO logic** - Made LED behavior opposite + +### Static vs Automatic Variables + +| Aspect | Automatic (Regular) | Static | +| ------------------ | ------------------------ | --------------------------- | +| **Storage** | Stack | Static storage (.data/.bss) | +| **Lifetime** | Block/function scope | Entire program | +| **Initialization** | Every time block entered | Once at program start | +| **Persistence** | Lost when scope exits | Retained between calls | +| **Compiler view** | May be optimized away | Always has memory location | + +### GPIO Input Configuration + +``` ++-----------------------------------------------------------------+ +| GPIO Input Setup Steps | +| | +| 1. gpio_init(pin) - Initialize the pin | +| 2. gpio_set_dir(pin, GPIO_IN) - Set as input | +| 3. gpio_pull_up(pin) - Enable pull-up | +| OR gpio_pull_down(pin) - OR enable pull-down | +| 4. gpio_get(pin) - Read the state | +| | ++-----------------------------------------------------------------+ +``` + +### The Binary Hacking Workflow + +``` ++-----------------------------------------------------------------+ +| 1. Analyze the binary with GDB | +| - Disassemble functions with x/Ni | +| - Identify key instructions and addresses | ++-----------------------------------------------------------------+ +| 2. Understand compiler optimizations | +| - Some functions inlined (gpio_pull_up -> gpio_set_pulls) | +| - Some variables are optimized away | ++-----------------------------------------------------------------+ +| 3. Calculate file offsets | +| - file_offset = address - 0x10000000 | ++-----------------------------------------------------------------+ +| 4. Patch the .bin file with a hex editor | +| - Open the .bin file in HxD / ImHex | +| - Go to the calculated offset | +| - Change the target byte(s) | ++-----------------------------------------------------------------+ +| 5. Convert to UF2 | +| python uf2conv.py file.bin --base 0x10000000 | +| --family 0xe48bff59 --output hacked.uf2 | ++-----------------------------------------------------------------+ +| 6. Flash and verify | +| - Hold BOOTSEL, plug in, drag UF2 | +| - Check serial output and button/LED behavior | ++-----------------------------------------------------------------+ +``` + +### Key Memory Addresses + +| Address | Description | +| ------------ | ----------------------------------- | +| `0x10000234` | Typical main() entry point | +| `0x10003014` | stdio_init_all() function | +| `0x200005a8` | Static variable storage (example) | +| `0xd0000000` | SIO (Single-cycle I/O) base address | + +--- + +--- + +## Key Takeaways + +1. **Static variables persist** - They keep their value between function calls and loop iterations. + +2. **Static storage ? heap** - Static variables are in a fixed location, not dynamically allocated. + +3. **Compilers optimize aggressively** - Regular variables may be optimized away if the compiler sees no effect. + +4. **Function inlining is common** - `gpio_pull_up` becomes `gpio_set_pulls` in the binary. + +5. **Pull-up resistors invert logic** - Button pressed = LOW, button released = HIGH. + +6. **XOR is useful for inverting** - `eor r3,r3,#0x1` flips a bit between 0 and 1. + +7. **Static variables have fixed addresses** - You can find them in the .data section at known RAM addresses. + +8. **Overflow wraps around** - A `uint8_t` at 255 becomes 0 when incremented. + +9. **UBFX extracts bits** - Used to read a single GPIO pin from a register. + +10. **Binary patching is powerful** - Change values and logic without source code! + +--- + +## Glossary + +| Term | Definition | +| --------------------- | ---------------------------------------------------------------- | +| **Automatic** | Variable that's created and destroyed automatically (local vars) | +| **eor/XOR** | Exclusive OR - flips bits where operands differ | +| **Floating Input** | GPIO input with undefined voltage (reads random values) | +| **Function Inlining** | Compiler replaces function call with the function's code | +| **gpio_get** | Function to read the current state of a GPIO pin | +| **Heap** | Memory area for dynamic allocation (malloc/free) | +| **Overflow** | When a value exceeds its type's maximum and wraps around | +| **Pull-Down** | Resistor that holds a pin LOW when nothing drives it | +| **Pull-Up** | Resistor that holds a pin HIGH when nothing drives it | +| **SIO** | Single-cycle I/O - fast GPIO access on RP2350 | +| **Stack** | Memory area for local variables and function call frames | +| **Static Storage** | Fixed memory area for static and global variables | +| **Static Variable** | Variable declared with `static` that persists across calls | +| **Ternary Operator** | `condition ? value_if_true : value_if_false` | +| **UBFX** | Unsigned Bit Field Extract - extracts bits from a register | +| **Varargs** | Variable arguments - functions that take unlimited parameters | + +--- + +## Additional Resources + +### GPIO Input Reference + +| Function | Purpose | +| ----------------------------- | -------------------------- | +| `gpio_init(pin)` | Initialize GPIO pin | +| `gpio_set_dir(pin, GPIO_IN)` | Set pin as input | +| `gpio_set_dir(pin, GPIO_OUT)` | Set pin as output | +| `gpio_pull_up(pin)` | Enable internal pull-up | +| `gpio_pull_down(pin)` | Enable internal pull-down | +| `gpio_disable_pulls(pin)` | Disable all pull resistors | +| `gpio_get(pin)` | Read pin state (0 or 1) | +| `gpio_put(pin, value)` | Set pin output (0 or 1) | + +### Key Assembly Instructions + +| Instruction | Description | +| ----------------------- | -------------------------------------------- | +| `movs rN, #imm` | Move immediate value to register | +| `ldrb rN, [rM, #off]` | Load byte from memory | +| `strb rN, [rM, #off]` | Store byte to memory | +| `adds rN, #imm` | Add immediate value to register | +| `eor rN, rM, #imm` | Exclusive OR (XOR) with immediate | +| `ubfx rN, rM, #lsb, #w` | Extract unsigned bit field | +| `mcrr p0, ...` | Move to coprocessor (GPIO control on RP2350) | +| `b LABEL` | Unconditional branch (jump) | + +### Memory Map Quick Reference + +| Address Range | Description | +| --------------------- | ------------------------------ | +| `0x10000000` | XIP Flash (code execution) | +| `0x20000000-200005xx` | SRAM (.data section) | +| `0x20082000` | Stack top (initial SP) | +| `0x40038000` | PADS_BANK0 (pad configuration) | +| `0xd0000000` | SIO (single-cycle I/O) | + +--- + +**Remember:** Static variables are your friends when you need to remember values across function calls. But they also make your program's behavior more complex to analyze - which is exactly why we practice reverse engineering! + +Happy hacking! ? + + + diff --git a/WEEK06/WEEK06.pdf b/WEEK06/WEEK06.pdf new file mode 100644 index 0000000..e30339f Binary files /dev/null and b/WEEK06/WEEK06.pdf differ diff --git a/WEEK06/slides/WEEK06-IMG00.svg b/WEEK06/slides/WEEK06-IMG00.svg new file mode 100644 index 0000000..0feedec --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 06 + + +Static Variables in Embedded Systems: +Debugging and Hacking Static Variables +w/ GPIO Input Basics + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK06/slides/WEEK06-IMG01.svg b/WEEK06/slides/WEEK06-IMG01.svg new file mode 100644 index 0000000..45e7fee --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG01.svg @@ -0,0 +1,74 @@ + + + + + +Static vs Regular Vars +Persistence across loop iterations + + + +Regular (auto) + + +Loop 1: 42 -> 43 -> destroy + + +Loop 2: 42 -> 43 -> destroy + + +Loop 3: 42 -> 43 -> destroy + +Always prints: +42 + + + +Static + + +Loop 1: 42 -> 43 (kept!) + + +Loop 2: 43 -> 44 (kept!) + + +Loop 3: 44 -> 45 (kept!) + +Keeps incrementing: +42,43,44... + + + +Declaration Syntax + + +uint8_t reg = 42; +Recreated each iteration + + +static uint8_t s = 42; +Persists for program life + + + +uint8_t Overflow + +255 + 1 = +0 +(wraps around!) + +Binary: 11111111 + 1 = 100000000 (9 bits) +Only 8 bits kept: 00000000 = 0 + \ No newline at end of file diff --git a/WEEK06/slides/WEEK06-IMG02.svg b/WEEK06/slides/WEEK06-IMG02.svg new file mode 100644 index 0000000..dca4829 --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG02.svg @@ -0,0 +1,93 @@ + + + + + +Memory Layout +Where variables live in RAM + + + +RP2350 SRAM Map + + + +STACK (grows down) +Local/auto variables + +0x20082000 + + + +(free space) + + + +HEAP (grows up) +malloc / free + + + +.bss section +Uninit static/global + + + +.data section +Initialized static/global + +0x20000000 + +Static vars: .data (init) +Static vars: .bss (uninit) + + + +Variable Storage + +Type +Location + + + +Automatic +Stack + +Static +.data / .bss + +Global +.data / .bss + +Dynamic +Heap + +Static vars are NOT on heap! +Fixed location, set at compile +time. Lives entire program. +Example: +static_fav_num @ 0x200005A8 + + + +Key Insight + +Stack vars: +Created + destroyed +each function call + +Static vars: +Fixed RAM address +persist entire runtime + \ No newline at end of file diff --git a/WEEK06/slides/WEEK06-IMG03.svg b/WEEK06/slides/WEEK06-IMG03.svg new file mode 100644 index 0000000..279854a --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG03.svg @@ -0,0 +1,93 @@ + + + + + +GPIO Input Basics +Reading buttons with the RP2350 + + + +OUTPUT (before) + + +Pico GPIO 16 +--> +LED + +We CONTROL the LED +gpio_put(pin, value) + + +INPUT (new!) + + +Pico GPIO 15 +<-- +BTN + +We READ button state +gpio_get(pin) + + + +Floating Input + +No connection = +RANDOM values! + +Read 1: HIGH +Read 2: LOW +Read 3: HIGH +Read 4: HIGH +??? + + + +Pull Resistors + +Type +Default +Pressed + + + +Pull-Up +HIGH(1) +LOW(0) + +Pull-Down +LOW(0) +HIGH(1) + +Pico 2 has internal pull resistors! + + + +GPIO Input Functions + +gpio_init(pin) +Initialize pin + +gpio_set_dir(pin, GPIO_IN) +Set as input + +gpio_pull_up(pin) +Enable pull-up + +gpio_get(pin) +Read state (0 or 1) + +No external resistor needed -- internal pull-up! + \ No newline at end of file diff --git a/WEEK06/slides/WEEK06-IMG04.svg b/WEEK06/slides/WEEK06-IMG04.svg new file mode 100644 index 0000000..4ab0ae6 --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG04.svg @@ -0,0 +1,99 @@ + + + + + +Pull-Up Resistor +Internal pull-up on GPIO 15 + + + +Circuit + +3.3V + + + +pull-up R + + + +GPIO 15 + + + + +BTN + + +GND + + + +Button Logic + +State +GPIO +LED + + + +Released +HIGH (1) +OFF + +Pressed +LOW (0) +ON + +Inverted! Pull-up = backwards + + + +Ternary Operator + + +gpio_put(LED, pressed?0:1); + +pressed=1 -> LED OFF (inverted!) + + + +Hardware Wiring + + +Pico 2 +GPIO 15 +GPIO 16 +GND +BOOTSEL + +--> +--> +--> + + +Components +Button (one leg) +LED + resistor +Button (other leg) +Hold to flash UF2 + + +Key Point +No external +resistor needed! +Internal pull-up +handles it all + \ No newline at end of file diff --git a/WEEK06/slides/WEEK06-IMG05.svg b/WEEK06/slides/WEEK06-IMG05.svg new file mode 100644 index 0000000..b612ee1 --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG05.svg @@ -0,0 +1,83 @@ + + + + + +Source Code +0x0014_static-variables.c + + + +main() + + +int main(void) { +stdio_init_all(); +// GPIO setup +gpio_init(15); +gpio_set_dir(15, GPIO_IN); +gpio_pull_up(15); +gpio_init(16); +gpio_set_dir(16, GPIO_OUT); +while (true) { +uint8_t reg = 42; +static uint8_t s = 42; +printf(... reg, s); +reg++; s++; +} +} + + + +GPIO Setup + +Pin 15: +Input +Pull-up enabled + +Pin 16: +Output +LED control + + + +Serial Output + + +reg: 42 +s: 42 +reg: 42 +s: 43 + +reg always 42, s grows + + + +Button Logic + +pressed = gpio_get(15); + + + +Key Behaviors + + +reg: always 42 + + +s: 42,43,44... + + +wraps at 255->0 + \ No newline at end of file diff --git a/WEEK06/slides/WEEK06-IMG06.svg b/WEEK06/slides/WEEK06-IMG06.svg new file mode 100644 index 0000000..a0919c3 --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG06.svg @@ -0,0 +1,65 @@ + + + + + +Compiler Optimizations +What the compiler does to your code + + + +Optimization 1: Dead Code Removal + + +Your code: +uint8_t reg = 42; +reg++; +// No lasting effect! + + +Compiler output: +movs r1, #42 +// reg++ is GONE +// Uses constant 42 directly + + + +Optimization 2: Function Inlining + + +Your code: +gpio_pull_up(15); +// Simple wrapper func + + +Compiler output: +gpio_set_pulls(15,1,0); +// Inlined to real func + + + +Optimization 3: Scheduling + +Compiler reorders instructions +to avoid pipeline stalls: + + +Load SIO base + + +Increment s + + +Read GPIO + \ No newline at end of file diff --git a/WEEK06/slides/WEEK06-IMG07.svg b/WEEK06/slides/WEEK06-IMG07.svg new file mode 100644 index 0000000..c359080 --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG07.svg @@ -0,0 +1,90 @@ + + + + + +Assembly Analysis +Key instructions in main() loop + + + +Loop Body (0x10000264) + + +movs r1, #0x2a +; reg=42 +bl __wrap_printf +; print reg +ldrb r1, [r4] +; load static +bl __wrap_printf +; print s +mov.w r1, #0xd0000000 +; SIO base +ldrb r3, [r4] +; reload s +adds r3, #1 +; s++ +strb r3, [r4] +; store s +ldr r3, [r1, #4] +; read GPIO +ubfx r3, r3, #15, #1 +; bit 15 +eor.w r3, r3, #1 +; invert +mcrr 0, 4, r2, r3, cr0 +; GPIO out +b.n 0x10000264 +; loop + + + +Key Registers + +r1: +printf arg +r3: +temp / static +r4: +0x200005a8 +(static var addr) +r2: +LED pin (16) + + + +Key Patterns + +ldrb/adds/strb +Load-increment-store +(static var update) + +ubfx #15, #1 +Extract GPIO bit 15 + +eor.w #1 +Inverts (? 0 : 1) + + + +Infinite Loop + + +b.n 0x10000264 +; while(true) + +No pop/bx lr +main() never returns + \ No newline at end of file diff --git a/WEEK06/slides/WEEK06-IMG08.svg b/WEEK06/slides/WEEK06-IMG08.svg new file mode 100644 index 0000000..702d322 --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG08.svg @@ -0,0 +1,75 @@ + + + + + +GDB: Static Variable +Finding static_fav_num in RAM + + + +Literal Pool Lookup + + +ldr r4, [pc, #44] +@ 0x10000290 + +Examine literal pool: + +x/1wx 0x10000290 = 0x200005A8 + +r4 now holds the +RAM address of +static_fav_num! + + + +Read Value + + +x/1db 0x200005a8 = 42 + +After one loop iteration: + +x/1db 0x200005a8 = 43 + +It incremented! Persists in RAM. + + + +Disasm Gotcha + +x/i 0x10000290 shows: + + +lsls r0, r5, #22 + +GDB decodes DATA as code! +Bytes A8 05 00 20 = 0x200005A8 +Use x/wx not x/i for data + + + +GPIO Input Register + +Read button state in GDB: + + +p/x (*(uint*)0xd0000004 >> 15) & 1 + +Returns 1: +not pressed (pull-up) +Returns 0: +button pressed + \ No newline at end of file diff --git a/WEEK06/slides/WEEK06-IMG09.svg b/WEEK06/slides/WEEK06-IMG09.svg new file mode 100644 index 0000000..c603290 --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG09.svg @@ -0,0 +1,63 @@ + + + + + +Hacking the Binary +Two patches with a hex editor + + + +File Offset Formula +offset = address - 0x10000000 +Example: 0x10000264 -> 0x264 + + + +Hack 1: Change 42 to 43 + +Target: movs r1, #0x2a +at address 0x10000264 (offset 0x264) + + +Before: 2A 21 +movs r1, #42 + + +After: 2B 21 +movs r1, #43 + +Thumb encoding: imm8 byte first, opcode 0x21 second + + + +Hack 2: Invert Button Logic + +Target: eor.w r3, r3, #1 +at address 0x10000286 (offset 0x286) + + +Before: 83 F0 01 03 + + +After: 83 F0 00 03 + +Byte at offset 0x288: +01 +-> +00 + +XOR with 0 = no invert +LED now ON by default, OFF when pressed + \ No newline at end of file diff --git a/WEEK06/slides/WEEK06-IMG10.svg b/WEEK06/slides/WEEK06-IMG10.svg new file mode 100644 index 0000000..f88fa45 --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG10.svg @@ -0,0 +1,112 @@ + + + + + +Static Vars & GPIO Input +Static variables, GPIO input, hacking + + + +Static vs Auto + +Aspect +Auto +Static + + + +Where +Stack +.data + +Life +Scope +Forever + +Init +Every +Once + +Keeps? +No +Yes + +Optimized? +Often +In RAM + +Compiler may remove auto vars + + + +GPIO Input Setup + +1. +gpio_init(pin) + +2. +gpio_set_dir(pin, GPIO_IN) + +3. +gpio_pull_up(pin) + +4. +gpio_get(pin) + +Pull-up: released=HIGH +pressed=LOW (inverted!) +Internal R, no hardware + + + +Key Instructions + +ubfx r3,r3,#15,#1 +Extract single GPIO bit + +eor.w r3,r3,#1 +XOR to invert logic + +b.n 0x10000264 + + + +Hacking Workflow + +1. +Analyze in GDB + +2. +Calculate offset + +3. +Patch .bin in HxD + +4. +uf2conv.py + flash + + + +Takeaways + + +42 -> 43 (1 byte) + + +XOR 1->0 (invert) + + +offset = addr - base + \ No newline at end of file diff --git a/WEEK07/WEEK07-BN.md b/WEEK07/WEEK07-BN.md new file mode 100644 index 0000000..829d2cd --- /dev/null +++ b/WEEK07/WEEK07-BN.md @@ -0,0 +1,1498 @@ +# Week 7-BN: Binary Ninja Personal — Read, Hack, and Patch Constants and a 1602 LCD String (Raw `.bin`) + +*** + +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +- Build the lesson project with `Release` and get both an `.elf` and a raw `.bin` +- Dump the **ELF symbol map** with `arm-none-eabi-nm` and use it as ground truth +- Load the raw `.bin` into Binary Ninja at `0x10000000` +- Read how a `#define` macro **and** a `const` variable both fold to bare instruction immediates +- Tell a 16-bit `movs r1, #42` from a 32-bit `movw r1, #1337` in the disassembly +- Follow the I2C path through the `i2c_inst_t` struct: `I2C_PORT` -> `i2c1` -> `&i2c1_inst` -> `hw` -> `0x40098000` +- **Break at the `printf` call** on live silicon and hack the printed constant live +- Optionally **hack the LCD string live** by pointing `r0` at a RAM replacement +- **Resolve the functions in the Binary Ninja GUI** using the ELF symbol map, including the `lcd_1602.c` driver symbols +- **Patch** both constants (`42 -> 43`, `1337 -> 1344`) and the LCD string (`"Reverse" -> "Exploit"`) +- **Export** the patched image, convert it to UF2, and flash it + +--- + +## How This Guide Works + +The build produces two files for the project: + +| File | What it is | How we use it | +| ---- | ---------- | ------------- | +| `.elf` | The linked image with a full symbol table | Ground truth for every function address and name | +| `.bin` | The raw flash image, no headers, no symbols | The image we load into Binary Ninja and reverse | + +The `.bin` is built **from** the `.elf`, so the ELF tells you exactly what is at every address. We use the ELF symbol map to resolve functions in Binary Ninja, and we reverse-engineer the raw `.bin` the way a real extracted firmware image is reversed. + +> **Build `Release`, not `Debug`.** Every address in this guide matches the Week 7 lesson, and the Week 7 lesson is a `Release` build. `Release` optimizes the code the same way the original lesson was built: it **inlines** the `static` helpers `init_i2c_and_lcd` and `write_lcd_greeting` (and the whole `lcd_1602.c` static helper chain) straight into `main` or into the public `lcd_*` functions, and it folds both `FAV_NUM` and `OTHER_FAV_NUM` down to immediate values. If you build `Debug`, the SDK function addresses move and the helpers stay separate calls, so nothing lines up. Always build `Release` for this lesson. + +The order is **dynamic first, static second**: + +1. Break on the live target and prove what the code does. +2. Hack it live in the debugger and watch the output change. +3. Resolve the functions in Binary Ninja using the ELF symbol map. +4. Patch the bytes, export, convert, and flash. + +| Project | Serial output | Also does | The hacks | +| ------- | ------------- | --------- | --------- | +| `0x0017_constants` | `FAV_NUM: 42`, `OTHER_FAV_NUM: 1337` | I2C1 (SDA GP2, SCL GP3) drives a 1602 LCD, writing `Reverse` / `Engineering` | `42 -> 43`, `1337 -> 1344`, and `"Reverse" -> "Exploit"` | + +> **Addresses come from your build.** Every address here is from the `Release` build produced in Step 3. Confirm against your own `.elf` with the command in Step 4. + +> **The surprise of this week is that `const` is not in memory.** `#define FAV_NUM 42` becomes the 16-bit `movs r1, #42`; `const int OTHER_FAV_NUM = 1337` *also* becomes an immediate, the 32-bit `movw r1, #1337`. The compiler only keeps a `const` in `.rodata` if the program **takes its address** (`&OTHER_FAV_NUM`); this program never does, so the `const` is inlined exactly like the macro. You patch an instruction operand, not a data word. + +--- + +## Part 1: Build, Flash, and Get the Symbol Map + +### Step 1: Install the toolchain + +**Windows x64** + +- Install the **Raspberry Pi Pico** extension in VS Code. It installs the ARM GNU toolchain, CMake, Ninja, and the Pico SDK. +- Install **Binary Ninja Personal** and complete its license activation. +- Install **PuTTY** for the serial monitor. + +**macOS Apple Silicon** + +```bash +brew install cmake ninja +``` + +- Install **Binary Ninja Personal** and complete its license activation. +- Install the **Arm GNU Toolchain**, or let the VS Code Pico extension manage it. + +**Linux x64** + +```bash +sudo apt install cmake ninja-build gcc-arm-none-eabi libnewlib-arm-none-eabi git python3 openocd minicom +``` + +- Install **Binary Ninja Personal** and complete its license activation. + +### Step 2: Verify your tools are the right architecture (do not skip this) + +On **macOS Apple Silicon**, the most common failure is an Intel `x86_64` tool on your `PATH`: + +``` +zsh: bad CPU type in executable: cmake +``` + +You may have **two Homebrews**: the arm64 one at `/opt/homebrew` and the Intel one at `/usr/local`. If `/usr/local/bin` wins, every `brew` tool is x86_64. Check: + +```bash +file "$(which cmake)" +file "$(which ninja)" +file "$(which arm-none-eabi-gdb)" +file "$(which arm-none-eabi-nm)" +file "$(which openocd)" +file "$(which telnet)" +``` + +All must report `arm64`. If any is `x86_64`, put the Apple Silicon prefix first for the session and check again: + +```bash +export PATH="/opt/homebrew/bin:$PATH" +hash -r +file "$(which cmake)" +``` + +To make it permanent, add that `export` to `~/.zshrc`. Do not use Rosetta as a fix; OpenOCD and GDB are exactly the kind of programs where a translation layer produces failures that look like debugger bugs. + +**`telnet` is special — and optional.** The GDB MI workflow does not need it; it is only used by the command-port fallback. macOS no longer ships `telnet`, and the Homebrew build is often the Intel one, so `telnet 127.0.0.1 4444` fails with `bad CPU type in executable`. Call the Apple Silicon Homebrew explicitly: + +```bash +/opt/homebrew/bin/brew install telnet +``` + +If you would rather not install anything, macOS ships an arm64 `nc`, which can connect to the same OpenOCD port: + +```bash +nc 127.0.0.1 4444 +``` + +**Windows x64** and **Linux x64** do not have this problem. Skip to Step 3. + +### Step 3: Build the project with `Release` + +Run this inside `0x0017_constants/`: + +```bash +cmake -B build -G Ninja -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 -DCMAKE_BUILD_TYPE=Release +cmake --build build +``` + +**Point Binary Ninja at this repository (once).** Every console snippet below reads the repo root from `~/.embedded-hacking-repo`, so Binary Ninja never needs a database open and nothing is hardcoded. From the repo root, run once: + +**macOS / Linux:** + +```bash +pwd > ~/.embedded-hacking-repo +``` + +**Windows (PowerShell):** + +```powershell +(Get-Location).Path | Set-Content "$env:USERPROFILE\.embedded-hacking-repo" +``` + +**Then build from the Binary Ninja console**, so the whole build -> patch -> flash loop stays inside Binary Ninja. The console inherits a minimal `PATH` — on macOS just `/usr/bin:/bin:/usr/sbin:/sbin` — so it does not see Homebrew; add your package manager's `bin` first, then run plain `cmake`. + +**macOS Apple Silicon:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +os.environ["PATH"] = "/opt/homebrew/bin:" + os.environ["PATH"] # the console's PATH omits Homebrew +proj = os.path.join(root, "0x0017_constants") +subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) +subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +**Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +proj = os.path.join(root, "0x0017_constants") +subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) +subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +proj = os.path.join(root, "0x0017_constants") +subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) +subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +The build directory now contains the pair we need: + +- `0x0017_constants/build/0x0017_constants.elf` and `.bin` — the `.bin` is **17980** bytes (`0x463c`) + +If the ARM toolchain is not on your `PATH`, add `-DPICO_TOOLCHAIN_PATH=...`: + +| OS | Typical toolchain path | +| -- | ---------------------- | +| Windows x64 | `C:/Program Files/Arm GNU Toolchain arm-none-eabi/14.2 rel1/bin` | +| macOS Apple Silicon | `~/.pico-sdk/toolchain/14_2_Rel1/bin` | +| Linux x64 | `/usr` | + +> **This guide's toolchain lives at `~/.pico-sdk/toolchain/14_2_Rel1/bin`.** All of `arm-none-eabi-nm`, `arm-none-eabi-objdump`, and `arm-none-eabi-gdb` resolved in this document come from there. If your install is elsewhere, `which arm-none-eabi-nm` tells you where to point. + +### Step 4: Dump the ELF symbol map + +This is the ground truth for the whole lesson. Run `arm-none-eabi-nm` on the ELF and keep the output in a terminal or a text file: + +**macOS Apple Silicon / Linux x64:** + +```bash +arm-none-eabi-nm -n --defined-only build/0x0017_constants.elf | grep -E ' [Tt] ' +``` + +**Windows x64:** + +```powershell +arm-none-eabi-nm -n --defined-only build\0x0017_constants.elf | Select-String ' [Tt] ' +``` + +Each line is `address type name`. The `T`/`t` type is a function. Here are the functions this lesson uses. The signatures come from the ELF's DWARF debug info queried with `arm-none-eabi-gdb -batch -ex "ptype "`, so they are exact. + +**Our code and the startup chain:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | reset entry | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | calls `runtime_init`, `main`, `exit` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | copies `.data` from flash to SRAM | +| `0x100001e4` | `_init` | `void _init(void)` | runs `.init_array` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | C runtime boilerplate | +| `0x10000234` | `main` | `int main(void)` | the lesson function (`init_i2c_and_lcd` and `write_lcd_greeting` inlined) | + +**Our I2C/LCD driver (`lcd_1602.c`) and the SDK I2C functions `main` reaches:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x100002bc` | `lcd_i2c_init` | `void lcd_i2c_init(i2c_inst_t*, uint8_t, int, uint8_t)` | store config + HD44780 reset/configure | +| `0x100006f4` | `lcd_set_cursor` | `void lcd_set_cursor(int, int)` | move the HD44780 cursor | +| `0x100007f0` | `lcd_puts` | `void lcd_puts(const char*)` | write a string to the LCD | +| `0x10003cdc` | `i2c_init` | `uint i2c_init(i2c_inst_t*, uint)` | SDK I2C init (100 kHz) | +| `0x10003d28` | `i2c_write_blocking` | `int i2c_write_blocking(i2c_inst_t*, uint8_t, const uint8_t*, size_t, bool)` | one blocking I2C transfer | +| `0x100008f0` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | SDK GPIO function select (I2C pins) | +| `0x1000092c` | `gpio_set_pulls` | `void gpio_set_pulls(uint, bool, bool)` | SDK pull config (`gpio_pull_up` inlined) | + +> The `lcd_1602.c` static helpers — `pcf_write_byte`, `pcf_pulse_enable`, `lcd_write4`, `lcd_send`, `lcd_store_config`, `lcd_hd44780_reset`, `lcd_hd44780_configure` — have **no symbol of their own** in the `Release` build. They are inlined into `lcd_i2c_init`, `lcd_set_cursor`, and `lcd_puts`, which is why those three functions are large and call `i2c_write_blocking` and the `sleep_*` helpers directly. + +**The stdio/UART and printf chain `main` reaches:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x10001368` | `sleep_us` | `void sleep_us(uint64_t)` | SDK microsecond delay | +| `0x10001440` | `sleep_ms` | `void sleep_ms(uint32_t)` | SDK millisecond delay | +| `0x10001624` | `time_us_64` | `uint64_t time_us_64(void)` | SDK microsecond clock | +| `0x10001638` | `busy_wait_us` | `void busy_wait_us(uint64_t)` | UART timing loop | +| `0x100016b8` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | SDK UART init | +| `0x1000188c` | `clock_get_hz` | `unsigned long clock_get_hz(clock_handle_t)` | UART clock lookup | +| `0x100035a4` | `exit` | `void exit(int)` | C runtime exit | +| `0x100035ac` | `runtime_init` | `void runtime_init(void)` | SDK runtime init | +| `0x100035d8` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | CRLF output driver | +| `0x100036e8` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | buffered string output | +| `0x100037d4` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | enable the UART driver | +| `0x100037fc` | `stdio_init_all` | `bool stdio_init_all(void)` | SDK serial init | +| `0x100038c8` | `__wrap_vprintf` | `int __wrap_vprintf(const char*, va_list)` | printf core | +| `0x1000398c` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | the `printf` wrapper | +| `0x10003b48` | `stdio_uart_init` | `void stdio_uart_init(void)` | SDK UART stdio init | +| `0x10003e24` | `strlen` | `size_t strlen(const char*)` | C runtime string length | + +**SDK helpers the `printf` path reaches:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x10003548` | `vfctprintf` | `int vfctprintf(void (*)(char, void*), void*, const char*, va_list)` | printf format engine | +| `0x10002b64` | `_vsnprintf` | `int _vsnprintf(out_fct_type, char*, size_t, const char*, va_list)` | the format dispatcher | +| `0x10001f48` | `_ntoa_format` | `unsigned _ntoa_format(out_fct_type, char*, size_t, size_t, char*, size_t, bool, unsigned, unsigned, unsigned, unsigned)` | number formatter | +| `0x10001eac` | `_out_rev` | `unsigned _out_rev(out_fct_type, char*, size_t, size_t, const char*, size_t, unsigned, unsigned)` | reversed-digit output | +| `0x1000211c` | `_out_char` | `void _out_char(char, void*, size_t, size_t)` | single-char sink | + +Two things in this project have **no symbol of their own**, because the compiler inlined them into `main`: + +- `init_i2c_and_lcd` and `write_lcd_greeting` — the two `static` helpers in our own source are inlined, so there is no address to rename. You see their bodies directly inside `main`. +- `gpio_pull_up` — `static inline` in the SDK, so `gpio_pull_up(2)` and `gpio_pull_up(3)` compile to direct calls to `gpio_set_pulls` at `0x10000258` and `0x10000262`. + +### Step 5: Flash and confirm the output + +A `.bin` has no headers, so OpenOCD must be told the base address `0x10000000`. From the repository root: + +**macOS Apple Silicon / Linux x64:** + +```bash +./flash.sh 0x0017_constants/build/0x0017_constants.bin +``` + +**Windows x64 (PowerShell):** + +```powershell +.\flash.ps1 -Bin 0x0017_constants\build\0x0017_constants.bin +``` + +**Or flash from the Binary Ninja console** (the console reads the repo root from the marker file, so it works with no database open): + +**macOS Apple Silicon / Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x0017_constants", "build", "0x0017_constants.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("flashing in the background; log:", log) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x0017_constants", "build", "0x0017_constants.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # free the probe first +subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "flash.ps1"), "-Bin", bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT) +print("flashing in the background; log:", log) +``` + +Wait for `wrote 17980 bytes ...` and `** Verified OK **`. Open a serial monitor at **115200** baud: + +- **Windows x64:** PuTTY -> Connection type **Serial**, the Pico's COM port, speed `115200`. +- **macOS Apple Silicon:** `screen /dev/tty.usbmodem* 115200` (quit with `Ctrl-A` then `K`). +- **Linux x64:** `minicom -D /dev/ttyACM0 -b 115200`. + +``` +FAV_NUM: 42 +OTHER_FAV_NUM: 1337 +FAV_NUM: 42 +OTHER_FAV_NUM: 1337 +... +``` + +The 1602 LCD shows `Reverse` on line 1 and `Engineering` on line 2. Both numbers print forever, because both constants are baked into the loop as immediates. That is the behavior we will change. + +--- + +## Part 2: Load the Raw `.bin` into Binary Ninja + +Start from a fresh Binary Ninja state. If you already have a `.bndb` for this lesson, **close it and start over**; a stale database keeps old names and patches. + +### Step 6: Bring the raw `.bin` into Binary Ninja + +A raw `.bin` has no headers, so Binary Ninja cannot know where it belongs or what architecture it is. You must supply both. If you just double-click the `.bin`, Binary Ninja may load it at address `0x0` with a guessed architecture, and every address in this lesson will be wrong. + +1. Choose `File -> Open with Options...` (do **not** use plain `File -> Open`). +2. Select `0x0017_constants/build/0x0017_constants.bin`. +3. In the loader options, set: + - **Architecture:** `thumb2` (the ARMv7-M / ARMv8-M Thumb-2 architecture, which covers the Cortex-M33) + - **Platform:** `thumb2` + - **Base Address:** `0x10000000` (the XIP flash base) +4. Click **Open**. + +Binary Ninja analyzes the image and opens the linear view. + +**Verify the load before going further.** Press `G`, type `0x10000000`, and read the first two words: + +``` +0x10000000 0x20082000 initial stack pointer +0x10000004 0x1000015d reset vector (bit 0 = Thumb) +``` + +If you instead see data at `0x00000000`, or a vector word without bit 0 set, close the tab and repeat with `Open with Options`. The Cortex-M33 only executes Thumb-2, so `thumb2` is the only correct architecture. + +> **Console equivalent:** +> ```python +> import os +> root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +> load(os.path.join(root, "0x0017_constants", "build", "0x0017_constants.bin"), +> options={"loader.imageBase": 0x10000000, "loader.platform": "thumb2"}) +> ``` + +### Step 7: Save it as a Binary Ninja database (`.bndb`) + +Binary Ninja never writes back into the `.bin`. Your names, comments, types, and patches live in a separate **`.bndb`** database. Save one now, before you make any changes: + +1. Choose `File -> Save As...`. +2. Save it next to the image as `0x0017_constants.bndb`. +3. From now on, save with `File -> Save` (`Cmd+S` on macOS, `Ctrl+S` on Windows/Linux) whenever you rename or patch. + +The two files have different roles: + +| File | Role | +| ---- | ---- | +| `0x0017_constants.bin` | the raw firmware image; Binary Ninja never modifies it | +| `0x0017_constants.bndb` | your analysis database: names, types, comments, and patches | + +When you come back later, **open the `.bndb`**, not the `.bin`; that restores all your work. If a database gets messy, delete the `.bndb` and re-import the `.bin` from Step 6 — the firmware is never at risk. You export the patched image out of this view later, in Step 19. + +### Step 8: The views you will use + +- **Linear view:** the disassembly listing. You navigate, read, and patch here. +- **Graph view:** the control-flow graph of the current function. +- **Decompiler (HLIL):** the pseudo-C decompilation. +- **Hex view:** raw bytes, used for patching. +- **Function list:** the sidebar list of every detected function. + +Navigation: `G` go to address, `N` rename, `Y` set type or signature, `;` add a comment. Breakpoints are set from the GUI through the GDB MI adapter — see Step 12. + +> **macOS function keys:** the top-row `F` keys are usually mapped to system functions. Every step here uses menu paths that work without them. + +--- + +## Part 3: Dynamic — Break at the `printf` Call and Hack Live + +### Step 9: Start OpenOCD as a live debug server + +Make sure no other OpenOCD is running; a forgotten server holds port `3333`. + +**macOS / Linux:** + +```bash +ps aux | grep -i openocd +``` + +**Windows (PowerShell):** + +```powershell +Get-Process | Where-Object { $_.ProcessName -like '*openocd*' } +``` + +Stop any leftover server gracefully: + +**macOS / Linux:** + +```bash +pkill -TERM -f openocd +``` + +**Windows (PowerShell):** + +```powershell +Get-Process openocd -ErrorAction SilentlyContinue | Stop-Process +``` + +Start the server **parked at `main`**: + +**macOS Apple Silicon / Linux x64:** + +```bash +BP_ADDR=0x10000234 ./debug-server.sh +``` + +**Windows x64 (PowerShell):** + +```powershell +$env:BP_ADDR="0x10000234"; .\debug-server.ps1 +``` + +**Or start it from the Binary Ninja console**, freeing the probe first and launching the server in the background so the console returns immediately: + +**macOS Apple Silicon / Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # stop any running server first +log = os.path.join(root, "openocd.log") +p = subprocess.Popen([os.path.join(root, "debug-server.sh")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("OpenOCD started (pid", p.pid, "); log:", log) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # stop any running server first +log = os.path.join(root, "openocd.log") +p = subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "debug-server.ps1")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT) +print("OpenOCD started (pid", p.pid, "); log:", log) +``` + +`Popen` returns in a few milliseconds; the server keeps running in the background. Check `openocd.log` for `Listening on port 3333`, then connect in Step 10. + +Wait for: + +``` +Info : [rp2350.dap.core0] Examination succeed +Startup breakpoint at 0x10000234 (2-byte hardware execute, one-shot). +Info : starting gdb server for rp2350.dap.core0 on 3333 +Info : Listening on port 3333 for gdb connections +``` + +> **`BP_ADDR` parks the core at `main` before any client connects.** The script arms a 2-byte hardware breakpoint and then does the startup `reset run`, so the core runs from the vector table and stops at your address with no debugger attached yet. When Binary Ninja connects a moment later, the first thing it reads is already the truth: `Stopped at 0x10000234`. This is the whole reason the lab works cleanly — you never have to drive a reset from outside the GUI. +> +> Use the address you actually want to stop at: +> +> | What you want to stop at | Address | Command | +> | --- | --- | --- | +> | `main` (once per reset) | `0x10000234` | `BP_ADDR=0x10000234 ./debug-server.sh` | +> | The **loop** — the `FAV_NUM` `printf` call, hit every iteration | `0x10000292` | `BP_ADDR=0x10000292 ./debug-server.sh` | +> +> ```bash +> BP_ADDR=0x10000234 ./debug-server.sh # park at main +> BP_ADDR=0x10000292 ./debug-server.sh # park in the loop instead +> ``` +> +> ```powershell +> $env:BP_ADDR="0x10000234"; .\debug-server.ps1 # park at main +> $env:BP_ADDR="0x10000292"; .\debug-server.ps1 # park in the loop +> ``` +> +> **This startup stop is single-use.** OpenOCD flushes breakpoints when a client connects, so this one is gone once Binary Ninja attaches — fine for `main`, which only runs once per reset. Every breakpoint after that is set from the Binary Ninja GUI (Step 12) and is repeatable. To stop at `main` again, restart the server with `BP_ADDR` and reconnect. + +> **Exactly one core.** The line must say `core0` and must **not** mention `core1`. Core1 is never started by this firmware; exposing it makes Binary Ninja read core1's reset-state registers, which are not real addresses, and OpenOCD floods the log with `Failed to read memory at 0xf0000000`. The scripts already use `USE_CORE=0`; do not change it. + +> **Windows driver note:** the Debug Probe must use the **WinUSB** driver. If OpenOCD reports `unable to open CMSIS-DAP device`, install it with [Zadig](https://zadig.akeo.ie/) (select `Debug Probe (CMSIS-DAP)` -> WinUSB). + +### Step 10: Connect Binary Ninja to the GDB server + +1. Make sure the image is open and analyzed (Part 2) and the server from Step 9 is running (parked at `main`). +2. Choose `Debugger -> Connect to Remote Process`. +3. In the **adapter** dropdown, select **GDB MI**. +4. In the **connect** settings group, set **IP Address** to `127.0.0.1` and **Port** to `3333`. +5. Set **Full GDB Executable Path** to the `arm-none-eabi-gdb` from the **Arm GNU Toolchain 14.2.rel1**. It ships for all three hosts, and the Raspberry Pi Pico VS Code extension installs that same 14.2.rel1 toolchain (including `arm-none-eabi-gdb`) on all of them: + + | OS | `arm-none-eabi-gdb` path | + | -- | ------------------------ | + | macOS Apple Silicon | `/Applications/ArmGNUToolchain/14.2.rel1/arm-none-eabi/bin/arm-none-eabi-gdb` (or the Pico extension's `~/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb`) | + | Windows x64 | `%USERPROFILE%\.pico-sdk\toolchain\14_2_Rel1\bin\arm-none-eabi-gdb.exe` (Pico extension), or `C:\Program Files (x86)\Arm GNU Toolchain arm-none-eabi\14.2 rel1\bin\arm-none-eabi-gdb.exe` | + | Linux x64 | `~/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb` (Pico extension), or the `bin/` directory of the extracted `arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi` tarball | +6. Click **Accept**. + +> **Use the GDB MI adapter.** It launches a real `arm-none-eabi-gdb --interpreter=mi2` and lets Binary Ninja drive it, so breakpoints and stepping go through real GDB — which sends the correct 2-byte breakpoint length and handles step-over itself. Verified working end to end: connect, GUI breakpoints (**Add Hardware Breakpoint...**, hardware execute), **Step Into** / **Step Over**, and register edits. Stops are reported as `Breakpoint` (not `SingleStep`). +> +> **Do NOT have any breakpoints set in Binary Ninja before you connect.** With the GDB MI adapter, attaching while Binary Ninja already has a breakpoint **hangs the session**. Start the server parked with `BP_ADDR` (Step 9), connect, and only add hardware breakpoints *after* the connection is up. This is a Binary Ninja bug; it is the single most common GDB MI failure. +> +> **The GDB executable path matters.** Use the **14.2.rel1** build on every OS (Windows, macOS, Linux). The 13.3.rel1 build did **not** connect in testing. +> +> **This step is temporary.** Vector35 plans to ship a GDB binary with the GDB MI adapter ([Vector35/debugger#929](https://github.com/Vector35/debugger/issues/929), milestone *Langara*). Once that lands, Binary Ninja provides GDB itself and you will not need to set **Full GDB Executable Path** at all. +> +> **Do not pick Corellium.** Binary Ninja's adapter dropdown also lists **Corellium**, which is for Corellium's virtual devices and expects an API token, not a local OpenOCD server. It is not the adapter for this lab. The dropdown is a combo box, so an accidental arrow-key press can land on it — always read the label back and confirm it says **GDB MI** before clicking **Accept**. + +> **The adapter and port are not saved in the `.bndb`.** Every time you relaunch Binary Ninja you must re-select **GDB MI**, re-enter port `3333`, and re-set the GDB path. + +> **Watch for an off-screen error dialog.** When a connection fails, Binary Ninja pops a `Binary Ninja critical alert` window that can be positioned mostly outside the main window, which makes it look like nothing happened. If the connect seems to do nothing, check your other display. + +The target keeps running. Open the **Registers** tab (bug icon) and confirm you see live values. `pc` inside `0x10003xxx` and `sp` just below `0x20082000` are healthy. + +> **If `pc` is `0x00000088`, `0x000000ec`, or `sp` is `0xf0000000`, the session is bad.** Restart the server, then restart Binary Ninja (a server restart while attached leaves Binary Ninja in a stale session), and connect again. + +### Step 11: Find `main` without relying on its address + +`main` can move between programs, so we do not guess it. We follow the one fixed path to it. Press `G` and go to `0x10000000`: + +``` +0x10000000 0x20082000 initial stack pointer (top of SRAM) +0x10000004 0x1000015d reset vector +``` + +Bit 0 of a vector is the Thumb bit, so `0x1000015d` means "start at `0x1000015c`". That is `_reset_handler`. Follow the reset path to `0x10000186`, `platform_entry`: + +```asm +10000186: ldr r1, [pc, #80] @ (100001d8 ) +10000188: blx r1 +1000018a: ldr r1, [pc, #80] @ (100001dc ) +1000018c: blx r1 +1000018e: ldr r1, [pc, #80] @ (100001e0 ) +10000190: blx r1 +10000192: bkpt 0x0000 +10000194: b.n 10000192 @ +``` + +**The middle `blx` at `0x1000018c` is the call to `main`.** `platform_entry` is byte-identical in every project, so `0x1000018c` catches `main` no matter where the linker placed it. The literal pool at `0x100001dc` holds `main | 1`; clearing bit 0 gives `0x10000234`. + +### Step 12: Set a hardware breakpoint in the GUI + +With the **GDB MI** adapter, Binary Ninja sets breakpoints through real GDB, which sends the correct 2-byte length, so you set them **in the UI**. There is no command port here. + +> **Why older drafts used the command port.** Binary Ninja's **GDB RSP** adapter is its own minimal RSP client and sends a **1-byte** breakpoint (`Z0,,1`); the Cortex-M33 FPB comparators need 2 bytes, so OpenOCD rejected it with `only breakpoints of two bytes length supported`. The old workaround was to arm breakpoints by hand over telnet. **The GDB MI adapter does not have this problem** — it drives real `arm-none-eabi-gdb`, which sends the right length. So everything below is done in the GUI. The command port still exists as a fallback (see the end of this step), but you do not need it. + +#### Where you can stop + +| You want to stop at | Address | How | Repeatable? | +| --- | --- | --- | --- | +| **`main`** | `0x10000234` | The server starts parked there with `BP_ADDR=0x10000234` (Step 9), so Binary Ninja is already stopped at `main` when it connects. | No — `main` runs once per reset. | +| **The `FAV_NUM` `printf` call** | `0x10000292` | Set a hardware breakpoint in the GUI, then click **Resume**. | Yes — fires on every iteration. | +| **The `OTHER_FAV_NUM` `printf` call** | `0x1000029c` | Set a hardware breakpoint in the GUI, then click **Resume**. | Yes — fires on every iteration. | +| **The `lcd_puts("Reverse")` call** | `0x1000027c` | Set a hardware breakpoint while stopped at `main`, then click **Resume**. | No — the LCD is written once at init. | + +#### Set the loop breakpoint in the GUI + +1. Press `G`, type the loop address (`0x10000292`), and press Enter. +2. Set a **hardware execution** breakpoint at that address, either way: + - `Debugger -> Add Hardware Breakpoint...` — a **hardware execute** (`HE`) breakpoint. **Use this one.** + - click the line and press `F2` (`Debugger -> Toggle Breakpoint`) — a **software** breakpoint. It will **not** work here: the code is in read-only flash, so GDB cannot install it and the core just keeps running. +3. Click **Resume**. The core is already running the loop, so the breakpoint fires on the next iteration. Binary Ninja stops with the PC at the loop address and reports it as a **Breakpoint** — verified: `Stopped (Breakpoint) at 0x10000292`. + +> **No breakpoints before you connect.** With GDB MI, a breakpoint set before the connection hangs the session (Step 10). Start parked with `BP_ADDR`, connect, *then* add breakpoints. + +#### Stepping + +With the target halted at the breakpoint, **Step Into** (`F7`) and **Step Over** (`F8`) run through real GDB and move the PC. Verified: `0x10000292 -> 0x1000398c -> 0x1000398e -> ...`. + +> **Step Over on the raw `.bin` steps *into* calls.** The raw image has no symbol for `__wrap_printf`, so **Step Over** at the `printf` call behaves like **Step Into**. When the lab needs to execute the call and then stop, it moves the breakpoint to the return site and clicks **Resume** instead (Step 13 shows this). + +> **Never use Binary Ninja's Restart button.** On RP2350 it resets and halts inside the boot ROM (`pc=0x88`, `sp=0xf0000000`). To reset cleanly, restart the server with `BP_ADDR` and reconnect. + +> **If you ever need the command port.** It is still there — `nc 127.0.0.1 4444`, and `bp 2 hw` still arms a breakpoint, `rbp ` / `rbp all` still remove them. It is the fallback if you switch back to the **GDB RSP** adapter, whose 1-byte breakpoints the GUI cannot set. With GDB MI you do not need it for this lab. + +### Step 13: HACK IT LIVE — change the printed `FAV_NUM` + +`main` loads the `#define` `0x2a` (42) into `r1` and calls `printf` on every iteration. We break on that call and change it live: + +```asm +1000028e: movs r1, #42 @ 0x2a +10000290: ldr r0, [pc, #32] @ (100002b4 ) +10000292: bl 1000398c @ <__wrap_printf> +10000296: movw r1, #1337 @ 0x539 +1000029a: ldr r0, [pc, #28] @ (100002b8 ) +1000029c: bl 1000398c @ <__wrap_printf> +100002a0: b.n 1000028e @ +``` + +1. Press `G`, go to `0x10000292` (the `bl __wrap_printf` for `FAV_NUM`). +2. Set a **hardware execute** breakpoint there: `Debugger -> Add Hardware Breakpoint...`. (Do not use `F2` — that is a software breakpoint and will not work on read-only flash.) +3. Click **Resume** in Binary Ninja. The target is already running the loop, so the breakpoint fires on the next iteration. Binary Ninja stops with the program counter at `0x10000292` and `r1 = 0x2a`. +4. Open the **Registers** widget (bug icon -> **Registers**). +5. Find `r1`. Its value is `0x2a` (42), loaded by the `movs r1, #42` at `0x1000028e`. +6. **Set `r1` to `0x2b` (43).** From Binary Ninja's Python console (`Plugins -> Python Console`): + ```python + dbg.set_reg_value("r1", 0x2b) + ``` + `dbg.set_reg_value(name, value)` writes one register (returns `True` on success). You can also right-click `r1` in the **Registers** widget, press `E` (edit), type `2b`, and press Enter. The widget may not repaint the value, but the write reaches the target — you confirm it by the printed output in the next steps. +7. **Move the breakpoint past the call.** You want `printf` to run once and then stop, so move the breakpoint from `0x10000292` to the instruction *after* the call, `0x10000296` (the `movw r1, #1337` that begins the `OTHER_FAV_NUM` half of the loop): remove the breakpoint at `0x10000292` and set a hardware breakpoint at `0x10000296`. Two reasons not to just click **Step Over** here: a breakpoint left on the current PC re-traps the step, and Binary Ninja's **Step Over** steps *into* `__wrap_printf` on this raw `.bin` because the image carries no symbol for the call. Moving the breakpoint to the return site is deterministic. +8. Click **Resume** in Binary Ninja. The core executes `bl __wrap_printf` with `r1 = 0x2b`, so this iteration prints `FAV_NUM: 43`, then stops at `0x10000296`. +9. Look at your serial monitor — the `screen` session on the Pico's USB serial port — and at the **Target** tab in Binary Ninja: + + ``` + FAV_NUM: 43 + ``` + +You changed a running program's output without touching the binary. + +### Step 13b: HACK THE LCD STRING LIVE — change `"Reverse"` to `"Exploit"` (optional) + +The LCD text `"Reverse"` lives in flash (`.rodata`) at `0x10003ee8`, and flash is **read-only at runtime** — a debugger write there does not stick. So instead of overwriting the text in place, redirect the pointer: at the `lcd_puts` call for `"Reverse"`, `r0` holds the string address, so point `r0` at a replacement string you place in RAM. + +> **Do this one while stopped at `main`, before `Resume`.** The LCD is written once during init, at `0x1000027c`. If you have already resumed into the loop, restart the server parked at `0x10000234` (Step 9) and reconnect, or the breakpoint at `0x1000027c` never fires again. + +1. With Binary Ninja stopped at `main` (`0x10000234`), press `G` and go to `0x1000027c` (the `bl lcd_puts` that writes `"Reverse"`, loaded from the literal pool word at `0x100002ac`). +2. Set a **hardware execute** breakpoint at `0x1000027c` and click **Resume**. It fires once, with `r0 = 0x10003ee8`. +3. Put the replacement string into free RAM at `0x20080000` from Binary Ninja's **Python console** (`Plugins -> Python Console`) — no command port needed: + ```python + dbg.write_memory(0x20080000, b"Exploit\x00") + ``` + `dbg.write_memory(address, bytes)` is Binary Ninja's debugger memory-write API; it returns `True` on success. That writes `Exploit\0`. +4. Point `r0` at that string: + ```python + dbg.set_reg_value("r0", 0x20080000) + ``` + (Or right-click `r0` in the **Registers** widget, press `E`, type `0x20080000`, and press Enter.) +5. Move the breakpoint off the current PC (remove it at `0x1000027c`, set one at `0x10000280`, the `movs r0, #1` after the call) and click **Resume**. `lcd_puts` walks your RAM string and pushes `E x p l o i t` to the PCF8574 over I2C, so line 1 of the LCD now reads: + + ``` + Exploit + ``` + +Like the value hack, this is **one boot only**: the next `lcd_puts` for `"Engineering"` is unaffected, but a reset reloads `r0` from flash. The permanent version is the static patch in Step 18c. + +### Step 14: Why the hack reverts (and why we patch next) + +Press **Resume**. The loop branches back to `0x1000028e`, which reloads `movs r1, #42`, so the next line is `FAV_NUM: 42`. The live edit changed one iteration only. There is no memory variable to change; the value is baked into the instruction. To make `FAV_NUM: 43` permanent we must patch the instruction. That is the static pass. + +Press **Pause** to stop the output flood. + +### Step 15: Kill the debugger and OpenOCD + +The live hack is done. Do this **before** the static pass. + +1. In the **Debugger** sidebar, click the **X** (**Kill**) (or **`Debugger -> Kill`**) to disconnect Binary Ninja. +2. **Kill does not stop the OpenOCD process** — `debug-server.sh` started it separately, and it keeps running and holding the probe. Stop it from the Binary Ninja console: + + **macOS / Linux:** + + ```python + import subprocess + subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # stop the debug server, free the probe + ``` + + **Windows:** + + ```python + import subprocess + subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # stop the debug server, free the probe + ``` + +3. Confirm nothing is left: `pgrep -fl openocd` (macOS/Linux) prints nothing. + +From a terminal it is the same: `pkill -TERM -f openocd`, or `Get-Process openocd | Stop-Process` on Windows. + +--- + +## Part 4: Static — Resolve the Functions in Binary Ninja and Patch + +### Step 16: Resolve the functions in the Binary Ninja GUI + +We now name the functions in Binary Ninja using the ELF symbol map from Step 4. Binary Ninja loaded the raw `.bin` with **no symbols**, so every function shows as `sub_` — resolving means giving each one its real name and signature. + +Three keys do all the work: + +| Key | Binary Ninja action | Use it for | +| --- | ------------------- | ---------- | +| `G` | Go to address | Jump to a function's address | +| `Y` | **Change Type** | Set the function's signature. The dialog shows the full prototype, so this sets the name *and* the type in one step. | +| `N` | Rename | Rename only, when you just want the name and not the type | + +For each function below: `G` to its address, then **`Y` (Change Type)** and type the prototype from the table. + +#### How to resolve a function in Binary Ninja (`Y`) + +`Y` is the **Change Type** key, and it is what actually resolves the function — it turns `void sub_100037fc()` into `bool stdio_init_all(void)`. The Change Type dialog shows the full declaration (name and type), so typing the prototype sets both: + +1. `G` to the function's address. The cursor lands on the function. +2. Press **`Y`**. In the Change Type dialog, type the prototype from the table exactly — for example `bool stdio_init_all(void)` — and press Enter. + +The decompiler header then shows the real prototype, and calls to the function read cleanly instead of `sub_()`. `N` is only for renaming without touching the type; `Y` alone sets both the name and the type. + +If `Y` seems to do nothing, confirm the cursor is on the function, or right-click it and pick **Change Type...**. Binary Ninja parses what you type and silently keeps the old type if it does not parse, so glance at the header after each `Y`. + +#### Worked example: `main` + +1. Press `G`, type `0x10000234`, press Enter. The view jumps there; the cursor lands on `sub_10000234`. +2. Press **`Y`** (Change Type), type `int main(void)`, press Enter. That sets the name to `main` and the type to `int(void)`. + +> **Binary Ninja shows `int32_t` where Ghidra shows `int`.** After you set `int main(void)`, the decompiler header may read `int32_t main(void)`. That is the same type — on this platform `int` is 32 bits and Binary Ninja's parser normalises it to `int32_t`. Do not fight it; it is not an error. + +#### Worked example: `i2c_init` + +1. `G` -> `0x10003cdc`. +2. `Y` -> `uint i2c_init(i2c_inst_t* i2c, uint baudrate)`. + +> **`i2c_init` returns `uint`, not `void`.** The SDK's `i2c_init` returns the actual configured baud rate; the ELF says `unsigned int (i2c_inst_t *, uint)`. Keep the return type. + +#### Worked example: `lcd_i2c_init` + +1. `G` -> `0x100002bc`. +2. `Y` -> `void lcd_i2c_init(i2c_inst_t* i2c, uint8_t pcf_addr, int nibble_shift, uint8_t backlight_mask)`. + +This is our own `lcd_1602.c` code. In this build it is one big function: the compiler inlined `lcd_store_config`, `lcd_hd44780_reset`, and `lcd_hd44780_configure` into it. + +#### Worked example: `lcd_set_cursor` + +1. `G` -> `0x100006f4`. +2. `Y` -> `void lcd_set_cursor(int line, int position)`. + +#### Worked example: `lcd_puts` + +1. `G` -> `0x100007f0`. +2. `Y` -> `void lcd_puts(const char* s)`. + +#### Worked example: `gpio_set_function` + +1. `G` -> `0x100008f0`. +2. `Y` -> `void gpio_set_function(uint gpio, gpio_function_t fn)`. + +#### Worked example: `gpio_set_pulls` + +1. `G` -> `0x1000092c`. +2. `Y` -> `void gpio_set_pulls(uint gpio, bool up, bool down)`. + +This is what `gpio_pull_up(2)` in our source compiles to: the SDK's `static inline` `gpio_pull_up` disappears, and `main` calls `gpio_set_pulls(2, true, false)` directly at `0x10000258`. + +#### Worked example: `__wrap_printf` + +1. `G` -> `0x1000398c`. +2. `Y` -> `int __wrap_printf(const char *fmt, ...)`. Keep the `...` — `printf` is variadic. + +#### Worked example: `stdio_init_all` + +1. `G` -> `0x100037fc`. +2. `Y` -> `bool stdio_init_all(void)`. + +> **`printf` in our source is `__wrap_printf` in the binary.** The SDK links our `printf` calls to its `__wrap_printf` wrapper, which forwards to `__wrap_vprintf`. Rename it `printf` if you prefer the lesson's shorthand, but `__wrap_printf` is what the ELF says. + +The rest of the chain is the same two keystrokes per function (`G`, then `Y`). This is **our code plus the library functions it actually calls** — not the whole SDK. `main` calls `stdio_init_all`, `i2c_init`, `gpio_set_function`, `gpio_set_pulls`, `lcd_i2c_init`, `lcd_set_cursor`, `lcd_puts`, and `printf`, so we follow that chain down. + +The call chain for this project: + +``` +main +├── stdio_init_all ── stdio_uart_init ── gpio_set_function, uart_init, stdio_set_driver_enabled +│ └── uart_init ── clock_get_hz, busy_wait_us +├── i2c_init +├── gpio_set_function +├── gpio_set_pulls (gpio_pull_up inlined) +├── lcd_i2c_init ── i2c_write_blocking, sleep_us, sleep_ms +│ └── pcf_write_byte / pcf_pulse_enable / lcd_write4 / lcd_send / lcd_store_config / +│ lcd_hd44780_reset / lcd_hd44780_configure (all inlined; no calls) +├── lcd_set_cursor ── i2c_write_blocking, sleep_us +├── lcd_puts ── i2c_write_blocking, sleep_us +└── __wrap_printf ── __wrap_vprintf ── vfctprintf ── _vsnprintf + │ └── _ntoa_format / _out_rev / _out_char + ├── time_us_64 + └── stdio_out_chars_crlf +``` + +**Resolve every function in that chain:** + +| Address | Rename to (`N`) | Signature (`Y`) | +| ------- | --------------- | --------------- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | +| `0x100001e4` | `_init` | `void _init(void)` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | +| **`0x10000234`** | **`main`** | **`int main(void)`** | +| `0x100002bc` | `lcd_i2c_init` | `void lcd_i2c_init(i2c_inst_t*, uint8_t, int, uint8_t)` | +| `0x100006f4` | `lcd_set_cursor` | `void lcd_set_cursor(int, int)` | +| `0x100007f0` | `lcd_puts` | `void lcd_puts(const char*)` | +| `0x100008f0` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | +| `0x1000092c` | `gpio_set_pulls` | `void gpio_set_pulls(uint, bool, bool)` | +| `0x10001368` | `sleep_us` | `void sleep_us(uint64_t)` | +| `0x10001440` | `sleep_ms` | `void sleep_ms(uint32_t)` | +| `0x10001624` | `time_us_64` | `uint64_t time_us_64(void)` | +| `0x10001638` | `busy_wait_us` | `void busy_wait_us(uint64_t)` | +| `0x100016b8` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | +| `0x1000188c` | `clock_get_hz` | `unsigned long clock_get_hz(clock_handle_t)` | +| `0x100035a4` | `exit` | `void exit(int)` | +| `0x100035ac` | `runtime_init` | `void runtime_init(void)` | +| `0x100035d8` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | +| `0x100036e8` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | +| `0x100037d4` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | +| `0x100037fc` | `stdio_init_all` | `bool stdio_init_all(void)` | +| `0x100038c8` | `__wrap_vprintf` | `int __wrap_vprintf(const char*, va_list)` | +| `0x1000398c` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | +| `0x10003b48` | `stdio_uart_init` | `void stdio_uart_init(void)` | +| `0x10003cdc` | `i2c_init` | `uint i2c_init(i2c_inst_t*, uint)` | +| `0x10003d28` | `i2c_write_blocking` | `int i2c_write_blocking(i2c_inst_t*, uint8_t, const uint8_t*, size_t, bool)` | +| `0x10003e24` | `strlen` | `size_t strlen(const char*)` | + +**Resolve the SDK helpers the `printf` path reaches:** + +| Address | Rename to (`N`) | Signature (`Y`) | +| ------- | --------------- | --------------- | +| `0x10003548` | `vfctprintf` | `int vfctprintf(void (*)(char, void*), void*, const char*, va_list)` | +| `0x10002b64` | `_vsnprintf` | `int _vsnprintf(out_fct_type, char*, size_t, const char*, va_list)` | +| `0x10001f48` | `_ntoa_format` | `unsigned _ntoa_format(out_fct_type, char*, size_t, size_t, char*, size_t, bool, unsigned, unsigned, unsigned, unsigned)` | +| `0x10001eac` | `_out_rev` | `unsigned _out_rev(out_fct_type, char*, size_t, size_t, const char*, size_t, unsigned, unsigned)` | +| `0x1000211c` | `_out_char` | `void _out_char(char, void*, size_t, size_t)` | + +> **A `void` return type may not stick — here is the fix.** Binary Ninja treats `void` as low-confidence, and its analysis can override it with an inferred type — most often `int32_t` on this 32-bit target. It is most visible on `_reset_handler` (a hand-written assembly entry that never returns normally), but it can happen to **any** function whose return type Binary Ninja thinks it can infer. +> +> Setting the full signature with `Y` reproduces the unwanted `int32_t`, and `fn.return_type = ...` fails too. What works is the **return-value** setter: +> +> ```python +> from binaryninja import ReturnValue, Type +> fn = bv.get_function_at(0x1000015c) +> if fn is not None: +> fn.return_value = ReturnValue(Type.void()) +> ``` +> +> That holds `_reset_handler` at `void` even after reanalysis. If it still will not stick, leave it — it does not affect the rest of the lesson. + +> **`i2c1_inst` is data, not a function.** `arm-none-eabi-nm -n` lists `2000062c T i2c1_inst`. The `T` is a **global data** symbol: the linker parks the `i2c1_inst` struct at RAM address `0x2000062c`. Its first word is the hardware pointer `0x40098000`, the I2C1 register base. There is no function there; do not `Y` it with a prototype. + +> **Shortcut — resolves name *and* type for every function.** Instead of doing `N` + `Y` by hand, paste this into Binary Ninja's Python console (`Plugins -> Python Console`). It sets each function's name and signature programmatically: +> +> ```python +> from binaryninja import Symbol, SymbolType +> # The raw .bin has no headers, so these SDK types don't exist. set_user_type() +> # re-parses each signature as C, so an undefined name raises +> # "SyntaxError: unknown type name '...'". Define them first. +> sdk = bv.parse_types_from_string(""" +> typedef unsigned int uint; +> typedef char* va_list; +> typedef unsigned long clock_handle_t; +> typedef void (*out_fct_type)(char, void*, size_t, size_t); +> struct stdio_driver; +> typedef struct stdio_driver stdio_driver_t; +> struct uart_inst; +> typedef struct uart_inst uart_inst_t; +> struct i2c_inst; +> typedef struct i2c_inst i2c_inst_t; +> enum gpio_function { +> GPIO_FUNC_XIP = 0, GPIO_FUNC_SPI = 1, GPIO_FUNC_UART = 2, GPIO_FUNC_I2C = 3, +> GPIO_FUNC_PWM = 4, GPIO_FUNC_SIO = 5, GPIO_FUNC_PIO0 = 6, GPIO_FUNC_PIO1 = 7, +> GPIO_FUNC_GPCK = 8, GPIO_FUNC_USB = 9, GPIO_FUNC_NULL = 0x1f, +> }; +> typedef enum gpio_function gpio_function_t; +> """) +> for name, ty in sdk.types.items(): +> bv.define_user_type(name, ty) +> +> # address: (name, signature) +> funcs = { +> 0x1000015c: ("_reset_handler", "void _reset_handler(void)"), +> 0x10000186: ("platform_entry", "void platform_entry(void)"), +> 0x1000019a: ("data_cpy", "void data_cpy(void*, void*, void*)"), +> 0x100001e4: ("_init", "void _init(void)"), +> 0x10000210: ("frame_dummy", "void frame_dummy(void)"), +> 0x10000234: ("main", "int main(void)"), +> 0x100002bc: ("lcd_i2c_init", "void lcd_i2c_init(i2c_inst_t*, uint8_t, int, uint8_t)"), +> 0x100006f4: ("lcd_set_cursor", "void lcd_set_cursor(int, int)"), +> 0x100007f0: ("lcd_puts", "void lcd_puts(const char*)"), +> 0x100008f0: ("gpio_set_function", "void gpio_set_function(uint, gpio_function_t)"), +> 0x1000092c: ("gpio_set_pulls", "void gpio_set_pulls(uint, bool, bool)"), +> 0x10001368: ("sleep_us", "void sleep_us(uint64_t)"), +> 0x10001440: ("sleep_ms", "void sleep_ms(uint32_t)"), +> 0x10001624: ("time_us_64", "uint64_t time_us_64(void)"), +> 0x10001638: ("busy_wait_us", "void busy_wait_us(uint64_t)"), +> 0x100016b8: ("uart_init", "uint uart_init(uart_inst_t*, uint)"), +> 0x1000188c: ("clock_get_hz", "unsigned long clock_get_hz(clock_handle_t)"), +> 0x100035a4: ("exit", "void exit(int)"), +> 0x100035ac: ("runtime_init", "void runtime_init(void)"), +> 0x100035d8: ("stdio_out_chars_crlf", "void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)"), +> 0x100036e8: ("stdio_put_string", "int stdio_put_string(const char*, int, bool, bool)"), +> 0x100037d4: ("stdio_set_driver_enabled", "void stdio_set_driver_enabled(stdio_driver_t*, bool)"), +> 0x100037fc: ("stdio_init_all", "bool stdio_init_all(void)"), +> 0x100038c8: ("__wrap_vprintf", "int __wrap_vprintf(const char*, va_list)"), +> 0x1000398c: ("__wrap_printf", "int __wrap_printf(const char*, ...)"), +> 0x10003b48: ("stdio_uart_init", "void stdio_uart_init(void)"), +> 0x10003cdc: ("i2c_init", "uint i2c_init(i2c_inst_t*, uint)"), +> 0x10003d28: ("i2c_write_blocking", "int i2c_write_blocking(i2c_inst_t*, uint8_t, const uint8_t*, size_t, bool)"), +> 0x10003e24: ("strlen", "size_t strlen(const char*)"), +> 0x10003548: ("vfctprintf", "int vfctprintf(void (*)(char, void*), void*, const char*, va_list)"), +> 0x10002b64: ("_vsnprintf", "int _vsnprintf(out_fct_type, char*, size_t, const char*, va_list)"), +> 0x10001f48: ("_ntoa_format", "unsigned _ntoa_format(out_fct_type, char*, size_t, size_t, char*, size_t, bool, unsigned, unsigned, unsigned, unsigned)"), +> 0x10001eac: ("_out_rev", "unsigned _out_rev(out_fct_type, char*, size_t, size_t, const char*, size_t, unsigned, unsigned)"), +> 0x1000211c: ("_out_char", "void _out_char(char, void*, size_t, size_t)"), +> } +> for addr, (name, sig) in funcs.items(): +> bv.define_user_symbol(Symbol(SymbolType.FunctionSymbol, addr, name)) +> f = bv.get_function_at(addr) +> if f is not None: +> f.set_user_type(sig) +> ``` +> +> SDK type names (`i2c_inst_t`, `uart_inst_t`, `stdio_driver_t`, `gpio_function_t`, plus `uint`, `va_list`, `clock_handle_t`, and `out_fct_type`) are **not** in the raw `.bin`. `set_user_type` re-parses each signature as C, so an undefined name raises `SyntaxError: unknown type name '...'` and stops the loop — it is not harmless. The `sdk` block above defines them first (an opaque `struct`/`enum`/`typedef` is enough to parse). If you add a function that uses another SDK type, add a definition for it to that block too. + +### Step 17: Read `main` in the decompiler + +Open the **Decompiler** view on `main`. Once the functions above are typed, it reads roughly: + +```c +int32_t main(void) +{ + stdio_init_all(); + i2c_init(&i2c1_inst, 0x186a0); // i2c_init(i2c1, 100000) + gpio_set_function(2, GPIO_FUNC_I2C); + gpio_set_function(3, GPIO_FUNC_I2C); + gpio_set_pulls(2, true, false); // gpio_pull_up(2) + gpio_set_pulls(3, true, false); // gpio_pull_up(3) + lcd_i2c_init(&i2c1_inst, 0x27, 4, 8); // lcd_i2c_init(i2c1, 0x27, 4, 0x08) + lcd_set_cursor(0, 0); + lcd_puts("Reverse"); + lcd_set_cursor(1, 0); + lcd_puts("Engineering"); + while (true) { + __wrap_printf("FAV_NUM: %d\r\n", 0x2a); // FAV_NUM = 42 + __wrap_printf("OTHER_FAV_NUM: %d\r\n", 0x539); // OTHER_FAV_NUM = 1337 + } +} +``` + +The `0x2a` and `0x539` are the constants we will patch. Both are **immediates in the instruction stream** — there is no `.rodata` word to change, which is why the patch edits the instruction operand. Now make the hacks permanent. + +### Step 18: Patch 1 — change `FAV_NUM` from 42 to 43 + +Go to `0x1000028e`: + +```asm +1000028e: 2a 21 movs r1, #42 @ 0x2a +``` + +The halfword is `0x212a`, stored little-endian as `2a 21`. The immediate is the low byte, so the byte at the instruction's own address is `0x2a`. Change it to `0x2b` (43). + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x0017` | `0x1000028e` | `2a` | `2b` | `movs r1, #42` -> `#43`, prints `FAV_NUM: 43` | + +**Option A — Hex view:** + +1. Switch to the **Hex** view (`View -> Hex`). +2. Toggle the lock off so editing is enabled. +3. Go to `0x1000028e` and change the byte `2A` to `2B`. +4. Return to the linear view, right-click the function -> `Reanalyze`. + +**Option B — Python console:** + +```python +bv.write(0x1000028e, b"\x2b") +print(hex(bv.read(0x1000028e, 1)[0])) # -> 0x2b +``` + +After reanalysis the instruction reads `movs r1, #43`. + +### Step 18b: Patch 2 — change `OTHER_FAV_NUM` from 1337 to 1344 + +Go to `0x10000296`: + +```asm +10000296: 40 f2 39 51 movw r1, #1337 @ 0x539 +``` + +This is the 32-bit Thumb-2 encoding of `movw r1, #0x539`. The four bytes and their roles: + +``` ++-----------------------------------------------------------------+ +| movw r1, #0x539 -> bytes: 40 F2 39 51 | +| | +| Byte 0: 0x40 -+ | +| Byte 1: 0xF2 -+ First halfword (opcode + upper imm bits) | +| Byte 2: 0x39 ---- Lower 8 bits of immediate (imm8) <- CHANGE | +| Byte 3: 0x51 ---- Destination register (r1) + upper imm bits | +| | +| imm16 = 0x0539 = 1337 decimal | +| imm8 field = 0x39 (lower 8 bits of the value) | +| | ++-----------------------------------------------------------------+ +``` + +The imm8 byte is the **third** byte of the 4-byte instruction: the instruction starts at `0x10000296`, so the byte to change is `0x10000296 + 2 = 0x10000298`. Change `0x39` to `0x40`, which changes the value from `0x539` (1337) to `0x540` (1344). + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x0017` | `0x10000298` | `39` | `40` | `movw r1, #1337` -> `#1344`, prints `OTHER_FAV_NUM: 1344` | + +**Option A — Hex view:** + +1. Switch to the **Hex** view (`View -> Hex`). +2. Go to `0x10000298` and change the byte `39` to `40`. +3. Return to the linear view and reanalyze. + +**Option B — Python console:** + +```python +bv.write(0x10000298, b"\x40") +print(bv.read(0x10000296, 4).hex()) # -> 40f24051 +``` + +> **Do not patch `0x10000296` itself.** That is the instruction's first byte (the opcode), not the immediate. The immediate's low 8 bits are at `0x10000298`; patching the opcode corrupts the instruction. + +### Step 18c: Patch 3 — change the LCD text from `"Reverse"` to `"Exploit"` + +The string `"Reverse"` starts at `0x10003ee8`. Its eight bytes are `52 65 76 65 72 73 65 00` (`Reverse\0`). Change them to `45 78 70 6c 6f 69 74 00` (`Exploit\0`). **Both strings are exactly seven characters**, so the replacement fits without touching `"Engineering"` at `0x10003ef0`. + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x0017` | `0x10003ee8` | `52 65 76 65 72 73 65 00` | `45 78 70 6c 6f 69 74 00` | LCD line 1 prints `Exploit` instead of `Reverse` | + +**ASCII reference:** + +| Character | Hex | +| --------- | --- | +| E | `0x45` | +| x | `0x78` | +| p | `0x70` | +| l | `0x6c` | +| o | `0x6f` | +| i | `0x69` | +| t | `0x74` | + +**Option A — Hex view:** + +1. Switch to the **Hex** view (`View -> Hex`). +2. Go to `0x10003ee8` and change the eight bytes `52 65 76 65 72 73 65 00` to `45 78 70 6c 6f 69 74 00`. +3. Return to the linear view and reanalyze. + +**Option B — Python console:** + +```python +bv.write(0x10003ee8, b"Exploit\x00") +print(bv.read(0x10003ee8, 8)) # -> b'Exploit\x00' +``` + +Keep the replacement exactly eight bytes. If you use a shorter string you must pad it and keep the terminating `\0`, or `lcd_puts` will run into the `"Engineering"` string that follows at `0x10003ef0`. + +### Step 19: Export the patched `.bin` + +```python +import os +seg = next(s for s in bv.segments if s.data_length) # the loadable image segment +data = bv.read(seg.start, seg.data_length) # base + size come from the view itself +out = os.path.join(os.path.join(root, "0x0017_constants", "build"), "0x0017_constants-h.bin") +open(out, "wb").write(data) +print(len(data), out) # -> 17980 /.../build/0x0017_constants-h.bin +``` + +Where the two numbers come from — nothing is hardcoded: + +- **`seg.start`** is the image base Binary Ninja loaded the `.bin` at (`0x10000000`), the same value you pass to `uf2conv --base`. +- **`seg.data_length`** is the segment's size in the file (`0x463c` = 17980). Exactly one segment carries data (the image); every peripheral and synthetic segment has `data_length == 0`, so `next(...)` picks the image. +- Reading `seg.start` for `seg.data_length` bytes therefore grabs exactly the image. + +Two gotchas this avoids: + +- **No relative path.** Binary Ninja's Python console runs with a read-only working directory (inside the app bundle), so `open("0x0017_constants-h.bin", "wb")` fails with `OSError: [Errno 30] Read-only file system`. `root` (from `~/.embedded-hacking-repo`, Step 3) is the repo, so the file is written into the project's `build/` — no machine-specific path and no database needed. +- **Read the image, not the whole view.** `bv.read(bv.start, bv.length)` spans the entire mapped range, which is not the image. The segment's `data_length` is the image size. + +A different size means you exported a partial view. + +### Step 20: Convert to UF2 + +Run from the project directory: + +**macOS Apple Silicon / Linux x64:** + +```bash +python3 ../uf2conv.py 0x0017_constants-h.bin \ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +**Windows x64:** + +```cmd +python ..\uf2conv.py 0x0017_constants-h.bin ^ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +> **Or convert from the Binary Ninja console** — it is a normal Python interpreter, so you never have to leave the app. `chdir` to a writable directory first (the default one is read-only), then run the script: +> +> ```python +> import os, sys, runpy +> os.chdir(os.path.join(root, "0x0017_constants", "build")) # the project build dir (writable) +> sys.argv = ["uf2conv.py", "0x0017_constants-h.bin", +> "--base", "0x10000000", "--family", "0xe48bff59", "--output", "hacked.uf2"] +> runpy.run_path("../../uf2conv.py", run_name="__main__") # path to your uf2conv.py +> ``` +> +> This writes `hacked.uf2` next to the `.bin`, ready to drag onto the Pico. + +### Step 21: Flash and verify + +Hold **BOOTSEL**, plug in the Pico 2, and drag `hacked.uf2` onto the **`RP2350`** drive. Open the serial monitor: + +``` +FAV_NUM: 43 +OTHER_FAV_NUM: 1344 +FAV_NUM: 43 +OTHER_FAV_NUM: 1344 +... +``` + +and the **LCD line 1 reads `Exploit`** while line 2 still reads `Engineering`. + +**Both constants changed and the LCD string changed — with nine operand bytes patched and no source code.** (`0x2a -> 0x2b`, `0x39 -> 0x40`, and the eight-byte string.) + +> **Faster: flash over the Debug Probe (no BOOTSEL).** The repo's `flash.sh` writes the raw `.bin` straight into XIP flash over SWD (`program 0x10000000 verify reset exit`), so you never touch BOOTSEL or a UF2. Run it from a terminal (`./flash.sh `), or from the Binary Ninja console **without freezing it** — use `subprocess.Popen`, which returns immediately, and send OpenOCD's output to a log file. (`subprocess.run` blocks the console until the flash finishes; do not use it here.) +> +> ```python +> import os, subprocess +> root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +> bin_path = os.path.join(os.path.join(root, "0x0017_constants", "build"), "0x0017_constants-h.bin") +> log = os.path.join(os.path.join(root, "0x0017_constants", "build"), "flash.log") +> subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +> p = subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], +> stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +> print("flashing in the background; log:", log) +> ``` +> +> The `pkill` frees the probe first; on Windows use `subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"])`. +> +> The console is free the moment this returns. Check it with `print(p.poll())` (`None` = still running, `0` = done) or read `flash.log` — success ends with `** Verified OK **`. +> +> The same non-blocking form without the script: +> +> ```python +> import os, subprocess +> ocd = os.path.expanduser("~/.pico-sdk/openocd/0.12.0+dev") +> bin_path = os.path.join(os.path.join(root, "0x0017_constants", "build"), "0x0017_constants-h.bin") +> log = os.path.join(os.path.join(root, "0x0017_constants", "build"), "flash.log") +> subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +> p = subprocess.Popen([f"{ocd}/openocd", "-s", f"{ocd}/scripts", +> "-f", "interface/cmsis-dap.cfg", "-f", "target/rp2350.cfg", +> "-c", "adapter speed 5000", +> "-c", f"program {bin_path} 0x10000000 verify reset exit"], +> stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +> print("flashing in the background; log:", log) +> ``` +> +> **The Debug Probe is single-owner.** If Binary Ninja is still attached (the `debug-server.sh` OpenOCD is running), the flash cannot grab the probe. Detach in Binary Ninja and stop that OpenOCD first: +> +> ```bash +> # macOS / Linux +> pkill -TERM -f openocd +> ``` +> ```powershell +> # Windows +> Get-Process openocd -ErrorAction SilentlyContinue | Stop-Process +> ``` +> +> Success looks like `Programming Finished` -> `Verified OK` -> `Resetting Target`. On Windows use `flash.ps1` (`.\flash.ps1 -Bin `) the same way. + +--- + +## Cheatsheet + +### Binary Ninja GUI actions + +| Action | How | +| ------ | --- | +| Go to address | `G` | +| Rename function/symbol | `N` | +| Set type or signature | `Y` | +| Add comment | `;` | +| Open Hex view | `View -> Hex` | +| Enable hex editing | Toggle the lock in the status bar | +| Reanalyze after a patch | Right-click function -> `Reanalyze` | +| Edit a register live | `dbg.set_reg_value("r1", 0x2b)` in the Python console (or right-click the register, press `E`, type hex, Enter) | +| Write a RAM string live | `dbg.write_memory(0x20080000, b"Exploit\x00")` | +| Set a breakpoint | `Debugger -> Add Hardware Breakpoint...` (hardware execute). Do **not** use `F2` — software breakpoints cannot be written to read-only flash. | +| Move a breakpoint | Remove it and set it at the new address in the GUI (command-port fallback: `rbp ` then `bp 2 hw`) | +| Confirm what is armed | The **Breakpoints** widget lists it (command-port fallback: `mdw 0xE0002000 8`, each armed breakpoint shows as ``) | +| Apply the ELF symbol map | Paste the Python snippet from Step 16 into the Python Console | + +### OpenOCD server and reset + +The server runs with `gdb_breakpoint_override hard` so that flash-writes are never attempted. Breakpoints in this lab are set in the Binary Ninja GUI through the **GDB MI** adapter (Step 12). The command-port rows below are the fallback if you use the **GDB RSP** adapter instead. + +| Action | Command | +| ------ | ------- | +| Connect to the OpenOCD prompt (fallback) | `nc 127.0.0.1 4444` (or `telnet 127.0.0.1 4444`) | +| Reset and run (command port) | `reset run` | +| Check core state (command port) | `targets` | +| Set a breakpoint in the GUI | `Debugger -> Add Hardware Breakpoint...` (hardware execute; `F2` software breakpoints do not work on flash) | +| (fallback) Add a breakpoint without the GUI | `bp 2 hw` | +| Remove one breakpoint | `rbp ` — **address only, no length, no `hw`** | +| Remove every breakpoint | `rbp all` | +| Start the server parked at `main` | macOS/Linux: `BP_ADDR=0x10000234 ./debug-server.sh` — Windows: `$env:BP_ADDR="0x10000234"; .\debug-server.ps1` (one-shot) | +| Start the server parked in the loop | macOS/Linux: `BP_ADDR=0x10000292 ./debug-server.sh` — Windows: `$env:BP_ADDR="0x10000292"; .\debug-server.ps1` | +| Break on the loop in a running target | set a hardware breakpoint in the GUI at the loop address, then **Resume** — repeatable | +| Make Binary Ninja stepping work | `rp2350.dap.core0 configure -rtos none` (already in the scripts) | +| Step without re-trapping | move the breakpoint off the current PC first, then **Step Into**/**Step Over** | +| Reset without desyncing Binary Ninja | **Detach**, `reset run` on the port, reconnect — never `reset run` while attached | + +### Every address and byte we changed + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x0017` | `0x1000028e` | `2a` | `2b` | `movs r1, #42` -> `#43`, prints `FAV_NUM: 43` | +| `0x0017` | `0x10000298` | `39` | `40` | `movw r1, #1337` -> `#1344`, prints `OTHER_FAV_NUM: 1344` | +| `0x0017` | `0x10003ee8` | `52 65 76 65 72 73 65 00` | `45 78 70 6c 6f 69 74 00` | LCD line 1 prints `Exploit` instead of `Reverse` | + +### The I2C/LCD memory map + +| Item | Address | Notes | +| ---- | ------- | ----- | +| `i2c1_inst` | `0x2000062c` | RAM `.data`, fixed for the life of the program | +| `i2c1_inst.hw` | `0x40098000` | I2C1 hardware register base (first word of the struct) | +| `i2c1_inst.restart_on_next` | `0x20000630` | second word of the struct, `0` (false) | +| Literal-pool word 1 | `0x100002a4` | `0x000186a0` — I2C baud rate, 100000 | +| Literal-pool word 2 | `0x100002a8` | `0x2000062c` — `&i2c1_inst` | +| Literal-pool word 3 | `0x100002ac` | `0x10003ee8` — pointer to `"Reverse"` | +| Literal-pool word 4 | `0x100002b0` | `0x10003ef0` — pointer to `"Engineering"` | +| Literal-pool word 5 | `0x100002b4` | `0x10003efc` — pointer to `"FAV_NUM: %d\r\n"` | +| Literal-pool word 6 | `0x100002b8` | `0x10003f0c` — pointer to `"OTHER_FAV_NUM: %d\r\n"` | + +### Raw image facts + +| Item | Value | +| ---- | ----- | +| Build type | `Release` | +| Load base address | `0x10000000` | +| Project size | `17980` bytes (`0x463c`) | +| Fixed `main` anchor | `0x1000018c` (reset handler middle `blx`) | +| `main` | `0x10000234` | +| `printf` call / return, `FAV_NUM` | `0x10000292` / `0x10000296` | +| `printf` call, `OTHER_FAV_NUM` | `0x1000029c` | +| `i2c1_inst` RAM address | `0x2000062c` | +| I2C1 hardware registers | `0x40098000` | +| `FAV_NUM` format string | `0x10003efc` | +| `OTHER_FAV_NUM` format string | `0x10003f0c` | +| `"Reverse"` string | `0x10003ee8` | +| `"Engineering"` string | `0x10003ef0` | +| RP2350 UF2 family ID | `0xe48bff59` | + +--- + +## Troubleshooting + +### Binary Ninja hangs or crashes when you connect (macOS 27) + +Three different causes have been seen on this setup; check them in this order. + +- **A breakpoint set before connecting.** With the **GDB MI** adapter, if the binary view already has a breakpoint, the session hangs. Start parked with `BP_ADDR`, connect, then add breakpoints (see the next entry). +- **The wrong GDB executable.** Point **Full GDB Executable Path** at the **14.2.rel1** toolchain (Step 10). The 13.3.rel1 build did **not** connect in testing. +- **The LLDB adapter.** A crash report with `libdebuggercore.dylib -> std::terminate() -> abort()` and `liblldb` in the stack is the **LLDB** adapter, not GDB MI. Avoid LLDB on this setup. + +**Use GDB MI**, with the 14.2.rel1 path above. If it still fails, fall back to plain `arm-none-eabi-gdb` against the same server — the addresses and register values are identical to the GUI steps. + +If Binary Ninja hangs, force-quit it; the connect dialog has no working Cancel. The static steps (resolve, patch, export, flash) never touch the debugger and always work. + +### The GUI refuses to set a breakpoint (GDB RSP adapter only) + +If you are on the **GDB RSP** adapter, the GUI cannot set breakpoints on this target. That adapter is Binary Ninja's own minimal RSP client and sends a **1-byte** breakpoint (`Z0,,1`); the Cortex-M33 comparators need 2 bytes, so OpenOCD answers `only breakpoints of two bytes length supported`. It affects every address, both `Toggle Breakpoint` and `Add Hardware Breakpoint`, and the dialog's **Size** field is disabled. `gdb_breakpoint_override` makes no difference. + +**Fix: use the GDB MI adapter** (Step 10). It drives real GDB, which sends the correct length, so GUI breakpoints just work. If you must stay on GDB RSP, arm breakpoints from the command port after connecting (`bp 2 hw`) — but the lab uses GDB MI and does not need that. + +### GDB MI hangs when you connect (a breakpoint already existed) + +With the **GDB MI** adapter, if Binary Ninja already has a breakpoint set when you connect, the session **hangs**. This is a Binary Ninja bug. The working order is: + +1. Start the server parked, e.g. `BP_ADDR=0x10000234 ./debug-server.sh` (Windows: `$env:BP_ADDR="0x10000234"; .\debug-server.ps1`). +2. Connect with the **GDB MI** adapter. +3. Only *then* set hardware breakpoints in the UI. + +Never have a breakpoint in the binary view before the GDB MI connection. If it hangs, quit Binary Ninja, restart the server with `BP_ADDR`, and connect again before adding any breakpoints. + +### Step Into / Step Over does nothing (PC never moves) + +Two causes have been seen on this target. + +1. **A breakpoint on the current PC re-traps the step.** OpenOCD's step-over-breakpoint logic fails with `Duplicate Breakpoint address` and the PC stays put. Fix: move the breakpoint off the current PC (in the GUI), then step. +2. **The `hwthread` RTOS (GDB RSP adapter only).** With the **GDB RSP** adapter, OpenOCD can log `fake step thread 0` and reply without stepping, because the RP2350 config's `-rtos hwthread` makes the current thread id 1 while Binary Ninja sends thread id 0. Fix: `rp2350.dap.core0 configure -rtos none` (the launcher scripts already pass this). **GDB MI does not hit this.** + +To tell them apart, turn on OpenOCD logging (`log_output /tmp/ocd.log`, then `debug_level 3` on the command port) and look for `fake step` versus `Duplicate Breakpoint`. + +### `zsh: bad CPU type in executable: cmake` + +An Intel `x86_64` tool is on your `PATH` on Apple Silicon. Run Step 2: `export PATH="/opt/homebrew/bin:$PATH"`, then `hash -r`. Add it to `~/.zshrc` to make it permanent. + +### My addresses do not match this guide + +You probably built `Debug`. This lesson is a `Release` build. Re-run Step 3 with `-DCMAKE_BUILD_TYPE=Release`. A `Debug` build moves the SDK functions and keeps `init_i2c_and_lcd` / `write_lcd_greeting` as separate calls, so `main` is not at `0x10000234`. + +### A breakpoint never fires + +First, confirm you actually set one, and that it is a **hardware** breakpoint. With the **GDB MI** adapter, `Debugger -> Add Hardware Breakpoint...` (hardware execute) should land in the **Breakpoints** widget. If nothing lands, or the core keeps running, you probably used `F2` (`Toggle Breakpoint`) — that is a software breakpoint and cannot be written to read-only flash, so it never installs. Also check you are on **GDB MI**, not **GDB RSP** (the GDB RSP adapter cannot set breakpoints on this target at all). + +Then check the order and the state: + +- **Arm it only after Binary Ninja is connected.** OpenOCD flushes every breakpoint when a client attaches, so anything armed earlier is gone. This also applies to `BP_ADDR` on the startup command line. +- **Verify it is armed:** `mdw 0xE0002000 8`. You should see your address with the low bit set (`0x10000292` -> `0x10000293`). All zeros means nothing is armed — re-read this first, because it distinguishes "not armed" from "armed but never reached". +- **Is the core running?** `poll` on the command port should not report a halt. If it is stopped, click **Resume**. +- **Does the address get reached again?** `main` runs once per reset, so use `BP_ADDR` at startup (Step 9) rather than `reset run` while attached. Loop addresses such as `0x10000292` fire on the next pass with no reset — arm them and click **Resume** in Binary Ninja. +- **With GDB MI the stop is reported as `Breakpoint`** and appears in the **Breakpoints** widget, because GDB really did set it. + +### I edit `r1` (or another register) and it reverts + +`main` reloads the value at the top of every loop iteration — `movs r1, #42` at `0x1000028e` runs right before the `printf` at `0x10000292`. So `r1` is only `0x2b` for the instant between your edit and the next pass; then it is `0x2a` again. The edit sticks only if the core is **genuinely stopped** at the breakpoint and stays stopped. + +If it keeps reverting, the core is running, which almost always means the breakpoint is not installed — usually because it is a **software** breakpoint (`F2`) that cannot be written to read-only flash. Use `Debugger -> Add Hardware Breakpoint...` (hardware execute). + +> **The Registers widget is a snapshot, not a live view.** Binary Ninja reads the registers at each stop and shows that snapshot; it does not poll the target, and there is no "refresh registers" command. So a value changed outside Binary Ninja will not appear until the next stop. + +### The LCD string hack does nothing + +The LCD is written **once**, during `lcd_i2c_init` / `write_lcd_greeting`, before the loop starts. You must break at `0x1000027c` and redirect `r0` **before** that call runs. If you already let the target run into the loop, the LCD already shows the old strings. Restart the server parked at `main` (`BP_ADDR=0x10000234`) and reconnect, then set the breakpoint at `0x1000027c` while stopped. + +Also confirm you wrote a NUL-terminated string. `lcd_puts` walks bytes until `*s == 0`; without the trailing `\x00`, it keeps sending RAM garbage to the PCF8574. + +### The compiler did not keep `const` in `.rodata` + +It is not supposed to in this build. `const int OTHER_FAV_NUM = 1337` becomes `movw r1, #1337` because the program never takes its address (`&OTHER_FAV_NUM`) and the value fits in a 16-bit immediate. A `const` only stays in `.rodata` when something forces it there — an address-taken `const`, an array, a pointer, or `volatile`. When you reverse a real binary, never assume a `const` is a memory load; check the instruction. + +### The `movw` patch did not take + +You patched the wrong byte. `movw` is a 32-bit instruction and its low immediate byte (`imm8`) is the **third** byte. The instruction starts at `0x10000296`, so the byte to change is `0x10000298` (`0x39 -> 0x40`). Patching `0x10000296` (the opcode) corrupts the instruction and the core will fault. + +### The LCD shows garbage after patching + +The replacement string is the wrong length or is missing its NUL. `"Reverse"` and `"Exploit"` are both seven characters, and the original eight-byte block is `52 65 76 65 72 73 65 00`. Write exactly `45 78 70 6c 6f 69 74 00`. A shorter string without padding runs into `"Engineering"` at `0x10003ef0`; a longer one overwrites it. + +### The serial capture is garbage on macOS + +Reading `/dev/cu.usbmodem*` with a bare `read()` returns garbage. Set **raw termios at 115200** first: clear canonical/echo flags, set `CLOCAL|CREAD`, and `B115200` on input and output. `screen /dev/cu.usbmodem* 115200` does all of this for you; a script must call `tcsetattr` itself. Once set, the capture reads clean `FAV_NUM: 42` lines. + +### It worked for a second, then stopped (Binary Ninja's view desyncs) + +This is the most common failure, and it has one main cause: **driving the core from the OpenOCD command port while Binary Ninja is connected.** + +- If you send `reset run` from the port while attached, the core resets, runs, and halts at your breakpoint — but Binary Ninja never receives the stop event. Its sidebar keeps showing the *previous* location, so **Step** and **Resume** act on a stale PC and appear to do nothing. +- If the OpenOCD process dies (or you restart it) while attached, Binary Ninja keeps believing it is connected: the sidebar stays, but the menu shows **Pause** enabled and **Resume**/**Step** disabled because Binary Ninja last saw the target *running*. + +Recovery: **Detach, then reconnect.** If Detach does nothing (the connection is already dead), restart Binary Ninja — its menu still shows a session that no longer exists. + +Prevention: + +- Stop at `main` with `BP_ADDR` on a fresh server start, not with `reset run` while attached. +- For loop addresses, set the breakpoint in the GUI and click **Resume**. Let Binary Ninja be the thing that starts the core. +- If you must reset, **Detach first**, `reset run`, then reconnect. +- Never leave a breakpoint on the PC you are about to step or resume from. + +### The target "blows past" `main` and stops at `0x10003ab4` instead + +`0x10003ab4` is inside `stdio_uart_out_flush`: + +```asm +10003ab0: 4b02 ldr r3, [pc, #8] @ (10003abc ) +10003ab2: 681a ldr r2, [r3] +10003ab4: 6993 ldr r3, [r2, #24] @ the core sits here while the UART drains +10003ab6: 071b lsls r3, r3, #28 +10003ab8: d4fc bmi.n 10003ab4 +10003aba: 4770 bx lr +10003abc: 2000086c .word 0x2000086c +``` + +That is the UART transmit-FIFO drain loop inside `printf`, so the core is running `main`'s loop and simply spends nearly all its time there. The breakpoint at `main` did not fire because `main`'s entry runs exactly **once per reset**. If you arm the breakpoint after the reset, or set it while the target is already running and just resume, the core is already past `main` and will never re-execute it. Either arm the breakpoint **before** resetting, or break inside the loop at `0x10000292`, which fires every iteration. + +**`0x10003ab4` is not a function.** It is one instruction inside `stdio_uart_out_flush`, which starts at `0x10003ab0`. If Binary Ninja has created a function at `0x10003ab4` (for example because the debugger stopped at that PC), the decompiler shows garbage. Delete that bogus function (right-click it -> `Delete Function`, or put the cursor on it and press `U` to undefine) and reanalyze. The real function is `stdio_uart_out_flush` at `0x10003ab0`. + +### The console floods with `Failed to read memory at 0xf0000000` + +Core1 is exposed. The scripts must run with `USE_CORE=0`. Stop the server, confirm only `core0` is reported, restart, then restart Binary Ninja. + +### `Connect to Remote Process` is greyed out and Pause does nothing + +Binary Ninja is in a stale session, usually because the debug server restarted while attached. Quit and reopen Binary Ninja (or the `.bndb`) and connect again. + +### The decompiler still shows the old value after patching + +Right-click the function and choose `Reanalyze`. + +--- + +## Fallback: do the dynamic steps with GDB (macOS 27) + +If Binary Ninja's debugger crashes on attach on macOS 27 (see Troubleshooting), you can still do the live hack with the ARM GDB from the toolchain, against the same OpenOCD server. The addresses and register values are identical to the GUI steps. + +Start the debug server (Step 9), then in a new terminal: + +``` +arm-none-eabi-gdb +``` + +At the `(gdb)` prompt: + +``` +set architecture armv8-m.main +target extended-remote :3333 +hbreak *0x10000292 +continue +``` + +Do **not** run `monitor reset run` before `hbreak`. `0x10000292` is inside `main`'s loop, so the breakpoint fires on the next iteration with no reset. If you reset first, the core runs `main` and you will not catch it. + +GDB stops at the `printf` call. Confirm the value, change it, and let it run: + +``` +info registers pc r1 # pc = 0x10000292, r1 = 0x2a +set $r1 = 0x2b +stepi +continue +``` + +The serial monitor prints `FAV_NUM: 43` for the iteration you changed — the same temporary live hack as editing `r1` in the Binary Ninja Registers widget. When you are done, press `Ctrl-C`, then `detach` and `quit`. + +**If you specifically want to stop at `main` (`0x10000234`),** remember its entry runs only once per reset, so the breakpoint must be armed *before* the reset: + +``` +monitor reset halt +hbreak *0x10000234 +continue +``` + +If you instead set it while the target is running and just `continue`, you will "blow past" `main` and catch the core inside `printf` — in this build at `0x10003ab4`, the `stdio_uart_out_flush` UART-drain loop. + +`hbreak` sets a hardware breakpoint, which is required for read-only flash. It works from plain GDB because GDB sends the 2-byte length the Cortex-M33 comparators need. Binary Ninja's **GDB MI** adapter goes through the same GDB, so its GUI breakpoints work too; the old **GDB RSP** adapter was the one that sent a 1-byte length and could not set breakpoints here. + +## Glossary + +| Term | Definition | +| ---- | ---------- | +| **AAPCS** | ARM Architecture Procedure Call Standard — `r0`-`r3` for the first four arguments, `r0` for the return value | +| **`.bss`** | Section for uninitialized (or zero-initialized) static/global variables; zeroed by startup code | +| **`const`** | A source-level "read-only" qualifier; the compiler may still inline it as an immediate | +| **`.data`** | Section for initialized static/global variables; copied from flash to SRAM at boot | +| **`#define`** | Preprocessor text replacement performed before compilation; consumed by the compiler as a literal | +| **`.elf`** | Linked image with the symbol table; the ground truth for addresses and names | +| **`imm8`** | The low 8 bits of a `movw` immediate, stored in the third byte of the 32-bit instruction | +| **Immediate value** | A constant embedded directly in an instruction, not fetched from memory | +| **I2C** | Inter-Integrated Circuit — a two-wire (SDA/SCL) serial bus; open-drain, needs pull-ups | +| **Literal pool** | A block of 32-bit constants that Thumb-2 code reaches with PC-relative `ldr` | +| **`movs`** | 16-bit Thumb move that loads an 8-bit immediate (0-255) | +| **`movw`** | 32-bit Thumb-2 "move wide" that loads any 16-bit immediate (0-65535) | +| **Open-drain** | An output that can only pull a line LOW, not drive it HIGH; pull-ups restore HIGH | +| **PCF8574** | The I2C I/O expander on a typical 1602 LCD backpack; commonly at `0x27` | +| **`.rodata`** | Read-only section for constants and string literals; stays in flash | +| **SCL / SDA** | I2C Serial Clock and Serial Data lines | +| **Struct** | A user-defined type that groups related variables; the SDK uses one per I2C controller | +| **Thumb bit** | Bit 0 of a Cortex-M function pointer; selects Thumb instruction mode | +| **`typedef`** | Creates an alias for a type (for example `typedef struct i2c_inst i2c_inst_t`) | +| **UF2** | USB Flashing Format — the file format the Pico 2 bootloader accepts | +| **Vector table** | The first words of flash: initial stack pointer and exception vectors | + +--- + +**Remember:** the ELF tells you what every address is, and the `.bin` is what you actually patch. Prove the behavior dynamically, read `r1` at the `printf` call, resolve the names from the ELF (including the `lcd_1602.c` symbols), then patch the bytes — `0x2a -> 0x2b`, `0x39 -> 0x40`, and the eight-byte LCD string — and flash. diff --git a/WEEK07/WEEK07-BN.pdf b/WEEK07/WEEK07-BN.pdf new file mode 100644 index 0000000..c8a45ec Binary files /dev/null and b/WEEK07/WEEK07-BN.pdf differ diff --git a/WEEK07/WEEK07-SLIDES.pdf b/WEEK07/WEEK07-SLIDES.pdf new file mode 100644 index 0000000..7ffc94a Binary files /dev/null and b/WEEK07/WEEK07-SLIDES.pdf differ diff --git a/WEEK07/WEEK07.md b/WEEK07/WEEK07.md new file mode 100644 index 0000000..58c2f87 --- /dev/null +++ b/WEEK07/WEEK07.md @@ -0,0 +1,1097 @@ +# Week 7: Constants in Embedded Systems: Debugging and Hacking Constants w/ 1602 LCD I2C Basics + +*** +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: + +- Understand the difference between `#define` macros and `const` variables +- Know how constants are stored differently in memory (compile-time vs runtime) +- Understand the I2C (Inter-Integrated Circuit) communication protocol +- Configure I2C peripherals and communicate with LCD displays +- Understand C structs and how the Pico SDK uses them for hardware abstraction +- Use GDB to examine constants, structs, and string literals in memory +- Hack constant values and string literals using a hex editor +- Patch LCD display text without access to source code + +--- + +## Part 1: Understanding Constants in C + +### Two Types of Constants + +In C, there are two ways to create values that shouldn't change: + +| Type | Syntax | Where It Lives | When Resolved | +| ----------- | ----------------------- | ------------------ | ------------- | +| **#define** | `#define FAV_NUM 42` | Nowhere (replaced) | Compile time | +| **const** | `const int NUM = 1337;` | Flash (.rodata) | Runtime | + +### Preprocessor Macros (#define) + +A **preprocessor macro** is a text replacement that happens BEFORE your code is compiled: + +```c +#define FAV_NUM 42 + +printf("Value: %d", FAV_NUM); +// Becomes: printf("Value: %d", 42); +``` + +Think of it like a "find and replace" in a text editor. The compiler never sees `FAV_NUM` - it only sees `42`! + +``` ++-----------------------------------------------------------------+ +| Preprocessor Macro Flow | +| | +| Source Code Preprocessor Compiler | +| +----------+ +----------+ +----------+ | +| | #define | | Replace | | Compile | | +| | FAV_NUM | -----> | FAV_NUM | -----> | binary | | +| | 42 | | with 42 | | code | | +| +----------+ +----------+ +----------+ | +| | +| FAV_NUM doesn't exist in the final binary! | +| The value 42 is embedded directly in instructions. | +| | ++-----------------------------------------------------------------+ +``` + +### Const Variables + +A **const variable** is an actual variable stored in memory, but marked as read-only: + +```c +const int OTHER_FAV_NUM = 1337; +``` + +Unlike `#define`, this creates a real memory location in the `.rodata` (read-only data) section of flash: + +``` ++-----------------------------------------------------------------+ +| Const Variable in Memory | +| | +| Flash Memory (.rodata section) | +| +------------------------------------------------------------+ | +| | Address: 0x10001234 | | +| | Value: 0x00000539 (1337 in hex) | | +| | Name: OTHER_FAV_NUM (in debug symbols only) | | +| +------------------------------------------------------------+ | +| | +| The variable EXISTS in memory and can be read at runtime. | +| | ++-----------------------------------------------------------------+ +``` + +### Comparison: #define vs const + +| Feature | #define | const | +| -------------------- | ------------------------ | ---------------------------- | +| **Type checking** | None (just text) | Yes (compiler enforced) | +| **Memory usage** | None (inlined) | Uses flash space | +| **Debugger visible** | No | Yes (with symbols) | +| **Can take address** | No (`&FAV_NUM` fails) | Yes (`&OTHER_FAV_NUM` works) | +| **Scope** | Global (from definition) | Normal C scoping rules | + +--- + +## Part 2: Understanding I2C Communication + +### What is I2C? + +**I2C** (pronounced "I-squared-C" or "I-two-C") stands for **Inter-Integrated Circuit**. It's a way for chips to talk to each other using just TWO wires! + +``` ++-----------------------------------------------------------------+ +| I2C Bus - Two Wires, Many Devices | +| | +| 3.3V | +| | | +| + Pull-up + Pull-up | +| | | | +| SDA -+------------+--------------------------------------- | +| | | | +| SCL -+------------+--------------------------------------- | +| | | | | | +| +---+----+ +--+---+ +----+--+ +-----+---+ | +| | Pico | | LCD | |Sensor | | EEPROM | | +| |(Master)| | 0x27 | | 0x48 | | 0x50 | | +| +--------+ +------+ +-------+ +---------+ | +| | +| Each device has a unique address (0x27, 0x48, 0x50...) | +| | ++-----------------------------------------------------------------+ +``` + +### The Two I2C Wires + +| Wire | Name | Purpose | +| ------- | ------------ | ------------------------------------ | +| **SDA** | Serial Data | Carries the actual data bits | +| **SCL** | Serial Clock | Timing signal that synchronizes data | + +### Why Pull-Up Resistors? + +I2C uses **open-drain** signals, meaning devices can only pull the line LOW. They can't drive it HIGH! Pull-up resistors are needed to bring the lines back to HIGH when no device is pulling them down. + +The Pico 2 has internal pull-ups that we can enable with `gpio_pull_up()`. + +### I2C Addresses + +Every I2C device has a unique **7-bit address**. Common addresses: + +| Device Type | Typical Address | +| --------------------- | ---------------- | +| 1602 LCD with PCF8574 | `0x27` or `0x3F` | +| Temperature sensor | `0x48` | +| EEPROM | `0x50` | +| Real-time clock | `0x68` | + +### I2C Communication Flow + +``` ++-----------------------------------------------------------------+ +| I2C Transaction | +| | +| 1. Master sends START condition | +| 2. Master sends device address (7 bits) + R/W bit | +| 3. Addressed device sends ACK (acknowledge) | +| 4. Data is transferred (8 bits at a time) | +| 5. Receiver sends ACK after each byte | +| 6. Master sends STOP condition | +| | +| START --> Address --> ACK --> Data --> ACK --> STOP | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 3: Understanding C Structs + +### What is a Struct? + +A **struct** (short for "structure") is a way to group related variables together under one name. Think of it like a form with multiple fields: + +```c +// A struct definition - like a template +struct student { + char name[50]; + int age; + float gpa; +}; + +// Creating a variable of this struct type +struct student alice = {"Alice", 16, 3.8}; +``` + +### Why Use Structs? + +Instead of passing many separate variables: +```c +void print_student(char *name, int age, float gpa); // Messy! +``` + +You pass one struct: +```c +void print_student(struct student s); // Clean! +``` + +### The typedef Keyword + +Writing `struct student` everywhere is tedious. The `typedef` keyword creates an alias: + +```c +typedef struct student student_t; + +// Now you can write: +student_t alice; // Instead of: struct student alice; +``` + +### Forward Declaration + +Sometimes you need to tell the compiler "this struct exists" before defining it: + +```c +typedef struct i2c_inst i2c_inst_t; // Forward declaration + alias + +// Later, the full definition: +struct i2c_inst { + i2c_hw_t *hw; + bool restart_on_next; +}; +``` + +--- + +## Part 4: Understanding the Pico SDK's I2C Structs + +### The i2c_inst_t Struct + +The Pico SDK uses a struct to represent each I2C controller: + +```c +struct i2c_inst { + i2c_hw_t *hw; // Pointer to hardware registers + bool restart_on_next; // SDK internal flag +}; +``` + +**What each member means:** + +| Member | Type | Purpose | +| ----------------- | ------------ | ---------------------------------------- | +| `hw` | `i2c_hw_t *` | Pointer to the actual hardware registers | +| `restart_on_next` | `bool` | Tracks if next transfer needs a restart | + +### The Macro Chain + +When you write `I2C_PORT` in your code, here's what happens: + +``` ++-----------------------------------------------------------------+ +| Macro Expansion Chain | +| | +| In your code: #define I2C_PORT i2c1 | +| | | +| ↓ | +| In i2c.h: #define i2c1 (&i2c1_inst) | +| | | +| ↓ | +| In i2c.c: i2c_inst_t i2c1_inst = {i2c1_hw, false}; | +| | | +| ↓ | +| In i2c.h: #define i2c1_hw ((i2c_hw_t *)I2C1_BASE) | +| | | +| ↓ | +| In addressmap.h: #define I2C1_BASE 0x40098000 | +| | ++-----------------------------------------------------------------+ +``` + +So `I2C_PORT` eventually becomes a pointer to a struct that contains a pointer to hardware registers at address `0x40098000`! + +### The Hardware Register Pointer + +The `i2c_hw_t *hw` member points to the actual silicon: + +``` ++-----------------------------------------------------------------+ +| Memory Map | +| | +| Address 0x40098000: I2C1 Hardware Registers | +| +------------------------------------------------------------+ | +| | Offset 0x00: IC_CON (Control register) | | +| | Offset 0x04: IC_TAR (Target address register) | | +| | Offset 0x10: IC_DATA_CMD (Data command register) | | +| | ... | | +| +------------------------------------------------------------+ | +| | +| The i2c_hw_t struct maps directly to these registers! | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 5: The ARM Calling Convention (AAPCS) + +### How Arguments Are Passed + +On ARM Cortex-M, the **ARM Architecture Procedure Call Standard (AAPCS)** defines how functions receive arguments: + +| Register | Purpose | +| -------- | ---------------- | +| `r0` | First argument | +| `r1` | Second argument | +| `r2` | Third argument | +| `r3` | Fourth argument | +| Stack | Fifth+ arguments | +| `r0` | Return value | + +### Example: i2c_init(i2c1, 100000) + +```c +i2c_init(I2C_PORT, 100000); +``` + +In assembly: +```assembly +ldr r0, [address of i2c1_inst] ; r0 = pointer to struct (first arg) +ldr r1, =0x186A0 ; r1 = 100000 (second arg) +bl i2c_init ; Call the function +``` + +--- + +## Part 6: Setting Up Your Environment + +### Prerequisites + +Before we start, make sure you have: + +1. A Raspberry Pi Pico 2 board +2. A Raspberry Pi Pico Debug Probe +3. OpenOCD installed and configured +4. GDB (`arm-none-eabi-gdb`) installed +5. Python installed (for UF2 conversion) +6. A serial monitor (PuTTY, minicom, or screen) +7. A 1602 LCD display with I2C backpack (PCF8574) +8. A hex editor (HxD, ImHex, or similar) +9. The sample project: `0x0017_constants` + +### Hardware Setup + +Connect your LCD like this: + +| LCD Pin | Pico 2 Pin | +| ------- | ---------- | +| VCC | 3.3V or 5V | +| GND | GND | +| SDA | GPIO 2 | +| SCL | GPIO 3 | + +``` ++-----------------------------------------------------------------+ +| I2C LCD Wiring | +| | +| Pico 2 1602 LCD + I2C Backpack | +| +----------+ +----------------------+ | +| | | | | | +| | GPIO 2 |------- SDA ----->| SDA | | +| | (SDA) | | | | +| | | | +------------+ | | +| | GPIO 3 |------- SCL ----->| SCL| Reverse | | | +| | (SCL) | | |Engineering | | | +| | | | +------------+ | | +| | 3.3V |------- VCC ----->| VCC | | +| | | | | | +| | GND |------- GND ----->| GND | | +| | | | | | +| +----------+ +----------------------+ | +| | ++-----------------------------------------------------------------+ +``` + +### Project Structure + +``` +Embedded-Hacking/ ++-- 0x0017_constants/ +| +-- build/ +| | +-- 0x0017_constants.uf2 +| | +-- 0x0017_constants.bin +| +-- 0x0017_constants.c +| +-- lcd_1602.h ++-- uf2conv.py +``` + +--- + +## Part 7: Hands-On Tutorial - Constants and I2C LCD + +### Step 1: Review the Source Code + +Let's examine the constants code: + +**File: `0x0017_constants.c`** + +```c +#include +#include +#include "pico/stdlib.h" +#include "hardware/i2c.h" +#include "lcd_1602.h" + +#define FAV_NUM 42 +#define I2C_PORT i2c1 +#define I2C_SDA_PIN 2 +#define I2C_SCL_PIN 3 + +const int OTHER_FAV_NUM = 1337; + +int main(void) { + stdio_init_all(); + + i2c_init(I2C_PORT, 100000); + gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C); + gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C); + gpio_pull_up(I2C_SDA_PIN); + gpio_pull_up(I2C_SCL_PIN); + + lcd_i2c_init(I2C_PORT, 0x27, 4, 0x08); + lcd_set_cursor(0, 0); + lcd_puts("Reverse"); + lcd_set_cursor(1, 0); + lcd_puts("Engineering"); + + while (true) { + printf("FAV_NUM: %d\r\n", FAV_NUM); + printf("OTHER_FAV_NUM: %d\r\n", OTHER_FAV_NUM); + } +} +``` + +**What this code does:** + +1. **Lines 7-10:** Define preprocessor macros for constants and I2C configuration +2. **Line 12:** Define a `const` variable stored in flash +3. **Line 15:** Initialize UART for serial output +4. **Lines 17-21:** Initialize I2C1 at 100kHz, configure GPIO pins, enable pull-ups +5. **Lines 23-27:** Initialize LCD and display "Reverse" on line 0, "Engineering" on line 1 +6. **Lines 29-32:** Infinite loop printing both constant values to serial terminal + +### Step 2: Flash the Binary to Your Pico 2 + +1. Hold the BOOTSEL button on your Pico 2 +2. Plug in the USB cable (while holding BOOTSEL) +3. Release BOOTSEL - a drive called "RPI-RP2" appears +4. Drag and drop `0x0017_constants.uf2` onto the drive +5. The Pico will reboot and start running! + +### Step 3: Verify It's Working + +**Check the LCD:** + +- Line 1 should show: `Reverse` +- Line 2 should show: `Engineering` + +**Check the serial monitor (PuTTY/screen):** +``` +FAV_NUM: 42 +OTHER_FAV_NUM: 1337 +FAV_NUM: 42 +OTHER_FAV_NUM: 1337 +... +``` + +--- + +## Part 8: Debugging with GDB (Dynamic Analysis) + +> **REVIEW:** This setup is identical to previous weeks. If you need a refresher on OpenOCD and GDB connection, refer back to Week 3 Part 6. + +### Starting the Debug Session + +**Terminal 1 - Start OpenOCD:** + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +**Terminal 2 - Start GDB:** + +```cmd +arm-none-eabi-gdb build\0x0017_constants.elf +``` + +**Connect to target:** + +```gdb +(gdb) target extended-remote :3333 +(gdb) monitor reset halt +``` + +### Step 4: Examine Main Function + +Let's examine the main function. Disassemble from the entry point: + +``` +x/54i 0x10000234 +``` + +You should see output like: + +``` +(gdb) x/54i 0x10000234 + 0x10000234
: push {r3, lr} + 0x10000236 : bl 0x100037fc + 0x1000023a : ldr r1, [pc, #104] @ (0x100002a4 ) + 0x1000023c : ldr r0, [pc, #104] @ (0x100002a8 ) + 0x1000023e : bl 0x10003cdc + 0x10000242 : movs r1, #3 + 0x10000244 : movs r0, #2 + 0x10000246 : bl 0x100008f0 + 0x1000024a : movs r1, #3 + 0x1000024c : mov r0, r1 + 0x1000024e : bl 0x100008f0 + 0x10000252 : movs r2, #0 + 0x10000254 : movs r1, #1 + 0x10000256 : movs r0, #2 + 0x10000258 : bl 0x1000092c + 0x1000025c : movs r2, #0 + 0x1000025e : movs r1, #1 + 0x10000260 : movs r0, #3 + 0x10000262 : bl 0x1000092c + 0x10000266 : movs r3, #8 + 0x10000268 : movs r2, #4 + 0x1000026a : movs r1, #39 @ 0x27 + 0x1000026c : ldr r0, [pc, #56] @ (0x100002a8 ) + 0x1000026e : bl 0x100002bc + 0x10000272 : movs r1, #0 + 0x10000274 : mov r0, r1 + 0x10000276 : bl 0x100006f4 + 0x1000027a : ldr r0, [pc, #48] @ (0x100002ac ) + 0x1000027c : bl 0x100007f0 + 0x10000280 : movs r0, #1 + 0x10000282 : movs r1, #0 + 0x10000284 : bl 0x100006f4 + 0x10000288 : ldr r0, [pc, #36] @ (0x100002b0 ) + 0x1000028a : bl 0x100007f0 + 0x1000028e : movs r1, #42 @ 0x2a + 0x10000290 : ldr r0, [pc, #32] @ (0x100002b4 ) + 0x10000292 : bl 0x1000398c <__wrap_printf> + 0x10000296 : movw r1, #1337 @ 0x539 + 0x1000029a : ldr r0, [pc, #28] @ (0x100002b8 ) + 0x1000029c : bl 0x1000398c <__wrap_printf> + 0x100002a0 : b.n 0x1000028e + 0x100002a2 : nop + 0x100002a4 : strh r0, [r4, #52] @ 0x34 + 0x100002a6 : movs r1, r0 + 0x100002a8 : lsls r4, r5, #24 + 0x100002aa : movs r0, #0 + 0x100002ac : subs r6, #232 @ 0xe8 + 0x100002ae : asrs r0, r0, #32 + 0x100002b0 : subs r6, #240 @ 0xf0 + 0x100002b2 : asrs r0, r0, #32 + 0x100002b4 : subs r6, #252 @ 0xfc + 0x100002b6 : asrs r0, r0, #32 + 0x100002b8 : subs r7, #12 + 0x100002ba : asrs r0, r0, #32 +``` + +### Step 5: Set a Breakpoint at Main + +``` +b *0x10000234 +c +``` + +### Step 6: Find the #define Constant (FAV_NUM) + +Step through to the printf call and examine the registers: + +``` +x/20i 0x1000028e +``` + +Look for: +``` +... + 0x1000028e : movs r1, #42 @ 0x2a +... +``` + +The `#define` constant is embedded directly as an immediate value in the instruction! + +### Step 7: Find the const Variable (OTHER_FAV_NUM) + +Continue examining the loop body: + +```gdb +(gdb) x/5i 0x10000296 +``` + +Look for this instruction: + +``` +... + 0x10000296 : movw r1, #1337 @ 0x539 +... +``` + +**Surprise!** The `const` variable is ALSO embedded as an immediate value - not loaded from memory! The compiler saw that `OTHER_FAV_NUM` is never address-taken (`&OTHER_FAV_NUM` is never used), so it optimized the `const` the same way as `#define` - as a constant embedded directly in the instruction. + +The difference is the instruction encoding: + +- `FAV_NUM` (42): `movs r1, #0x2a` - 16-bit Thumb instruction (values 0-255) +- `OTHER_FAV_NUM` (1337): `movw r1, #0x539` - 32-bit Thumb-2 instruction (values 0-65535) + +> Tip: **Why `movw` instead of `movs`?** The value 1337 doesn't fit in 8 bits (max 255), so the compiler uses `movw` (Move Wide) which can encode any 16-bit immediate (0-65535) in a 32-bit instruction. + +### Step 8: Examine the Literal Pool + +The literal pool after the loop contains addresses and constants that are too large for regular instruction immediates. Let's examine it: + +```gdb +(gdb) x/6wx 0x100002a4 +0x100002a4 : 0x000186a0 0x2000062c 0x10003ee8 0x10003ef0 +0x100002b4 : 0x10003efc 0x10003f0c +``` + +These are the values that `ldr rN, [pc, #offset]` instructions load: + +| Literal Pool Addr | Value | Used By | +| ----------------- | -------------- | ------------------------------ | +| `0x100002a4` | `0x000186A0` | I2C baudrate (100000) | +| `0x100002a8` | `0x2000062C` | &i2c1_inst (I2C struct in RAM) | +| `0x100002ac` | `0x10003EE8` | "Reverse" string address | +| `0x100002b0` | `0x10003EF0` | "Engineering" string address | +| `0x100002b4` | `0x10003EFC` | `"FAV_NUM: %d\r\n"` format str | +| `0x100002b8` | `0x10003F0C` | `"OTHER_FAV_NUM: %d\r\n"` fmt | + +> Tip: **Why does the disassembly at `0x100002a4` show `strh r0, [r4, #52]` instead of data?** Same reason as Week 6 - GDB's `x/i` tries to decode raw data as instructions. Use `x/wx` to see the actual word values or we can also use `x/x`. + +```gdb +(gdb) x/x 0x100002a4 +0x100002a4 : 0x000186a0 +(gdb) x/x 0x100002a8 +0x100002a8 : 0x2000062c +(gdb) x/x 0x100002ac +0x100002ac : 0x10003ee8 +(gdb) x/x 0x100002b0 +0x100002b0 : 0x10003ef0 +(gdb) x/x 0x100002b4 +0x100002b4 : 0x10003efc +(gdb) x/x 0x100002b8 +0x100002b8 : 0x10003f0c +``` + +### Step 9: Examine the I2C Struct + +Find the i2c1_inst struct address loaded into r0 before i2c_init: + +``` +x/2wx 0x2000062c +``` + +You should see: +``` +0x2000062c : 0x40098000 0x00000000 +``` + +### Step 10: Examine the LCD String Literals + +Find the strings passed to lcd_puts: + +``` +x/s 0x10003ee8 +``` + +Output: +``` +0x10003ee8: "Reverse" +``` + +``` +x/s 0x10003ef0 +``` + +Output: +``` +0x10003ef0: "Engineering" +``` + +### Step 11: Step Through I2C Initialization + +Step through instructions and watch the I2C setup: + +```gdb +(gdb) b *0x1000023e +(gdb) c +(gdb) i r r0 r1 +r0 0x2000062c 536872492 +r1 0x186a0 100000 +``` + +--- + +## Part 9: Understanding the Assembly + +Now that we've explored the binary in GDB, let's make sense of the key patterns we found. + +### Step 12: Analyze #define vs const in Assembly + +From GDB, we discovered something interesting - **both constants ended up as instruction immediates!** + +**For FAV_NUM (42) - a `#define` macro:** +``` +0x1000028e <+90>: movs r1, #42 @ 0x2a +``` + +The value 42 is embedded directly in a 16-bit Thumb instruction. This is expected - `#define` is text replacement, so the compiler never sees `FAV_NUM`, only `42`. + +**For OTHER_FAV_NUM (1337) - a `const` variable:** +``` +0x10000296 <+98>: movw r1, #1337 @ 0x539 +``` + +The value 1337 is ALSO embedded directly in an instruction - but this time a 32-bit Thumb-2 `movw` because the value doesn't fit in 8 bits. + +**Why wasn't `const` stored in memory?** In theory, `const int OTHER_FAV_NUM = 1337` creates a variable in the `.rodata` section. But the compiler optimized it away because: + +1. We never take the address of `OTHER_FAV_NUM` (no `&OTHER_FAV_NUM`) +2. The value fits in a 16-bit `movw` immediate +3. Loading from an immediate is faster than loading from memory + +> Tip: **Key takeaway for reverse engineering:** Don't assume `const` variables will appear as memory loads. Modern compilers aggressively inline constant values. The C keyword `const` is a **source-level** concept - the compiler may or may not honor it in the final binary. + +### Step 13: Analyze the I2C Struct Layout + +In GDB, we examined the `i2c1_inst` struct at `0x2000062c`: + +```gdb +(gdb) x/2wx 0x2000062c +0x2000062c : 0x40098000 0x00000000 +``` + +This maps to the `i2c_inst_t` struct: + +``` ++-----------------------------------------------------------------+ +| i2c_inst_t at 0x2000062c | +| | +| +------------------------------------------------------------+ | +| | Offset Type Name Value | | +| | 0x00 i2c_hw_t * hw 0x40098000 | | +| | 0x04 bool restart_on_next 0x00 (false) | | +| +------------------------------------------------------------+ | +| | ++-----------------------------------------------------------------+ +``` + +The first member (`hw`) points to `0x40098000` - the I2C1 hardware register base. This is the end of the macro chain: `I2C_PORT` -> `i2c1` -> `&i2c1_inst` -> `hw` -> `0x40098000`. + +### Step 14: Locate the String Literals + +We found the LCD strings in flash memory: + +```gdb +(gdb) x/s 0x10003ee8 +0x10003ee8: "Reverse" + +(gdb) x/s 0x10003ef0 +0x10003ef0: "Engineering" +``` + +These are stored consecutively in the `.rodata` section. Note the addresses - we'll need them for patching. + +--- + +## Part 10: Hacking the Binary with a Hex Editor + +Now for the fun part - we'll patch the `.bin` file directly using a hex editor! + +> Tip: **Why a hex editor?** GDB **cannot write to flash memory** - the `0x10000000+` address range where program instructions and read-only data live. Trying `set *(char *)0x1000028e = 0x2b` in GDB gives `Writing to flash memory forbidden in this context`. To make **permanent** patches that survive a power cycle, we edit the `.bin` file directly with a hex editor and re-flash it. + +### Step 15: Open the Binary in a Hex Editor + +1. Open **HxD** (or your preferred hex editor: ImHex, 010 Editor, etc.) +2. Click **File** -> **Open** +3. Navigate to `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0017_constants\build\` +4. Open `0x0017_constants.bin` + +### Step 16: Calculate the File Offset + +The binary is loaded at base address `0x10000000`. To find the file offset of any address: + +``` +file_offset = address - 0x10000000 +``` + +For example: + +- Address `0x1000028e` -> file offset `0x28E` (654 in decimal) +- Address `0x10003ee8` -> file offset `0x3EE8` (16104 in decimal) + +### Step 17: Understand FAV_NUM Encoding (movs - 16-bit Thumb) + +From our GDB analysis, we know the instruction at `0x1000028e` is: + +``` +movs r1, #0x2a -> bytes: 2a 21 +``` + +In HxD, use **Ctrl+G** to navigate to file offset `28E` and verify you see the byte `2A` followed by `21`. + +> **How Thumb encoding works:** In `movs r1, #imm8`, the immediate value is the first byte, and the opcode `21` is the second byte. So the bytes `2a 21` encode `movs r1, #0x2a` (42). If you wanted to change this to 43, you'd change `2A` to `2B`. + +### Step 18: Understand OTHER_FAV_NUM Encoding (movw - 32-bit Thumb-2) + +From GDB, we found the `movw r1, #1337` instruction at `0x10000296`. Examine the exact bytes: + +```gdb +(gdb) x/4bx 0x10000296 +0x10000296 : 0x40 0xf2 0x39 0x51 +``` + +This is the 32-bit Thumb-2 encoding of `movw r1, #0x539` (1337). The bytes break down as: + +``` ++-----------------------------------------------------------------+ +| movw r1, #0x539 -> bytes: 40 F2 39 51 | +| | +| Byte 0: 0x40 -+ | +| Byte 1: 0xF2 -+ First halfword (opcode + upper imm bits) | +| Byte 2: 0x39 ---- Lower 8 bits of immediate (imm8) <- CHANGE | +| Byte 3: 0x51 ---- Destination register (r1) + upper imm bits | +| | +| imm16 = 0x0539 = 1337 decimal | +| imm8 field = 0x39 (lower 8 bits of the value) | +| | ++-----------------------------------------------------------------+ +``` + +The file offset is `0x10000296 - 0x10000000 = 0x296`. The imm8 byte is the 3rd byte of the instruction: `0x296 + 2 = 0x298`. + +To change `movw r1, #1337` to `movw r1, #1344`: + +1. In HxD, press **Ctrl+G** (Go to offset) +2. Enter offset: `298` (the third byte of the 4-byte instruction) +3. You should see the byte `39` at this position +4. Change `39` to `40` + +> **Why offset `0x298` and not `0x296`?** The lower 8 bits of the immediate (`imm8`) are in the **third byte** of the 4-byte `movw` instruction. The instruction starts at file offset `0x296`, so imm8 is at `0x296 + 2 = 0x298`. Changing `0x39` to `0x40` changes the value from `0x539` (1337) to `0x540` (1344). + +### Step 19: Hack - Change LCD Text from "Reverse" to "Exploit" + +**IMPORTANT:** The new string must be the **same length** as the original! "Reverse" and "Exploit" are both 7 characters - perfect! + +From our GDB analysis in Step 10, we found the string at `0x10003ee8`. File offset = `0x10003ee8 - 0x10000000 = 0x3EE8`. + +1. In HxD, press **Ctrl+G** and enter offset: `3EE8` +2. You should see the bytes for "Reverse": `52 65 76 65 72 73 65 00` +3. Change the bytes to spell "Exploit": `45 78 70 6c 6f 69 74 00` + +**ASCII Reference:** + +| Character | Hex | +| --------- | ------ | +| E | `0x45` | +| x | `0x78` | +| p | `0x70` | +| l | `0x6c` | +| o | `0x6f` | +| i | `0x69` | +| t | `0x74` | + +### Step 20: Save the Patched Binary + +1. Click **File** -> **Save As** +2. Save as `0x0017_constants-h.bin` in the build directory +3. Close the hex editor + +--- + +## Part 11: Converting and Flashing the Hacked Binary + +### Step 21: Convert to UF2 Format + +Open a terminal and navigate to your project directory: + +```cmd +cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0017_constants +``` + +Run the conversion command: + +```cmd +python ..\uf2conv.py build\0x0017_constants-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +### Step 22: Flash the Hacked Binary + +1. Hold BOOTSEL and plug in your Pico 2 +2. Drag and drop `hacked.uf2` onto the RPI-RP2 drive +3. Check your LCD and serial monitor + +### Step 23: Verify the Hack + +**Check the LCD:** + +- Line 1 should now show: `Exploit` (instead of "Reverse") +- Line 2 should still show: `Engineering` + +**Check the serial monitor:** +``` +FAV_NUM: 42 +OTHER_FAV_NUM: 1337 +FAV_NUM: 42 +OTHER_FAV_NUM: 1337 +... +``` + +The numbers are unchanged - we only patched the LCD string! + + **BOOM! We successfully changed the LCD text from "Reverse" to "Exploit" without access to the source code!** + +--- + +## Part 12: Summary and Review + +### What We Accomplished + +1. **Learned about constants** - `#define` macros vs `const` variables +2. **Understood I2C communication** - Two-wire protocol for peripheral communication +3. **Explored C structs** - How the Pico SDK abstracts hardware +4. **Mastered the macro chain** - From `I2C_PORT` to `0x40098000` +5. **Examined structs in GDB** - Inspected memory layout of `i2c_inst_t` +6. **Analyzed instruction encodings** - Both `movs` (8-bit) and `movw` (16-bit) immediates in the hex editor +7. **Patched a string literal** - Changed LCD display text from "Reverse" to "Exploit" + +### #define vs const Summary + +``` ++-----------------------------------------------------------------+ +| #define FAV_NUM 42 | +| ------------------- | +| - Text replacement at compile time | +| - No memory allocated | +| - Cannot take address (&FAV_NUM is invalid) | +| - In binary: value appears as immediate (movs r1, #0x2a) | +| - To hack: patch the instruction operand | ++-----------------------------------------------------------------+ +| const int OTHER_FAV_NUM = 1337 | +| ------------------------------ | +| - Theoretically in .rodata, but compiler optimized it away | +| - Value embedded as immediate: movw r1, #0x539 (32-bit instr) | +| - Optimization: compiler saw &OTHER_FAV_NUM is never used | +| - In binary: immediate in instruction, same as #define! | +| - To hack: patch instruction operand (imm8 byte at offset +2) | ++-----------------------------------------------------------------+ +``` + +### I2C Configuration Summary + +``` ++-----------------------------------------------------------------+ +| I2C Setup Steps | +| | +| 1. i2c_init(i2c1, 100000) - Initialize at 100kHz | +| 2. gpio_set_function(pin, I2C) - Assign pins to I2C | +| 3. gpio_pull_up(sda_pin) - Enable SDA pull-up | +| 4. gpio_pull_up(scl_pin) - Enable SCL pull-up | +| 5. lcd_i2c_init(...) - Initialize the device | +| | ++-----------------------------------------------------------------+ +``` + +### The Struct Chain + +``` ++-----------------------------------------------------------------+ +| I2C_PORT -> i2c1 -> &i2c1_inst -> i2c_inst_t | +| | | +| +-- hw -> i2c_hw_t * | +| | +-- 0x40098000 | +| | | +| +-- restart_on_next (bool) | +| | ++-----------------------------------------------------------------+ +``` + +### Key Memory Addresses + +| Address | Description | +| ------------ | ---------------------------------- | +| `0x10000234` | main() entry point | +| `0x1000028e` | FAV_NUM value in instruction | +| `0x10000296` | OTHER_FAV_NUM value in instruction | +| `0x10003ee8` | "Reverse" string literal (example) | +| `0x40098000` | I2C1 hardware registers base | +| `0x2000062C` | i2c1_inst struct in SRAM | + +--- + +--- + +## Key Takeaways + +1. **#define is text replacement** - It happens before compilation, no memory used. + +2. **const creates real variables** - Stored in .rodata, takes memory, has an address. + +3. **I2C uses two wires** - SDA for data, SCL for clock, pull-ups required. + +4. **Structs group related data** - The SDK uses them to abstract hardware. + +5. **Macros can chain** - `I2C_PORT` -> `i2c1` -> `&i2c1_inst` -> hardware pointer. + +6. **ARM passes args in registers** - r0-r3 for first four arguments. + +7. **GDB reveals struct layouts** - Examine memory to understand data organization. + +8. **String hacking requires same length** - Or you'll corrupt adjacent data! + +9. **Constants aren't constant** - With binary patching, everything can change! + +10. **Compiler optimization changes code** - `gpio_pull_up` becomes `gpio_set_pulls`. + +--- + +## Glossary + +| Term | Definition | +| ----------------------- | --------------------------------------------------- | +| **#define** | Preprocessor directive for text replacement | +| **AAPCS** | ARM Architecture Procedure Call Standard | +| **const** | Keyword marking a variable as read-only | +| **Forward Declaration** | Telling compiler a type exists before defining it | +| **I2C** | Inter-Integrated Circuit - two-wire serial protocol | +| **Immediate Value** | A constant embedded directly in an instruction | +| **Open-Drain** | Output that can only pull low, not drive high | +| **PCF8574** | Common I2C I/O expander chip used in LCD backpacks | +| **Preprocessor** | Tool that processes code before compilation | +| **Pull-Up Resistor** | Resistor that holds a line HIGH by default | +| **SCL** | Serial Clock - I2C timing signal | +| **SDA** | Serial Data - I2C data line | +| **Struct** | User-defined type grouping related variables | +| **typedef** | Creates an alias for a type | + +--- + +## Additional Resources + +### I2C Timing Reference + +| Speed Mode | Maximum Frequency | +| ---------- | ----------------- | +| Standard | 100 kHz | +| Fast | 400 kHz | +| Fast Plus | 1 MHz | + +### Common I2C Addresses + +| Device | Address | +| --------------------- | ------------- | +| PCF8574 LCD (default) | `0x27` | +| PCF8574A LCD | `0x3F` | +| DS3231 RTC | `0x68` | +| BMP280 Sensor | `0x76`/`0x77` | +| SSD1306 OLED | `0x3C`/`0x3D` | + +### Key ARM Instructions for Constants + +| Instruction | Description | +| -------------------- | ------------------------------------------- | +| `movs rN, #imm` | Load small immediate (0-255) directly | +| `ldr rN, [pc, #off]` | Load larger value from literal pool | +| `ldr rN, =value` | Pseudo-instruction for loading any constant | + +### RP2350 I2C Memory Map + +| Address | Description | +| ------------ | ----------------------- | +| `0x40090000` | I2C0 hardware registers | +| `0x40098000` | I2C1 hardware registers | + +--- + +**Remember:** When you see complex nested structures in a binary, take your time to understand the hierarchy. Use GDB to examine struct layouts in memory and trace pointer chains. And always remember - even "constants" can be hacked! + +Happy hacking! \ No newline at end of file diff --git a/WEEK07/WEEK07.pdf b/WEEK07/WEEK07.pdf new file mode 100644 index 0000000..57c5535 Binary files /dev/null and b/WEEK07/WEEK07.pdf differ diff --git a/WEEK07/slides/WEEK07-IMG00.svg b/WEEK07/slides/WEEK07-IMG00.svg new file mode 100644 index 0000000..427c6f7 --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 07 + + +Constants in Embedded Systems: +Debugging and Hacking Constants +w/ 1602 LCD I2C Basics + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK07/slides/WEEK07-IMG01.svg b/WEEK07/slides/WEEK07-IMG01.svg new file mode 100644 index 0000000..bbe54c4 --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG01.svg @@ -0,0 +1,63 @@ + + + + +#define vs const +Preprocessor Macros vs Constant Variables + + + +#define FAV_NUM 42 + +Preprocessor text replacement +Happens BEFORE compilation +No memory allocated +Cannot take address (&) + +In Binary: + +movs r1, #42 @ 0x2a + +16-bit Thumb instruction +Value embedded as immediate +Compiler sees only "42" + + + +const int OTHER_FAV_NUM=1337 + +Creates real variable +Theoretically in .rodata +Has an address (if needed) +Type-checked by compiler + +In Binary: + +movw r1, #1337 @ 0x539 + +32-bit Thumb-2 instruction +Also embedded as immediate! +Compiler optimized it away + + + +KEY INSIGHT: +Both ended up as instruction immediates! +The compiler saw &OTHER_FAV_NUM is never used, so it +optimized const the same way as #define -- no memory load needed. +Lesson: const is a source-level concept -- not guaranteed in binary + + + + \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG02.svg b/WEEK07/slides/WEEK07-IMG02.svg new file mode 100644 index 0000000..d798cd5 --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG02.svg @@ -0,0 +1,85 @@ + + + + +I2C Protocol +Two-Wire Serial Communication + + + +What is I2C? +Two-wire serial protocol +SDA += Serial Data +SCL += Serial Clock +Open-drain with pull-up resistors + + + +I2C Bus + +Pico + + +SDA +SCL + +LCD +GPIO 2 = SDA, GPIO 3 = SCL +Pull-ups hold lines HIGH + + + +Common I2C Addresses (7-bit) + +0x27 +LCD + +0x3F +LCD Alt + +0x48 +Sensor + +0x50 +EEPROM + + + +I2C Transaction Flow + +START +--> + +Address +--> + +ACK +--> + +Data +--> + +ACK +--> + +STOP + +Master sends START, then 7-bit address + R/W bit +Slave responds with ACK, then data bytes follow + + + + \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG03.svg b/WEEK07/slides/WEEK07-IMG03.svg new file mode 100644 index 0000000..d69b142 --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG03.svg @@ -0,0 +1,66 @@ + + + + +C Structs & typedef +Grouping Related Data in C + + + +Struct Definition + +typedef struct { +i2c_hw_t *hw; +bool restart_on_next; +} i2c_inst_t; +typedef creates an alias +so we can write: i2c_inst_t var; +instead of: struct { ... } var; + + + +Memory Layout +i2c_inst_t at 0x2000062C + + +Offset 0x00 +hw += 0x40098000 +i2c_hw_t* (4 bytes) + + +Offset 0x04 +restart_on_next += 0x00 (false) +bool (1 byte) + +Total struct size: 8 bytes +hw points to I2C1 registers + + + +Forward Declaration + +struct i2c_inst; +// tells compiler: this type exists, define later + + + +Why Structs Matter in RE +GDB shows raw memory -- you must recognize struct layouts +x/2wx 0x2000062c shows: 0x40098000 0x00000000 + + + + \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG04.svg b/WEEK07/slides/WEEK07-IMG04.svg new file mode 100644 index 0000000..927fd8c --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG04.svg @@ -0,0 +1,80 @@ + + + + +Pico SDK Macro Chain +From I2C_PORT to Hardware Registers + + + +Macro Expansion Chain + + + +I2C_PORT +#define I2C_PORT i2c1 + + +--> + + + +i2c1 +#define i2c1 (&i2c1_inst) + + +--> + + + +&i2c1_inst +Address of global struct + + +Struct Contents at 0x2000062C + +i2c_inst_t i2c1_inst = { +.hw = (i2c_hw_t *)0x40098000, +.restart_on_next = false +}; + + +Hardware Register Access + + +i2c1_inst.hw +--> +i2c1_hw +--> +(i2c_hw_t*)0x40098000 + +I2C1_BASE = 0x40098000 +I2C0_BASE = 0x40090000 +Direct memory-mapped I/O to RP2350 peripheral + + + +FULL CHAIN: +I2C_PORT +--> +i2c1 +--> +&i2c1_inst +--> +0x40098000 +Macro --> Macro --> Struct pointer --> HW register base + + + + \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG05.svg b/WEEK07/slides/WEEK07-IMG05.svg new file mode 100644 index 0000000..de2e153 --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG05.svg @@ -0,0 +1,53 @@ + + + + +Source Code +0x0017_constants.c + + + + + +//--- Defines and Constants --- +#define FAV_NUM 42 +#define I2C_PORT i2c1 +#define I2C_SDA_PIN 2 +#define I2C_SCL_PIN 3 +const int OTHER_FAV_NUM = 1337; + +//--- Main Loop --- +lcd_set_cursor(0, 0); +lcd_puts("Reverse"); +lcd_set_cursor(1, 0); +lcd_puts("Engineering"); + +//--- Serial Output Loop --- +printf("FAV_NUM: %d\r\n", FAV_NUM); +printf("OTHER_FAV_NUM: %d\r\n", OTHER_FAV_NUM); + + + +LCD Output +Line 0: "Reverse" +Line 1: "Engineering" + + +Serial Output +FAV_NUM: 42 +OTHER_FAV_NUM: 1337 + + + + \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG06.svg b/WEEK07/slides/WEEK07-IMG06.svg new file mode 100644 index 0000000..738b366 --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG06.svg @@ -0,0 +1,65 @@ + + + + +GDB Analysis +Disassembly of main() at 0x10000234 + + + +Key Instructions from x/54i 0x10000234 + + +push {r3, lr} +// save return addr +bl stdio_init_all +// init serial +ldr r1, [pc, #104] +// r1 = 100000 (baud) +ldr r0, [pc, #104] +// r0 = &i2c1_inst +bl i2c_init +// init I2C at 100kHz +movs r0, #2 +// GPIO 2 (SDA) +bl gpio_set_function +// set pin to I2C +movs r1, #39 +// 0x27 = LCD addr +bl lcd_i2c_init +// init LCD device + +b.n 0x1000028e +// infinite loop start +... +AAPCS: r0-r3 = first 4 args, r0 = return value + + + +Literal Pool at 0x100002A4 + + +0x000186A0 +I2C baudrate (100000) +0x2000062C +&i2c1_inst struct in RAM +0x10003EE8 +"Reverse" string in flash +0x10003EF0 +"Engineering" string in flash +0x10003EFC +"FAV_NUM: %d\r\n" +0x10003F0C +"OTHER_FAV_NUM: %d\r\n" + \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG07.svg b/WEEK07/slides/WEEK07-IMG07.svg new file mode 100644 index 0000000..15a1e54 --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG07.svg @@ -0,0 +1,71 @@ + + + + +Instruction Encoding +movs (16-bit Thumb) vs movw (32-bit Thumb-2) + + + +movs r1, #42 (FAV_NUM) +At address 0x1000028E + + +Bytes: 2A 21 + +2A = immediate value (42) +21 = opcode (movs r1) +16-bit Thumb instruction +Fits values 0-255 in 8 bits +File offset: 0x28E + + + +movw r1, #1337 (OTHER_FAV) +At address 0x10000296 + + +Bytes: 40 F2 39 51 + +40 F2 = opcode (first halfword) +39 = imm8 (lower 8 bits) +51 = dest reg + upper imm +32-bit Thumb-2 instruction +File offset: 0x296 + + + +movw Byte Layout (40 F2 39 51) + + +40 F2 +Opcode + upper imm + + +39 +imm8 (lower 8 bits) + + +51 +Dest reg (r1) + bits + +imm16 = 0x539 += 1337 decimal + + + +Why movw instead of movs? +1337 > 255 -- does not fit in 8-bit movs immediate +movw encodes 0-65535 in 32-bit instruction + \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG08.svg b/WEEK07/slides/WEEK07-IMG08.svg new file mode 100644 index 0000000..9f8ea92 --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG08.svg @@ -0,0 +1,68 @@ + + + + +I2C Struct in Memory +Examining i2c1_inst at 0x2000062C + + + +GDB Memory Dump + +x/2wx 0x2000062c: +0x40098000 +0x00000000 + + + +i2c_inst_t Struct Layout + + + +Offset 0x00 | 4 bytes +i2c_hw_t *hw += 0x40098000 + + +--> + + + +I2C1 HW Registers +Base: 0x40098000 (MMIO) + + + +Offset 0x04 | 1 byte +bool restart_on_next += false + +I2C0 base = 0x40090000 +I2C1 base = 0x40098000 + + + +String Literals in Flash (.rodata) + + +x/s 0x10003ee8: +"Reverse" + + +x/s 0x10003ef0: +"Engineering" + +Stored consecutively in .rodata (flash) +These addresses are targets for patching + \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG09.svg b/WEEK07/slides/WEEK07-IMG09.svg new file mode 100644 index 0000000..5934306 --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG09.svg @@ -0,0 +1,63 @@ + + + + +Hacking the Binary +Patching LCD Text: "Reverse" --> "Exploit" + + + +File Offset Formula +file_offset = address - 0x10000000 +Binary loaded at 0x10000000 + + + +Hack: Change LCD String +Address 0x10003EE8 --> File offset 0x3EE8 + +Original: + +52 65 76 65 72 73 65 00 +"Reverse" + +Patched: + +45 78 70 6C 6F 69 74 00 +"Exploit" + +Same length (7 chars) -- null terminator stays + + + +Flash the Hacked Binary + + +python uf2conv.py build\patched.bin + +1. +Save patched .bin file +2. +Convert to .uf2 format +3. +Hold BOOTSEL, plug in Pico +4. +Drag hacked.uf2 to drive + + + +LCD now shows: "Exploit" +instead of "Reverse" +No source code needed! + \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG10.svg b/WEEK07/slides/WEEK07-IMG10.svg new file mode 100644 index 0000000..3fda6dc --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG10.svg @@ -0,0 +1,88 @@ + + + + +I2C & Macro Exploitation +Constants, I2C, Structs, and Hacking + + + +Key Concepts + +#define +Text replacement, no memory +const +Variable in .rodata (maybe) +I2C +Two-wire: SDA + SCL +struct +Groups related data fields +typedef +Creates type alias +AAPCS +r0-r3 args, r0 return +movs +16-bit, imm 0-255 +movw +32-bit, imm 0-65535 +Literal Pool +Large consts after code + + + +Key Addresses + +0x10000234 +main() entry +0x1000028E +FAV_NUM (movs) +0x10000296 +OTHER_FAV_NUM (movw) +0x10003EE8 +"Reverse" string +0x10003EF0 +"Engineering" string +0x40098000 +I2C1 HW base +0x2000062C +i2c1_inst struct + +file_offset = addr - 0x10000000 +String patches must be same length + + + +Macro Chain +I2C_PORT +--> +i2c1 +--> +&i2c1_inst +--> +0x40098000 + + + +Binary Hack Result +LCD: "Reverse" --> +"Exploit" +Patched at 0x3EE8 +Compiler may optimize const same as #define + + + +TAKEAWAY: +const is a source-level concept. +In binary, everything can change! + \ No newline at end of file diff --git a/WEEK09/WEEK09-BN.md b/WEEK09/WEEK09-BN.md new file mode 100644 index 0000000..711b4d1 --- /dev/null +++ b/WEEK09/WEEK09-BN.md @@ -0,0 +1,1535 @@ +# Week 9-BN: Binary Ninja Personal — Read, Hack, and Patch Operator Immediates and a DHT11 Float Scale (Raw `.bin`) + +*** + +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +- Build the lesson project with `Release` and get both an `.elf` and a raw `.bin` +- Dump the **ELF symbol map** with `arm-none-eabi-nm` and use it as ground truth +- Load the raw `.bin` into Binary Ninja at `0x10000000` +- Read the six C operator results as **bare instruction immediates** (`movs r1, #50`, `movs r1, #5`, …) +- Read the DHT11 single-wire driver, including the inlined `gpio_*` and `time_us_32` helpers +- **Break at the arithmetic `printf` call** on live silicon and hack the printed value live +- Optionally **hack the format string live** by pointing `r0` at a RAM replacement +- **Resolve the functions in the Binary Ninja GUI** using the ELF symbol map, including the `dht11.c` symbols +- **Patch** the arithmetic immediate (`50 -> 99`) and the DHT11 scaling constant (`0.1f -> 5.0f`) +- **Rename** the printed label by patching the `"arithmetic_operator:"` string +- **Export** the patched image, convert it to UF2, and flash it + +--- + +## How This Guide Works + +The build produces two files for the project: + +| File | What it is | How we use it | +| ---- | ---------- | ------------- | +| `.elf` | The linked image with a full symbol table | Ground truth for every function address and name | +| `.bin` | The raw flash image, no headers, no symbols | The image we load into Binary Ninja and reverse | + +The `.bin` is built **from** the `.elf`, so the ELF tells you exactly what is at every address. We use the ELF symbol map to resolve functions in Binary Ninja, and we reverse-engineer the raw `.bin` the way a real extracted firmware image is reversed. + +> **Build `Release`, not `Debug`.** Every address in this guide matches the Week 9 lesson, and the Week 9 lesson is a `Release` build. `Release` optimizes the code the same way the original lesson was built: it **inlines** the `static` helpers `print_operator_results`, `compute_arithmetic_ops`, `compute_operators`, and `print_dht11_reading` straight into `main`, and it inlines the whole `dht11.c` static helper chain (`send_start_signal`, `wait_for_level`, `wait_response`, `read_bit`, `read_40_bits`, `validate_checksum`) into `dht11_read`. Every operator result is folded to a bare `movs r1, #imm` immediate. If you build `Debug`, the SDK function addresses move and the helpers stay separate calls, so nothing lines up. Always build `Release` for this lesson. + +The order is **dynamic first, static second**: + +1. Break on the live target and prove what the code does. +2. Hack it live in the debugger and watch the output change. +3. Resolve the functions in Binary Ninja using the ELF symbol map. +4. Patch the bytes, export, convert, and flash. + +| Project | Serial output | Also does | The hacks | +| ------- | ------------- | --------- | --------- | +| `0x001a_operators` | six operator lines, then `Humidity: …%, Temperature: …°C` | DHT11 single-wire sensor on GPIO 4 | `50 -> 99` (arithmetic immediate), `0.1f -> 5.0f` (DHT11 scale), and `"arithmetic_operator:" -> "hacked_operator:"` | + +> **Addresses come from your build.** Every address here is from the `Release` build produced in Step 3. Confirm against your own `.elf` with the command in Step 4. + +> **The surprise of this week is how small the operator code is.** The C source computes `x * y`, `x > y`, `x << 1`, and `x += 5` in a helper function, but `Release` folds every one of those into a compile-time constant and stores it as a one-byte immediate inside a `movs r1, #imm` instruction. There is no variable in memory to change — the answer is baked into the instruction stream. The DHT11 reading is different: it is a real measurement scaled by a `float` constant (`0.1f`) sitting in the literal pool, and *that* one is a data word you can patch. + +> **A note on the sensor reading.** The humidity/temperature numbers depend on the physical DHT11 attached to GPIO 4. This guide's ground truth is about the *code*, not the sensor: the scale constant, where `dht11_read` lives, and which register holds the reading. + +--- + +## Part 1: Build, Flash, and Get the Symbol Map + +### Step 1: Install the toolchain + +**Windows x64** + +- Install the **Raspberry Pi Pico** extension in VS Code. It installs the ARM GNU toolchain, CMake, Ninja, and the Pico SDK. +- Install **Binary Ninja Personal** and complete its license activation. +- Install **PuTTY** for the serial monitor. + +**macOS Apple Silicon** + +```bash +brew install cmake ninja +``` + +- Install **Binary Ninja Personal** and complete its license activation. +- Install the **Arm GNU Toolchain**, or let the VS Code Pico extension manage it. + +**Linux x64** + +```bash +sudo apt install cmake ninja-build gcc-arm-none-eabi libnewlib-arm-none-eabi git python3 openocd minicom +``` + +- Install **Binary Ninja Personal** and complete its license activation. + +### Step 2: Verify your tools are the right architecture (do not skip this) + +On **macOS Apple Silicon**, the most common failure is an Intel `x86_64` tool on your `PATH`: + +``` +zsh: bad CPU type in executable: cmake +``` + +You may have **two Homebrews**: the arm64 one at `/opt/homebrew` and the Intel one at `/usr/local`. If `/usr/local/bin` wins, every `brew` tool is x86_64. Check: + +```bash +file "$(which cmake)" +file "$(which ninja)" +file "$(which arm-none-eabi-gdb)" +file "$(which arm-none-eabi-nm)" +file "$(which openocd)" +file "$(which telnet)" +``` + +All must report `arm64`. If any is `x86_64`, put the Apple Silicon prefix first for the session and check again: + +```bash +export PATH="/opt/homebrew/bin:$PATH" +hash -r +file "$(which cmake)" +``` + +To make it permanent, add that `export` to `~/.zshrc`. Do not use Rosetta as a fix; OpenOCD and GDB are exactly the kind of programs where a translation layer produces failures that look like debugger bugs. + +**`telnet` is special — and optional.** The GDB MI workflow does not need it; it is only used by the command-port fallback. macOS no longer ships `telnet`, and the Homebrew build is often the Intel one, so `telnet 127.0.0.1 4444` fails with `bad CPU type in executable`. Call the Apple Silicon Homebrew explicitly: + +```bash +/opt/homebrew/bin/brew install telnet +``` + +If you would rather not install anything, macOS ships an arm64 `nc`, which can connect to the same OpenOCD port: + +```bash +nc 127.0.0.1 4444 +``` + +**Windows x64** and **Linux x64** do not have this problem. Skip to Step 3. + +### Step 3: Build the project with `Release` + +Run this inside `0x001a_operators/`: + +```bash +cmake -B build -G Ninja -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 -DCMAKE_BUILD_TYPE=Release +cmake --build build +``` + +**Point Binary Ninja at this repository (once).** Every console snippet below reads the repo root from `~/.embedded-hacking-repo`, so Binary Ninja never needs a database open and nothing is hardcoded. From the repo root, run once: + +**macOS / Linux:** + +```bash +pwd > ~/.embedded-hacking-repo +``` + +**Windows (PowerShell):** + +```powershell +(Get-Location).Path | Set-Content "$env:USERPROFILE\.embedded-hacking-repo" +``` + +**Then build from the Binary Ninja console**, so the whole build -> patch -> flash loop stays inside Binary Ninja. The console inherits a minimal `PATH` — on macOS just `/usr/bin:/bin:/usr/sbin:/sbin` — so it does not see Homebrew; add your package manager's `bin` first, then run plain `cmake`. + +**macOS Apple Silicon:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +os.environ["PATH"] = "/opt/homebrew/bin:" + os.environ["PATH"] # the console's PATH omits Homebrew +proj = os.path.join(root, "0x001a_operators") +subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) +subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +**Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +proj = os.path.join(root, "0x001a_operators") +subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) +subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +proj = os.path.join(root, "0x001a_operators") +subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) +subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +The build directory now contains the pair we need: + +- `0x001a_operators/build/0x001a_operators.elf` and `.bin` — the `.bin` is **17484** bytes (`0x444c`) + +If the ARM toolchain is not on your `PATH`, add `-DPICO_TOOLCHAIN_PATH=...`: + +| OS | Typical toolchain path | +| -- | ---------------------- | +| Windows x64 | `C:/Program Files/Arm GNU Toolchain arm-none-eabi/14.2 rel1/bin` | +| macOS Apple Silicon | `~/.pico-sdk/toolchain/14_2_Rel1/bin` | +| Linux x64 | `/usr` | + +> **This guide's toolchain lives at `~/.pico-sdk/toolchain/14_2_Rel1/bin`.** All of `arm-none-eabi-nm`, `arm-none-eabi-objdump`, and `arm-none-eabi-gdb` resolved in this document come from there. If your install is elsewhere, `which arm-none-eabi-nm` tells you where to point. + +### Step 4: Dump the ELF symbol map + +This is the ground truth for the whole lesson. Run `arm-none-eabi-nm` on the ELF and keep the output in a terminal or a text file: + +**macOS Apple Silicon / Linux x64:** + +```bash +arm-none-eabi-nm -n --defined-only build/0x001a_operators.elf | grep -E ' [Tt] ' +``` + +**Windows x64:** + +```powershell +arm-none-eabi-nm -n --defined-only build\0x001a_operators.elf | Select-String ' [Tt] ' +``` + +Each line is `address type name`. The `T`/`t` type is a function. Here are the functions this lesson uses. The signatures come from the ELF's DWARF debug info queried with `arm-none-eabi-gdb -batch -ex "ptype "`, so they are exact. + +**Our code and the startup chain:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | reset entry | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | calls `runtime_init`, `main`, `exit` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | copies `.data` from flash to SRAM | +| `0x100001e4` | `_init` | `void _init(void)` | runs `.init_array` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | C runtime boilerplate | +| `0x10000234` | `main` | `int main(void)` | the lesson function (all four static helpers inlined) | +| `0x100002d4` | `dht11_init` | `void dht11_init(uint8_t)` | the `dht11.c` init function | +| `0x100002f4` | `dht11_read` | `bool dht11_read(float*, float*)` | the `dht11.c` read function (all six static helpers inlined) | + +**The GPIO and timer functions `main` / `dht11.c` reach:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x10000440` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | SDK GPIO function select (UART pins) | +| `0x1000047c` | `gpio_set_pulls` | `void gpio_set_pulls(uint, bool, bool)` | SDK pull config (`gpio_pull_up` inlined) | +| `0x100004a4` | `gpio_init` | `void gpio_init(uint)` | SDK GPIO init (`dht11_init` calls it) | +| `0x10000f00` | `sleep_us` | `void sleep_us(uint64_t)` | SDK microsecond delay (`dht11_read` calls it) | +| `0x10000fd8` | `sleep_ms` | `void sleep_ms(uint32_t)` | SDK millisecond delay (`main`, `dht11_read`) | +| `0x100011bc` | `time_us_64` | `uint64_t time_us_64(void)` | SDK microsecond clock (`printf` path) | +| `0x100011d0` | `busy_wait_us` | `void busy_wait_us(uint64_t)` | UART timing loop | +| `0x10001250` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | SDK UART init | +| `0x10001424` | `clock_get_hz` | `unsigned long clock_get_hz(clock_handle_t)` | UART clock lookup | + +**The stdio/UART and printf chain `main` reaches:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x1000313c` | `exit` | `void exit(int)` | C runtime exit | +| `0x10003144` | `runtime_init` | `void runtime_init(void)` | SDK runtime init | +| `0x10003170` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | CRLF output driver | +| `0x10003280` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | buffered string output | +| `0x1000336c` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | enable the UART driver | +| `0x10003394` | `stdio_init_all` | `bool stdio_init_all(void)` | SDK serial init | +| `0x10003424` | `__wrap_puts` | `int __wrap_puts(const char*)` | the `puts` wrapper (`DHT11 read failed`) | +| `0x10003460` | `__wrap_vprintf` | `int __wrap_vprintf(const char*, va_list)` | printf core | +| `0x10003524` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | the `printf` wrapper (all seven calls) | +| `0x100036e0` | `stdio_uart_init` | `void stdio_uart_init(void)` | SDK UART stdio init | +| `0x10003820` | `strlen` | `size_t strlen(const char*)` | C runtime string length | + +**SDK helpers the `printf` path reaches:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x10001a44` | `_out_rev` | `unsigned _out_rev(out_fct_type, char*, size_t, size_t, const char*, size_t, unsigned, unsigned)` | reversed-digit output | +| `0x10001ae0` | `_ntoa_format` | `unsigned _ntoa_format(out_fct_type, char*, size_t, size_t, char*, size_t, bool, unsigned, unsigned, unsigned, unsigned)` | number formatter | +| `0x10001cb4` | `_out_char` | `void _out_char(char, void*, size_t, size_t)` | single-char sink | +| `0x100026fc` | `_vsnprintf` | `int _vsnprintf(out_fct_type, char*, size_t, const char*, va_list)` | the format dispatcher | +| `0x100030e0` | `vfctprintf` | `int vfctprintf(void (*)(char, void*), void*, const char*, va_list)` | printf format engine | + +Things in this project that have **no symbol of their own**, because the compiler inlined them: + +- `print_operator_results`, `compute_arithmetic_ops`, `compute_operators`, `print_dht11_reading` — the four `static` helpers in `0x001a_operators.c` are inlined into `main`, so their bodies appear directly inside `main`. +- `send_start_signal`, `wait_for_level`, `wait_response`, `read_bit`, `read_40_bits`, `validate_checksum` — the six `static` helpers in `dht11.c` are inlined into `dht11_read`, along with `gpio_set_dir`, `gpio_put`, `gpio_get`, and `time_us_32` from the SDK headers. You see `mcrr` (SIO writes), `ldr [.., #4]` (SIO reads), and `ldr [r0, #40]` (TIMER0 read) directly inside `dht11_read`. +- `gpio_pull_up` — `static inline` in the SDK, so `dht11_init` compiles to a direct tail-call to `gpio_set_pulls`. + +> **`dht_pin` is a `b` symbol, not a function.** `arm-none-eabi-nm -n` lists `20000b7c b dht_pin`. That lowercase `b` is a **local data** symbol: the `dht11.c` file-scope `static uint dht_pin;` is parked in RAM `.bss` at `0x20000b7c`. It holds the GPIO number (`4`) after `dht11_init(4)` runs. There is no function there; do not `Y` it with a prototype. + +### Step 5: Flash and confirm the output + +A `.bin` has no headers, so OpenOCD must be told the base address `0x10000000`. From the repository root: + +**macOS Apple Silicon / Linux x64:** + +```bash +./flash.sh 0x001a_operators/build/0x001a_operators.bin +``` + +**Windows x64 (PowerShell):** + +```powershell +.\flash.ps1 -Bin 0x001a_operators\build\0x001a_operators.bin +``` + +**Or flash from the Binary Ninja console** (the console reads the repo root from the marker file, so it works with no database open): + +**macOS Apple Silicon / Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x001a_operators", "build", "0x001a_operators.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("flashing in the background; log:", log) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x001a_operators", "build", "0x001a_operators.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # free the probe first +subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "flash.ps1"), "-Bin", bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT) +print("flashing in the background; log:", log) +``` + +Wait for `wrote 17484 bytes ...` and `** Verified OK **`. Open a serial monitor at **115200** baud: + +- **Windows x64:** PuTTY -> Connection type **Serial**, the Pico's COM port, speed `115200`. +- **macOS Apple Silicon:** `screen /dev/tty.usbmodem* 115200` (quit with `Ctrl-A` then `K`). +- **Linux x64:** `minicom -D /dev/ttyACM0 -b 115200`. + +``` +arithmetic_operator: 50 +increment_operator: 5 +relational_operator: 0 +logical_operator: 0 +bitwise_operator: 10 +assignment_operator: 10 +Humidity: 51.0%, Temperature: 23.8°C +... +``` + +Two of these values are *not* what a naive reading of the source predicts, and the build is correct: + +- **`increment_operator: 5`** — `compute_arithmetic_ops` takes `x` **by value**, so the `*incr = x++;` inside the helper increments the helper's private copy. `compute_operators`' own `x` is still `5` afterwards. +- **`bitwise_operator: 10`** and **`assignment_operator: 10`** — because `x` is still `5`, `x << 1` is `10` (not `12`), and `x += 5` is `10` (not `11`). The relational and logical operators compare `5 > 10`, so both are `0`. + +The humidity and temperature lines come from the physical DHT11 and vary with your sensor. + +--- + +## Part 2: Load the Raw `.bin` into Binary Ninja + +Start from a fresh Binary Ninja state. If you already have a `.bndb` for this lesson, **close it and start over**; a stale database keeps old names and patches. + +### Step 6: Bring the raw `.bin` into Binary Ninja + +A raw `.bin` has no headers, so Binary Ninja cannot know where it belongs or what architecture it is. You must supply both. If you just double-click the `.bin`, Binary Ninja may load it at address `0x0` with a guessed architecture, and every address in this lesson will be wrong. + +1. Choose `File -> Open with Options...` (do **not** use plain `File -> Open`). +2. Select `0x001a_operators/build/0x001a_operators.bin`. +3. In the loader options, set: + - **Architecture:** `thumb2` (the ARMv7-M / ARMv8-M Thumb-2 architecture, which covers the Cortex-M33) + - **Platform:** `thumb2` + - **Base Address:** `0x10000000` (the XIP flash base) +4. Click **Open**. + +Binary Ninja analyzes the image and opens the linear view. + +**Verify the load before going further.** Press `G`, type `0x10000000`, and read the first two words: + +``` +0x10000000 0x20082000 initial stack pointer +0x10000004 0x1000015d reset vector (bit 0 = Thumb) +``` + +If you instead see data at `0x00000000`, or a vector word without bit 0 set, close the tab and repeat with `Open with Options`. The Cortex-M33 only executes Thumb-2, so `thumb2` is the only correct architecture. + +> **Console equivalent:** +> ```python +> import os +> root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +> load(os.path.join(root, "0x001a_operators", "build", "0x001a_operators.bin"), +> options={"loader.imageBase": 0x10000000, "loader.platform": "thumb2"}) +> ``` + +### Step 7: Save it as a Binary Ninja database (`.bndb`) + +Binary Ninja never writes back into the `.bin`. Your names, comments, types, and patches live in a separate **`.bndb`** database. Save one now, before you make any changes: + +1. Choose `File -> Save As...`. +2. Save it next to the image as `0x001a_operators.bndb`. +3. From now on, save with `File -> Save` (`Cmd+S` on macOS, `Ctrl+S` on Windows/Linux) whenever you rename or patch. + +The two files have different roles: + +| File | Role | +| ---- | ---- | +| `0x001a_operators.bin` | the raw firmware image; Binary Ninja never modifies it | +| `0x001a_operators.bndb` | your analysis database: names, types, comments, and patches | + +When you come back later, **open the `.bndb`**, not the `.bin`; that restores all your work. If a database gets messy, delete the `.bndb` and re-import the `.bin` from Step 6 — the firmware is never at risk. You export the patched image out of this view later, in Step 19. + +### Step 8: The views you will use + +- **Linear view:** the disassembly listing. You navigate, read, and patch here. +- **Graph view:** the control-flow graph of the current function. +- **Decompiler (HLIL):** the pseudo-C decompilation. +- **Hex view:** raw bytes, used for patching. +- **Function list:** the sidebar list of every detected function. + +Navigation: `G` go to address, `N` rename, `Y` set type or signature, `;` add a comment. Breakpoints are set from the GUI through the GDB MI adapter — see Step 12. + +> **macOS function keys:** the top-row `F` keys are usually mapped to system functions. Every step here uses menu paths that work without them. + +> **Optional — load the RP2350 SVD for peripheral names.** If you want Binary Ninja to label peripheral registers (TIMER0, SIO, UART0, …) instead of bare addresses, load the RP2350 SVD. It ships with Week 4, so it lives at **`WEEK04/rp2350.svd`** inside the repository — remember it is in **Week 4**, not this week. This lesson does not need it; the ELF symbol map already names every function. + +--- + +## Part 3: Dynamic — Break at the `printf` Call and Hack Live + +### Step 9: Start OpenOCD as a live debug server + +Make sure no other OpenOCD is running; a forgotten server holds port `3333`. + +**macOS / Linux:** + +```bash +ps aux | grep -i openocd +``` + +**Windows (PowerShell):** + +```powershell +Get-Process | Where-Object { $_.ProcessName -like '*openocd*' } +``` + +Stop any leftover server gracefully: + +**macOS / Linux:** + +```bash +pkill -TERM -f openocd +``` + +**Windows (PowerShell):** + +```powershell +Get-Process openocd -ErrorAction SilentlyContinue | Stop-Process +``` + +Start the server **parked at `main`**: + +**macOS Apple Silicon / Linux x64:** + +```bash +BP_ADDR=0x10000234 ./debug-server.sh +``` + +**Windows x64 (PowerShell):** + +```powershell +$env:BP_ADDR="0x10000234"; .\debug-server.ps1 +``` + +**Or start it from the Binary Ninja console**, freeing the probe first and launching the server in the background so the console returns immediately: + +**macOS Apple Silicon / Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # stop any running server first +log = os.path.join(root, "openocd.log") +p = subprocess.Popen([os.path.join(root, "debug-server.sh")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("OpenOCD started (pid", p.pid, "); log:", log) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # stop any running server first +log = os.path.join(root, "openocd.log") +p = subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "debug-server.ps1")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT) +print("OpenOCD started (pid", p.pid, "); log:", log) +``` + +`Popen` returns in a few milliseconds; the server keeps running in the background. Check `openocd.log` for `Listening on port 3333`, then connect in Step 10. + +Wait for: + +``` +Info : [rp2350.dap.core0] Examination succeed +Startup breakpoint at 0x10000234 (2-byte hardware execute, one-shot). +Info : starting gdb server for rp2350.dap.core0 on 3333 +Info : Listening on port 3333 for gdb connections +``` + +> **`BP_ADDR` parks the core at `main` before any client connects.** The script arms a 2-byte hardware breakpoint and then does the startup `reset run`, so the core runs from the vector table and stops at your address with no debugger attached yet. When Binary Ninja connects a moment later, the first thing it reads is already the truth: `Stopped at 0x10000234`. This is the whole reason the lab works cleanly — you never have to drive a reset from outside the GUI. +> +> Use the address you actually want to stop at: +> +> | What you want to stop at | Address | Command | +> | --- | --- | --- | +> | `main` (once per reset) | `0x10000234` | `BP_ADDR=0x10000234 ./debug-server.sh` | +> | The **loop** — the arithmetic `printf` call, hit every iteration | `0x10000272` | `BP_ADDR=0x10000272 ./debug-server.sh` | +> +> ```bash +> BP_ADDR=0x10000234 ./debug-server.sh # park at main +> BP_ADDR=0x10000272 ./debug-server.sh # park in the loop instead +> ``` +> +> ```powershell +> $env:BP_ADDR="0x10000234"; .\debug-server.ps1 # park at main +> $env:BP_ADDR="0x10000272"; .\debug-server.ps1 # park in the loop +> ``` +> +> **This startup stop is single-use.** OpenOCD flushes breakpoints when a client connects, so this one is gone once Binary Ninja attaches — fine for `main`, which only runs once per reset. Every breakpoint after that is set from the Binary Ninja GUI (Step 12) and is repeatable. To stop at `main` again, restart the server with `BP_ADDR` and reconnect. + +> **Exactly one core.** The line must say `core0` and must **not** mention `core1`. Core1 is never started by this firmware; exposing it makes Binary Ninja read core1's reset-state registers, which are not real addresses, and OpenOCD floods the log with `Failed to read memory at 0xf0000000`. The scripts already use `USE_CORE=0`; do not change it. + +> **Windows driver note:** the Debug Probe must use the **WinUSB** driver. If OpenOCD reports `unable to open CMSIS-DAP device`, install it with [Zadig](https://zadig.akeo.ie/) (select `Debug Probe (CMSIS-DAP)` -> WinUSB). + +### Step 10: Connect Binary Ninja to the GDB server + +1. Make sure the image is open and analyzed (Part 2) and the server from Step 9 is running (parked at `main`). +2. Choose `Debugger -> Connect to Remote Process`. +3. In the **adapter** dropdown, select **GDB MI**. +4. In the **connect** settings group, set **IP Address** to `127.0.0.1` and **Port** to `3333`. +5. Set **Full GDB Executable Path** to the `arm-none-eabi-gdb` from the **Arm GNU Toolchain 14.2.rel1**. It ships for all three hosts, and the Raspberry Pi Pico VS Code extension installs that same 14.2.rel1 toolchain (including `arm-none-eabi-gdb`) on all of them: + + | OS | `arm-none-eabi-gdb` path | + | -- | ------------------------ | + | macOS Apple Silicon | `/Applications/ArmGNUToolchain/14.2.rel1/arm-none-eabi/bin/arm-none-eabi-gdb` (or the Pico extension's `~/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb`) | + | Windows x64 | `%USERPROFILE%\.pico-sdk\toolchain\14_2_Rel1\bin\arm-none-eabi-gdb.exe` (Pico extension), or `C:\Program Files (x86)\Arm GNU Toolchain arm-none-eabi\14.2 rel1\bin\arm-none-eabi-gdb.exe` | + | Linux x64 | `~/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb` (Pico extension), or the `bin/` directory of the extracted `arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi` tarball | +6. Click **Accept**. + +> **Use the GDB MI adapter.** It launches a real `arm-none-eabi-gdb --interpreter=mi2` and lets Binary Ninja drive it, so breakpoints and stepping go through real GDB — which sends the correct 2-byte breakpoint length and handles step-over itself. Verified working end to end: connect, GUI breakpoints (**Add Hardware Breakpoint...**, hardware execute), **Step Into** / **Step Over**, and register edits. Stops are reported as `Breakpoint` (not `SingleStep`). +> +> **Do NOT have any breakpoints set in Binary Ninja before you connect.** With the GDB MI adapter, attaching while Binary Ninja already has a breakpoint **hangs the session**. Start the server parked with `BP_ADDR` (Step 9), connect, and only add hardware breakpoints *after* the connection is up. This is a Binary Ninja bug; it is the single most common GDB MI failure. +> +> **The GDB executable path matters.** Use the **14.2.rel1** build on every OS (Windows, macOS, Linux). The 13.3.rel1 build did **not** connect in testing. +> +> **This step is temporary.** Vector35 plans to ship a GDB binary with the GDB MI adapter ([Vector35/debugger#929](https://github.com/Vector35/debugger/issues/929), milestone *Langara*). Once that lands, Binary Ninja provides GDB itself and you will not need to set **Full GDB Executable Path** at all. +> +> **Do not pick Corellium.** Binary Ninja's adapter dropdown also lists **Corellium**, which is for Corellium's virtual devices and expects an API token, not a local OpenOCD server. It is not the adapter for this lab. The dropdown is a combo box, so an accidental arrow-key press can land on it — always read the label back and confirm it says **GDB MI** before clicking **Accept**. + +> **The adapter and port are not saved in the `.bndb`.** Every time you relaunch Binary Ninja you must re-select **GDB MI**, re-enter port `3333`, and re-set the GDB path. + +> **Watch for an off-screen error dialog.** When a connection fails, Binary Ninja pops a `Binary Ninja critical alert` window that can be positioned mostly outside the main window, which makes it look like nothing happened. If the connect seems to do nothing, check your other display. + +The target keeps running. Open the **Registers** tab (bug icon) and confirm you see live values. `pc` inside `0x10003xxx` and `sp` just below `0x20082000` are healthy. + +> **If `pc` is `0x00000088`, `0x000000ec`, or `sp` is `0xf0000000`, the session is bad.** Restart the server, then restart Binary Ninja (a server restart while attached leaves Binary Ninja in a stale session), and connect again. + +### Step 11: Find `main` without relying on its address + +`main` can move between programs, so we do not guess it. We follow the one fixed path to it. Press `G` and go to `0x10000000`: + +``` +0x10000000 0x20082000 initial stack pointer (top of SRAM) +0x10000004 0x1000015d reset vector +``` + +Bit 0 of a vector is the Thumb bit, so `0x1000015d` means "start at `0x1000015c`". That is `_reset_handler`. Follow the reset path to `0x10000186`, `platform_entry`: + +```asm +10000186: ldr r1, [pc, #80] @ (100001d8 ) +10000188: blx r1 +1000018a: ldr r1, [pc, #80] @ (100001dc ) +1000018c: blx r1 +1000018e: ldr r1, [pc, #80] @ (100001e0 ) +10000190: blx r1 +10000192: bkpt 0x0000 +10000194: b.n 10000192 +``` + +**The middle `blx` at `0x1000018c` is the call to `main`.** `platform_entry` is byte-identical in every project, so `0x1000018c` catches `main` no matter where the linker placed it. The literal pool at `0x100001dc` holds `main | 1`; clearing bit 0 gives `0x10000234`. + +### Step 12: Set a hardware breakpoint in the GUI + +With the **GDB MI** adapter, Binary Ninja sets breakpoints through real GDB, which sends the correct 2-byte length, so you set them **in the UI**. There is no command port here. + +> **Why older drafts used the command port.** Binary Ninja's **GDB RSP** adapter is its own minimal RSP client and sends a **1-byte** breakpoint (`Z0,,1`); the Cortex-M33 FPB comparators need 2 bytes, so OpenOCD rejected it with `only breakpoints of two bytes length supported`. The old workaround was to arm breakpoints by hand over telnet. **The GDB MI adapter does not have this problem** — it drives real `arm-none-eabi-gdb`, which sends the right length. So everything below is done in the GUI. The command port still exists as a fallback (see the end of this step), but you do not need it. + +#### Where you can stop + +| You want to stop at | Address | How | Repeatable? | +| --- | --- | --- | --- | +| **`main`** | `0x10000234` | The server starts parked there with `BP_ADDR=0x10000234` (Step 9), so Binary Ninja is already stopped at `main` when it connects. | No — `main` runs once per reset. | +| **The arithmetic `printf` call** | `0x10000272` | Set a hardware breakpoint in the GUI, then click **Resume**. | Yes — fires on every iteration. | +| **The second `printf` call (increment)** | `0x1000027a` | Same. | Yes. | +| **The `dht11_read` call** | `0x100002a2` | Same. | Yes. | + +#### Set the loop breakpoint in the GUI + +1. Press `G`, type the loop address (`0x10000272`), and press Enter. +2. Set a **hardware execution** breakpoint at that address, either way: + - `Debugger -> Add Hardware Breakpoint...` — a **hardware execute** (`HE`) breakpoint. **Use this one.** + - click the line and press `F2` (`Debugger -> Toggle Breakpoint`) — a **software** breakpoint. It will **not** work here: the code is in read-only flash, so GDB cannot install it and the core just keeps running. +3. Click **Resume**. The core is already running the loop, so the breakpoint fires on the next iteration. Binary Ninja stops with the PC at the loop address and reports it as a **Breakpoint** — verified: `Stopped (Breakpoint) at 0x10000272`. + +> **No breakpoints before you connect.** With GDB MI, a breakpoint set before the connection hangs the session (Step 10). Start parked with `BP_ADDR`, connect, *then* add breakpoints. + +#### Stepping + +With the target halted at the breakpoint, **Step Into** (`F7`) and **Step Over** (`F8`) run through real GDB and move the PC. Verified: `0x10000272 -> 0x10003524 -> 0x10003526 -> ...`. + +> **Step Over on the raw `.bin` steps *into* calls.** The raw image has no symbol for `__wrap_printf`, so **Step Over** at the `printf` call behaves like **Step Into**. When the lab needs to execute the call and then stop, it moves the breakpoint to the return site and clicks **Resume** instead (Step 13 shows this). + +> **Never use Binary Ninja's Restart button.** On RP2350 it resets and halts inside the boot ROM (`pc=0x88`, `sp=0xf0000000`). To reset cleanly, restart the server with `BP_ADDR` and reconnect. + +> **If you ever need the command port.** It is still there — `nc 127.0.0.1 4444`, and `bp 2 hw` still arms a breakpoint, `rbp ` / `rbp all` still remove them. It is the fallback if you switch back to the **GDB RSP** adapter, whose 1-byte breakpoints the GUI cannot set. With GDB MI you do not need it for this lab. + +### Step 13: HACK IT LIVE — change the printed `arithmetic_operator` + +`main` loads the constant `0x32` (50) into `r1` and calls `printf` on every iteration. We break on that call and change it live: + +```asm +1000026e: movs r1, #50 @ 0x32 +10000270: ldr r0, [pc, #68] @ (100002b8 ) +10000272: bl 10003524 <__wrap_printf> +10000276: movs r1, #5 +10000278: ldr r0, [pc, #64] @ (100002bc ) +1000027a: bl 10003524 <__wrap_printf> +``` + +1. Press `G`, go to `0x10000272` (the `bl __wrap_printf` for `arithmetic_operator`). +2. Set a **hardware execute** breakpoint there: `Debugger -> Add Hardware Breakpoint...`. (Do not use `F2` — that is a software breakpoint and will not work on read-only flash.) +3. Click **Resume** in Binary Ninja. The target is already running the loop, so the breakpoint fires on the next iteration. Binary Ninja stops with the program counter at `0x10000272`, `r0 = 0x100038e8`, and `r1 = 0x32`. +4. Open the **Registers** widget (bug icon -> **Registers**). +5. Find `r1`. Its value is `0x32` (50), loaded by the `movs r1, #50` at `0x1000026e`. +6. **Set `r1` to `0x63` (99).** From Binary Ninja's Python console (`Plugins -> Python Console`): + ```python + dbg.set_reg_value("r1", 0x63) + ``` + `dbg.set_reg_value(name, value)` writes one register (returns `True` on success). You can also right-click `r1` in the **Registers** widget, press `E` (edit), type `63`, and press Enter. The widget may not repaint the value, but the write reaches the target — you confirm it by the printed output in the next steps. +7. **Move the breakpoint past the call.** You want `printf` to run once and then stop, so move the breakpoint from `0x10000272` to the instruction *after* the call, `0x10000276` (the `movs r1, #5` that begins the increment half of the loop): remove the breakpoint at `0x10000272` and set a hardware breakpoint at `0x10000276`. Two reasons not to just click **Step Over** here: a breakpoint left on the current PC re-traps the step, and Binary Ninja's **Step Over** steps *into* `__wrap_printf` on this raw `.bin` because the image carries no symbol for the call. Moving the breakpoint to the return site is deterministic. +8. Click **Resume** in Binary Ninja. The core executes `bl __wrap_printf` with `r1 = 0x63`, so this iteration prints `arithmetic_operator: 99`, then stops at `0x10000276`. +9. Look at your serial monitor — the `screen` session on the Pico's USB serial port — and at the **Target** tab in Binary Ninja: + + ``` + arithmetic_operator: 99 + ``` + +You changed a running program's output without touching the binary. + +### Step 13b: HACK THE STRING LIVE — change `arithmetic_operator:` to `hacked_operator:` (optional) + +The text `"arithmetic_operator: %d\r\n"` lives in flash (`.rodata`) at `0x100038e8`, and flash is **read-only at runtime** — a debugger write there does not stick. So instead of overwriting the text in place, redirect the pointer: at the `printf` call, `r0` holds the string address, so point `r0` at a replacement string you place in RAM. + +1. Arm the breakpoint at the `printf` call and hit it, exactly as in Step 13 steps 1-3. At the stop, `r0 = 0x100038e8` and `r1 = 0x32`. +2. Put the replacement string into free RAM at `0x20080000` from Binary Ninja's **Python console** (`Plugins -> Python Console`) — no command port needed: + ```python + dbg.write_memory(0x20080000, b"hacked_operator: %d\r\n\x00") + ``` + `dbg.write_memory(address, bytes)` is Binary Ninja's debugger memory-write API; it returns `True` on success. That writes `hacked_operator: %d\r\n\0`. +3. Point `r0` at that string: + ```python + dbg.set_reg_value("r0", 0x20080000) + ``` + (Or right-click `r0` in the **Registers** widget, press `E`, type `0x20080000`, and press Enter.) +4. If you want the value hack too, set `r1` to `0x63` as in Step 13. Then move the breakpoint past the call in the GUI (remove it at `0x10000272`, set one at `0x10000276`) and click **Resume**. The core runs `printf` with `r0` pointing at your RAM string and `r1 = 0x63`, so this iteration prints: + ``` + hacked_operator: 99 + ``` + then stops at `0x10000276`. + +Like the value hack, this is **one iteration only**: the loop reloads `r0` (and `r1`) from flash on every pass, so the next line is `arithmetic_operator: 50` again. The permanent version is the static patch in Step 18c. + +### Step 14: Why the hack reverts (and why we patch next) + +Press **Resume**. The loop branches back to `0x1000026e`, which reloads `movs r1, #50`, so the next line is `arithmetic_operator: 50`. The live edit changed one iteration only. There is no variable in memory to change; the value is baked into the instruction. To make `arithmetic_operator: 99` permanent we must patch the instruction. That is the static pass. + +Press **Pause** to stop the output flood. + +### Step 15: Kill the debugger and OpenOCD + +The live hack is done. Do this **before** the static pass. + +1. In the **Debugger** sidebar, click the **X** (**Kill**) (or **`Debugger -> Kill`**) to disconnect Binary Ninja. +2. **Kill does not stop the OpenOCD process** — `debug-server.sh` started it separately, and it keeps running and holding the probe. Stop it from the Binary Ninja console: + + **macOS / Linux:** + + ```python + import subprocess + subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # stop the debug server, free the probe + ``` + + **Windows:** + + ```python + import subprocess + subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # stop the debug server, free the probe + ``` + +3. Confirm nothing is left: `pgrep -fl openocd` (macOS/Linux) prints nothing. + +From a terminal it is the same: `pkill -TERM -f openocd`, or `Get-Process openocd | Stop-Process` on Windows. + +--- + +## Part 4: Static — Resolve the Functions in Binary Ninja and Patch + +### Step 16: Resolve the functions in the Binary Ninja GUI + +We now name the functions in Binary Ninja using the ELF symbol map from Step 4. Binary Ninja loaded the raw `.bin` with **no symbols**, so every function shows as `sub_` — resolving means giving each one its real name and signature. + +Three keys do all the work: + +| Key | Binary Ninja action | Use it for | +| --- | ------------------- | ---------- | +| `G` | Go to address | Jump to a function's address | +| `Y` | **Change Type** | Set the function's signature. The dialog shows the full prototype, so this sets the name *and* the type in one step. | +| `N` | Rename | Rename only, when you just want the name and not the type | + +For each function below: `G` to its address, then **`Y` (Change Type)** and type the prototype from the table. + +#### How to resolve a function in Binary Ninja (`Y`) + +`Y` is the **Change Type** key, and it is what actually resolves the function — it turns `void sub_10003394()` into `bool stdio_init_all(void)`. The Change Type dialog shows the full declaration (name and type), so typing the prototype sets both: + +1. `G` to the function's address. The cursor lands on the function. +2. Press **`Y`**. In the Change Type dialog, type the prototype from the table exactly — for example `bool stdio_init_all(void)` — and press Enter. + +The decompiler header then shows the real prototype, and calls to the function read cleanly instead of `sub_()`. `N` is only for renaming without touching the type; `Y` alone sets both the name and the type. + +If `Y` seems to do nothing, confirm the cursor is on the function, or right-click it and pick **Change Type...**. Binary Ninja parses what you type and silently keeps the old type if it does not parse, so glance at the header after each `Y`. + +#### Worked example: `main` + +1. Press `G`, type `0x10000234`, press Enter. The view jumps there; the cursor lands on `sub_10000234`. +2. Press **`Y`** (Change Type), type `int main(void)`, press Enter. That sets the name to `main` and the type to `int(void)`. + +> **Binary Ninja shows `int32_t` where Ghidra shows `int`.** After you set `int main(void)`, the decompiler header may read `int32_t main(void)`. That is the same type — on this platform `int` is 32 bits and Binary Ninja's parser normalises it to `int32_t`. Do not fight it; it is not an error. + +#### Worked example: `dht11_init` + +1. `G` -> `0x100002d4`. +2. `Y` -> `void dht11_init(uint8_t pin)`. + +This is our own `dht11.c` code. In this build the SDK's `gpio_init` and `gpio_pull_up` are called directly (`gpio_pull_up` as a tail-call to `gpio_set_pulls`), so the function is small. + +#### Worked example: `dht11_read` + +1. `G` -> `0x100002f4`. +2. `Y` -> `bool dht11_read(float* humidity, float* temperature)`. + +This is the big one: all six `dht11.c` static helpers are inlined here, along with `gpio_set_dir` / `gpio_put` / `gpio_get` / `time_us_32`, so `dht11_read` contains the entire single-wire protocol and the `0.1f` scale. + +#### Worked example: `gpio_init` + +1. `G` -> `0x100004a4`. +2. `Y` -> `void gpio_init(uint gpio)`. + +#### Worked example: `gpio_set_pulls` + +1. `G` -> `0x1000047c`. +2. `Y` -> `void gpio_set_pulls(uint gpio, bool up, bool down)`. + +This is what `gpio_pull_up(4)` in `dht11_init` compiles to. + +#### Worked example: `__wrap_printf` + +1. `G` -> `0x10003524`. +2. `Y` -> `int __wrap_printf(const char *fmt, ...)`. Keep the `...` — `printf` is variadic. + +> **`printf` in our source is `__wrap_printf` in the binary.** The SDK links our `printf` calls to its `__wrap_printf` wrapper, which forwards to `__wrap_vprintf`. Rename it `printf` if you prefer the lesson's shorthand, but `__wrap_printf` is what the ELF says. + +#### Worked example: `__wrap_puts` + +1. `G` -> `0x10003424`. +2. `Y` -> `int __wrap_puts(const char *s)`. + +This is the `else` branch of `print_dht11_reading`, which prints `"DHT11 read failed\r\n"`. (`__wrap_puts` is aliased to `stdio_puts` at the same address.) + +The rest of the chain is the same two keystrokes per function (`G`, then `Y`). This is **our code plus the library functions it actually calls** — not the whole SDK. `main` calls `stdio_init_all`, `dht11_init`, `__wrap_printf`, `dht11_read`, `__wrap_puts`, and `sleep_ms`, so we follow that chain down. + +The call chain for this project: + +``` +main +├── stdio_init_all ── stdio_uart_init ── gpio_set_function, uart_init, stdio_set_driver_enabled +│ └── uart_init ── clock_get_hz, busy_wait_us +├── dht11_init ── gpio_init, gpio_set_pulls (gpio_pull_up inlined) +├── __wrap_printf ── __wrap_vprintf ── vfctprintf ── _vsnprintf +│ │ └── _ntoa_format / _out_rev / _out_char +│ ├── stdio_out_chars_crlf +│ └── time_us_64 +├── dht11_read ── sleep_ms, sleep_us +│ └── send_start_signal / wait_for_level / wait_response / read_bit / +│ read_40_bits / validate_checksum / gpio_set_dir / gpio_put / +│ gpio_get / time_us_32 (all inlined; no calls) +└── __wrap_puts ── stdio_put_string +``` + +**Resolve every function in that chain:** + +| Address | Rename to (`N`) | Signature (`Y`) | +| ------- | --------------- | --------------- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | +| `0x100001e4` | `_init` | `void _init(void)` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | +| **`0x10000234`** | **`main`** | **`int main(void)`** | +| `0x100002d4` | `dht11_init` | `void dht11_init(uint8_t)` | +| `0x100002f4` | `dht11_read` | `bool dht11_read(float*, float*)` | +| `0x10000440` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | +| `0x1000047c` | `gpio_set_pulls` | `void gpio_set_pulls(uint, bool, bool)` | +| `0x100004a4` | `gpio_init` | `void gpio_init(uint)` | +| `0x10000f00` | `sleep_us` | `void sleep_us(uint64_t)` | +| `0x10000fd8` | `sleep_ms` | `void sleep_ms(uint32_t)` | +| `0x100011bc` | `time_us_64` | `uint64_t time_us_64(void)` | +| `0x100011d0` | `busy_wait_us` | `void busy_wait_us(uint64_t)` | +| `0x10001250` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | +| `0x10001424` | `clock_get_hz` | `unsigned long clock_get_hz(clock_handle_t)` | +| `0x1000313c` | `exit` | `void exit(int)` | +| `0x10003144` | `runtime_init` | `void runtime_init(void)` | +| `0x10003170` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | +| `0x10003280` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | +| `0x1000336c` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | +| `0x10003394` | `stdio_init_all` | `bool stdio_init_all(void)` | +| `0x10003424` | `__wrap_puts` | `int __wrap_puts(const char*)` | +| `0x10003460` | `__wrap_vprintf` | `int __wrap_vprintf(const char*, va_list)` | +| `0x10003524` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | +| `0x100036e0` | `stdio_uart_init` | `void stdio_uart_init(void)` | +| `0x10003820` | `strlen` | `size_t strlen(const char*)` | + +**Resolve the SDK helpers the `printf` path reaches:** + +| Address | Rename to (`N`) | Signature (`Y`) | +| ------- | --------------- | --------------- | +| `0x10001a44` | `_out_rev` | `unsigned _out_rev(out_fct_type, char*, size_t, size_t, const char*, size_t, unsigned, unsigned)` | +| `0x10001ae0` | `_ntoa_format` | `unsigned _ntoa_format(out_fct_type, char*, size_t, size_t, char*, size_t, bool, unsigned, unsigned, unsigned, unsigned)` | +| `0x10001cb4` | `_out_char` | `void _out_char(char, void*, size_t, size_t)` | +| `0x100026fc` | `_vsnprintf` | `int _vsnprintf(out_fct_type, char*, size_t, const char*, va_list)` | +| `0x100030e0` | `vfctprintf` | `int vfctprintf(void (*)(char, void*), void*, const char*, va_list)` | + +> **A `void` return type may not stick — here is the fix.** Binary Ninja treats `void` as low-confidence, and its analysis can override it with an inferred type — most often `int32_t` on this 32-bit target. It is most visible on `_reset_handler` (a hand-written assembly entry that never returns normally), but it can happen to **any** function whose return type Binary Ninja thinks it can infer. +> +> Setting the full signature with `Y` reproduces the unwanted `int32_t`, and `fn.return_type = ...` fails too. What works is the **return-value** setter: +> +> ```python +> from binaryninja import ReturnValue, Type +> fn = bv.get_function_at(0x1000015c) +> if fn is not None: +> fn.return_value = ReturnValue(Type.void()) +> ``` +> +> That holds `_reset_handler` at `void` even after reanalysis. If it still will not stick, leave it — it does not affect the rest of the lesson. + +> **`dht_pin` is data, not a function.** `arm-none-eabi-nm -n` lists `20000b7c b dht_pin`. That lowercase `b` is a **local data** symbol: the linker parks the `dht11.c` file-scope static at RAM address `0x20000b7c`. `dht11_init(4)` stores `4` into it. There is no function there; do not `Y` it with a prototype. + +> **Shortcut — resolves name *and* type for every function.** Instead of doing `N` + `Y` by hand, paste this into Binary Ninja's Python console (`Plugins -> Python Console`). It sets each function's name and signature programmatically: +> +> ```python +> from binaryninja import Symbol, SymbolType +> # The raw .bin has no headers, so these SDK types don't exist. set_user_type() +> # re-parses each signature as C, so an undefined name raises +> # "SyntaxError: unknown type name '...'". Define them first. +> sdk = bv.parse_types_from_string(""" +> typedef unsigned int uint; +> typedef char* va_list; +> typedef unsigned long clock_handle_t; +> typedef void (*out_fct_type)(char, void*, size_t, size_t); +> struct stdio_driver; +> typedef struct stdio_driver stdio_driver_t; +> struct uart_inst; +> typedef struct uart_inst uart_inst_t; +> enum gpio_function { +> GPIO_FUNC_XIP = 0, GPIO_FUNC_SPI = 1, GPIO_FUNC_UART = 2, GPIO_FUNC_I2C = 3, +> GPIO_FUNC_PWM = 4, GPIO_FUNC_SIO = 5, GPIO_FUNC_PIO0 = 6, GPIO_FUNC_PIO1 = 7, +> GPIO_FUNC_GPCK = 8, GPIO_FUNC_USB = 9, GPIO_FUNC_NULL = 0x1f, +> }; +> typedef enum gpio_function gpio_function_t; +> """) +> for name, ty in sdk.types.items(): +> bv.define_user_type(name, ty) +> +> # address: (name, signature) +> funcs = { +> 0x1000015c: ("_reset_handler", "void _reset_handler(void)"), +> 0x10000186: ("platform_entry", "void platform_entry(void)"), +> 0x1000019a: ("data_cpy", "void data_cpy(void*, void*, void*)"), +> 0x100001e4: ("_init", "void _init(void)"), +> 0x10000210: ("frame_dummy", "void frame_dummy(void)"), +> 0x10000234: ("main", "int main(void)"), +> 0x100002d4: ("dht11_init", "void dht11_init(uint8_t)"), +> 0x100002f4: ("dht11_read", "bool dht11_read(float*, float*)"), +> 0x10000440: ("gpio_set_function", "void gpio_set_function(uint, gpio_function_t)"), +> 0x1000047c: ("gpio_set_pulls", "void gpio_set_pulls(uint, bool, bool)"), +> 0x100004a4: ("gpio_init", "void gpio_init(uint)"), +> 0x10000f00: ("sleep_us", "void sleep_us(uint64_t)"), +> 0x10000fd8: ("sleep_ms", "void sleep_ms(uint32_t)"), +> 0x100011bc: ("time_us_64", "uint64_t time_us_64(void)"), +> 0x100011d0: ("busy_wait_us", "void busy_wait_us(uint64_t)"), +> 0x10001250: ("uart_init", "uint uart_init(uart_inst_t*, uint)"), +> 0x10001424: ("clock_get_hz", "unsigned long clock_get_hz(clock_handle_t)"), +> 0x1000313c: ("exit", "void exit(int)"), +> 0x10003144: ("runtime_init", "void runtime_init(void)"), +> 0x10003170: ("stdio_out_chars_crlf", "void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)"), +> 0x10003280: ("stdio_put_string", "int stdio_put_string(const char*, int, bool, bool)"), +> 0x1000336c: ("stdio_set_driver_enabled", "void stdio_set_driver_enabled(stdio_driver_t*, bool)"), +> 0x10003394: ("stdio_init_all", "bool stdio_init_all(void)"), +> 0x10003424: ("__wrap_puts", "int __wrap_puts(const char*)"), +> 0x10003460: ("__wrap_vprintf", "int __wrap_vprintf(const char*, va_list)"), +> 0x10003524: ("__wrap_printf", "int __wrap_printf(const char*, ...)"), +> 0x100036e0: ("stdio_uart_init", "void stdio_uart_init(void)"), +> 0x10003820: ("strlen", "size_t strlen(const char*)"), +> 0x10001a44: ("_out_rev", "unsigned _out_rev(out_fct_type, char*, size_t, size_t, const char*, size_t, unsigned, unsigned)"), +> 0x10001ae0: ("_ntoa_format", "unsigned _ntoa_format(out_fct_type, char*, size_t, size_t, char*, size_t, bool, unsigned, unsigned, unsigned, unsigned)"), +> 0x10001cb4: ("_out_char", "void _out_char(char, void*, size_t, size_t)"), +> 0x100026fc: ("_vsnprintf", "int _vsnprintf(out_fct_type, char*, size_t, const char*, va_list)"), +> 0x100030e0: ("vfctprintf", "int vfctprintf(void (*)(char, void*), void*, const char*, va_list)"), +> } +> for addr, (name, sig) in funcs.items(): +> bv.define_user_symbol(Symbol(SymbolType.FunctionSymbol, addr, name)) +> f = bv.get_function_at(addr) +> if f is not None: +> f.set_user_type(sig) +> ``` +> +> SDK type names (`stdio_driver_t`, `uart_inst_t`, `gpio_function_t`, plus `uint`, `va_list`, `clock_handle_t`, and `out_fct_type`) are **not** in the raw `.bin`. `set_user_type` re-parses each signature as C, so an undefined name raises `SyntaxError: unknown type name '...'` and stops the loop — it is not harmless. The `sdk` block above defines them first (an opaque `struct`/`enum`/`typedef` is enough to parse). Standard names (`uint8_t`, `uint32_t`, `uint64_t`, `bool`, `size_t`) are built in. If you add a function that uses another SDK type, add a definition for it to that block too. + +### Step 17: Read `main` in the decompiler + +Open the **Decompiler** view on `main`. Once the functions above are typed, it reads roughly: + +```c +int32_t main(void) +{ + stdio_init_all(); + dht11_init(4); + while (true) { + __wrap_printf("arithmetic_operator: %d\r\n", 0x32); // 50 + __wrap_printf("increment_operator: %d\r\n", 5); // 5 + __wrap_printf("relational_operator: %d\r\n", 0); // false + __wrap_printf("logical_operator: %d\r\n", 0); // false + __wrap_printf("bitwise_operator: %d\r\n", 0xa); // 10 + __wrap_printf("assignment_operator: %d\r\n", 0xa); // 10 + if (!dht11_read(&hum, &temp)) { + __wrap_puts("DHT11 read failed\r\n"); + } else { + __wrap_printf("Humidity: %.1f%%, Temperature: %.1f°C\r\n", hum, temp); + } + sleep_ms(2000); + } +} +``` + +The `0x32`, `5`, `0`, `0xa`, and `0xa` are the constants we will patch first. Every one is an **immediate in the instruction stream** — there is no variable in memory to change, which is why the patch edits the instruction operand. + +### Step 18: Patch 1 — change `arithmetic_operator` from 50 to 99 + +Go to `0x1000026e`: + +```asm +1000026e: 32 21 movs r1, #50 @ 0x32 +``` + +`movs r1, #imm8` is a 16-bit Thumb instruction. Its encoding is `0x21XX` (`movs r1, #imm8`), stored little-endian as **`XX 21`** — so the immediate is the byte at the instruction's **own** address, `0x1000026e`. Change `0x32` (50) to `0x63` (99). + +> **This is the opposite byte from the Week 7 `movw` patch.** For a 16-bit `movs`, the immediate is the first byte. For the 32-bit `movw`, the low immediate byte was the *third* byte. Always decode the instruction before you decide which byte to touch. + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x001a` | `0x1000026e` | `32` | `63` | `movs r1, #50` -> `#99`, prints `arithmetic_operator: 99` | + +**Option A — Hex view:** + +1. Switch to the **Hex** view (`View -> Hex`). +2. Toggle the lock off so editing is enabled. +3. Go to `0x1000026e` and change the byte `32` to `63`. +4. Return to the linear view, right-click the function -> `Reanalyze`. + +**Option B — Python console:** + +```python +bv.write(0x1000026e, b"\x63") +print(hex(bv.read(0x1000026e, 1)[0])) # -> 0x63 +``` + +After reanalysis the instruction reads `movs r1, #99`. + +### Step 18b: Patch 2 — change the DHT11 scale from `0.1f` to `5.0f` + +The DHT11 driver converts the integer and decimal bytes to a float and scales the decimal part: + +``` +humidity = data[0] + data[1] * 0.1f +temperature = data[2] + data[3] * 0.1f +``` + +`dht11_read` computes both with a fused multiply-add against a single `0.1f` constant. Look at the end of `dht11_read`: + +```asm +100003ee: vmov s15, r6 +100003f2: vcvt.f32.s32 s12, s15 +100003f6: vmov s15, r1 +100003fa: vcvt.f32.s32 s14, s15 +100003fe: vmov s15, r0 +10000402: vcvt.f32.s32 s13, s15 +10000406: vmov s15, r2 +1000040a: vldr s11, [pc, #44] @ 10000438 +1000040e: vcvt.f32.s32 s15, s15 +10000412: movs r0, #1 +10000414: vfma.f32 s14, s12, s11 +10000418: vfma.f32 s15, s13, s11 +1000041c: vstr s14, [r5] +10000420: vstr s15, [fp] +``` + +The constant `s11` is loaded by the `vldr s11, [pc, #44]` at `0x1000040a`, which reads the literal-pool word at `0x10000438`. That word is `0x3dcccccd` (float `0.1`), stored little-endian as `cd cc cc 3d`. Change it to `0x40a00000` (float `5.0`), bytes `00 00 a0 40`: + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x001a` | `0x10000438` | `cd cc cc 3d` | `00 00 a0 40` | `0.1f` -> `5.0f`; the humidity/temperature decimal parts read ~50x larger | + +``` ++-----------------------------------------------------------------+ +| IEEE-754 little-endian change | +| | +| 0.1f = 0x3dcccccd -> bytes cd cc cc 3d | +| 5.0f = 0x40a00000 -> bytes 00 00 a0 40 | +| | +| humidity = int + (decimal * 0.1f) becomes | +| humidity = int + (decimal * 5.0f) | +| | ++-----------------------------------------------------------------+ +``` + +**Option A — Hex view:** + +1. Switch to the **Hex** view (`View -> Hex`). +2. Go to `0x10000438` and change `cd cc cc 3d` to `00 00 a0 40`. +3. Return to the linear view and reanalyze. + +**Option B — Python console:** + +```python +bv.write(0x10000438, bytes.fromhex("0000a040")) +print(bv.read(0x10000438, 4).hex()) # -> 0000a040 +``` + +> **The scale is applied to both humidity and temperature.** The two `vfma.f32` instructions both read `s11`, so patching the one constant changes both readings, not just temperature. + +### Step 18c: Patch 3 — rename the printed label + +The string `"arithmetic_operator: %d\r\n"` starts at `0x100038e8`. Its first 26 bytes are: + +``` +61 72 69 74 68 6d 65 74 69 63 5f 6f 70 65 72 61 74 6f 72 3a 20 25 64 0d 0a 00 + a r i t h m e t i c _ o p e r a t o r : % d \r \n \0 +``` + +We replace it with `"hacked_operator: %d\r\n\0"`, which is **four bytes shorter**, and pad the tail with NULs: + +``` +68 61 63 6b 65 64 5f 6f 70 65 72 61 74 6f 72 3a 20 25 64 0d 0a 00 00 00 00 00 + h a c k e d _ o p e r a t o r : % d \r \n \0 +``` + +The `\0` at offset 21 terminates the string, so `printf` stops there and the four padding bytes are never read. The next format string (`"increment_operator: …"`) begins at `0x10003904`, which is 28 bytes past `0x100038e8`, so our 26-byte write cannot reach it. + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x001a` | `0x100038e8` | `61 72 69 74 68 6d 65 74 69 63 5f 6f 70 65 72 61 74 6f 72 3a 20 25 64 0d 0a 00` | `68 61 63 6b 65 64 5f 6f 70 65 72 61 74 6f 72 3a 20 25 64 0d 0a 00 00 00 00 00` | prints `hacked_operator:` instead of `arithmetic_operator:` | + +**Option A — Hex view:** + +1. Switch to the **Hex** view (`View -> Hex`). +2. Go to `0x100038e8` and replace the 26 bytes above. +3. Return to the linear view and reanalyze. + +**Option B — Python console:** + +```python +bv.write(0x100038e8, b"hacked_operator: %d\r\n\x00\x00\x00\x00") +print(bv.read(0x100038e8, 26)) # -> b'hacked_operator: %d\r\n\x00\x00\x00\x00' +``` + +> **Keep the NUL.** `printf` walks the format string until it hits `\0`. If you omit it, the printer runs on into the padding and then into the next string, producing garbage. The four padding bytes guarantee the terminator. + +### Step 19: Export the patched `.bin` + +```python +import os +seg = next(s for s in bv.segments if s.data_length) # the loadable image segment +data = bv.read(seg.start, seg.data_length) # base + size come from the view itself +out = os.path.join(os.path.join(root, "0x001a_operators", "build"), "0x001a_operators-h.bin") +open(out, "wb").write(data) +print(len(data), out) # -> 17484 /.../build/0x001a_operators-h.bin +``` + +Where the two numbers come from — nothing is hardcoded: + +- **`seg.start`** is the image base Binary Ninja loaded the `.bin` at (`0x10000000`), the same value you pass to `uf2conv --base`. +- **`seg.data_length`** is the segment's size in the file (`0x444c` = 17484). Exactly one segment carries data (the image); every peripheral and synthetic segment has `data_length == 0`, so `next(...)` picks the image. +- Reading `seg.start` for `seg.data_length` bytes therefore grabs exactly the image. + +Two gotchas this avoids: + +- **No relative path.** Binary Ninja's Python console runs with a read-only working directory (inside the app bundle), so `open("0x001a_operators-h.bin", "wb")` fails with `OSError: [Errno 30] Read-only file system`. `root` (from `~/.embedded-hacking-repo`, Step 3) is the repo, so the file is written into the project's `build/` — no machine-specific path and no database needed. +- **Read the image, not the whole view.** `bv.read(bv.start, bv.length)` spans the entire mapped range, which is not the image. The segment's `data_length` is the image size. + +A different size means you exported a partial view. + +### Step 20: Convert to UF2 + +Run from the project directory: + +**macOS Apple Silicon / Linux x64:** + +```bash +python3 ../uf2conv.py 0x001a_operators-h.bin \ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +**Windows x64:** + +```cmd +python ..\uf2conv.py 0x001a_operators-h.bin ^ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +> **Or convert from the Binary Ninja console** — it is a normal Python interpreter, so you never have to leave the app. `chdir` to a writable directory first (the default one is read-only), then run the script: +> +> ```python +> import os, sys, runpy +> os.chdir(os.path.join(root, "0x001a_operators", "build")) # the project build dir (writable) +> sys.argv = ["uf2conv.py", "0x001a_operators-h.bin", +> "--base", "0x10000000", "--family", "0xe48bff59", "--output", "hacked.uf2"] +> runpy.run_path("../../uf2conv.py", run_name="__main__") # path to your uf2conv.py +> ``` +> +> This writes `hacked.uf2` next to the `.bin`, ready to drag onto the Pico. + +### Step 21: Flash and verify + +Hold **BOOTSEL**, plug in the Pico 2, and drag `hacked.uf2` onto the **`RP2350`** drive. Open the serial monitor: + +``` +hacked_operator: 99 +increment_operator: 5 +relational_operator: 0 +logical_operator: 0 +bitwise_operator: 10 +assignment_operator: 10 +Humidity: 255.0%, Temperature: 119.0°C +... +``` + +- The first line now reads **`hacked_operator: 99`**. +- The humidity/temperature decimals are scaled by **5.0** instead of **0.1**, so the readings are roughly 50x larger (exact numbers depend on your sensor). + +**All three changes — one instruction byte, one float word, and one string — with no source code.** + +> **Faster: flash over the Debug Probe (no BOOTSEL).** The repo's `flash.sh` writes the raw `.bin` straight into XIP flash over SWD (`program 0x10000000 verify reset exit`), so you never touch BOOTSEL or a UF2. Run it from a terminal (`./flash.sh `), or from the Binary Ninja console **without freezing it** — use `subprocess.Popen`, which returns immediately, and send OpenOCD's output to a log file. (`subprocess.run` blocks the console until the flash finishes; do not use it here.) +> +> ```python +> import os, subprocess +> root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +> bin_path = os.path.join(os.path.join(root, "0x001a_operators", "build"), "0x001a_operators-h.bin") +> log = os.path.join(os.path.join(root, "0x001a_operators", "build"), "flash.log") +> subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +> p = subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], +> stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +> print("flashing in the background; log:", log) +> ``` +> +> The `pkill` frees the probe first; on Windows use `subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"])`. +> +> The console is free the moment this returns. Check it with `print(p.poll())` (`None` = still running, `0` = done) or read `flash.log` — success ends with `** Verified OK **`. +> +> The same non-blocking form without the script: +> +> ```python +> import os, subprocess +> ocd = os.path.expanduser("~/.pico-sdk/openocd/0.12.0+dev") +> bin_path = os.path.join(os.path.join(root, "0x001a_operators", "build"), "0x001a_operators-h.bin") +> log = os.path.join(os.path.join(root, "0x001a_operators", "build"), "flash.log") +> subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +> p = subprocess.Popen([f"{ocd}/openocd", "-s", f"{ocd}/scripts", +> "-f", "interface/cmsis-dap.cfg", "-f", "target/rp2350.cfg", +> "-c", "adapter speed 5000", +> "-c", f"program {bin_path} 0x10000000 verify reset exit"], +> stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +> print("flashing in the background; log:", log) +> ``` +> +> **The Debug Probe is single-owner.** If Binary Ninja is still attached (the `debug-server.sh` OpenOCD is running), the flash cannot grab the probe. Detach in Binary Ninja and stop that OpenOCD first: +> +> ```bash +> # macOS / Linux +> pkill -TERM -f openocd +> ``` +> ```powershell +> # Windows +> Get-Process openocd -ErrorAction SilentlyContinue | Stop-Process +> ``` +> +> Success looks like `Programming Finished` -> `Verified OK` -> `Resetting Target`. On Windows use `flash.ps1` (`.\flash.ps1 -Bin `) the same way. + +--- + +## Cheatsheet + +### Binary Ninja GUI actions + +| Action | How | +| ------ | --- | +| Go to address | `G` | +| Rename function/symbol | `N` | +| Set type or signature | `Y` | +| Add comment | `;` | +| Open Hex view | `View -> Hex` | +| Enable hex editing | Toggle the lock in the status bar | +| Reanalyze after a patch | Right-click function -> `Reanalyze` | +| Edit a register live | `dbg.set_reg_value("r1", 0x63)` in the Python console (or right-click the register, press `E`, type hex, Enter) | +| Write a RAM string live | `dbg.write_memory(0x20080000, b"hacked_operator: %d\r\n\x00")` | +| Set a breakpoint | `Debugger -> Add Hardware Breakpoint...` (hardware execute). Do **not** use `F2` — software breakpoints cannot be written to read-only flash. | +| Move a breakpoint | Remove it and set it at the new address in the GUI (command-port fallback: `rbp ` then `bp 2 hw`) | +| Confirm what is armed | The **Breakpoints** widget lists it (command-port fallback: `mdw 0xE0002000 8`, each armed breakpoint shows as ``) | +| Apply the ELF symbol map | Paste the Python snippet from Step 16 into the Python Console | +| Load peripheral names (optional) | Load the RP2350 SVD — it lives at **`WEEK04/rp2350.svd`** | + +### OpenOCD server and reset + +The server runs with `gdb_breakpoint_override hard` so that flash-writes are never attempted. Breakpoints in this lab are set in the Binary Ninja GUI through the **GDB MI** adapter (Step 12). The command-port rows below are the fallback if you use the **GDB RSP** adapter instead. + +| Action | Command | +| ------ | ------- | +| Connect to the OpenOCD prompt (fallback) | `nc 127.0.0.1 4444` (or `telnet 127.0.0.1 4444`) | +| Reset and run (command port) | `reset run` | +| Check core state (command port) | `targets` | +| Set a breakpoint in the GUI | `Debugger -> Add Hardware Breakpoint...` (hardware execute; `F2` software breakpoints do not work on flash) | +| (fallback) Add a breakpoint without the GUI | `bp 2 hw` | +| Remove one breakpoint | `rbp ` — **address only, no length, no `hw`** | +| Remove every breakpoint | `rbp all` | +| Start the server parked at `main` | macOS/Linux: `BP_ADDR=0x10000234 ./debug-server.sh` — Windows: `$env:BP_ADDR="0x10000234"; .\debug-server.ps1` (one-shot) | +| Start the server parked in the loop | macOS/Linux: `BP_ADDR=0x10000272 ./debug-server.sh` — Windows: `$env:BP_ADDR="0x10000272"; .\debug-server.ps1` | +| Break on the loop in a running target | set a hardware breakpoint in the GUI at the loop address, then **Resume** — repeatable | +| Make Binary Ninja stepping work | `rp2350.dap.core0 configure -rtos none` (already in the scripts) | +| Step without re-trapping | move the breakpoint off the current PC first, then **Step Into**/**Step Over** | +| Reset without desyncing Binary Ninja | **Detach**, `reset run` on the port, reconnect — never `reset run` while attached | + +### Every address and byte we changed + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x001a` | `0x1000026e` | `32` | `63` | `movs r1, #50` -> `#99`, prints `arithmetic_operator: 99` | +| `0x001a` | `0x10000438` | `cd cc cc 3d` | `00 00 a0 40` | DHT11 scale `0.1f` -> `5.0f`, readings ~50x larger | +| `0x001a` | `0x100038e8` | `61 72 69 74 68 6d 65 74 69 63 5f 6f 70 65 72 61 74 6f 72 3a 20 25 64 0d 0a 00` | `68 61 63 6b 65 64 5f 6f 70 65 72 61 74 6f 72 3a 20 25 64 0d 0a 00 00 00 00 00` | prints `hacked_operator:` instead of `arithmetic_operator:` | + +### The operator / DHT11 memory map + +| Item | Address | Notes | +| ---- | ------- | ----- | +| `dht_pin` | `0x20000b7c` | RAM `.bss`, the `dht11.c` static holding the GPIO number (`4`) | +| Literal-pool word — humidity/temp format | `0x100002b4` | `0x10003988` — `"Humidity: %.1f%%, Temperature: %.1f°C\r\n"` | +| Literal-pool word — arithmetic format | `0x100002b8` | `0x100038e8` — `"arithmetic_operator: %d\r\n"` | +| Literal-pool word — increment format | `0x100002bc` | `0x10003904` — `"increment_operator: %d\r\n"` | +| Literal-pool word — relational format | `0x100002c0` | `0x10003920` — `"relational_operator: %d\r\n"` | +| Literal-pool word — logical format | `0x100002c4` | `0x1000393c` — `"logical_operator: %d\r\n"` | +| Literal-pool word — bitwise format | `0x100002c8` | `0x10003954` — `"bitwise_operator: %d\r\n"` | +| Literal-pool word — assignment format | `0x100002cc` | `0x1000396c` — `"assignment_operator: %d\r\n"` | +| Literal-pool word — failure string | `0x100002d0` | `0x100039b4` — `"DHT11 read failed\r\n"` | +| `dht11_read` literal-pool word | `0x10000434` | `0x400b0000` — TIMER0 base, used by the inlined `time_us_32` | +| DHT11 scale constant | `0x10000438` | `0x3dcccccd` — `0.1f` | +| `dht11_read` literal-pool word | `0x1000043c` | `0x20000b7c` — `&dht_pin` | + +### Raw image facts + +| Item | Value | +| ---- | ----- | +| Build type | `Release` | +| Load base address | `0x10000000` | +| Project size | `17484` bytes (`0x444c`) | +| Fixed `main` anchor | `0x1000018c` (reset handler middle `blx`) | +| `main` | `0x10000234` | +| `dht11_init` | `0x100002d4` | +| `dht11_read` | `0x100002f4` | +| `printf` call / return, `arithmetic_operator` | `0x10000272` / `0x10000276` | +| `arithmetic_operator` format string | `0x100038e8` | +| `increment_operator` format string | `0x10003904` | +| `relational_operator` format string | `0x10003920` | +| `logical_operator` format string | `0x1000393c` | +| `bitwise_operator` format string | `0x10003954` | +| `assignment_operator` format string | `0x1000396c` | +| Humidity/Temperature format string | `0x10003988` | +| `"DHT11 read failed\r\n"` string | `0x100039b4` | +| DHT11 scale constant | `0x10000438` | +| DHT11 GPIO pin | `4` | +| RP2350 UF2 family ID | `0xe48bff59` | + +--- + +## Troubleshooting + +### My operator values are 12 and 11, not 10 and 10 + +You are reading the original Week 9 text, not this build. In `0x001a_operators.c`, `compute_arithmetic_ops` receives `x` **by value**, so its `*incr = x++;` never touches `compute_operators`' `x`. That `x` stays `5`, so `x << 1` is `10` and `x += 5` is `10`. Confirm it on your own ELF in Step 4: the immediates in `main` are `#50`, `#5`, `#0`, `#0`, `#10`, `#10`. + +### Binary Ninja hangs or crashes when you connect (macOS 27) + +Three different causes have been seen on this setup; check them in this order. + +- **A breakpoint set before connecting.** With the **GDB MI** adapter, if the binary view already has a breakpoint, the session hangs. Start parked with `BP_ADDR`, connect, then add breakpoints (see the next entry). +- **The wrong GDB executable.** Point **Full GDB Executable Path** at the **14.2.rel1** toolchain (Step 10). The 13.3.rel1 build did **not** connect in testing. +- **The LLDB adapter.** A crash report with `libdebuggercore.dylib -> std::terminate() -> abort()` and `liblldb` in the stack is the **LLDB** adapter, not GDB MI. Avoid LLDB on this setup. + +**Use GDB MI**, with the 14.2.rel1 path above. If it still fails, fall back to plain `arm-none-eabi-gdb` against the same server — the addresses and register values are identical to the GUI steps. + +If Binary Ninja hangs, force-quit it; the connect dialog has no working Cancel. The static steps (resolve, patch, export, flash) never touch the debugger and always work. + +### The GUI refuses to set a breakpoint (GDB RSP adapter only) + +If you are on the **GDB RSP** adapter, the GUI cannot set breakpoints on this target. That adapter is Binary Ninja's own minimal RSP client and sends a **1-byte** breakpoint (`Z0,,1`); the Cortex-M33 comparators need 2 bytes, so OpenOCD answers `only breakpoints of two bytes length supported`. It affects every address, both `Toggle Breakpoint` and `Add Hardware Breakpoint`, and the dialog's **Size** field is disabled. `gdb_breakpoint_override` makes no difference. + +**Fix: use the GDB MI adapter** (Step 10). It drives real GDB, which sends the correct length, so GUI breakpoints just work. If you must stay on GDB RSP, arm breakpoints from the command port after connecting (`bp 2 hw`) — but the lab uses GDB MI and does not need that. + +### GDB MI hangs when you connect (a breakpoint already existed) + +With the **GDB MI** adapter, if Binary Ninja already has a breakpoint set when you connect, the session **hangs**. This is a Binary Ninja bug. The working order is: + +1. Start the server parked, e.g. `BP_ADDR=0x10000234 ./debug-server.sh` (Windows: `$env:BP_ADDR="0x10000234"; .\debug-server.ps1`). +2. Connect with the **GDB MI** adapter. +3. Only *then* set hardware breakpoints in the UI. + +Never have a breakpoint in the binary view before the GDB MI connection. If it hangs, quit Binary Ninja, restart the server with `BP_ADDR`, and connect again before adding any breakpoints. + +### Step Into / Step Over does nothing (PC never moves) + +Two causes have been seen on this target. + +1. **A breakpoint on the current PC re-traps the step.** OpenOCD's step-over-breakpoint logic fails with `Duplicate Breakpoint address` and the PC stays put. Fix: move the breakpoint off the current PC (in the GUI), then step. +2. **The `hwthread` RTOS (GDB RSP adapter only).** With the **GDB RSP** adapter, OpenOCD can log `fake step thread 0` and reply without stepping, because the RP2350 config's `-rtos hwthread` makes the current thread id 1 while Binary Ninja sends thread id 0. Fix: `rp2350.dap.core0 configure -rtos none` (the launcher scripts already pass this). **GDB MI does not hit this.** + +To tell them apart, turn on OpenOCD logging (`log_output /tmp/ocd.log`, then `debug_level 3` on the command port) and look for `fake step` versus `Duplicate Breakpoint`. + +### `zsh: bad CPU type in executable: cmake` + +An Intel `x86_64` tool is on your `PATH` on Apple Silicon. Run Step 2: `export PATH="/opt/homebrew/bin:$PATH"`, then `hash -r`. Add it to `~/.zshrc` to make it permanent. + +### My addresses do not match this guide + +You probably built `Debug`. This lesson is a `Release` build. Re-run Step 3 with `-DCMAKE_BUILD_TYPE=Release`. A `Debug` build moves the SDK functions and keeps `print_operator_results`, `compute_operators`, and the `dht11.c` helpers as separate calls, so `main` is not at `0x10000234` and the operator results are not bare immediates. + +### A breakpoint never fires + +First, confirm you actually set one, and that it is a **hardware** breakpoint. With the **GDB MI** adapter, `Debugger -> Add Hardware Breakpoint...` (hardware execute) should land in the **Breakpoints** widget. If nothing lands, or the core keeps running, you probably used `F2` (`Toggle Breakpoint`) — that is a software breakpoint and cannot be written to read-only flash, so it never installs. Also check you are on **GDB MI**, not **GDB RSP** (the GDB RSP adapter cannot set breakpoints on this target at all). + +Then check the order and the state: + +- **Arm it only after Binary Ninja is connected.** OpenOCD flushes every breakpoint when a client attaches, so anything armed earlier is gone. This also applies to `BP_ADDR` on the startup command line. +- **Verify it is armed:** `mdw 0xE0002000 8`. You should see your address with the low bit set (`0x10000272` -> `0x10000273`). All zeros means nothing is armed — re-read this first, because it distinguishes "not armed" from "armed but never reached". +- **Is the core running?** `poll` on the command port should not report a halt. If it is stopped, click **Resume**. +- **Does the address get reached again?** `main` runs once per reset, so use `BP_ADDR` at startup (Step 9) rather than `reset run` while attached. Loop addresses such as `0x10000272` fire on the next pass with no reset — arm them and click **Resume** in Binary Ninja. +- **With GDB MI the stop is reported as `Breakpoint`** and appears in the **Breakpoints** widget, because GDB really did set it. + +### I edit `r1` (or another register) and it reverts + +`main` reloads the value at the top of every loop iteration — `movs r1, #50` at `0x1000026e` runs right before the `printf` at `0x10000272`. So `r1` is only `0x63` for the instant between your edit and the next pass; then it is `0x32` again. The edit sticks only if the core is **genuinely stopped** at the breakpoint and stays stopped. + +If it keeps reverting, the core is running, which almost always means the breakpoint is not installed — usually because it is a **software** breakpoint (`F2`) that cannot be written to read-only flash. Use `Debugger -> Add Hardware Breakpoint...` (hardware execute). + +> **The Registers widget is a snapshot, not a live view.** Binary Ninja reads the registers at each stop and shows that snapshot; it does not poll the target, and there is no "refresh registers" command. So a value changed outside Binary Ninja will not appear until the next stop. + +### The string hack does nothing + +The live version needs to run **before** the `printf` call, which means breaking at `0x10000272` and redirecting `r0` while stopped. If you let it resume, the loop reloads `r0` from the literal pool on the next pass and the hack is gone. + +Also confirm you wrote a NUL-terminated string. `printf` walks bytes until `*r0 == 0`; without the trailing `\x00`, it keeps reading RAM garbage. + +### The patched instruction still shows the old value + +Right-click the function and choose `Reanalyze`. Binary Ninja caches the disassembly text; a byte edit does not always trigger a re-lift by itself. + +### The arith patch corrupts the instruction + +You patched the wrong byte. `movs r1, #imm8` is 16-bit and its immediate is the **first** byte: the instruction starts at `0x1000026e`, so the byte to change is `0x1000026e` (`0x32 -> 0x63`). Patching `0x1000026f` (the `0x21` opcode) corrupts the instruction and the core will fault. + +### The DHT11 reading did not change + +Confirm you patched the right word. The scale is the literal loaded by `vldr s11, [pc, #44]` at `0x1000040a`; that literal lives at `0x10000438` and is `cd cc cc 3d` (`0.1f`). Change it to `00 00 a0 40` (`5.0f`). If your sensor is not connected or not answering, `dht11_read` returns `false` and the firmware prints `DHT11 read failed` instead — the scale patch only matters when a reading succeeds. + +### The `arithmetic_operator` string hack overran + +The replacement must be NUL-terminated. `"hacked_operator: %d\r\n"` is 21 characters; write it plus a `\0` (22 bytes) and pad the remaining four bytes with `00`. The next format string begins at `0x10003904`, 28 bytes past `0x100038e8`, so a 26-byte write stays clear of it. A longer write overwrites `"increment_operator: …"`. + +### The serial capture is garbage on macOS + +Reading `/dev/cu.usbmodem*` with a bare `read()` returns garbage. Set **raw termios at 115200** first: clear canonical/echo flags, set `CLOCAL|CREAD`, and `B115200` on input and output. `screen /dev/cu.usbmodem* 115200` does all of this for you; a script must call `tcsetattr` itself. Once set, the capture reads clean `arithmetic_operator: 50` lines. + +### It worked for a second, then stopped (Binary Ninja's view desyncs) + +This is the most common failure, and it has one main cause: **driving the core from the OpenOCD command port while Binary Ninja is connected.** + +- If you send `reset run` from the port while attached, the core resets, runs, and halts at your breakpoint — but Binary Ninja never receives the stop event. Its sidebar keeps showing the *previous* location, so **Step** and **Resume** act on a stale PC and appear to do nothing. +- If the OpenOCD process dies (or you restart it) while attached, Binary Ninja keeps believing it is connected: the sidebar stays, but the menu shows **Pause** enabled and **Resume**/**Step** disabled because Binary Ninja last saw the target *running*. + +Recovery: **Detach, then reconnect.** If Detach does nothing (the connection is already dead), restart Binary Ninja — its menu still shows a session that no longer exists. + +Prevention: + +- Stop at `main` with `BP_ADDR` on a fresh server start, not with `reset run` while attached. +- For loop addresses, set the breakpoint in the GUI and click **Resume**. Let Binary Ninja be the thing that starts the core. +- If you must reset, **Detach first**, `reset run`, then reconnect. +- Never leave a breakpoint on the PC you are about to step or resume from. + +### The target "blows past" `main` and stops at `0x10003648` instead + +`0x10003648` is `stdio_uart_out_flush`, the UART transmit-FIFO drain loop inside `printf`: + +```asm +10003648: 4b02 ldr r3, [pc, #8] @ (10003654 ) +1000364a: 681a ldr r2, [r3] +1000364c: 6993 ldr r3, [r2, #24] +1000364e: 071b lsls r3, r3, #28 +10003650: d4fc bmi.n 1000364c +10003652: 4770 bx lr +``` + +That is where the core sits while the UART drains, so the core is running `main`'s loop and simply spends nearly all its time there. The breakpoint at `main` did not fire because `main`'s entry runs exactly **once per reset**. If you arm the breakpoint after the reset, or set it while the target is already running and just resume, the core is already past `main` and will never re-execute it. Either arm the breakpoint **before** resetting, or break inside the loop at `0x10000272`, which fires every iteration. + +### The console floods with `Failed to read memory at 0xf0000000` + +Core1 is exposed. The scripts must run with `USE_CORE=0`. Stop the server, confirm only `core0` is reported, restart, then restart Binary Ninja. + +### `Connect to Remote Process` is greyed out and Pause does nothing + +Binary Ninja is in a stale session, usually because the debug server restarted while attached. Quit and reopen Binary Ninja (or the `.bndb`) and connect again. + +### The decompiler still shows the old value after patching + +Right-click the function and choose `Reanalyze`. + +--- + +## Fallback: do the dynamic steps with GDB (macOS 27) + +If Binary Ninja's debugger crashes on attach on macOS 27 (see Troubleshooting), you can still do the live hack with the ARM GDB from the toolchain, against the same OpenOCD server. The addresses and register values are identical to the GUI steps. + +Start the debug server (Step 9), then in a new terminal: + +``` +arm-none-eabi-gdb +``` + +At the `(gdb)` prompt: + +``` +set architecture armv8-m.main +target extended-remote :3333 +hbreak *0x10000272 +continue +``` + +Do **not** run `monitor reset run` before `hbreak`. `0x10000272` is inside `main`'s loop, so the breakpoint fires on the next iteration with no reset. If you reset first, the core runs `main` and you will not catch it. + +GDB stops at the `printf` call. Confirm the value, change it, and let it run: + +``` +info registers pc r0 r1 # pc = 0x10000272, r0 = 0x100038e8, r1 = 0x32 +set $r1 = 0x63 +stepi +continue +``` + +The serial monitor prints `arithmetic_operator: 99` for the iteration you changed — the same temporary live hack as editing `r1` in the Binary Ninja Registers widget. When you are done, press `Ctrl-C`, then `detach` and `quit`. + +**If you specifically want to stop at `main` (`0x10000234`),** remember its entry runs only once per reset, so the breakpoint must be armed *before* the reset: + +``` +monitor reset halt +hbreak *0x10000234 +continue +``` + +If you instead set it while the target is running and just `continue`, you will "blow past" `main` and catch the core inside `printf` — in this build at `0x10003648`, the `stdio_uart_out_flush` UART-drain loop. + +`hbreak` sets a hardware breakpoint, which is required for read-only flash. It works from plain GDB because GDB sends the 2-byte length the Cortex-M33 comparators need. Binary Ninja's **GDB MI** adapter goes through the same GDB, so its GUI breakpoints work too; the old **GDB RSP** adapter was the one that sent a 1-byte length and could not set breakpoints here. + +## Glossary + +| Term | Definition | +| ---- | ---------- | +| **AAPCS** | ARM Architecture Procedure Call Standard — `r0`-`r3` for the first four arguments, `r0` for the return value | +| **Arithmetic operator** | C operators for math (`+`, `-`, `*`, `/`, `%`); here `x * y` folds to `#50` | +| **Assignment operator** | Compound assignment (`+=`, `-=`, …); here `x += 5` folds to `#10` | +| **Bitwise operator** | Operators on individual bits (`<<`, `>>`, `&`, `\|`, `^`); here `x << 1` folds to `#10` | +| **`.bss`** | Section for uninitialized (or zero-initialized) static/global variables; `dht_pin` lives here | +| **`.data`** | Section for initialized static/global variables; copied from flash to SRAM at boot | +| **DHT11** | Low-cost single-wire digital humidity and temperature sensor | +| **`.elf`** | Linked image with the symbol table; the ground truth for addresses and names | +| **Fused multiply-add** | `vfma.f32 Sd, Sn, Sm` computes `Sd = Sd + (Sn * Sm)` in one operation | +| **Immediate value** | A constant embedded directly in an instruction, not fetched from memory | +| **Increment operator** | `x++` (post) or `++x` (pre); here `x++` returns the old value, `5` | +| **IEEE-754** | Standard for floating-point representation; `0.1f` is `0x3dcccccd` | +| **Literal pool** | A block of 32-bit constants that Thumb-2 code reaches with PC-relative `ldr` | +| **Logical operator** | Operators combining conditions (`&&`, `\|\|`, `!`); here `false` | +| **`movs`** | 16-bit Thumb move that loads an 8-bit immediate (0-255); immediate is the first byte | +| **`movw`** | 32-bit Thumb-2 "move wide" that loads a 16-bit immediate; low imm8 is the third byte | +| **PCF8574** | An I2C I/O expander; not used this week (that was the LCD, Week 7) | +| **Relational operator** | Comparison operators (`<`, `>`, `==`, `!=`); here `5 > 10` is `false` | +| **`.rodata`** | Read-only section for constants and string literals; stays in flash | +| **SIO** | Single-cycle I/O block; `gpio_set_dir` / `gpio_put` write it with `mcrr`, `gpio_get` reads it | +| **SVD** | System View Description file — register names for a peripheral; the RP2350 one is at **`WEEK04/rp2350.svd`** | +| **Thumb bit** | Bit 0 of a Cortex-M function pointer; selects Thumb instruction mode | +| **TIMER0** | RP2350 timer block at `0x400b0000`; `time_us_32` reads its `TIMERAWL` register (`+0x28`) | +| **UF2** | USB Flashing Format — the file format the Pico 2 bootloader accepts | +| **Vector table** | The first words of flash: initial stack pointer and exception vectors | + +--- + +**Remember:** the ELF tells you what every address is, and the `.bin` is what you actually patch. Prove the behavior dynamically, read `r1` at the arithmetic `printf` call, resolve the names from the ELF (including the `dht11.c` symbols), then patch the bytes — the `movs` immediate (`0x32 -> 0x63`), the DHT11 float word (`0.1f -> 5.0f`), and the format string (`arithmetic_operator:` -> `hacked_operator:`) — and flash. diff --git a/WEEK09/WEEK09-BN.pdf b/WEEK09/WEEK09-BN.pdf new file mode 100644 index 0000000..8fbd2f0 Binary files /dev/null and b/WEEK09/WEEK09-BN.pdf differ diff --git a/WEEK09/WEEK09-SLIDES.pdf b/WEEK09/WEEK09-SLIDES.pdf new file mode 100644 index 0000000..c6593ec Binary files /dev/null and b/WEEK09/WEEK09-SLIDES.pdf differ diff --git a/WEEK09/WEEK09.md b/WEEK09/WEEK09.md new file mode 100644 index 0000000..3589e26 --- /dev/null +++ b/WEEK09/WEEK09.md @@ -0,0 +1,1230 @@ +# Week 9: Operators in Embedded Systems: Debugging and Hacking Operators w/ DHT11 Temperature & Humidity Sensor Single-Wire Protocol Basics. + +*** +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: + +- Understand all six types of C operators (arithmetic, increment, relational, logical, bitwise, assignment) +- Know how the DHT11 temperature and humidity sensor communicates with the Pico 2 +- Understand how post-increment operators affect variable values +- Navigate to the Reset_Handler and main function in stripped binaries +- Identify function arguments by analyzing register values in Ghidra +- Understand IEEE-754 floating-point representation +- Hack floating-point constants to manipulate sensor readings + +--- + +## Part 1: Understanding C Operators + +### What Are Operators? + +**Operators** are symbols that tell the compiler to perform specific mathematical, logical, or data manipulation operations. Think of them as the "verbs" of programming - they describe actions to perform on data. + +### The Six Types of Operators + +| Type | Example | What It Does | +| -------------- | -------------------- | ----------------------------------- | +| **Arithmetic** | `x * y` | Math operations (+, -, *, /, %) | +| **Increment** | `x++` or `++x` | Increase/decrease by 1 | +| **Relational** | `x > y` | Compare values (returns true/false) | +| **Logical** | `(x > y) && (y > x)` | Combine conditions (AND, OR, NOT) | +| **Bitwise** | `x << 1` | Manipulate individual bits | +| **Assignment** | `x += 5` | Assign and modify values | + +--- + +## Part 2: Arithmetic Operators + +### Basic Math in C + +Arithmetic operators perform mathematical calculations: + +```c +int x = 5; +int y = 10; +int result = x * y; // result = 50 +``` + +``` ++-----------------------------------------------------------------+ +| Arithmetic Operators | +| | +| Operator Example Result Description | +| ------------------------------------------------------------- | +| + 5 + 10 15 Addition | +| - 10 - 5 5 Subtraction | +| * 5 * 10 50 Multiplication | +| / 10 / 5 2 Division | +| % 10 % 3 1 Modulus (remainder) | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 3: Increment and Decrement Operators + +### Pre-Increment vs Post-Increment + +This is where many beginners get confused! There are TWO ways to increment: + +```c +int x = 5; +int a = x++; // Post-increment: a = 5, then x becomes 6 +int b = ++x; // Pre-increment: x becomes 7, then b = 7 +``` + +**The Key Difference:** + +| Type | Syntax | When Value Changes | Example Result | +| ------------------ | ------ | --------------------- | -------------------- | +| **Post-increment** | `x++` | AFTER the expression | `a = x++` -> a=5, x=6 | +| **Pre-increment** | `++x` | BEFORE the expression | `b = ++x` -> x=7, b=7 | + +``` ++-----------------------------------------------------------------+ +| Post-Increment (x++) | +| | +| int x = 5; | +| int result = x++; | +| | +| Step 1: result = x (result gets 5) | +| Step 2: x = x + 1 (x becomes 6) | +| | +| Final: result = 5, x = 6 | +| | +| Think of it as: "Use first, THEN increment" | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 4: Relational Operators + +### Comparing Values + +Relational operators compare two values and return `true` (1) or `false` (0): + +```c +int x = 6; +int y = 10; +bool result = (x > y); // false, because 6 is NOT greater than 10 +``` + +``` ++-----------------------------------------------------------------+ +| Relational Operators | +| | +| Operator Example Result Description | +| ------------------------------------------------------------- | +| > 6 > 10 false Greater than | +| < 6 < 10 true Less than | +| >= 6 >= 6 true Greater than or equal | +| <= 6 <= 10 true Less than or equal | +| == 6 == 10 false Equal to | +| != 6 != 10 true Not equal to | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 5: Logical Operators + +### Combining Conditions + +Logical operators combine multiple conditions: + +```c +int x = 6; +int y = 10; +bool result = (x > y) && (y > x); // false AND true = false +``` + +``` ++-----------------------------------------------------------------+ +| Logical Operators | +| | +| Operator Name Example Result | +| ------------------------------------------------------------- | +| && AND true && true true | +| && AND true && false false | +| || OR true || false true | +| || OR false || false false | +| ! NOT !true false | +| | +| Truth Table for AND (&&): | +| +-------+-------+--------+ | +| | A | B | A && B | | +| +-------+-------+--------+ | +| | false | false | false | | +| | false | true | false | | +| | true | false | false | | +| | true | true | true | | +| +-------+-------+--------+ | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 6: Bitwise Operators + +### Manipulating Individual Bits + +Bitwise operators work on the binary representation of numbers: + +```c +int x = 6; // Binary: 0b00000110 +int result = x << 1; // Shift left by 1: 0b00001100 = 12 +``` + +``` ++-----------------------------------------------------------------+ +| Bitwise Left Shift (<<) | +| | +| x = 6 = 0b00000110 | +| | +| x << 1 means "shift all bits LEFT by 1 position" | +| | +| Before: 0 0 0 0 0 1 1 0 (6) | +| ? ? ? ? ? ? ? ? | +| After: 0 0 0 0 1 1 0 0 (12) | +| | +| Each left shift DOUBLES the value! | +| 6 << 1 = 12 (same as 6 * 2) | +| | ++-----------------------------------------------------------------+ +``` + +**Common Bitwise Operators:** + +| Operator | Name | Example | Result | +| -------- | ----------- | -------- | ------------------ | +| `<<` | Left shift | `6 << 1` | 12 (multiply by 2) | +| `>>` | Right shift | `6 >> 1` | 3 (divide by 2) | +| `&` | AND | `6 & 3` | 2 (bits in common) | +| `\|` | OR | `6 \| 3` | 7 (all set bits) | +| `^` | XOR | `6 ^ 3` | 5 (different bits) | +| `~` | NOT | `~6` | Inverts all bits | + +--- + +## Part 7: Assignment Operators + +### Shorthand for Math + Assign + +Assignment operators combine math with assignment: + +```c +int x = 6; +x += 5; // Same as: x = x + 5; Result: x = 11 +``` + +``` ++-----------------------------------------------------------------+ +| Compound Assignment Operators | +| | +| Operator Example Equivalent To Result (if x=6) | +| ------------------------------------------------------------- | +| += x += 5 x = x + 5 x = 11 | +| -= x -= 2 x = x - 2 x = 4 | +| *= x *= 3 x = x * 3 x = 18 | +| /= x /= 2 x = x / 2 x = 3 | +| %= x %= 4 x = x % 4 x = 2 | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 8: Understanding the DHT11 Sensor + +### What is the DHT11? + +The **DHT11** is a low-cost digital temperature and humidity sensor. It uses a single wire for communication (plus power and ground). + +``` ++-----------------------------------------------------------------+ +| DHT11 Pinout | +| | +| +-------------+ | +| | DHT11 | | +| | +-----+ | | +| | | | | | +| | | | | | +| | +-----+ | | +| | 1 2 3 4 | | +| +--+--+--+--+-+ | +| | | | | | +| VCC DATA NC GND | +| | +| Pin 1: VCC (3.3V or 5V) | +| Pin 2: DATA (connect to GPIO) | +| Pin 3: Not Connected | +| Pin 4: GND (Ground) | +| | ++-----------------------------------------------------------------+ +``` + +### DHT11 Specifications + +| Parameter | Range | Accuracy | +| --------------- | ------------ | -------- | +| **Humidity** | 20% - 90% RH | ?5% RH | +| **Temperature** | 0 deg C - 50 deg C | ?2 deg C | + +### How DHT11 Communication Works + +The DHT11 uses a custom one-wire protocol: + +1. **Host sends start signal** - Pull data line LOW for 18ms +2. **DHT11 responds** - Pulls line LOW for 80 us, then HIGH for 80 us +3. **Data transmission** - 40 bits sent (8 humidity int, 8 humidity decimal, 8 temp int, 8 temp decimal, 8 checksum) + +--- + +## Part 9: Understanding Pointers (Quick Review) + +### The & Operator (Address-Of) + +When you see `&variable`, it means "the memory address of variable": + +```c +float hum, temp; + +// Pass ADDRESSES to the function so it can modify our variables +if (dht11_read(&hum, &temp)) { + printf("Humidity: %.1f%%, Temperature: %.1f?C\n", hum, temp); +} +``` + +``` ++-----------------------------------------------------------------+ +| Passing by Reference | +| | +| Stack Memory | +| +----------------------------+ | +| | Address 0x20000008: hum |?--- &hum (passed to function) | +| | Value: 51.0 | | +| +----------------------------+ | +| | Address 0x2000000C: temp |?--- &temp (passed to function) | +| | Value: 23.8 | | +| +----------------------------+ | +| | +| dht11_read() receives the ADDRESSES, so it can write | +| new values directly into hum and temp! | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 10: Setting Up Your Environment + +### Prerequisites + +Before we start, make sure you have: + +1. A Raspberry Pi Pico 2 board +2. A Raspberry Pi Pico Debug Probe +3. Ghidra installed (for static analysis) +4. Python installed (for UF2 conversion) +5. A serial monitor (PuTTY, minicom, or screen) +6. A DHT11 temperature and humidity sensor +7. The sample project: `0x001a_operators` + +### Hardware Setup + +Connect your DHT11 like this: + +| DHT11 Pin | Pico 2 Pin | +| --------- | ---------- | +| VCC | 3.3V | +| DATA | GPIO 4 | +| GND | GND | + +``` ++-----------------------------------------------------------------+ +| DHT11 Wiring | +| | +| Pico 2 DHT11 Sensor | +| +----------+ +----------+ | +| | | | | | +| | GPIO 4 |---------- DATA ------?| DATA | | +| | | | | | +| | 3.3V |---------- VCC -------?| VCC | | +| | | | | | +| | GND |---------- GND -------?| GND | | +| | | | | | +| +----------+ +----------+ | +| | +| Note: Some DHT11 modules have a built-in pull-up resistor. | +| If yours doesn't, add a 10K resistor between DATA and VCC. | +| | ++-----------------------------------------------------------------+ +``` + +### Project Structure + +``` +Embedded-Hacking/ ++-- 0x001a_operators/ +| +-- build/ +| | +-- 0x001a_operators.uf2 +| | +-- 0x001a_operators.bin +| +-- main/ +| | +-- 0x001a_operators.c +| +-- dht11.h ++-- uf2conv.py +``` + +--- + +## Part 11: Hands-On Tutorial - The Operators Code + +### Step 1: Review the Source Code + +Let's examine the operators code: + +**File: `0x001a_operators.c`** + +```c +#include +#include "pico/stdlib.h" +#include "dht11.h" + +int main(void) { + stdio_init_all(); + + dht11_init(4); + + int x = 5; + int y = 10; + int arithmetic_operator = (x * y); + int increment_operator = x++; + bool relational_operator = (x > y); + bool logical_operator = (x > y) && (y > x); + int bitwise_operator = (x<<1); // x is now 6 because of x++ or 0b00000110 and (x<<1) is 0b00001100 or 12 + int assignment_operator = (x += 5); + + while (true) { + printf("arithmetic_operator: %d\r\n", arithmetic_operator); + printf("increment_operator: %d\r\n", increment_operator); + printf("relational_operator: %d\r\n", relational_operator); + printf("logical_operator: %d\r\n", logical_operator); + printf("bitwise_operator: %d\r\n", bitwise_operator); + printf("assignment_operator: %d\r\n", assignment_operator); + + float hum, temp; + if (dht11_read(&hum, &temp)) { + printf("Humidity: %.1f%%, Temperature: %.1f?C\r\n", hum, temp); + } else { + printf("DHT11 read failed\r\n"); + } + + sleep_ms(2000); + } +} +``` + +### Step 2: Understand the Variable Flow + +Let's trace through what happens to `x`: + +``` ++-----------------------------------------------------------------+ +| Variable x Through the Program | +| | +| Line | x value | Result | +| ------------------+---------+--------------------------------- | +| int x = 5; | 5 | x initialized to 5 | +| x * y | 5 | arithmetic = 5 * 10 = 50 | +| x++ | 5->6 | increment = 5 (then x becomes 6)| +| x > y | 6 | relational = (6 > 10) = false | +| (x>y) && (y>x) | 6 | logical = false && true = false | +| x << 1 | 6 | bitwise = 6 << 1 = 12 | +| x += 5 | 6->11 | assignment = 6 + 5 = 11 | +| | ++-----------------------------------------------------------------+ +``` + +### Step 3: Flash the Binary to Your Pico 2 + +1. Hold the BOOTSEL button on your Pico 2 +2. Plug in the USB cable (while holding BOOTSEL) +3. Release BOOTSEL - a drive called "RPI-RP2" appears +4. Drag and drop `0x001a_operators.uf2` onto the drive +5. The Pico will reboot and start running! + +### Step 4: Verify It's Working + +Open your serial monitor (PuTTY at 115200 baud) and you should see: + +``` +arithmetic_operator: 50 +increment_operator: 5 +relational_operator: 0 +logical_operator: 0 +bitwise_operator: 12 +assignment_operator: 11 +Humidity: 51.0%, Temperature: 23.8 deg C +``` + +**Understanding the Output:** + +| Variable | Value | Explanation | +| ------------------- | ------ | --------------------------------------------- | +| arithmetic_operator | 50 | 5 * 10 = 50 | +| increment_operator | 5 | Post-increment returns value BEFORE increment | +| relational_operator | 0 | 6 > 10 is false (0) | +| logical_operator | 0 | false AND true = false (0) | +| bitwise_operator | 12 | 6 (0b0110) << 1 = 12 (0b1100) | +| assignment_operator | 11 | 6 + 5 = 11 | +| Humidity | 51.0% | Real reading from DHT11 | +| Temperature | 23.8 deg C | Real reading from DHT11 | + +--- + +## Part 12: Debugging with GDB + +### Step 5: Start OpenOCD (Terminal 1) + +Open a terminal and start OpenOCD: + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +You should see output indicating OpenOCD connected successfully to your Pico 2 via the Debug Probe. + +### Step 6: Start GDB (Terminal 2) + +Open a **new terminal** and launch GDB with the binary: + +```cmd +arm-none-eabi-gdb build\0x001a_operators.elf +``` + +### Step 7: Connect to the Remote Target + +Inside GDB, type: + +``` +target extended-remote :3333 +``` + +This connects GDB to OpenOCD. + +### Step 8: Halt the Running Binary + +``` +monitor reset halt +``` + +This stops the Pico 2 so we can examine its state. + +### Step 9: Examine Main Function + +Let's examine the main function. Disassemble from the entry point: + +``` +x/60i 0x10000234 +``` + +You should see the operator calculations and function calls: + +``` + 0x10000234
: push {r4, r5, r6, r7, lr} + 0x10000236 : sub sp, #20 + 0x10000238 : bl 0x10003384 + 0x1000023c : movs r0, #4 + 0x1000023e : bl 0x100002d4 +... +``` + +### Step 10: Set a Breakpoint at Main + +``` +b *0x10000234 +c +``` + +### Step 11: Find the Operator Calculations + +The compiler likely optimized many of these calculations at compile time. Look for immediate values: + +``` +x/32i 0x10000240 +``` + +You may see values like: + +- `#0x32` (50) for arithmetic_operator +- `#0x5` (5) for increment_operator +- `#0x0` (0) for relational and logical operators +- `#0xc` (12) for bitwise_operator +- `#0xb` (11) for assignment_operator + +### Step 12: Examine Printf Arguments + +Set a breakpoint before the first printf and examine registers: + +```gdb +b *0x10000262 +c +i r r0 r1 +``` + +You should see: + +- `r0` = address of format string +- `r1` = value to print + +### Step 13: Examine the Format Strings + +```gdb +x/s 0x10003978 +``` + +Find the format strings and value for print: +```gdb +(gdb) x/s 0x10003978 +0x10003978: "Humidity: %.1f%%, Temperature: %.1fA°C\r\n" +(gdb) x/x 0x4037cccc +0x4037cccc: 0x00 +(gdb) x/x $r1 +0x4037cccc: 0x00 +... +``` + +### Step 14: Examine DHT11 Function Call + +Find where dht11_read is called: + +```gdb +(gdb) x/3i 0x1000029f +``` + +You'll see stack addresses being passed as arguments: +``` + 0x1000029f : add r1, sp, #12 + 0x100002a1 : add r0, sp, #8 + 0x100002a3 : bl 0x100002f4 +``` + +### Step 15: Watch the Float Values + +After dht11_read returns, examine the float values on the stack: + +```gdb +(gdb) x/2fw $sp+8 +0x20081fe0: 62 23.7999992 +``` + +This shows the humidity and temperature as floats. + +### Step 16: Step Through the Loop + +Continue execution and watch the values: + +```gdb +c +``` + +The program will loop, printing values to serial. + +--- + +## Part 13: Setting Up Ghidra for Analysis + +### Step 17: Start Ghidra + +Open a terminal and type: + +```cmd +ghidraRun +``` + +### Step 18: Create a New Project + +1. Click **File** -> **New Project** +2. Select **Non-Shared Project** +3. Click **Next** +4. Enter Project Name: `0x001a_operators` +5. Click **Finish** + +### Step 19: Import the Binary + +1. Open your file explorer +2. Navigate to the `0x001a_operators/build/` folder +3. **Drag and drop** the `.bin` file into Ghidra's project window + +### Step 20: Configure the Binary Format + +**Click the three dots (...) next to "Language" and:** + +1. Search for "Cortex" +2. Select **ARM Cortex 32 little endian default** +3. Click **OK** + +**Click the "Options..." button and:** + +1. Change **Block Name** to `.text` +2. Change **Base Address** to `10000000` +3. Click **OK** + +### Step 21: Analyze the Binary + +1. Double-click on the file in the project window +2. A dialog asks "Analyze now?" - Click **Yes** +3. Use default analysis options and click **Analyze** + +Wait for analysis to complete. + +--- + +## Part 14: Finding the Reset_Handler + +### Step 22: Understand the Vector Table + +In ARM Cortex-M, the **vector table** is at the base of flash (0x10000000). The second entry (offset 4) contains the Reset_Handler address. + +``` ++-----------------------------------------------------------------+ +| ARM Vector Table at 0x10000000 | +| | +| Offset Contents Description | +| ------------------------------------------------------------- | +| 0x00 Initial SP value Stack pointer at reset | +| 0x04 Reset_Handler addr First code to execute | +| 0x08 NMI_Handler addr Non-maskable interrupt | +| 0x0C HardFault_Handler Hard fault handler | +| ... | +| | ++-----------------------------------------------------------------+ +``` + +### Step 23: Read the Reset_Handler Address + +1. Press `G` (Go to address) and type `10000004` +2. You'll see bytes like `5d 01 00 10` (your exact bytes may vary) + +**Important:** This is **little-endian**, so we need to reverse the byte order! + +``` ++-----------------------------------------------------------------+ +| Little-Endian Byte Order | +| | +| In memory: 5d 01 00 10 | +| Reversed: 10 00 01 5d | +| As hex: 0x1000015d | +| | +| But wait! ARM uses the THUMB bit! | +| The lowest bit indicates Thumb mode (always set for Cortex-M) | +| Real address: 0x1000015d - 1 = 0x1000015c | +| | ++-----------------------------------------------------------------+ +``` + +### Step 24: Navigate to Reset_Handler + +1. Press `G` and type `1000015c` (or your calculated address) +2. You might see undefined data - that's OK! + +### Step 25: Create the Reset_Handler Function + +If Ghidra didn't automatically recognize this as a function: + +1. Click on the address `0x1000015c` +2. Right-click and press `F` to create a function +3. Right-click -> **Edit Function Signature** +4. Change the name to `Reset_Handler` +5. Click **OK** + +### Step 26: Find Main from Reset_Handler + +The Reset_Handler typically calls three functions: + +``` ++-----------------------------------------------------------------+ +| Reset_Handler Flow (crt0.S) | +| | +| Reset_Handler: | +| 1. Call some_init() ?-- Initialize hardware | +| 2. Call main() ?-- THIS IS WHAT WE WANT! | +| 3. Call exit() ?-- Never returns | +| | +| The MIDDLE function is main! | +| | ++-----------------------------------------------------------------+ +``` + +Look at the end of Reset_Handler for three function calls. The middle one is `main`! + +### Step 27: Navigate to Main + +1. Double-click on the middle function call (should be around `0x10000234`) +2. Right-click -> **Edit Function Signature** +3. Change to: `int main(void)` +4. Click **OK** + +--- + +## Part 15: Resolving Functions in Ghidra + +### Step 28: Resolve stdio_init_all + +The first function call in main is `stdio_init_all`: + +1. Find the call at approximately `0x10000238` +2. Double-click to navigate to the function +3. Right-click -> **Edit Function Signature** +4. Change to: `bool stdio_init_all(void)` +5. Click **OK** + +### Step 29: Resolve dht11_init + +Look for a function call where `r0` is loaded with `0x4`: + +```assembly +movs r0, #0x4 ; GPIO pin 4 +bl FUN_xxxxx ; dht11_init +``` + +**How do we know it's dht11_init?** + +- The argument `4` is the GPIO pin number +- We physically connected the DHT11 to GPIO 4! + +1. Right-click -> **Edit Function Signature** +2. Change to: `void dht11_init(uint pin)` +3. Click **OK** + +### Step 30: Resolve printf + +Look for repeated function calls with string addresses: + +1. Find a call like the one at `0x10000262` +2. Right-click -> **Edit Function Signature** +3. Change to: `int printf(char *format,...)` +4. Check the **Varargs** checkbox +5. Click **OK** + +### Step 31: Resolve sleep_ms + +Look for a function call where `r0` is loaded with `0x7d0` (2000 in decimal): + +```assembly +ldr r0, =0x7d0 ; 2000 milliseconds +bl FUN_xxxxx ; sleep_ms +``` + +1. Right-click -> **Edit Function Signature** +2. Change to: `void sleep_ms(uint ms)` +3. Click **OK** + +### Step 32: Resolve dht11_read + +This is trickier! Look for a function call with TWO address arguments: + +```assembly +add r1, sp, #0xc ; Address of temp on stack +add r0, sp, #0x8 ; Address of hum on stack +bl FUN_xxxxx ; dht11_read +``` + +**Understanding the stack offsets:** + +- `sp + 0x8` = address of `hum` variable +- `sp + 0xc` = address of `temp` variable +- These are `float` pointers passed to the function + +1. Right-click -> **Edit Function Signature** +2. Change to: `bool dht11_read(float *humidity, float *temperature)` +3. Click **OK** + +### Step 33: Resolve puts + +Look for a function call after the `if` statement that takes a single string argument: + +```assembly +ldr r0, ="DHT11 read failed" +bl FUN_xxxxx ; puts +``` + +1. Right-click -> **Edit Function Signature** +2. Change to: `int puts(char *s)` +3. Click **OK** + +--- + +## Part 16: Understanding IEEE-754 Floating-Point + +### What is IEEE-754? + +IEEE-754 is the standard for representing decimal numbers in binary. A 32-bit float is divided into three parts: + +``` ++-----------------------------------------------------------------+ +| IEEE-754 Single Precision (32-bit) Float | +| | +| +-----+-------------+---------------------------------------+ | +| | S | Exponent | Mantissa (Fraction) | | +| | 1 | 8 bits | 23 bits | | +| +-----+-------------+---------------------------------------+ | +| bit bits bits | +| 31 30-23 22-0 | +| | +| Value = (-1)^S * (1 + Mantissa) * 2^(Exponent - 127) | +| | ++-----------------------------------------------------------------+ +``` + +### Example: Decoding 0x3dcccccc (0.1f) + +Let's decode the bytes `cc cc cc 3d`: + +1. **Reverse for little-endian:** `0x3dcccccc` +2. **Convert to binary:** `00111101 11001100 11001100 11001100` +3. **Extract fields:** + - Sign (bit 31): `0` (positive) + - Exponent (bits 30-23): `01111011` = 123 + - Mantissa (bits 22-0): `10011001100110011001100` + +4. **Calculate value:** + - Actual exponent: 123 - 127 = -4 + - Mantissa value: 1.6 (approximately) + - Final value: 1.6 * 2^(-4) ? 0.1 + +### Example: Encoding -1.0f as 0xbf800000 + +For the number -1.0: + +1. **Sign:** 1 (negative) +2. **Exponent:** 127 (for 2^0 = 1) +3. **Mantissa:** 0 (because value is exactly 1.0) + +Binary: `1 01111111 00000000000000000000000` +Hex: `0xbf800000` +Little-endian: `00 00 80 bf` + +### Python for Float Conversion + +```python +import struct + +# Decode bytes to float +bytes_data = bytes.fromhex('cdcccc3d') +value = struct.unpack(' **Bytes** +2. A new panel appears showing raw hex bytes + +### Step 38: Enable Editing + +Look for the pencil icon in the Bytes window toolbar and click it to enable editing mode. + +### Step 39: Hack the Scaling Constant + +Let's change the temperature scaling to add 25% more! + +1. Press `G` and go to address `1000042c` +2. Current bytes: `cd cc cc 3d` (0.1f in little-endian) +3. Change to: `00 00 a0 40` (5.0f in little-endian) + +This changes the multiplier from 0.1 to 5.0, which will dramatically increase the temperature reading! + +### Step 40: Verify the Change + +Use Python to verify what we changed: + +```python +import struct + +# Original value +original = struct.unpack('>> import struct +>>> +>>> # Original value +>>> original = struct.unpack('>> print(f"Original: {original}") # 0.1 +Original: 0.10000000149011612 +>>> +>>> # New value +>>> new = struct.unpack('>> print(f"New: {new}") # 5.0 +New: 5.0 +``` + +--- + +## Part 19: Exporting and Testing + +### Step 41: Export the Patched Binary + +1. Click **File** -> **Export Program** +2. Set **Format** to **Raw Bytes** +3. Navigate to your build directory +4. Name the file `0x001a_operators-h.bin` +5. Click **OK** + +### Step 42: Convert to UF2 Format + +Open a terminal and run: + +```cmd +cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x001a_operators +python ..\uf2conv.py build\0x001a_operators-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +### Step 43: Flash and Test + +1. Hold BOOTSEL and plug in your Pico 2 +2. Drag and drop `hacked.uf2` onto the RPI-RP2 drive +3. Open your serial monitor + +You should see dramatically increased temperature readings! + +``` +Humidity: 60.0%, Temperature: 63.0°C +arithmetic_operator: 50 +increment_operator: 5 +relational_operator: 0 +logical_operator: 0 +bitwise_operator: 12 +assignment_operator: 11 +``` + +--- + +## Part 20: Summary and Review + +### What We Accomplished + +1. **Learned all six C operator types** - Arithmetic, increment, relational, logical, bitwise, assignment +2. **Understood post-increment behavior** - `x++` returns value BEFORE incrementing +3. **Learned about the DHT11 sensor** - One-wire protocol for temperature/humidity +4. **Found Reset_Handler from vector table** - Offset 4 contains the address +5. **Identified functions by their arguments** - GPIO pin 4, sleep 2000ms, etc. +6. **Understood IEEE-754 floating-point** - How computers represent decimals +7. **Hacked floating-point constants** - Changed 0.1f to other values + +### The Hacking Workflow + +``` ++-----------------------------------------------------------------+ +| Binary Hacking Workflow | +| | +| 1. Analyze the binary in Ghidra | +| 2. Identify target values/instructions | +| 3. Calculate file offsets from memory addresses | +| 4. Determine replacement bytes | +| 5. Patch the binary (manual in hex editor) | +| 6. Export and convert to UF2 | +| 7. Flash and test | +| | ++-----------------------------------------------------------------+ +``` + +### Key Memory Addresses + +| Memory Address | File Offset | Description | +| -------------- | ----------- | ------------------------------- | +| `0x10000000` | `0x000` | Vector table start | +| `0x10000004` | `0x004` | Reset_Handler address | +| `0x10000234` | `0x234` | main() function (approximately) | +| `0x10000410` | `0x410` | Humidity vfma instruction | +| `0x10000414` | `0x414` | Temperature vfma instruction | +| `0x1000042C` | `0x42C` | 0.1f scaling constant | + +--- + +--- + +## Key Takeaways + +1. **Post-increment returns the OLD value** - `x++` gives you x, THEN adds 1 + +2. **Bitwise left shift multiplies by 2** - `x << 1` is the same as `x * 2` + +3. **Vector table points to Reset_Handler** - Offset 4 from flash base + +4. **Arguments go in r0-r3** - Follow them to identify functions + +5. **IEEE-754 is how floats are stored** - Sign, exponent, mantissa + +6. **File offset = Memory address - Base** - 0x10000410 -> offset 0x410 + +7. **Little-endian reverses byte order** - 0x3dcccccc stored as cc cc cc 3d + +8. **Incremental testing is essential** - Test each change before the next + +10. **Binary patching has real consequences** - Sensor spoofing can be dangerous! + +--- + +## Glossary + +| Term | Definition | +| ------------------ | --------------------------------------------------- | +| **Arithmetic Op** | Operators for math (+, -, *, /, %) | +| **Assignment Op** | Operators that assign and modify (+=, -=, etc.) | +| **Bitwise Op** | Operators on individual bits (<<, >>, &, \|, ^) | +| **DHT11** | Digital humidity and temperature sensor | +| **Exponent** | Power of 2 in IEEE-754 float representation | +| **IEEE-754** | Standard for floating-point number representation | +| **Increment Op** | Operators that add/subtract 1 (++, --) | +| **Little-Endian** | Byte order where least significant byte comes first | +| **Logical Op** | Operators combining conditions (&&, \|\|, !) | +| **Mantissa** | Fractional part of IEEE-754 float | +| **Post-Increment** | `x++` - returns value, then increments | +| **Pre-Increment** | `++x` - increments, then returns value | +| **Relational Op** | Operators comparing values (<, >, ==, !=) | +| **Reset_Handler** | First function executed after CPU reset | +| **Thumb Bit** | Lowest bit of ARM address indicating Thumb mode | +| **Vector Table** | Table of exception/interrupt handler addresses | +| **vfma.f32** | ARM floating-point fused multiply-add instruction | +| **vadd.f32** | ARM floating-point add instruction | + +--- + +## Additional Resources + +### IEEE-754 Float Quick Reference + +| Value | Hex Encoding | Bytes (LE) | +| ----- | ------------ | ----------- | +| 0.1 | 0x3dcccccd | cd cc cc 3d | +| 1.0 | 0x3f800000 | 00 00 80 3f | +| -1.0 | 0xbf800000 | 00 00 80 bf | +| 2.0 | 0x40000000 | 00 00 00 40 | +| -2.0 | 0xc0000000 | 00 00 00 c0 | +| 5.0 | 0x40a00000 | 00 00 a0 40 | +| 10.0 | 0x41200000 | 00 00 20 41 | + +### ARM Floating-Point Instructions + +| Instruction | Description | +| --------------------- | ---------------------------------------- | +| `vfma.f32 Sd, Sn, Sm` | Sd = Sd + (Sn * Sm) (fused multiply-add) | +| `vadd.f32 Sd, Sn, Sm` | Sd = Sn + Sm | +| `vsub.f32 Sd, Sn, Sm` | Sd = Sn - Sm | +| `vmul.f32 Sd, Sn, Sm` | Sd = Sn * Sm | +| `vldr.f32 Sd, [addr]` | Load float from memory | +| `vstr.f32 Sd, [addr]` | Store float to memory | + +--- + +## Real-World Implications + +### Why This Matters + +Imagine a scenario where temperature sensors control critical systems: + +- **Industrial processes** - Chemical reactions that must stay within temperature ranges +- **Medical equipment** - Refrigerators storing vaccines or organs +- **Nuclear facilities** - Cooling systems for reactors +- **HVAC systems** - Climate control in sensitive environments + +By manipulating sensor readings, an attacker could: + +- Cause equipment to overheat while displaying normal temperatures +- Trigger false alarms +- Bypass safety interlocks +- Cause physical damage or safety hazards + +### Defensive Measures + +1. **Redundant sensors** - Multiple sensors with consistency checks +2. **Physical security** - Prevent access to programming interfaces +3. **Anomaly detection** - Alert on sudden reading changes + +--- + +**Remember:** The techniques you learned today can be used for good (security research, debugging) or bad (sabotage, fraud). Always use your skills ethically and legally. Understanding how attacks work helps us build more secure systems! + +Happy hacking! diff --git a/WEEK09/WEEK09.pdf b/WEEK09/WEEK09.pdf new file mode 100644 index 0000000..b901bf2 Binary files /dev/null and b/WEEK09/WEEK09.pdf differ diff --git a/WEEK09/slides/WEEK09-IMG00.svg b/WEEK09/slides/WEEK09-IMG00.svg new file mode 100644 index 0000000..3aadd8e --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 09 + + +Operators in Embedded Systems: +Debugging and Hacking Operators +w/ DHT11 Sensor Single-Wire Protocol + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK09/slides/WEEK09-IMG01.svg b/WEEK09/slides/WEEK09-IMG01.svg new file mode 100644 index 0000000..b431706 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG01.svg @@ -0,0 +1,74 @@ + + + + +C Operators Overview +Six Types of Operators in C + + + +Arithmetic + ++ - * / % +Math operations +5 * 10 = 50 + + +Increment + +x++ ++x x-- +Add/subtract by 1 +x++ returns old val + + +Relational + +> < >= <= == != +Compare values +(6 > 10) = false + + + +Logical + +&& || ! +Combine conditions +AND, OR, NOT + + +Bitwise + +<< >> & | ^ ~ +Manipulate bits +6 << 1 = 12 + + +Assignment + ++= -= *= /= +Assign and modify +x += 5 (x=x+5) + + + +This Week's Program +0x001a_operators.c demonstrates all 6 types +DHT11 temperature/humidity sensor + operator calculations + + + +KEY: +Compiler pre-computes constant expressions +In the binary, most operators become immediate values + \ No newline at end of file diff --git a/WEEK09/slides/WEEK09-IMG02.svg b/WEEK09/slides/WEEK09-IMG02.svg new file mode 100644 index 0000000..3ba9f91 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG02.svg @@ -0,0 +1,75 @@ + + + + +Arithmetic & Increment +Math Operations and Post/Pre Increment + + + +Arithmetic Operators + ++ +5 + 10 = 15 +Addition +- +10 - 5 = 5 +Subtraction +* +5 * 10 = 50 +Multiplication +/ +10 / 5 = 2 +Division +% +10 % 3 = 1 +Modulus + + + +Post vs Pre Increment + + +Post: x++ +Use value THEN increment +a = x++ --> a=5, x=6 + + +Pre: ++x +Increment THEN use value +b = ++x --> x=7, b=7 + + + +Post-Increment Step by Step + + +int x = 5; +int result = x++; + +Step 1: result = x +result gets 5 +Step 2: x = x + 1 +x becomes 6 + +Final: result = 5 +x = 6 +"Use first, THEN increment" + + + +In our code: +int increment_operator = x++; +x was 5, so increment_operator = 5, then x becomes 6 + \ No newline at end of file diff --git a/WEEK09/slides/WEEK09-IMG03.svg b/WEEK09/slides/WEEK09-IMG03.svg new file mode 100644 index 0000000..1244936 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG03.svg @@ -0,0 +1,95 @@ + + + + +Relational & Logical +Comparing Values and Combining Conditions + + + +Relational Operators +Compare two values --> true (1) or false (0) + +> +6 > 10 +false +Greater than +< +6 < 10 +true +Less than +>= +6 >= 6 +true +Greater/equal +<= +6 <= 10 +true +Less or equal +== +6 == 10 +false +Equal to +!= +6 != 10 +true +Not equal + + + +Logical Operators +Combine conditions into one result + +&& +AND -- both must be true +|| +OR -- at least one true +! +NOT -- inverts result + + +AND Truth Table + +A +B +A && B +false +false +false +false +true +false +true +false +false +true +true +true + + + +In Our Code (x=6, y=10) + +bool relational = (x > y); +(6 > 10) = false = 0 +bool logical = (x>y) && (y>x); +false && true = false = 0 + + + +In the binary: +Both compile to immediate #0 +Compiler pre-computes: constants are known at compile time +Result 0 = false, Result 1 = true + \ No newline at end of file diff --git a/WEEK09/slides/WEEK09-IMG04.svg b/WEEK09/slides/WEEK09-IMG04.svg new file mode 100644 index 0000000..02cb827 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG04.svg @@ -0,0 +1,89 @@ + + + + +Bitwise & Assignment +Bit Manipulation and Compound Assignment + + + +Bitwise Operators + +<< +6 << 1 = 12 +Left shift +>> +6 >> 1 = 3 +Right shift +& +6 & 3 = 2 +AND +| +6 | 3 = 7 +OR +^ +6 ^ 3 = 5 +XOR +~ +~6 +NOT (invert) + + +Left shift = multiply by 2 + +0 0 0 0 0 1 1 0 += 6 +0 0 0 0 1 1 0 0 += 12 + + + +Assignment Operators +Shorthand for math + assign + ++= +x += 5 +x = x + 5 +-= +x -= 2 +x = x - 2 +*= +x *= 3 +x = x * 3 +/= +x /= 2 +x = x / 2 +%= +x %= 4 +x = x % 4 + + +In our code (x=6 after x++): + +x += 5 --> 6 + 5 = 11 + + + +In Our Code (x=6, y=10) + +int bitwise = (x<<1); +6 << 1 = 12 (0b0110 --> 0b1100) + + + +Expected Output +bitwise_operator: 12 +assignment_operator: 11 +Both pre-computed by compiler as immediates + \ No newline at end of file diff --git a/WEEK09/slides/WEEK09-IMG05.svg b/WEEK09/slides/WEEK09-IMG05.svg new file mode 100644 index 0000000..4975302 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG05.svg @@ -0,0 +1,72 @@ + + + + +DHT11 Sensor +Single-Wire Temperature and Humidity + + + +DHT11 Pinout + + +DHT11 +1:VCC 2:DATA 3:NC 4:GND + +Humidity: 20-90% RH (+/-5%) +Temp: 0-50C (+/-2C) +Protocol: custom one-wire + + + +Wiring to Pico 2 + + +Pico + + +DHT11 + + + + +GPIO 4 = DATA +3.3V = VCC +GND = GND + + + +1. Host pulls LOW 18ms +2. DHT11 responds, sends 40 bits + + + +Source Code: 0x001a_operators.c + +int x = 5, y = 10; +int arithmetic = (x * y); +// 50 +int increment = x++; +// 5 (post) +bool relational = (x > y); +// false +bool logical = (x>y)&&(y>x); +// false +int bitwise = (x<<1); +// 12 +int assignment = (x += 5); +// 11 +float hum, temp; +dht11_read(&hum, &temp); + \ No newline at end of file diff --git a/WEEK09/slides/WEEK09-IMG06.svg b/WEEK09/slides/WEEK09-IMG06.svg new file mode 100644 index 0000000..ef11927 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG06.svg @@ -0,0 +1,75 @@ + + + + +Variable Flow +Tracing x Through Every Operator + + + +Tracing x Step-by-Step + + +Line +x +Result + + + +int x = 5, y = 10; +5 +x initialized to 5 + + +int arithmetic = (x * y); +5 +arithmetic = 50 + + +int increment = x++; +5-->6 +increment = 5 +use THEN increment + + +bool relational = (x > y); +6 +relational = false +6 > 10 is false + + +bool logical = (x>y)&&(y>x); +6 +logical = false +false AND true = false + + +int bitwise = (x<<1); +6 +bitwise = 12 +0b0110 << 1 = 0b1100 + + +int assignment = (x += 5); +6-->11 +assignment = 11 +6 + 5 = 11 + + + +DHT11 Output +Humidity: 51.0% +Temperature: 23.8C +dht11_read(&hum, &temp) -- passes addresses so function can write values + diff --git a/WEEK09/slides/WEEK09-IMG07.svg b/WEEK09/slides/WEEK09-IMG07.svg new file mode 100644 index 0000000..1e5d297 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG07.svg @@ -0,0 +1,77 @@ + + + + +Vector Table +Finding Reset_Handler and main() + + + +ARM Vector Table +Base address: 0x10000000 + +Offset +Contents +Purpose + + +0x00 +Initial SP +Stack ptr + +0x04 +Reset_Handler +Entry point + +0x08 +NMI_Handler +NMI + +0x0C +HardFault +Fault + + + +Decoding the Address + +At 0x10000004: +Bytes: 5d 01 00 10 + +Step 1: Reverse (little-endian) +10 00 01 5d = 0x1000015d + +Step 2: Remove Thumb bit +0x1000015d - 1 = 0x1000015c + + + +Reset_Handler --> main() + +Reset_Handler at 0x1000015c calls 3 functions: + + +Call 1: some_init() +Hardware initialization + +Call 2: main() +THIS IS WHAT WE WANT +Address: 0x10000234 + +Call 3: exit() +Never returns + +The MIDDLE function call is always main() +Navigate to 0x10000234 in Ghidra to find it + diff --git a/WEEK09/slides/WEEK09-IMG08.svg b/WEEK09/slides/WEEK09-IMG08.svg new file mode 100644 index 0000000..f520db0 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG08.svg @@ -0,0 +1,81 @@ + + + + +IEEE-754 Floats +How Computers Store Decimal Numbers + + + +32-bit Float Structure + + + +S +1 bit + + +Exponent +8 bits + + +Mantissa (Fraction) +23 bits + +Value = (-1)^S x (1 + Mantissa) x 2^(Exponent - 127) + + + +Example: Decoding 0.1f + +Little-endian bytes: +cd cc cc 3d + +Reversed (big-endian): +0x3dcccccd + +Sign: 0 +Exp: 01111011 = 123 +Mantissa: 1001100... + +Exp - 127 = -4, so value = 1.6 x 2^(-4) += 0.1 + + + +IEEE-754 Quick Reference + +Value +Hex +Bytes (LE) + + +0.1 +0x3dcccccd +cd cc cc 3d +1.0 +0x3f800000 +00 00 80 3f + +5.0 +0x40a00000 +00 00 a0 40 +10.0 +0x41200000 +00 00 20 41 + +-1.0 +0xbf800000 +00 00 80 bf + diff --git a/WEEK09/slides/WEEK09-IMG09.svg b/WEEK09/slides/WEEK09-IMG09.svg new file mode 100644 index 0000000..5c40649 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG09.svg @@ -0,0 +1,64 @@ + + + + +Hacking the Float +Changing the DHT11 Scaling Constant + + + +DHT11 Scaling Calculation +result = integer + (decimal x 0.1) +Example: temp = 23 + (8 x 0.1) = 23.8C +0.1f is our target! + + + +Key Offsets in Binary + +Offset +Bytes +Meaning + + +0x410 +a6 ee 25 7a +vfma.f32 s14,s12,s11 (humidity) + +0x414 +e6 ee a5 7a +vfma.f32 s15,s13,s11 (temp) + +0x42C +cd cc cc 3d +0.1f -- the scaling constant + + + +The Hack: 0.1f --> 5.0f + +At offset 0x42C, change: + + +Original: cd cc cc 3d +(0.1f) + + +Patched: 00 00 a0 40 +(5.0f) + +New result: 23 + (8 x 5.0) = 63.0C +Decimal part is now multiplied by 5.0 instead of 0.1 +Export .bin from Ghidra, convert to UF2, flash to Pico + diff --git a/WEEK09/slides/WEEK09-IMG10.svg b/WEEK09/slides/WEEK09-IMG10.svg new file mode 100644 index 0000000..9655865 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG10.svg @@ -0,0 +1,97 @@ + + + + +Operators & DHT11 Hacking +Operators, DHT11, IEEE-754, and Hacking + + + +6 Operator Types + +Arithmetic +x * y = 50 + +Increment +x++ returns 5, x becomes 6 + +Relational +(6 > 10) = false + +Logical +false && true = false + +Bitwise +6 << 1 = 12 + +Assignment +x += 5 = 11 + +Post-increment: use THEN increment + + + +Key Addresses + +0x10000000 +Vector table + +0x10000004 +Reset_Handler addr + +0x10000234 +main() + +0x10000410 +Humidity vfma + +0x10000414 +Temp vfma + +0x1000042C +0.1f constant (hack) + + + +IEEE-754 Format +S(1) + Exp(8) + Mantissa(23) +(-1)^S x (1+M) x 2^(E-127) +0.1f = 0x3dcccccd = cd cc cc 3d + + + +Hack Workflow +1. Analyze in Ghidra +2. Find float at 0x42C +3. Patch cd cc cc 3d + + + +Binary Hacking Steps + +Analyze +--> +Identify +--> +Offset +--> +Patch +--> +Export +--> +Test + +Project: 0x001a_operators +Source: 0x001a_operators.c with DHT11 sensor on GPIO 4 + diff --git a/WEEK10/WEEK10-BN.md b/WEEK10/WEEK10-BN.md new file mode 100644 index 0000000..e4d47c8 --- /dev/null +++ b/WEEK10/WEEK10-BN.md @@ -0,0 +1,1870 @@ +# Week 10-BN: Binary Ninja Personal — Hack Static & Dynamic Conditionals with the SG90 Servo (Raw `.bin`) + +*** + +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +- Build both lesson projects with `Release` and get an `.elf` and a raw `.bin` for each +- Dump the **ELF symbol map** with `arm-none-eabi-nm` and use it as ground truth +- Load each raw `.bin` into Binary Ninja at `0x10000000` +- **Break at `main`** on live silicon, even though `main` can move between programs +- **Hack each running target live** from Binary Ninja's Registers widget and Python console +- **Resolve the functions in the Binary Ninja GUI** using the ELF symbol map +- **Patch** the bytes that control behavior — strings, an IEEE-754 float, an immediate delay, and two `beq` targets — export, convert, and flash +- Understand how a **static** conditional is optimized away while a **dynamic** conditional must keep its `cmp`/`beq`/`bne` branches + +--- + +## How This Guide Works + +Each project builds two files: + +| File | What it is | How we use it | +| ---- | ---------- | ------------- | +| `.elf` | The linked image with a full symbol table and DWARF | Ground truth for every function address and signature | +| `.bin` | The raw flash image, no headers, no symbols | The image we load into Binary Ninja and reverse | + +The `.bin` is built **from** the `.elf`, so the ELF tells you exactly what is at every address. We reverse-engineer the raw `.bin` the way a real extracted firmware image is reversed. + +> **Build `Release`, not `Debug`.** Every address in this guide matches the current `Release` builds. `Release` folds the `static` helpers (`print_if_else`, `print_switch`, `sweep_servo`, `eval_if_else`, `process_servo_command`) into `main` and keeps the code layout stable. If you build `Debug`, the SDK function addresses move and the helpers stay separate, so nothing lines up. Always build `Release` for this lesson. + +The order is **dynamic first, static second**, twice — once per project: + +1. Break on the live target and prove what the code does. +2. Hack it live in the debugger and watch the behavior change. +3. Resolve the functions in Binary Ninja using the ELF symbol map. +4. Patch the bytes, export, convert, and flash. + +| Project | Prints | Also does | The hack | +| ------- | ------ | --------- | -------- | +| `0x001d_static-conditionals` | `1`, then `one`, forever | sweeps the SG90 servo 0° → 180° | angle `180` → `30`, delay `500` → `100`, string `1` → `2`, `one` → `fun` | +| `0x0020_dynamic-conditionals` | `1`+`one` or `2`+`two` from the keyboard | sweeps the servo on `1` / `2` | keys `1`/`2` → `x`/`y`, angle `180` → `30`, skip the prints to go stealth | + +> **Two conditionals, two fates.** In Project 1 the `choice` value is hard-coded (`int choice = 1;`), so the compiler proves the condition at compile time and **deletes the `cmp` and the dead branches** — that is a *static* conditional. In Project 2 `choice = getchar()`, so the value is only known at run time and the compiler must emit the `cmp`/`beq`/`bne` chain — that is a *dynamic* conditional. You will see both in the disassembly. + +> **Addresses come from your build.** Every address here is from the `Release` build produced in Step 3 and was verified against the current `.elf` files with `arm-none-eabi-nm` and `arm-none-eabi-objdump`. Confirm against your own `.elf` with the command in Step 4. + +> **The SVD file lives in `WEEK04`.** If you want the RP2350 peripheral register map for the PWM/UART side of the lab, it is `Embedded-Hacking/WEEK04/rp2350.svd` — it is **not** in `WEEK10`. + +### Background: PWM and the SG90 in one paragraph + +A servo wants a **50 Hz** signal (a 20 ms frame). The RP2350 system clock is **150 MHz**; the `servo.c` driver divides that down to a **1 MHz** tick (1 tick = 1 µs) and wraps the counter at **20,000**, giving a 20 ms frame. The pulse width picks the angle: **1000 µs = 0°**, **1500 µs = 90°**, **2000 µs = 180°**. `servo_set_angle(float)` clamps the angle, maps it to a pulse in `[1000, 2000]`, and writes the PWM compare level. The float travels in a **general-purpose register (`r0`)**, not `s0` — you will see `vmov s14, r0` at the top of `servo_set_angle`. That is why the live angle hack edits `r0`. + +--- + +## Part 1: Build, Flash, and Get the Symbol Map + +### Step 1: Install the toolchain + +**Windows x64** + +- Install the **Raspberry Pi Pico** extension in VS Code. It installs the ARM GNU toolchain, CMake, Ninja, and the Pico SDK. +- Install **Binary Ninja Personal** and complete its license activation. +- Install **PuTTY** for the serial monitor. + +**macOS Apple Silicon** + +```bash +brew install cmake ninja +``` + +- Install **Binary Ninja Personal** and complete its license activation. +- Install the **Arm GNU Toolchain**, or let the VS Code Pico extension manage it. + +**Linux x64** + +```bash +sudo apt install cmake ninja-build gcc-arm-none-eabi libnewlib-arm-none-eabi git python3 openocd minicom +``` + +- Install **Binary Ninja Personal** and complete its license activation. + +### Step 2: Verify your tools are the right architecture (do not skip this) + +On **macOS Apple Silicon**, the most common failure is an Intel `x86_64` tool on your `PATH`: + +``` +zsh: bad CPU type in executable: cmake +``` + +You may have **two Homebrews**: the arm64 one at `/opt/homebrew` and the Intel one at `/usr/local`. If `/usr/local/bin` wins, every `brew` tool is x86_64. Check: + +```bash +file "$(which cmake)" +file "$(which ninja)" +file "$(which arm-none-eabi-gdb)" +file "$(which arm-none-eabi-nm)" +file "$(which openocd)" +``` + +All must report `arm64`. If any is `x86_64`, put the Apple Silicon prefix first for the session and check again: + +```bash +export PATH="/opt/homebrew/bin:$PATH" +hash -r +file "$(which cmake)" +``` + +To make it permanent, add that `export` to `~/.zshrc`. Do not use Rosetta as a fix; OpenOCD and GDB are exactly the kind of programs where a translation layer produces failures that look like debugger bugs. + +**Windows x64** and **Linux x64** do not have this problem. Skip to Step 3. + +### Step 3: Build the two projects with `Release` + +Run this once inside `0x001d_static-conditionals/` and once inside `0x0020_dynamic-conditionals/`: + +```bash +cmake -B build -G Ninja -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 -DCMAKE_BUILD_TYPE=Release +cmake --build build +``` + +**Point Binary Ninja at this repository (once).** Every console snippet below reads the repo root from `~/.embedded-hacking-repo`, so Binary Ninja never needs a database open and nothing is hardcoded. From the repo root, run once: + +**macOS / Linux:** + +```bash +pwd > ~/.embedded-hacking-repo +``` + +**Windows (PowerShell):** + +```powershell +(Get-Location).Path | Set-Content "$env:USERPROFILE\.embedded-hacking-repo" +``` + +**Then build from the Binary Ninja console**, so the whole build → patch → flash loop stays inside Binary Ninja. The console inherits a minimal `PATH` — on macOS just `/usr/bin:/bin:/usr/sbin:/sbin` — so it does not see Homebrew; add your package manager's `bin` first, then run plain `cmake`. + +**macOS Apple Silicon:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +os.environ["PATH"] = "/opt/homebrew/bin:" + os.environ["PATH"] # the console's PATH omits Homebrew +for name in ("0x001d_static-conditionals", "0x0020_dynamic-conditionals"): + proj = os.path.join(root, name) + subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) + subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +**Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +for name in ("0x001d_static-conditionals", "0x0020_dynamic-conditionals"): + proj = os.path.join(root, name) + subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) + subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +for name in ("0x001d_static-conditionals", "0x0020_dynamic-conditionals"): + proj = os.path.join(root, name) + subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) + subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +Each build directory now contains the pair we need: + +- `0x001d_static-conditionals/build/0x001d_static-conditionals.elf` and `.bin` — `.bin` is **8084** bytes (`0x1f94`) +- `0x0020_dynamic-conditionals/build/0x0020_dynamic-conditionals.elf` and `.bin` — `.bin` is **16188** bytes (`0x3f3c`) + +If the ARM toolchain is not on your `PATH`, add `-DPICO_TOOLCHAIN_PATH=...`: + +| OS | Typical toolchain path | +| -- | ---------------------- | +| Windows x64 | `C:/Program Files/Arm GNU Toolchain arm-none-eabi/14.2 rel1/bin` | +| macOS Apple Silicon | `/Applications/ArmGNUToolchain/14.2.rel1/arm-none-eabi/bin` | +| Linux x64 | `/usr` | + +### Step 4: Dump the ELF symbol map + +This is the ground truth for the whole lesson. Run `arm-none-eabi-nm` on each ELF and keep the output in a terminal or a text file: + +**macOS Apple Silicon / Linux x64:** + +```bash +arm-none-eabi-nm -n --defined-only build/0x001d_static-conditionals.elf | grep -E ' [Tt] ' +arm-none-eabi-nm -n --defined-only build/0x0020_dynamic-conditionals.elf | grep -E ' [Tt] ' +``` + +**Windows x64:** + +```powershell +arm-none-eabi-nm -n --defined-only build\0x001d_static-conditionals.elf | Select-String ' [Tt] ' +arm-none-eabi-nm -n --defined-only build\0x0020_dynamic-conditionals.elf | Select-String ' [Tt] ' +``` + +Each line is `address type name`. The `T`/`t` type is a function. The signatures below come from the ELF's DWARF debug info queried with `arm-none-eabi-gdb -batch -ex "ptype "`, so they are exact. + +**Project 1 — our code and the startup chain:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | reset entry | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | calls `runtime_init`, `main`, `exit` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | copies `.data` from flash to SRAM | +| `0x100001e4` | `_init` | `void _init(void)` | runs `.init_array` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | C runtime boilerplate | +| `0x10000234` | `main` | `int main(void)` | the lesson function (all three `static` helpers inlined) | +| `0x1000027c` | `servo_init` | `void servo_init(uint8_t)` | the `servo.c` init function | +| `0x10000318` | `servo_set_angle` | `void servo_set_angle(float)` | the `servo.c` angle setter (clamp + PWM level, all helpers inlined) | + +**Project 1 — the GPIO, timer, stdio, and `puts` chain `main` reaches:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x100003d8` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | SDK GPIO function select | +| `0x10000e28` | `sleep_ms` | `void sleep_ms(uint32_t)` | SDK millisecond delay | +| `0x1000100c` | `time_us_64` | `uint64_t time_us_64(void)` | SDK microsecond clock | +| `0x10001020` | `busy_wait_us` | `void busy_wait_us(uint64_t)` | UART timing loop | +| `0x100010a0` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | SDK UART init | +| `0x10001274` | `clock_get_hz` | `unsigned long clock_get_hz(clock_handle_t)` | UART/servo clock lookup | +| `0x10001604` | `exit` | `void exit(int)` | C runtime exit | +| `0x1000160c` | `runtime_init` | `void runtime_init(void)` | SDK runtime init | +| `0x10001638` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | CRLF output driver | +| `0x100016e8` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | buffered string output | +| `0x100017d4` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | enable the UART driver | +| `0x100017fc` | `stdio_init_all` | `bool stdio_init_all(void)` | SDK serial init | +| `0x1000188c` | `__wrap_puts` | `int __wrap_puts(const char*)` | the `puts` wrapper (both prints) | +| `0x10001a68` | `stdio_uart_init` | `void stdio_uart_init(void)` | SDK UART stdio init | +| `0x10001ba8` | `strlen` | `size_t strlen(const char*)` | C runtime string length | + +**Project 2 — our code and the startup chain:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | reset entry | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | calls `runtime_init`, `main`, `exit` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | copies `.data` from flash to SRAM | +| `0x100001e4` | `_init` | `void _init(void)` | runs `.init_array` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | C runtime boilerplate | +| `0x10000234` | `main` | `int main(void)` | the lesson function (both `static` helpers inlined) | +| `0x100002dc` | `servo_init` | `void servo_init(uint8_t)` | the `servo.c` init function | +| `0x10000378` | `servo_set_angle` | `void servo_set_angle(float)` | the `servo.c` angle setter (all helpers inlined) | + +**Project 2 — the GPIO, timer, stdio, `printf`, `puts`, and `getchar` chain `main` reaches:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x10000438` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | SDK GPIO function select | +| `0x10000e88` | `sleep_ms` | `void sleep_ms(uint32_t)` | SDK millisecond delay | +| `0x1000106c` | `time_us_64` | `uint64_t time_us_64(void)` | SDK microsecond clock | +| `0x10001080` | `busy_wait_us` | `void busy_wait_us(uint64_t)` | UART timing loop | +| `0x10001100` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | SDK UART init | +| `0x100012d4` | `clock_get_hz` | `unsigned long clock_get_hz(clock_handle_t)` | UART/servo clock lookup | +| `0x10002f90` | `vfctprintf` | `int vfctprintf(void (*)(char, void*), void*, const char*, va_list)` | printf format engine | +| `0x10002fec` | `exit` | `void exit(int)` | C runtime exit | +| `0x10002ff4` | `runtime_init` | `void runtime_init(void)` | SDK runtime init | +| `0x10003020` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | CRLF output driver | +| `0x10003130` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | buffered string output | +| `0x1000321c` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | enable the UART driver | +| `0x10003244` | `stdio_init_all` | `bool stdio_init_all(void)` | SDK serial init | +| `0x10003250` | `__wrap_getchar` | `int __wrap_getchar(void)` | the `getchar` wrapper (reads the UART) | +| `0x10003344` | `__wrap_puts` | `int __wrap_puts(const char*)` | the `puts` wrapper | +| `0x10003380` | `__wrap_vprintf` | `int __wrap_vprintf(const char*, va_list)` | printf core | +| `0x10003444` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | the `printf` wrapper (`%s` calls) | +| `0x10003600` | `stdio_uart_init` | `void stdio_uart_init(void)` | SDK UART stdio init | +| `0x10003740` | `strlen` | `size_t strlen(const char*)` | C runtime string length | + +> **`main` is `0x10000234` in both projects.** In Project 1 the three `static` helpers are inlined into `main`; in Project 2 `eval_if_else` and `process_servo_command` are inlined, and `sweep_servo` with them. That is why both projects put `main` at the same address. In a `Debug` build the helpers stay separate and `main` moves — another reason to build `Release`. + +> **`printf` in our source is `__wrap_printf` in the binary.** The SDK links our `printf` calls to its `__wrap_printf` wrapper, which forwards to `__wrap_vprintf`. Project 1 has no `printf` at all: the compiler replaced every `printf("...")` with a `__wrap_puts` because the strings have no format specifiers. + +### Step 5: Flash Project 1 and confirm `1` / `one` + servo sweep + +A `.bin` has no headers, so OpenOCD must be told the base address `0x10000000`. From the repository root: + +**macOS Apple Silicon / Linux x64:** + +```bash +./flash.sh 0x001d_static-conditionals/build/0x001d_static-conditionals.bin +``` + +**Windows x64 (PowerShell):** + +```powershell +.\flash.ps1 -Bin 0x001d_static-conditionals\build\0x001d_static-conditionals.bin +``` + +**Or flash from the Binary Ninja console:** + +**macOS Apple Silicon / Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x001d_static-conditionals", "build", "0x001d_static-conditionals.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("flashing in the background; log:", log) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x001d_static-conditionals", "build", "0x001d_static-conditionals.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # free the probe first +subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "flash.ps1"), "-Bin", bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT) +print("flashing in the background; log:", log) +``` + +Wait for `wrote 8084 bytes ...` and `** Verified OK **`. Open a serial monitor at **115200** baud: + +- **Windows x64:** PuTTY → Connection type **Serial**, the Pico's COM port, speed `115200`. +- **macOS Apple Silicon:** `screen /dev/tty.usbmodem* 115200` (quit with `Ctrl-A` then `K`). +- **Linux x64:** `minicom -D /dev/ttyACM0 -b 115200`. + +``` +1 +one +1 +one +1 +one +... +``` + +The servo sweeps **0° → 180° → 0°** once per second, and because `choice` is hard-coded the same two lines repeat forever. + +### Step 6: Flash Project 2 and confirm the dynamic behavior + +```bash +# macOS / Linux +./flash.sh 0x0020_dynamic-conditionals/build/0x0020_dynamic-conditionals.bin +``` +```powershell +# Windows +.\flash.ps1 -Bin 0x0020_dynamic-conditionals\build\0x0020_dynamic-conditionals.bin +``` + +**Or flash from the Binary Ninja console** (same form, pointing at the Project 2 `.bin`): + +**macOS / Linux:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x0020_dynamic-conditionals", "build", "0x0020_dynamic-conditionals.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("flashing in the background; log:", log) +``` + +**Windows:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x0020_dynamic-conditionals", "build", "0x0020_dynamic-conditionals.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # free the probe first +subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "flash.ps1"), "-Bin", bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT) +print("flashing in the background; log:", log) +``` + +Wait for `wrote 16188 bytes ...`. Nothing prints until you type. In the serial monitor: + +- type `1` → the Pico prints `1` then `one`, and the servo sweeps **0° → 180°**; +- type `2` → it prints `2` then `two`, and the servo sweeps **180° → 0°**; +- type anything else → it prints `??` twice and waits for another key. + +--- + +## Part 2: Load the Raw `.bin` into Binary Ninja (Project 1) + +Start from a fresh Binary Ninja state. If you already have a `.bndb` for this lesson, **close it and start over**; a stale database keeps old names and patches. + +### Step 7: Bring the raw `.bin` into Binary Ninja + +A raw `.bin` has no headers, so Binary Ninja cannot know where it belongs or what architecture it is. You must supply both. If you just double-click the `.bin`, Binary Ninja may load it at address `0x0` with a guessed architecture, and every address in this lesson will be wrong. + +1. Choose `File -> Open with Options...` (do **not** use plain `File -> Open`). +2. Select `0x001d_static-conditionals/build/0x001d_static-conditionals.bin`. +3. In the loader options, set: + - **Architecture:** `thumb2` (the ARMv7-M / ARMv8-M Thumb-2 architecture, which covers the Cortex-M33) + - **Platform:** `thumb2` + - **Base Address:** `0x10000000` (the XIP flash base) +4. Click **Open**. + +Binary Ninja analyzes the image and opens the linear view. + +**Verify the load before going further.** Press `G`, type `0x10000000`, and read the first two words: + +``` +0x10000000 0x20082000 initial stack pointer +0x10000004 0x1000015d reset vector (bit 0 = Thumb) +``` + +If you instead see data at `0x00000000`, or a vector word without bit 0 set, close the tab and repeat with `Open with Options`. The Cortex-M33 only executes Thumb-2, so `thumb2` is the only correct architecture. + +> **Console equivalent:** +> ```python +> load("0x001d_static-conditionals/build/0x001d_static-conditionals.bin", +> options={"loader.imageBase": 0x10000000, "loader.platform": "thumb2"}) +> ``` + +### Step 8: Save it as a Binary Ninja database (`.bndb`) + +Binary Ninja never writes back into the `.bin`. Your names, comments, types, and patches live in a separate **`.bndb`** database. Save one now, before you make any changes: + +1. Choose `File -> Save As...`. +2. Save it next to the image as `0x001d_static-conditionals.bndb`. +3. From now on, save with `File -> Save` (`Cmd+S` on macOS, `Ctrl+S` on Windows/Linux) whenever you rename or patch. + +| File | Role | +| ---- | ---- | +| `0x001d_static-conditionals.bin` | the raw firmware image; Binary Ninja never modifies it | +| `0x001d_static-conditionals.bndb` | your analysis database: names, types, comments, and patches | + +When you come back later, **open the `.bndb`**, not the `.bin`; that restores all your work. You export the patched image out of this view later, in Step 19. + +### Step 9: The views you will use + +- **Linear view:** the disassembly listing. You navigate, read, and patch here. +- **Graph view:** the control-flow graph of the current function. +- **Decompiler (HLIL):** the pseudo-C decompilation. +- **Hex view:** raw bytes, used for patching. +- **Function list:** the sidebar list of every detected function. + +Navigation: `G` go to address, `N` rename, `Y` set type or signature, `;` add a comment. Breakpoints are set from the GUI through the GDB MI adapter — see Step 13. + +> **macOS function keys:** the top-row `F` keys are usually mapped to system functions. Every step here uses menu paths that work without them. + +--- + +## Part 3: Dynamic — Break at `main` and Hack Live (Project 1) + +### Step 10: Start OpenOCD as a live debug server + +Make sure no other OpenOCD is running; a forgotten server holds port `3333`. + +**macOS / Linux:** + +```bash +ps aux | grep -i openocd +``` + +**Windows (PowerShell):** + +```powershell +Get-Process | Where-Object { $_.ProcessName -like '*openocd*' } +``` + +Stop any leftover server gracefully: + +**macOS / Linux:** + +```bash +pkill -TERM -f openocd +``` + +**Windows (PowerShell):** + +```powershell +Get-Process openocd -ErrorAction SilentlyContinue | Stop-Process +``` + +Start the server **parked at `main`**: + +**macOS Apple Silicon / Linux x64:** + +```bash +BP_ADDR=0x10000234 ./debug-server.sh +``` + +**Windows x64 (PowerShell):** + +```powershell +$env:BP_ADDR="0x10000234"; .\debug-server.ps1 +``` + +**Or start it from the Binary Ninja console**, freeing the probe first and launching the server in the background so the console returns immediately: + +**macOS Apple Silicon / Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # stop any running server first +log = os.path.join(root, "openocd.log") +p = subprocess.Popen([os.path.join(root, "debug-server.sh")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("OpenOCD started (pid", p.pid, "); log:", log) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # stop any running server first +log = os.path.join(root, "openocd.log") +p = subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "debug-server.ps1")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT) +print("OpenOCD started (pid", p.pid, "); log:", log) +``` + +`Popen` returns in a few milliseconds; the server keeps running in the background. Check `openocd.log` for `Listening on port 3333`, then connect in Step 11. + +Wait for: + +``` +Info : [rp2350.dap.core0] Examination succeed +Startup breakpoint at 0x10000234 (2-byte hardware execute, one-shot). +Info : starting gdb server for rp2350.dap.core0 on 3333 +Info : Listening on port 3333 for gdb connections +``` + +> **`BP_ADDR` parks the core at `main` before any client connects.** The script arms a 2-byte hardware breakpoint and then does the startup `reset run`, so the core runs from the vector table and stops at your address with no debugger attached yet. When Binary Ninja connects a moment later, the first thing it reads is already the truth: `Stopped at 0x10000234`. + +> **This startup stop is single-use.** OpenOCD flushes breakpoints when a client connects, so this one is gone once Binary Ninja attaches — fine for `main`, which only runs once per reset. Every breakpoint after that is set from the Binary Ninja GUI (Step 13) and is repeatable. To stop at `main` again, restart the server with `BP_ADDR` and reconnect. + +> **Exactly one core.** The line must say `core0` and must **not** mention `core1`. Core1 is never started by this firmware; exposing it makes Binary Ninja read core1's reset-state registers, which are not real addresses, and OpenOCD floods the log with `Failed to read memory at 0xf0000000`. The scripts already use `USE_CORE=0`; do not change it. + +> **Windows driver note:** the Debug Probe must use the **WinUSB** driver. If OpenOCD reports `unable to open CMSIS-DAP device`, install it with [Zadig](https://zadig.akeo.ie/) (select `Debug Probe (CMSIS-DAP)` → WinUSB). + +### Step 11: Connect Binary Ninja to the GDB server + +1. Make sure the image is open and analyzed (Part 2) and the server from Step 10 is running (parked at `main`). +2. Choose `Debugger -> Connect to Remote Process`. +3. In the **adapter** dropdown, select **GDB MI**. +4. In the **connect** settings group, set **IP Address** to `127.0.0.1` and **Port** to `3333`. +5. Set **Full GDB Executable Path** to the `arm-none-eabi-gdb` from the **Arm GNU Toolchain 14.2.rel1**. It ships for all three hosts, and the Raspberry Pi Pico VS Code extension installs that same 14.2.rel1 toolchain (including `arm-none-eabi-gdb`) on all of them: + + | OS | `arm-none-eabi-gdb` path | + | -- | ------------------------ | + | macOS Apple Silicon | `/Applications/ArmGNUToolchain/14.2.rel1/arm-none-eabi/bin/arm-none-eabi-gdb` (or the Pico extension's `~/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb`) | + | Windows x64 | `%USERPROFILE%\.pico-sdk\toolchain\14_2_Rel1\bin\arm-none-eabi-gdb.exe` (Pico extension), or `C:\Program Files (x86)\Arm GNU Toolchain arm-none-eabi\14.2 rel1\bin\arm-none-eabi-gdb.exe` | + | Linux x64 | `~/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb` (Pico extension), or the `bin/` directory of the extracted `arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi` tarball | +6. Click **Accept**. + +> **Use the GDB MI adapter.** It launches a real `arm-none-eabi-gdb --interpreter=mi2` and lets Binary Ninja drive it, so breakpoints and stepping go through real GDB — which sends the correct 2-byte breakpoint length. Verified working end to end: connect, GUI breakpoints (**Add Hardware Breakpoint...**, hardware execute), **Step Into** / **Step Over**, and register edits. + +> **Do NOT have any breakpoints set in Binary Ninja before you connect.** With the GDB MI adapter, attaching while Binary Ninja already has a breakpoint **hangs the session**. Start the server parked with `BP_ADDR` (Step 10), connect, and only add hardware breakpoints *after* the connection is up. This is a Binary Ninja bug; it is the single most common GDB MI failure. + +> **The GDB executable path matters.** Use the **14.2.rel1** build on every OS (Windows, macOS, Linux). The 13.3.rel1 build did **not** connect in testing. +> +> **This step is temporary.** Vector35 plans to ship a GDB binary with the GDB MI adapter ([Vector35/debugger#929](https://github.com/Vector35/debugger/issues/929), milestone *Langara*). Once that lands, Binary Ninja provides GDB itself and you will not need to set **Full GDB Executable Path** at all. + +> **Do not pick Corellium.** Binary Ninja's adapter dropdown also lists **Corellium**, which is for Corellium's virtual devices and expects an API token, not a local OpenOCD server. Always read the label back and confirm it says **GDB MI** before clicking **Accept**. + +> **The adapter and port are not saved in the `.bndb`.** Every time you relaunch Binary Ninja you must re-select **GDB MI**, re-enter port `3333`, and re-set the GDB path. + +The target keeps running. Open the **Registers** tab (bug icon) and confirm you see live values. `pc` inside `0x10003xxx` and `sp` just below `0x20082000` are healthy. + +> **If `pc` is `0x00000088`, `0x000000ec`, or `sp` is `0xf0000000`, the session is bad.** Restart the server, then restart Binary Ninja (a server restart while attached leaves Binary Ninja in a stale session), and connect again. + +### Step 12: Find `main` without relying on its address + +`main` can move between programs, so we do not guess it. We follow the one fixed path to it. Press `G` and go to `0x10000000`: + +``` +0x10000000 0x20082000 initial stack pointer (top of SRAM) +0x10000004 0x1000015d reset vector +``` + +Bit 0 of a vector is the Thumb bit, so `0x1000015d` means "start at `0x1000015c`". That is `_reset_handler`. Follow the reset path to `0x10000186`, `platform_entry`: + +```asm +10000186 : +10000186: ldr r1, [pc, #80] +10000188: blx r1 +1000018a: ldr r1, [pc, #80] +1000018c: blx r1 +1000018e: ldr r1, [pc, #80] +10000190: blx r1 +10000192: bkpt 0x0000 +``` + +**The middle `blx` at `0x1000018c` is the call to `main`.** `platform_entry` is byte-identical in both projects, so `0x1000018c` catches `main` no matter where the linker placed it. The literal pool at `0x100001dc` holds `main | 1`; clearing bit 0 gives `0x10000234`. + +### Step 13: Set a hardware breakpoint in the GUI + +With the **GDB MI** adapter, Binary Ninja sets breakpoints through real GDB, which sends the correct 2-byte length, so you set them **in the UI**. There is no command port here. + +#### Where you can stop + +| You want to stop at | Project 1 address | How | Repeatable? | +| --- | --- | --- | --- | +| **`main`** | `0x10000234` | The server starts parked there with `BP_ADDR=0x10000234` (Step 10), so Binary Ninja is already stopped at `main` when it connects. | No — `main` runs once per reset. | +| **First `puts` (`"1\r\n"`)** | `0x10000246` | Set a hardware breakpoint in the GUI, then click **Resume**. | Yes — fires on every iteration. | +| **Second `puts` (`"one\r"`)** | `0x1000024c` | Same. | Yes. | +| **`servo_set_angle(0.0f)`** | `0x10000252` | Same. | Yes. | +| **`servo_set_angle(180.0f)`** | `0x10000260` | Same — this is the angle we hack. | Yes. | + +#### Set a breakpoint in the GUI + +1. Press `G`, type the address (for example `0x10000260`), and press Enter. +2. Set a **hardware execution** breakpoint at that address, either way: + - `Debugger -> Add Hardware Breakpoint...` — a **hardware execute** (`HE`) breakpoint. **Use this one.** + - click the line and press `F2` (`Debugger -> Toggle Breakpoint`) — a **software** breakpoint. It will **not** work here: the code is in read-only flash, so GDB cannot install it and the core just keeps running. +3. Click **Resume**. The core is already running the loop, so the breakpoint fires on the next iteration. Binary Ninja stops with the PC at the address and reports it as a **Breakpoint**. + +> **No breakpoints before you connect.** With GDB MI, a breakpoint set before the connection hangs the session (Step 11). Start parked with `BP_ADDR`, connect, *then* add breakpoints. + +> **Step Over on the raw `.bin` steps *into* calls.** The raw image has no symbol for `__wrap_puts` or `servo_set_angle`, so **Step Over** at a `bl` behaves like **Step Into**. When the lab needs to execute the call and then stop, it moves the breakpoint to the return site and clicks **Resume** instead (Step 14 shows this). + +> **Never use Binary Ninja's Restart button.** On RP2350 it resets and halts inside the boot ROM (`pc=0x88`, `sp=0xf0000000`). To reset cleanly, restart the server with `BP_ADDR` and reconnect. + +### Step 14: HACK IT LIVE — change the servo angle from 180° to 30° + +`main` loads the constant `0x43340000` (180.0f) into `r4` once, before the loop, then copies it into `r0` at `0x1000025e` right before the `servo_set_angle` call at `0x10000260`. We break on that call and change the angle live. + +1. Press `G`, go to `0x10000260` (the `bl servo_set_angle` for 180.0°). +2. Set a **hardware execute** breakpoint there: `Debugger -> Add Hardware Breakpoint...`. (Do not use `F2` — that is a software breakpoint and will not work on read-only flash.) +3. Click **Resume** in Binary Ninja. The target is already running the loop, so the breakpoint fires on the next pass. Binary Ninja stops with the program counter at `0x10000260` and `r0 = 0x43340000` — the `mov r0, r4` at `0x1000025e` just loaded the 180.0f literal into `r0`. +4. Open the **Registers** widget (bug icon → **Registers**). +5. Find `r0`. Its value is `0x43340000`. +6. **Set `r0` to `0x41f00000` (30.0f).** From Binary Ninja's Python console (`Plugins -> Python Console`): + ```python + dbg.set_reg_value("r0", 0x41f00000) # 30.0f + ``` + `dbg.set_reg_value(name, value)` writes one register (returns `True` on success). You can also right-click `r0` in the **Registers** widget, press `E` (edit), type `41f00000`, and press Enter. The widget may not repaint the value, but the write reaches the target. +7. **Move the breakpoint past the call.** Remove the breakpoint at `0x10000260` and set a hardware breakpoint at `0x10000264` (the `mov.w r0, #500` right after the call). Two reasons not to just click **Step Over**: a breakpoint left on the current PC re-traps the step, and Binary Ninja's **Step Over** steps *into* `servo_set_angle` on this raw `.bin`. +8. Click **Resume**. The core executes `bl servo_set_angle` with `r0 = 0x41f00000`, so this sweep ends at **30°** instead of 180°, then stops at `0x10000264`. Watch the servo arm. + +> **`r4` is the real source — and it never reloads inside the loop.** `r4` is loaded once at `0x10000242` (before the loop starts at `0x10000244`) from the literal at `0x10000270`, so if you set `r4 = 0x41f00000` instead of `r0`, *every* pass uses 30° until the next reset. Editing `r0` changes only the current sweep because the next pass reloads `r0` from `r4`. Both edits are useful: `r0` shows a one-shot live change; `r4` shows a sticky one. + +### Step 14b: HACK THE STRING LIVE — change `one` to `fun` + +The text `"one\r"` lives in flash (`.rodata`) at `0x10001c64`, and flash is **read-only at runtime** — a debugger write there does not stick. So you cannot overwrite the text in place. Instead you redirect the pointer: at the `puts` call, `r0` holds the string address, so you point `r0` at a replacement string you place in RAM. + +1. Press `G`, go to `0x1000024c` (the second `bl __wrap_puts`) and set a **hardware execute** breakpoint. Resume; the loop hits it next pass. At the stop, `r0 = 0x10001c64` — the `ldr r0, [pc, #44]` at `0x1000024a` just loaded the `"one\r"` pointer from the literal at `0x10000278`. +2. Put the replacement string into free RAM at `0x20080000` from Binary Ninja's **Python console**: + ```python + dbg.write_memory(0x20080000, b"fun\r\x00") # puts appends the newline + ``` + `dbg.write_memory(address, bytes)` is Binary Ninja's debugger memory-write API; it returns `True` on success. The bytes are `66 75 6e 0d 00` = `"fun\r\0"`. We keep the `\r` and let `puts` add the `\n`, exactly as the compiler does for the original `"one\r"`. +3. Point `r0` at that string: + ```python + dbg.set_reg_value("r0", 0x20080000) + ``` + (Or right-click `r0` in the **Registers** widget, press `E`, type `20080000`, and press Enter.) +4. Move the breakpoint past the call (remove it at `0x1000024c`, set one at `0x10000250`) and click **Resume**. The core runs `puts` with `r0` pointing at your RAM string, so this iteration prints: + ``` + fun + ``` + then stops at `0x10000250`. + +Like the angle hack, this is **one iteration only**: the loop reloads `r0` from the literal pool on every pass, so the next line is `one` again. The permanent version is the static patch in Step 18. + +### Step 15: Why the hack reverts (and why we patch next) + +Press **Resume**. The loop branches back to `0x10000244`, which reloads `r0` from `0x10001c64` and `0x1000025e` reloads `r0` from `r4`, so the next line is `one` and the next sweep ends at 180° again. The live edits changed one iteration only; nothing in RAM controls these values. To make the changes permanent we must patch the bytes — the static pass. + +Press **Pause** to stop the output flood. + +### Step 15b: Kill the debugger and OpenOCD + +The live hack is done. Do this **before** the static pass. + +1. In the **Debugger** sidebar, click the **X** (**Kill**) (or **`Debugger -> Kill`**) to disconnect Binary Ninja. +2. **Kill does not stop the OpenOCD process** — `debug-server.sh` started it separately, and it keeps running and holding the probe. Stop it from the Binary Ninja console: + + **macOS / Linux:** + + ```python + import subprocess + subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # stop the debug server, free the probe + ``` + + **Windows:** + + ```python + import subprocess + subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # stop the debug server, free the probe + ``` + +3. Confirm nothing is left: `pgrep -fl openocd` (macOS/Linux) prints nothing. + +--- + +## Part 4: Static — Resolve the Functions in Binary Ninja and Patch (Project 1) + +### Step 16: Resolve the functions in the Binary Ninja GUI + +We now name the functions in Binary Ninja using the ELF symbol map from Step 4. Binary Ninja loaded the raw `.bin` with **no symbols**, so every function shows as `sub_` — resolving means giving each one its real name and signature. + +Three keys do all the work: + +| Key | Binary Ninja action | Use it for | +| --- | --- | --- | +| `G` | Go to address | Jump to a function's address | +| `Y` | **Change Type** | Set the function's signature. The dialog shows the full prototype, so this sets the name *and* the type in one step. | +| `N` | Rename | Rename only, when you just want the name and not the type | + +For each function below: `G` to its address, then **`Y` (Change Type)** and type the prototype from the table. + +#### How to resolve a function in Binary Ninja (`Y`) + +`Y` is the **Change Type** key, and it is what actually resolves the function — it turns `void sub_100017fc()` into `bool stdio_init_all(void)`. The Change Type dialog shows the full declaration (name and type), so typing the prototype sets both: + +1. `G` to the function's address. The cursor lands on the function. +2. Press **`Y`**. In the Change Type dialog, type the prototype from the table exactly — for example `bool stdio_init_all(void)` — and press Enter. + +If `Y` seems to do nothing, confirm the cursor is on the function, or right-click it and pick **Change Type...**. Binary Ninja parses what you type and silently keeps the old type if it does not parse, so glance at the header after each `Y`. + +#### Worked example: `main` + +1. Press `G`, type `0x10000234`, press Enter. The cursor lands on `sub_10000234`. +2. Press **`Y`** (Change Type), type `int main(void)`, press Enter. + +> **Binary Ninja shows `int32_t` where Ghidra shows `int`.** After you set `int main(void)`, the decompiler header may read `int32_t main(void)`. That is the same type — on this platform `int` is 32 bits and Binary Ninja's parser normalises it to `int32_t`. Do not fight it; it is not an error. + +#### Worked example: `servo_init` + +1. `G` -> `0x1000027c`. +2. `Y` -> `void servo_init(uint8_t pin)`. + +It takes a `uint8_t` pin number; `main` calls it with `6` (`movs r0, #6`). + +#### Worked example: `servo_set_angle` + +1. `G` -> `0x10000318`. +2. `Y` -> `void servo_set_angle(float degrees)`. + +The float arrives in **`r0`** (soft-float ABI), not `s0`. The function opens with `vmov s14, r0` and then clamps the resulting pulse to `[1000, 2000]` — you can see `cmp.w r3, #2000` and `cmp.w r3, #1000` inside it. + +#### Worked example: `__wrap_puts` + +1. `G` -> `0x1000188c`. +2. `Y` -> `int __wrap_puts(const char *s)`. + +Both prints in `main` land here. `printf("1\r\n")` has no format specifiers, so the compiler replaced it with `puts`; the `\n` was trimmed out of the string because `puts` adds one. + +#### Worked example: `stdio_init_all` + +1. `G` -> `0x100017fc`. +2. `Y` -> `bool stdio_init_all(void)`. + +It returns **`bool`**, not `void` — the ELF says `_Bool stdio_init_all(void)`. Our `main` ignores the return value, so the decompiler still reads cleanly. + +#### Worked example: `sleep_ms` + +1. `G` -> `0x10000e28`. +2. `Y` -> `void sleep_ms(uint32_t ms)`. + +Both delay instructions load `r0 = 0x1f4` (500) immediately before calling it. + +The rest of the chain is the same two keystrokes per function (`G`, then `Y`). This is **our code plus the library functions it actually calls** — not the whole SDK. + +The call chain for this project: + +``` +main +├── stdio_init_all ── stdio_uart_init ── gpio_set_function, uart_init, stdio_set_driver_enabled +│ └── uart_init ── clock_get_hz, busy_wait_us +├── servo_init ── gpio_set_function, clock_get_hz +├── __wrap_puts ── strlen, stdio_put_string ── time_us_64, strlen +└── servo_set_angle, sleep_ms +``` + +**Project 1 — resolve every function in that chain:** + +| Address | Rename to (`N`) | Signature (`Y`) | +| ------- | --------------- | --------------- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | +| `0x100001e4` | `_init` | `void _init(void)` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | +| **`0x10000234`** | **`main`** | **`int main(void)`** | +| `0x1000027c` | `servo_init` | `void servo_init(uint8_t)` | +| `0x10000318` | `servo_set_angle` | `void servo_set_angle(float)` | +| `0x100003d8` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | +| `0x10000e28` | `sleep_ms` | `void sleep_ms(uint32_t)` | +| `0x1000100c` | `time_us_64` | `uint64_t time_us_64(void)` | +| `0x10001020` | `busy_wait_us` | `void busy_wait_us(uint64_t)` | +| `0x100010a0` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | +| `0x10001274` | `clock_get_hz` | `unsigned long clock_get_hz(clock_handle_t)` | +| `0x10001604` | `exit` | `void exit(int)` | +| `0x1000160c` | `runtime_init` | `void runtime_init(void)` | +| `0x10001638` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | +| `0x100016e8` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | +| `0x100017d4` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | +| `0x100017fc` | `stdio_init_all` | `bool stdio_init_all(void)` | +| `0x1000188c` | `__wrap_puts` | `int __wrap_puts(const char*)` | +| `0x10001a68` | `stdio_uart_init` | `void stdio_uart_init(void)` | +| `0x10001ba8` | `strlen` | `size_t strlen(const char*)` | + +> **A `void` return type may not stick — here is the fix.** Binary Ninja treats `void` as low-confidence, and its analysis can override it with an inferred type — most often `int32_t` on this 32-bit target. It is most visible on `_reset_handler` (a hand-written assembly entry that never returns normally), but it can happen to **any** function whose return type Binary Ninja thinks it can infer. +> +> Setting the full signature with `Y` reproduces the unwanted `int32_t`, and `fn.return_type = ...` fails too. What works is the **return-value** setter: +> +> ```python +> from binaryninja import ReturnValue, Type +> fn = bv.get_function_at(0x1000015c) +> if fn is not None: +> fn.return_value = ReturnValue(Type.void()) +> ``` +> +> That holds `_reset_handler` at `void` even after reanalysis. If it still will not stick, leave it — it does not affect the rest of the lesson. + +> **Shortcut — resolves name *and* type for every function.** Instead of doing `N` + `Y` by hand, paste this into Binary Ninja's Python console (`Plugins -> Python Console`). It sets each function's name and signature programmatically: +> +> ```python +> from binaryninja import Symbol, SymbolType +> # The raw .bin has no headers, so these SDK types don't exist. set_user_type() +> # re-parses each signature as C, so an undefined name raises +> # "SyntaxError: unknown type name '...'". Define them first. +> sdk = bv.parse_types_from_string(""" +> typedef unsigned int uint; +> typedef char* va_list; +> typedef unsigned long clock_handle_t; +> struct stdio_driver; +> typedef struct stdio_driver stdio_driver_t; +> struct uart_inst; +> typedef struct uart_inst uart_inst_t; +> enum gpio_function { +> GPIO_FUNC_XIP = 0, GPIO_FUNC_SPI = 1, GPIO_FUNC_UART = 2, GPIO_FUNC_I2C = 3, +> GPIO_FUNC_PWM = 4, GPIO_FUNC_SIO = 5, GPIO_FUNC_PIO0 = 6, GPIO_FUNC_PIO1 = 7, +> GPIO_FUNC_GPCK = 8, GPIO_FUNC_USB = 9, GPIO_FUNC_NULL = 0x1f, +> }; +> typedef enum gpio_function gpio_function_t; +> """) +> for name, ty in sdk.types.items(): +> bv.define_user_type(name, ty) +> +> # address: (name, signature) +> funcs = { +> 0x1000015c: ("_reset_handler", "void _reset_handler(void)"), +> 0x10000186: ("platform_entry", "void platform_entry(void)"), +> 0x1000019a: ("data_cpy", "void data_cpy(void*, void*, void*)"), +> 0x100001e4: ("_init", "void _init(void)"), +> 0x10000210: ("frame_dummy", "void frame_dummy(void)"), +> 0x10000234: ("main", "int main(void)"), +> 0x1000027c: ("servo_init", "void servo_init(uint8_t)"), +> 0x10000318: ("servo_set_angle", "void servo_set_angle(float)"), +> 0x100003d8: ("gpio_set_function", "void gpio_set_function(uint, gpio_function_t)"), +> 0x10000e28: ("sleep_ms", "void sleep_ms(uint32_t)"), +> 0x1000100c: ("time_us_64", "uint64_t time_us_64(void)"), +> 0x10001020: ("busy_wait_us", "void busy_wait_us(uint64_t)"), +> 0x100010a0: ("uart_init", "uint uart_init(uart_inst_t*, uint)"), +> 0x10001274: ("clock_get_hz", "unsigned long clock_get_hz(clock_handle_t)"), +> 0x10001604: ("exit", "void exit(int)"), +> 0x1000160c: ("runtime_init", "void runtime_init(void)"), +> 0x10001638: ("stdio_out_chars_crlf", "void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)"), +> 0x100016e8: ("stdio_put_string", "int stdio_put_string(const char*, int, bool, bool)"), +> 0x100017d4: ("stdio_set_driver_enabled", "void stdio_set_driver_enabled(stdio_driver_t*, bool)"), +> 0x100017fc: ("stdio_init_all", "bool stdio_init_all(void)"), +> 0x1000188c: ("__wrap_puts", "int __wrap_puts(const char*)"), +> 0x10001a68: ("stdio_uart_init", "void stdio_uart_init(void)"), +> 0x10001ba8: ("strlen", "size_t strlen(const char*)"), +> } +> for addr, (name, sig) in funcs.items(): +> bv.define_user_symbol(Symbol(SymbolType.FunctionSymbol, addr, name)) +> f = bv.get_function_at(addr) +> if f is not None: +> f.set_user_type(sig) +> ``` +> +> SDK type names (`stdio_driver_t`, `uart_inst_t`, `gpio_function_t`, plus `uint`, `va_list`, `clock_handle_t`) are **not** in the raw `.bin`. `set_user_type` re-parses each signature as C, so an undefined name raises `SyntaxError: unknown type name '...'` and stops the loop — it is not harmless. The `sdk` block above defines them first. Standard names (`uint8_t`, `uint32_t`, `uint64_t`, `bool`, `size_t`) are built in. + +### Step 17: Read `main` in the decompiler + +Open the **Decompiler** view on `main`: + +```asm +10000234
: +10000234: push {r3, r4, r5, lr} +10000236: bl 100017fc +1000023a: movs r0, #6 +1000023c: bl 1000027c +10000240: movs r5, #0 +10000242: ldr r4, [pc, #44] +10000244: ldr r0, [pc, #44] +10000246: bl 1000188c <__wrap_puts> +1000024a: ldr r0, [pc, #44] +1000024c: bl 1000188c <__wrap_puts> +10000250: mov r0, r5 +10000252: bl 10000318 +10000256: mov.w r0, #500 +1000025a: bl 10000e28 +1000025e: mov r0, r4 +10000260: bl 10000318 +10000264: mov.w r0, #500 +10000268: bl 10000e28 +1000026c: b.n 10000244 +1000026e: nop +10000270: .word 0x43340000 +10000274: .word 0x10001c5c +10000278: .word 0x10001c64 +``` + +The whole program is one loop because the three `static` helpers were inlined: + +- **No `cmp` anywhere.** `choice` is the constant `1`, so the compiler folded `if (choice == 1)` to always-true, deleted `else if (choice == 2)` and `else`, and left only the `1` and `one` prints. That is the static conditional. +- `r5 = 0` (`movs r5, #0` at `0x10000240`) is the `0.0f` angle; `r4` holds `0x43340000` (180.0f) from the literal pool at `0x10000270`. +- `0x10000274` and `0x10000278` point at `"1\r\n"` and `"one\r"` in `.rodata`. + +The decompiler reads roughly: + +```c +int32_t main(void) +{ + stdio_init_all(); + servo_init(6); + do + { + __wrap_puts("1\r\n"); + __wrap_puts("one\r"); + servo_set_angle(0.0f); + sleep_ms(0x1f4); + servo_set_angle(180.0f); + sleep_ms(0x1f4); + } while (true); +} +``` + +### Step 18: Patch 1 — change the strings `1` to `2` and `one` to `fun` + +The strings live in `.rodata`: + +```asm +10000270: .word 0x43340000 +10000274: .word 0x10001c5c +10000278: .word 0x10001c64 +``` + +`0x10001c5c` holds `31 0d 00` = `"1\r"`, and `0x10001c64` holds `6f 6e 65 0d 00` = `"one\r"`. Change the first byte of each string in the **Hex** view (`View -> Hex`, lock off) or the Python console: + +```python +bv.write(0x10001c5c, b"\x32") # "1" -> "2" +bv.write(0x10001c64, b"fun") # "one" -> "fun" +print(bv.read(0x10001c5c, 4)) # -> b'2\r\x00\x00' +print(bv.read(0x10001c64, 6)) # -> b'fun\r\x00\x00' +``` + +Keep the replacement lengths identical: `"1"` is one byte, `"one"` is three. A shorter string must be padded and a longer one would run into the next string. + +### Step 18b: Patch 2 — change the angle from 180.0f to 30.0f + +The 180.0f literal sits at `0x10000270`: + +```asm +10000270: .word 0x43340000 +``` + +It is loaded into `r4` at `0x10000242` and copied into `r0` before the second `servo_set_angle`. IEEE-754: + +- `0x43340000` = 180.0f → little-endian bytes `00 00 34 43` +- `0x41f00000` = 30.0f → little-endian bytes `00 00 f0 41` + +**Option A — Hex view:** go to `0x10000270` and change `00 00 34 43` to `00 00 f0 41`, then reanalyze. + +**Option B — Python console:** + +```python +bv.write(0x10000270, bytes.fromhex("0000f041")) +print(bv.read(0x10000270, 4).hex(" ")) # -> 00 00 f0 41 +``` + +`30.0 = 1.875 × 2^4`; sign `0`, exponent `127 + 4 = 131 = 0x83`, mantissa `0.875 = 0x700000` → `0x41f00000`. + +### Step 18c: Patch 3 — speed up the sweep from 500 ms to 100 ms + +The compiler packed `500` directly into two 32-bit Thumb-2 `mov.w` instructions: + +```asm +10000256: mov.w r0, #500 +1000025a: bl 10000e28 +10000264: mov.w r0, #500 +10000268: bl 10000e28 +``` + +Each `mov.w r0, #500` is the four bytes `4f f4 fa 70`. The four bytes for `mov.w r0, #100` are `4f f0 64 00` (verified by assembling `mov.w r0, #100` with `arm-none-eabi-as`). Change both: + +```python +for addr in (0x10000256, 0x10000264): + bv.write(addr, bytes.fromhex("4ff06400")) # mov.w r0, #100 +``` + +> **Why the bytes change shape.** `500` does not fit in an 8-bit rotated immediate, so the encoder uses the `f4 4f`-family form `4f f4 fa 70`. `100` (`0x64`) does fit, so the encoder uses the `f04f`/`f0 4f` form `4f f0 64 00`. Same instruction, different immediate encoding. Both are exactly 4 bytes. + +Verify all five patches: + +```python +for addr in (0x10001c5c, 0x10001c64, 0x10000270, 0x10000256, 0x10000264): + print(hex(addr), bv.read(addr, 4).hex(" ")) +# -> 0x10001c5c 32 0d 00 00 +# -> 0x10001c64 66 75 6e 0d +# -> 0x10000270 00 00 f0 41 +# -> 0x10000256 4f f0 64 00 +# -> 0x10000264 4f f0 64 00 +``` + +### Step 19: Export the patched `.bin` + +```python +import os +seg = next(s for s in bv.segments if s.data_length) # the loadable image segment +data = bv.read(seg.start, seg.data_length) # base + size come from the view itself +out = os.path.join(os.path.join(root, "0x001d_static-conditionals", "build"), "0x001d_static-conditionals-h.bin") +open(out, "wb").write(data) +print(len(data), out) # -> 8084 /.../build/0x001d_static-conditionals-h.bin +``` + +Where the two numbers come from — nothing is hardcoded: + +- **`seg.start`** is the image base Binary Ninja loaded the `.bin` at (`0x10000000`), the same value you pass to `uf2conv --base`. +- **`seg.data_length`** is the segment's size in the file (`0x1f94` = 8084). Exactly one segment carries data (the image); every peripheral and synthetic segment has `data_length == 0`, so `next(...)` picks the image. + +> **No relative path.** Binary Ninja's Python console runs with a read-only working directory (inside the app bundle), so a relative `open(...)` fails with `OSError: [Errno 30] Read-only file system`. `root` (from `~/.embedded-hacking-repo`, Step 3) is the repo, so the file is written into the project's `build/`. + +### Step 20: Convert to UF2 + +Run from the project directory: + +**macOS Apple Silicon / Linux x64:** + +```bash +python3 ../uf2conv.py 0x001d_static-conditionals-h.bin \ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +**Windows x64:** + +```cmd +python ..\uf2conv.py 0x001d_static-conditionals-h.bin ^ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +> **Or convert from the Binary Ninja console** — `chdir` to a writable directory first (the default one is read-only), then run the script: +> +> ```python +> import os, sys, runpy +> os.chdir(os.path.join(root, "0x001d_static-conditionals", "build")) # the project build dir (writable) +> sys.argv = ["uf2conv.py", "0x001d_static-conditionals-h.bin", +> "--base", "0x10000000", "--family", "0xe48bff59", "--output", "hacked.uf2"] +> runpy.run_path("../../uf2conv.py", run_name="__main__") +> ``` + +### Step 21: Flash and verify + +Hold **BOOTSEL**, plug in the Pico 2, and drag `hacked.uf2` onto the **`RP2350`** drive. Or flash the `.bin` over the Debug Probe with SWD — no BOOTSEL — from the console (stop any running OpenOCD first, and use `Popen`, not `run`, so the console is not blocked): + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +bin_path = os.path.join(os.path.join(root, "0x001d_static-conditionals", "build"), "0x001d_static-conditionals-h.bin") +log = os.path.join(os.path.join(root, "0x001d_static-conditionals", "build"), "flash.log") +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +p = subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("flashing in the background; log:", log) +``` + +Open the serial monitor: + +``` +2 +fun +2 +fun +2 +fun +... +``` + +The servo now sweeps **0° → 30°** and does it **5× faster**. **Five bytes changed, no source code.** + +--- + +## Part 5: Reflash Project 2 and Load It into Binary Ninja + +### Step 22: Reflash Project 2 and restart the session + +Part 4 left the Pico running the patched Project 1 image. Put the original Project 2 back and start a fresh session. + +1. Stop any running debug server so the flash script can use the probe: + + ```bash + # macOS / Linux + pkill -TERM -f openocd + ``` + ```powershell + # Windows + Get-Process openocd -ErrorAction SilentlyContinue | Stop-Process + ``` + +2. Flash the original Project 2 image: + + ```bash + # macOS / Linux + ./flash.sh 0x0020_dynamic-conditionals/build/0x0020_dynamic-conditionals.bin + ``` + ```powershell + # Windows + .\flash.ps1 -Bin 0x0020_dynamic-conditionals\build\0x0020_dynamic-conditionals.bin + ``` + + **Or do steps 1–2 from the Binary Ninja console:** + + **macOS / Linux:** + + ```python + import os, subprocess + root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) + bin_path = os.path.join(root, "0x0020_dynamic-conditionals", "build", "0x0020_dynamic-conditionals.bin") + log = os.path.join(os.path.dirname(bin_path), "flash.log") + subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first + subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) + print("flashing Project 2 in the background; log:", log) + ``` + + **Windows:** + + ```python + import os, subprocess + root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) + bin_path = os.path.join(root, "0x0020_dynamic-conditionals", "build", "0x0020_dynamic-conditionals.bin") + log = os.path.join(os.path.dirname(bin_path), "flash.log") + subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # free the probe first + subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "flash.ps1"), "-Bin", bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT) + print("flashing Project 2 in the background; log:", log) + ``` + +3. Load Project 2 and save its database — see Step 22b. + +Confirm the Pico responds to `1` / `2` again. + +### Step 22b: Load Project 2 into Binary Ninja and save the database + +Exactly like Steps 7–8, but for Project 2. **Use `File -> Open with Options...`** (not plain `File -> Open`), select `0x0020_dynamic-conditionals/build/0x0020_dynamic-conditionals.bin`, and set: + +- **Architecture:** `thumb2` +- **Platform:** `thumb2` +- **Base Address:** `0x10000000` + +Click **Open**. Then press `G`, type `0x10000000`, and confirm the first two words: + +``` +0x10000000 0x20082000 initial stack pointer +0x10000004 0x1000015d reset vector (bit 0 = Thumb) +``` + +If you see data at `0x00000000`, close the tab and redo it with `Open with Options`. + +Save it with `File -> Save As...` as `0x0020_dynamic-conditionals.bndb` (next to the `.bin`). From now on open the `.bndb`, not the `.bin`; save with `Cmd+S` / `Ctrl+S` after every rename or patch. + +> **Console equivalent:** +> ```python +> load("0x0020_dynamic-conditionals/build/0x0020_dynamic-conditionals.bin", +> options={"loader.imageBase": 0x10000000, "loader.platform": "thumb2"}) +> ``` + +--- + +## Part 6: Dynamic — Break at `main` and Hack Live (Project 2) + +### Step 23: Break at `main` + +`main` is at `0x10000234` in this project too. Start the server parked at `main` (Step 10 form) and connect with the **GDB MI** adapter (Step 11): + +1. Restart the server parked at `main`: + + **macOS / Linux:** + + ```bash + BP_ADDR=0x10000234 ./debug-server.sh + ``` + ```powershell + # Windows + $env:BP_ADDR="0x10000234"; .\debug-server.ps1 + ``` + + **Or restart it from the Binary Ninja console:** + + **macOS / Linux:** + + ```python + import os, subprocess + root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() + log = os.path.join(root, "openocd.log") + subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # kill any running server first + subprocess.Popen([os.path.join(root, "debug-server.sh")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) + print("OpenOCD restarted parked at main; log:", log) + ``` + + **Windows:** + + ```python + import os, subprocess + root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() + log = os.path.join(root, "openocd.log") + subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # kill any running server first + subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "debug-server.ps1")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT) + print("OpenOCD restarted parked at main; log:", log) + ``` +2. Connect Binary Ninja (Step 11): adapter **GDB MI**, IP `127.0.0.1`, port `3333`. + +The target is already halted at `main` when Binary Ninja connects, and the sidebar reads `Stopped at 0x10000234`. + +### Step 24: Read `main` and find the dynamic conditional + +The whole loop is one function because both helpers were inlined. Notice the `cmp`/`beq`/`bne` chain the compiler had to keep this time: + +```asm +10000234
: +10000234: push {r3, r4, r5, lr} +10000236: bl 10003244 +1000023a: movs r0, #6 +1000023c: bl 100002dc +10000240: movs r4, #0 +10000242: ldr r5, [pc, #124] +10000244: bl 10003250 <__wrap_getchar> +10000248: uxtb r0, r0 +1000024a: cmp r0, #49 +1000024c: beq.n 10000268 +1000024e: cmp r0, #50 +10000250: beq.n 10000294 +10000252: ldr r0, [pc, #112] +10000254: bl 10003344 <__wrap_puts> +10000258: ldr r0, [pc, #104] +1000025a: bl 10003344 <__wrap_puts> +1000025e: bl 10003250 <__wrap_getchar> +10000262: uxtb r0, r0 +10000264: cmp r0, #49 +10000266: bne.n 1000024e +10000268: ldr r0, [pc, #92] +1000026a: bl 10003344 <__wrap_puts> +1000026e: ldr r1, [pc, #92] +10000270: ldr r0, [pc, #92] +10000272: bl 10003444 <__wrap_printf> +10000276: mov r0, r4 +10000278: bl 10000378 +1000027c: mov.w r0, #500 +10000280: bl 10000e88 +10000284: mov r0, r5 +10000286: bl 10000378 +1000028a: mov.w r0, #500 +1000028e: bl 10000e88 +10000292: b.n 10000244 +10000294: ldr r0, [pc, #60] +10000296: bl 10003344 <__wrap_puts> +1000029a: ldr r1, [pc, #60] +1000029c: ldr r0, [pc, #48] +1000029e: bl 10003444 <__wrap_printf> +100002a2: mov r0, r5 +100002a4: bl 10000378 +100002a8: mov.w r0, #500 +100002ac: bl 10000e88 +100002b0: mov r0, r4 +100002b2: bl 10000378 +100002b6: mov.w r0, #500 +100002ba: bl 10000e88 +100002be: b.n 10000244 +100002c0: .word 0x43340000 +100002c4: .word 0x1000381c +100002c8: .word 0x10003800 +100002cc: .word 0x10003808 +100002d0: .word 0x1000380c +100002d4: .word 0x10003814 +100002d8: .word 0x10003818 +``` + +Because `choice` is now `getchar()` and can be anything, the compiler cannot fold the condition. It emits the comparisons at `0x1000024a` (`cmp r0, #49` = `0x31` = `'1'`) and `0x1000024e` (`cmp r0, #50` = `0x32` = `'2'`), each followed by a `beq.n`. That is the dynamic conditional. + +The string map, read straight from `.rodata`: + +| Literal | Points at | String | +| ------- | --------- | ------ | +| `0x100002c4` | `0x1000381c` | `"??\r"` (the `else` / `default` prints) | +| `0x100002c8` | `0x10003800` | `"1\r\n"` (the `'1'` print) | +| `0x100002cc` | `0x10003808` | `"one"` (the `printf` argument) | +| `0x100002d0` | `0x1000380c` | `"%s\r\n"` (the `printf` format) | +| `0x100002d4` | `0x10003814` | `"2\r\n"` (the `'2'` print) | +| `0x100002d8` | `0x10003818` | `"two"` (the `printf` argument) | + +> **Why the `else` path has two `puts` and a second `getchar`.** The compiler inlined both `eval_if_else` and `process_servo_command`; both have a "default" that prints `"??\r\n"`, and the shared string is emitted twice (`0x10000252` and `0x10000258`). The second `getchar` at `0x1000025e` is the compiler's rotated loop back-edge. The observable behavior is what matters: `1` → `1`+`one`, `2` → `2`+`two`, anything else → `??` twice. + +### Step 25: HACK IT LIVE — drive the branch with `r0` + +`getchar` blocks until you press a key, so this breakpoint fires exactly when a key arrives. We stop right after the read and overwrite the value so the program takes whichever branch we want. + +1. Press `G`, go to `0x1000024a` (the first `cmp r0, #49`). Set a **hardware execute** breakpoint: `Debugger -> Add Hardware Breakpoint...`. +2. Click **Resume** and type any key in the serial monitor — for example `a`. `getchar` returns, the `uxtb` at `0x10000248` runs, and the breakpoint fires at `0x1000024a` with `r0 = 0x61` (`'a'`). +3. **Set `r0` to `0x31` (`'1'`)** from the Python console: + ```python + dbg.set_reg_value("r0", 0x31) # force the '1' branch + ``` + (Or right-click `r0` in the **Registers** widget, press `E`, type `31`, and press Enter.) +4. Remove the breakpoint at `0x1000024a` and click **Resume**. The core runs `cmp r0, #49`, sees the forced `0x31`, and takes the `'1'` branch — so even though you typed `a`, the Pico prints: + ``` + 1 + one + ``` + and sweeps the servo **0° → 180°**. + +> **Change the branch, not the register, if you prefer.** Setting `r0 = 0x32` instead forces the `'2'` path (`2`, `two`, servo 180° → 0°). Setting `r0` to anything else drops into the `??` path. One live register write steers the whole control-flow chain. + +### Step 25b: HACK THE STRING LIVE — change `1` to `7` + +The `'1'` print uses the string at `0x10003800` (`"1\r\n"`). Exactly like Project 1, redirect `r0` to a RAM string at the `puts` call. + +1. Press `G`, go to `0x10000268` (the `bl __wrap_puts` on the `'1'` path) and set a hardware execute breakpoint. Resume and type `1`. At the stop, `r0 = 0x10003800` — the `ldr r0, [pc, #92]` at `0x10000268` loaded the `"1\r\n"` pointer. +2. Write the replacement to RAM and repoint `r0`: + ```python + dbg.write_memory(0x20080000, b"7\r\x00") # puts appends the newline + dbg.set_reg_value("r0", 0x20080000) + ``` +3. Remove the breakpoint at `0x10000268`, set one at `0x1000026a`, and click **Resume**. This iteration prints: + ``` + 7 + one + ``` + One iteration only — the loop reloads `r0` from the literal pool each pass. The permanent version is the static patch in Step 27b. + +### Step 25c: Kill the debugger and OpenOCD + +Same as Step 15b: click the **X** (**Kill**) in the **Debugger** sidebar (or **`Debugger -> Kill`**), then stop OpenOCD from the Binary Ninja console: + +**macOS / Linux:** + +```python +import subprocess +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # stop the debug server, free the probe +``` + +**Windows:** + +```python +import subprocess +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # stop the debug server, free the probe +``` + +--- + +## Part 7: Static — Resolve the Functions and Patch (Project 2) + +### Step 26: Resolve the functions in the Binary Ninja GUI + +Same two keys as Step 16 — `G` to the address, then `Y` (Change Type) to set the prototype — using the Project 2 ELF symbol map from Step 4. + +#### Worked example: `main` + +1. `G` -> `0x10000234`. +2. `Y` -> `int main(void)` (Binary Ninja shows `int32_t main(void)` — the same 32-bit `int`). + +#### Worked example: `__wrap_getchar` + +1. `G` -> `0x10003250`. +2. `Y` -> `int __wrap_getchar(void)`. + +`getchar` returns an `int` in `r0`; `main` immediately narrows it with `uxtb r0, r0` before comparing. + +#### Worked example: `__wrap_printf` + +1. `G` -> `0x10003444`. +2. `Y` -> `int __wrap_printf(const char *fmt, ...)`. Keep the `...` — `printf` is variadic. It forwards to `__wrap_vprintf`. + +#### Worked example: `__wrap_puts` + +1. `G` -> `0x10003344`. +2. `Y` -> `int __wrap_puts(const char *s)`. + +#### Worked example: `servo_set_angle` + +1. `G` -> `0x10000378`. +2. `Y` -> `void servo_set_angle(float degrees)`. + +The clamp constants are inside it: + +```asm +100003ba: cmp.w r3, #2000 +100003be: it cs +100003c0: movcs.w r3, #2000 +100003c4: cmp.w r3, #1000 +100003c8: it cc +100003ca: movcc.w r3, #1000 +``` + +`0x7d0` is the 2000 µs maximum pulse and `0x3e8` is the 1000 µs minimum. + +The call chain for this project: + +``` +main +├── stdio_init_all ── stdio_uart_init ── gpio_set_function, uart_init, stdio_set_driver_enabled +│ └── uart_init ── clock_get_hz, busy_wait_us +├── servo_init ── gpio_set_function, clock_get_hz +├── __wrap_getchar ── busy_wait_us +├── __wrap_puts ── strlen, stdio_put_string ── time_us_64, strlen +├── __wrap_printf ── __wrap_vprintf ── vfctprintf, stdio_out_chars_crlf, time_us_64 +└── servo_set_angle, sleep_ms +``` + +**Project 2 — resolve every function in that chain:** + +| Address | Rename to (`N`) | Signature (`Y`) | +| ------- | --------------- | --------------- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | +| `0x100001e4` | `_init` | `void _init(void)` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | +| **`0x10000234`** | **`main`** | **`int main(void)`** | +| `0x100002dc` | `servo_init` | `void servo_init(uint8_t)` | +| `0x10000378` | `servo_set_angle` | `void servo_set_angle(float)` | +| `0x10000438` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | +| `0x10000e88` | `sleep_ms` | `void sleep_ms(uint32_t)` | +| `0x1000106c` | `time_us_64` | `uint64_t time_us_64(void)` | +| `0x10001080` | `busy_wait_us` | `void busy_wait_us(uint64_t)` | +| `0x10001100` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | +| `0x100012d4` | `clock_get_hz` | `unsigned long clock_get_hz(clock_handle_t)` | +| `0x10002f90` | `vfctprintf` | `int vfctprintf(void (*)(char, void*), void*, const char*, va_list)` | +| `0x10002fec` | `exit` | `void exit(int)` | +| `0x10002ff4` | `runtime_init` | `void runtime_init(void)` | +| `0x10003020` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | +| `0x10003130` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | +| `0x1000321c` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | +| `0x10003244` | `stdio_init_all` | `bool stdio_init_all(void)` | +| `0x10003250` | `__wrap_getchar` | `int __wrap_getchar(void)` | +| `0x10003344` | `__wrap_puts` | `int __wrap_puts(const char*)` | +| `0x10003380` | `__wrap_vprintf` | `int __wrap_vprintf(const char*, va_list)` | +| `0x10003444` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | +| `0x10003600` | `stdio_uart_init` | `void stdio_uart_init(void)` | +| `0x10003740` | `strlen` | `size_t strlen(const char*)` | + +> **Shortcut — resolves name *and* type for every function.** Paste this into Binary Ninja's Python console: +> +> ```python +> from binaryninja import Symbol, SymbolType +> # The raw .bin has no headers, so these SDK types don't exist. set_user_type() +> # re-parses each signature as C, so an undefined name raises +> # "SyntaxError: unknown type name '...'". Define them first. +> sdk = bv.parse_types_from_string(""" +> typedef unsigned int uint; +> typedef char* va_list; +> typedef unsigned long clock_handle_t; +> struct stdio_driver; +> typedef struct stdio_driver stdio_driver_t; +> struct uart_inst; +> typedef struct uart_inst uart_inst_t; +> enum gpio_function { +> GPIO_FUNC_XIP = 0, GPIO_FUNC_SPI = 1, GPIO_FUNC_UART = 2, GPIO_FUNC_I2C = 3, +> GPIO_FUNC_PWM = 4, GPIO_FUNC_SIO = 5, GPIO_FUNC_PIO0 = 6, GPIO_FUNC_PIO1 = 7, +> GPIO_FUNC_GPCK = 8, GPIO_FUNC_USB = 9, GPIO_FUNC_NULL = 0x1f, +> }; +> typedef enum gpio_function gpio_function_t; +> """) +> for name, ty in sdk.types.items(): +> bv.define_user_type(name, ty) +> +> # address: (name, signature) +> funcs = { +> 0x1000015c: ("_reset_handler", "void _reset_handler(void)"), +> 0x10000186: ("platform_entry", "void platform_entry(void)"), +> 0x1000019a: ("data_cpy", "void data_cpy(void*, void*, void*)"), +> 0x100001e4: ("_init", "void _init(void)"), +> 0x10000210: ("frame_dummy", "void frame_dummy(void)"), +> 0x10000234: ("main", "int main(void)"), +> 0x100002dc: ("servo_init", "void servo_init(uint8_t)"), +> 0x10000378: ("servo_set_angle", "void servo_set_angle(float)"), +> 0x10000438: ("gpio_set_function", "void gpio_set_function(uint, gpio_function_t)"), +> 0x10000e88: ("sleep_ms", "void sleep_ms(uint32_t)"), +> 0x1000106c: ("time_us_64", "uint64_t time_us_64(void)"), +> 0x10001080: ("busy_wait_us", "void busy_wait_us(uint64_t)"), +> 0x10001100: ("uart_init", "uint uart_init(uart_inst_t*, uint)"), +> 0x100012d4: ("clock_get_hz", "unsigned long clock_get_hz(clock_handle_t)"), +> 0x10002f90: ("vfctprintf", "int vfctprintf(void (*)(char, void*), void*, const char*, va_list)"), +> 0x10002fec: ("exit", "void exit(int)"), +> 0x10002ff4: ("runtime_init", "void runtime_init(void)"), +> 0x10003020: ("stdio_out_chars_crlf", "void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)"), +> 0x10003130: ("stdio_put_string", "int stdio_put_string(const char*, int, bool, bool)"), +> 0x1000321c: ("stdio_set_driver_enabled", "void stdio_set_driver_enabled(stdio_driver_t*, bool)"), +> 0x10003244: ("stdio_init_all", "bool stdio_init_all(void)"), +> 0x10003250: ("__wrap_getchar", "int __wrap_getchar(void)"), +> 0x10003344: ("__wrap_puts", "int __wrap_puts(const char*)"), +> 0x10003380: ("__wrap_vprintf", "int __wrap_vprintf(const char*, va_list)"), +> 0x10003444: ("__wrap_printf", "int __wrap_printf(const char*, ...)"), +> 0x10003600: ("stdio_uart_init", "void stdio_uart_init(void)"), +> 0x10003740: ("strlen", "size_t strlen(const char*)"), +> } +> for addr, (name, sig) in funcs.items(): +> bv.define_user_symbol(Symbol(SymbolType.FunctionSymbol, addr, name)) +> f = bv.get_function_at(addr) +> if f is not None: +> f.set_user_type(sig) +> ``` + +### Step 27: Patch 1 — change the servo angle from 180.0f to 30.0f + +The 180.0f literal sits at `0x100002c0`, loaded into `r5` at `0x10000242`: + +```asm +100002c0: .word 0x43340000 +``` + +Change the four bytes exactly as in Project 1: + +```python +bv.write(0x100002c0, bytes.fromhex("0000f041")) # 180.0f -> 30.0f +print(bv.read(0x100002c0, 4).hex(" ")) # -> 00 00 f0 41 +``` + +Both servo paths (`0x10000284`/`0x100002a2` use `r5`, `0x10000276`/`0x100002b0` use `r4` = 0.0f) now cap at 30°. + +### Step 27b: Patch 2 — rewrite the secret keys `1` → `x` and `2` → `y` + +The two key comparisons are at `0x1000024a` and `0x1000024e`: + +```asm +1000024a: cmp r0, #49 +1000024e: cmp r0, #50 +``` + +The immediate is the low byte of the 16-bit `cmp` encoding: `0x31` at `0x1000024a` and `0x32` at `0x1000024e`. Change them to `x` (`0x78`) and `y` (`0x79`): + +```python +bv.write(0x1000024a, b"\x78") # cmp r0, #0x78 ('x') +bv.write(0x1000024e, b"\x79") # cmp r0, #0x79 ('y') +print(bv.read(0x1000024a, 2).hex(" ")) # -> 78 28 +print(bv.read(0x1000024e, 2).hex(" ")) # -> 79 28 +``` + +Now `x` takes the old `1` path and `y` takes the old `2` path. + +### Step 27c (optional): Patch 3 — make `x` and `y` stealth (skip the prints) + +The original `1`/`2` paths print before moving the servo. To make the new `x`/`y` keys silent, redirect the two `beq.n` targets straight to the servo code, skipping both prints. The current targets are `0x10000268` (the `1` print block) and `0x10000294` (the `2` print block); the servo code starts at `0x10000276` (`mov r0, r4`) and `0x100002a2` (`mov r0, r5`). + +| Address | Instruction | Before | After | New target | +| ------- | ----------- | ------ | ----- | ---------- | +| `0x1000024c` | `beq.n` | `0c d0` | `13 d0` | `0x10000276` (skip `1`/`one` prints) | +| `0x10000250` | `beq.n` | `20 d0` | `27 d0` | `0x100002a2` (skip `2`/`two` prints) | + +```python +bv.write(0x1000024c, bytes.fromhex("13d0")) # beq.n -> 0x10000276 +bv.write(0x10000250, bytes.fromhex("27d0")) # beq.n -> 0x100002a2 +``` + +> **How the encoding was chosen.** A 16-bit conditional branch is `1101 cond imm8`; the target is `PC + 4 + (imm8 << 1)`. For `0x1000024c` → `0x10000276`: `(0x276 - 0x250) / 2 = 0x13`. For `0x10000250` → `0x100002a2`: `(0x2a2 - 0x254) / 2 = 0x27`. Both were verified by patching a copy of the raw `.bin` and disassembling it with `arm-none-eabi-objdump`. This patch is optional; the key rewrite in Step 27b works without it (it just still prints). + +### Step 28: Export, convert, and flash + +```python +import os +seg = next(s for s in bv.segments if s.data_length) # the loadable image segment +data = bv.read(seg.start, seg.data_length) # base + size from the view itself +out = os.path.join(os.path.join(root, "0x0020_dynamic-conditionals", "build"), "0x0020_dynamic-conditionals-h.bin") +open(out, "wb").write(data) +print(len(data), out) # -> 16188 /.../build/0x0020_dynamic-conditionals-h.bin +``` + +`seg.data_length` is the image size (`0x3f3c` = 16188) read from the view — nothing hardcoded. + +**macOS Apple Silicon / Linux x64:** + +```bash +python3 ../uf2conv.py 0x0020_dynamic-conditionals-h.bin \ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +**Windows x64:** + +```cmd +python ..\uf2conv.py 0x0020_dynamic-conditionals-h.bin ^ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +Or run the conversion from the Binary Ninja console, exactly as in Step 20 (`os.chdir` to the build dir, then `runpy.run_path("../../uf2conv.py", run_name="__main__")` with `sys.argv` set to the arguments above). + +Hold **BOOTSEL**, plug in the Pico 2, drag `hacked.uf2` onto the **`RP2350`** drive. Or flash the `.bin` over the Debug Probe with SWD — no BOOTSEL — from the console, exactly as in Step 21. + +### Step 29: Verify + +Open the serial monitor: + +- type `x` → with Step 27c applied, **no output** and the servo sweeps silently; without it, `x` prints `x`… actually it prints the original strings `1` / `one` because only the comparison changed (the print strings are untouched). With Step 27c applied the prints are skipped entirely — a **stealth command**. +- type `y` → likewise silent (Step 27c), and the servo sweeps the other way. +- the servo's maximum angle is now **30°**, not 180°. +- the original `1` and `2` keys no longer match the comparisons. + +**We changed the servo angle and hid two secret keys, with a handful of bytes and no source code.** + +--- + +## Cheatsheet + +### Binary Ninja GUI actions + +| Action | How | +| ------ | --- | +| Go to address | `G` | +| Rename function/symbol | `N` | +| Set type or signature | `Y` | +| Add comment | `;` | +| Open Hex view | `View -> Hex` | +| Enable hex editing | Toggle the lock in the status bar | +| Reanalyze after a patch | Right-click function -> `Reanalyze` | +| Edit a register live | `dbg.set_reg_value("r0", 0x41f00000)` in the Python console (or right-click the register, press `E`, type hex, Enter) | +| Write debugger memory | `dbg.write_memory(0x20080000, b"fun\r\x00")` | +| Set a breakpoint | `Debugger -> Add Hardware Breakpoint...` (hardware execute). Do **not** use `F2` — software breakpoints cannot be written to read-only flash. | +| Move a breakpoint | Remove it and set it at the new address in the GUI | +| Confirm what is armed | The **Breakpoints** widget lists it | +| Apply the ELF symbol map | Paste the Python snippet from Step 16 / 26 into the Python Console | + +### OpenOCD server and reset + +The server runs with `gdb_breakpoint_override hard` so that flash-writes are never attempted. Breakpoints in this lab are set in the Binary Ninja GUI through the **GDB MI** adapter (Step 13). + +| Action | Command | +| ------ | ------- | +| Start the server parked at `main` | macOS/Linux: `BP_ADDR=0x10000234 ./debug-server.sh` — Windows: `$env:BP_ADDR="0x10000234"; .\debug-server.ps1` (one-shot) | +| Break on a loop address in a running target | set a hardware breakpoint in the GUI, then **Resume** — repeatable | +| Kill the debugger | click **X** in the **Debugger** sidebar, or `Debugger -> Kill` | +| Stop OpenOCD | macOS/Linux: `pkill -TERM -f openocd` — Windows: `taskkill /F /IM openocd.exe` | +| Make Binary Ninja stepping work | `rp2350.dap.core0 configure -rtos none` (already in the scripts) | +| Step without re-trapping | move the breakpoint off the current PC first, then **Step Into**/**Step Over** | +| Reset without desyncing Binary Ninja | **Detach**, `reset run`, reconnect — never `reset run` while attached | + +### Where you can stop + +| Stop at | Project 1 `0x001d` | Project 2 `0x0020` | +| ------- | ------------------ | ------------------ | +| `main` (once per reset) | `0x10000234` | `0x10000234` | +| First `puts` | `0x10000246` (`"1\r\n"`) | `0x10000268` (`"1\r\n"`) | +| Second `puts` | `0x1000024c` (`"one\r"`) | — | +| `getchar` return (dynamic key) | — | `0x1000024a` (`cmp r0, #0x31`) | +| `servo_set_angle(0.0f)` | `0x10000252` | `0x10000276` / `0x100002a2` | +| `servo_set_angle(180.0f)` | `0x10000260` | `0x10000284` / `0x100002b0` | + +### Every address and byte we changed + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x001d` | `0x10001c5c` | `31` | `32` | prints `2` instead of `1` | +| `0x001d` | `0x10001c64` | `6f 6e 65` | `66 75 6e` | prints `fun` instead of `one` | +| `0x001d` | `0x10000270` | `00 00 34 43` | `00 00 f0 41` | servo max angle `180.0f` → `30.0f` | +| `0x001d` | `0x10000256` | `4f f4 fa 70` | `4f f0 64 00` | first `sleep_ms` `500` → `100` | +| `0x001d` | `0x10000264` | `4f f4 fa 70` | `4f f0 64 00` | second `sleep_ms` `500` → `100` | +| `0x0020` | `0x1000024a` | `31` | `78` | compare `'1'` → `'x'` | +| `0x0020` | `0x1000024e` | `32` | `79` | compare `'2'` → `'y'` | +| `0x0020` | `0x100002c0` | `00 00 34 43` | `00 00 f0 41` | servo max angle `180.0f` → `30.0f` | +| `0x0020` | `0x1000024c` | `0c` | `13` | `beq.n` skips the `1`/`one` prints (optional) | +| `0x0020` | `0x10000250` | `20` | `27` | `beq.n` skips the `2`/`two` prints (optional) | + +### Raw image facts + +| Item | Value | +| ---- | ----- | +| Build type | `Release` | +| Load base address | `0x10000000` | +| Project 1 size | `8084` bytes | +| Project 2 size | `16188` bytes | +| Initial stack pointer (both) | `0x20082000` | +| Reset vector (both) | `0x1000015d` | +| Fixed `main` anchor (both) | `0x1000018c` | +| `main` (both) | `0x10000234` | +| `servo_set_angle`, Project 1 | `0x10000318` | +| `servo_set_angle`, Project 2 | `0x10000378` | +| Project 1 `180.0f` literal | `0x10000270` | +| Project 2 `180.0f` literal | `0x100002c0` | +| Project 1 `one` string | `0x10001c64` | +| Project 2 `one` string | `0x10003808` | +| RP2350 UF2 family ID | `0xe48bff59` | + +--- + +## Troubleshooting + +### Binary Ninja hangs or crashes when you connect (macOS 27) + +Three different causes have been seen on this setup; check them in this order. + +- **A breakpoint set before connecting.** With the **GDB MI** adapter, if the binary view already has a breakpoint, the session hangs. Start parked with `BP_ADDR`, connect, then add breakpoints. +- **The wrong GDB executable.** Point **Full GDB Executable Path** at the **14.2.rel1** toolchain (Step 11). The 13.3.rel1 build did not connect in testing. +- **The LLDB adapter.** A crash report with `libdebuggercore.dylib -> std::terminate() -> abort()` and `liblldb` in the stack is the **LLDB** adapter, not GDB MI. Avoid LLDB on this setup. + +If Binary Ninja hangs, force-quit it; the connect dialog has no working Cancel. The static steps (resolve, patch, export, flash) never touch the debugger and always work. + +### GDB MI hangs when you connect (a breakpoint already existed) + +With the **GDB MI** adapter, if Binary Ninja already has a breakpoint set when you connect, the session **hangs**. The working order is: + +1. Start the server parked, e.g. `BP_ADDR=0x10000234 ./debug-server.sh` (Windows: `$env:BP_ADDR="0x10000234"; .\debug-server.ps1`). +2. Connect with the **GDB MI** adapter. +3. Only *then* set hardware breakpoints in the UI. + +### Step Into / Step Over does nothing (PC never moves) + +Two causes have been seen on this target. + +1. **A breakpoint on the current PC re-traps the step.** OpenOCD's step-over-breakpoint logic fails with `Duplicate Breakpoint address` and the PC stays put. Fix: move the breakpoint off the current PC (in the GUI), then step. +2. **The `hwthread` RTOS (GDB RSP adapter only).** With the **GDB RSP** adapter, OpenOCD can log `fake step thread 0` and reply without stepping. Fix: `rp2350.dap.core0 configure -rtos none`. **GDB MI does not hit this.** + +### `zsh: bad CPU type in executable: cmake` + +An Intel `x86_64` tool is on your `PATH` on Apple Silicon. Run Step 2: `export PATH="/opt/homebrew/bin:$PATH"`, then `hash -r`. + +### My addresses do not match this guide + +You probably built `Debug`. This lesson is a `Release` build. Re-run Step 3 with `-DCMAKE_BUILD_TYPE=Release`. A `Debug` build moves the SDK functions and keeps the `static` helpers separate. + +### A breakpoint never fires + +First, confirm you actually set one, and that it is a **hardware** breakpoint. `Debugger -> Add Hardware Breakpoint...` (hardware execute) should land in the **Breakpoints** widget. If nothing lands, or the core keeps running, you probably used `F2` (`Toggle Breakpoint`) — a software breakpoint cannot be written to read-only flash. + +Then check the order and the state: + +- **Arm it only after Binary Ninja is connected.** OpenOCD flushes every breakpoint when a client attaches, so anything armed earlier is gone. This also applies to `BP_ADDR` on the startup command line. +- **Is the core running?** If it is stopped, click **Resume**. +- **Does the address get reached again?** `main` runs once per reset, so use `BP_ADDR` at startup rather than `reset run` while attached. Loop addresses such as `0x10000260` fire on the next pass with no reset. For Project 2's `0x1000024a` you must also press a key, because it sits right after the blocking `getchar`. + +### I edit `r0` (or another register) and it reverts + +For the Project 1 angle, `mov r0, r4` at `0x1000025e` reloads `r0` on every pass, so the edit is visible for one sweep unless you stop the core again. Editing `r4` instead makes it stick, because `r4` is loaded once before the loop. For Project 2, `getchar` reloads `r0` on every key press. The edit sticks only while the core is **genuinely stopped** at the breakpoint. + +> **The Registers widget is a snapshot, not a live view.** Binary Ninja reads the registers at each stop and shows that snapshot; it does not poll the target. A value changed outside Binary Ninja will not appear until the next stop. + +### The string hack does nothing (or prints garbage) + +Pick a RAM address that is free — `0x20080000` is safe here (well above the `.data`/`.bss` end at about `0x2000062c`). Write a NUL-terminated string, and remember `__wrap_puts` appends its own `\n`, so keep the `\r` but not the `\n` (write `b"fun\r\x00"`). Then set `r0`, not `r1`. + +### The patched string shifted `printf` output + +In Project 1 both prints are `puts`, so only whole-string replacement matters. Keep `"one"` → `"fun"` exactly three bytes; a longer string would overwrite the `\r` terminator and a shorter one would leave a stray character. + +### `mov.w r0, #100` corrupts the instruction + +Use the four bytes `4f f0 64 00`, not the first two bytes of the `500` encoding. `500` needs the `4f f4 …` form; `100` uses the `4f f0 …` form. Both are 4 bytes. Verify with `arm-none-eabi-objdump` (Step 20/21 context) or by re-reading the bytes in Binary Ninja. + +### Project 2's `x`/`y` still print + +Step 27b only changes the *comparison* values. To make the keys silent you must also apply the optional `beq` redirects in Step 27c. If the servo moves but the terminal still shows `1`/`one`, you applied 27b but not 27c. + +### The optional `beq` redirect sends execution somewhere wrong + +Recompute from the ELF: `target = PC + 4 + (imm8 << 1)`. For `0x1000024c` the servo code is at `0x10000276` (imm8 `0x13`); for `0x10000250` it is at `0x100002a2` (imm8 `0x27`). Confirm the byte pair you write is little-endian (`13 d0`, `27 d0`). + +### The serial capture is garbage on macOS + +Reading `/dev/cu.usbmodem*` with a bare `read()` returns garbage. Set **raw termios at 115200** first, or just use `screen /dev/cu.usbmodem* 115200`, which does it for you. + +### It worked for a second, then stopped (Binary Ninja's view desyncs) + +The main cause is **driving the core from the OpenOCD command port while Binary Ninja is connected**. If you must reset, **Detach first**, reset, then reconnect. Never leave a breakpoint on the PC you are about to step or resume from. + +### The console floods with `Failed to read memory at 0xf0000000` + +Core1 is exposed. The scripts must run with `USE_CORE=0`. Stop the server, confirm only `core0` is reported, restart, then restart Binary Ninja. + +### The decompiler still shows the old value after patching + +Right-click the function and choose `Reanalyze`. + +--- + +## Fallback: do the dynamic steps with GDB (macOS 27) + +If Binary Ninja's debugger crashes on attach on macOS 27, you can still do the live hacks with the ARM GDB from the toolchain, against the same OpenOCD server. The addresses and register values are identical to the GUI steps. + +Start the debug server (Step 10), then in a new terminal: + +``` +arm-none-eabi-gdb +``` + +At the `(gdb)` prompt: + +``` +set architecture armv8-m.main +target extended-remote :3333 +hbreak *0x10000260 +continue +``` + +Do **not** run `monitor reset run` before `hbreak`. `0x10000260` is inside `main`'s loop, so the breakpoint fires on the next iteration with no reset. GDB stops at the `servo_set_angle` call: + +``` +info registers pc r0 # pc = 0x10000260, r0 = 0x43340000 +set $r0 = 0x41f00000 +continue +``` + +The servo's next sweep ends at 30° — the same temporary live hack as editing `r0` in the Binary Ninja Registers widget. For the string hack, break at `0x1000024c`, then `set {char[5]}0x20080000 = "fun\r"` and `set $r0 = 0x20080000`. + +Project 2 is the same with the other call site and value: + +``` +hbreak *0x1000024a +continue +info registers pc r0 # r0 holds the key you typed +set $r0 = 0x31 +continue +``` + +`hbreak` sets a hardware breakpoint, which is required for read-only flash. It works from plain GDB because GDB sends the 2-byte length the Cortex-M33 comparators need. Binary Ninja's **GDB MI** adapter goes through the same GDB, so its GUI breakpoints work too. + +## Glossary + +| Term | Definition | +| ---- | ---------- | +| **`beq`** | Branch if Equal — ARM conditional jump, taken when the Z flag is set | +| **`bne`** | Branch if Not Equal — ARM conditional jump, taken when the Z flag is clear | +| **`.bss`** | Section for uninitialized global variables; zeroed by startup code | +| **`.data`** | Section for initialized global variables; copied from flash to SRAM at boot | +| **Dynamic conditional** | A condition whose value is only known at run time (e.g. `getchar()`), so the compiler must emit the comparisons and branches | +| **`.elf`** | Linked image with the symbol table; the ground truth for addresses and names | +| **GPIO** | General Purpose Input/Output — controllable pins on the microcontroller | +| **Hardware breakpoint** | A breakpoint serviced by the CPU comparators, required for read-only flash | +| **Inlining** | The optimizer replacing a function call with the function body; why the `static` helpers disappear from `main` in `Release` | +| **Literal pool** | A block of 32-bit constants that Thumb-2 code reaches with PC-relative `ldr` | +| **PWM** | Pulse Width Modulation — a variable pulse-width signal; 50 Hz for the SG90 | +| **`.rodata`** | Read-only section for constants and string literals; stays in flash | +| **SG90** | A common 0°–180° hobby servo driven by a 1–2 ms pulse every 20 ms | +| **SIO** | Single-cycle I/O — the fast GPIO block in the RP2350, at `0xd0000000` | +| **Static conditional** | A condition whose value is known at compile time, so the compiler folds it and deletes dead branches | +| **Thumb bit** | Bit 0 of a Cortex-M function pointer; selects Thumb instruction mode | +| **UF2** | USB Flashing Format — the file format the Pico 2 bootloader accepts | +| **Vector table** | The first words of flash: initial stack pointer and exception vectors | + +--- + +**Remember:** the ELF tells you what every address is, and the `.bin` is what you actually patch. Prove the behavior dynamically, resolve the names from the ELF, then patch the bytes and flash. diff --git a/WEEK10/WEEK10-BN.pdf b/WEEK10/WEEK10-BN.pdf new file mode 100644 index 0000000..8a1d407 Binary files /dev/null and b/WEEK10/WEEK10-BN.pdf differ diff --git a/WEEK10/WEEK10-SLIDES.pdf b/WEEK10/WEEK10-SLIDES.pdf new file mode 100644 index 0000000..15d20a7 Binary files /dev/null and b/WEEK10/WEEK10-SLIDES.pdf differ diff --git a/WEEK10/WEEK10.md b/WEEK10/WEEK10.md new file mode 100644 index 0000000..46cdad8 --- /dev/null +++ b/WEEK10/WEEK10.md @@ -0,0 +1,1656 @@ +# Week 10: Conditionals in Embedded Systems: Debugging and Hacking Static & Dynamic Conditionals w/ SG90 Servo Motor PWM Basics + +*** +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: + +- Understand the difference between static and dynamic conditionals in C +- Know how if/else statements and switch/case blocks work at the assembly level +- Understand Pulse Width Modulation (PWM) and how it controls servo motors +- Calculate PWM timing from system clock to servo pulse width +- Identify conditional branches in Ghidra (beq, bne instructions) +- Hack string literals and timing delays in binary files +- Modify branch targets to change program flow +- Create "stealth" functionality by NOP-ing out print statements +- Understand IEEE-754 floating-point for angle calculations + +--- + +## Part 1: Understanding Conditionals in C + +### What Are Conditionals? + +**Conditionals** are programming structures that make decisions. They let your program choose different paths based on whether a condition is true or false. Think of them like a fork in the road - the program checks a condition and decides which way to go. + +### Two Types of Conditionals + +| Type | Description | Example | +| ----------- | ---------------------------------------------- | ------------------------------------------------- | +| **Static** | Condition value is known/fixed at compile time | `if (choice == 1)` where choice never changes | +| **Dynamic** | Condition value changes based on runtime input | `if (choice == getchar())` where user types input | + +--- + +## Part 2: Static Conditionals + +### What Makes a Conditional "Static"? + +A **static conditional** is one where the outcome is predetermined because the condition variable never changes during program execution: + +```c +int choice = 1; // This NEVER changes! + +while (true) { + if (choice == 1) { + printf("1\r\n"); // This ALWAYS runs + } else if (choice == 2) { + printf("2\r\n"); // This NEVER runs + } else { + printf("?\r\n"); // This NEVER runs + } +} +``` + +```ext ++-----------------------+ +| choice = 1 | +| set once, never | +| changes | ++-----------------------+ + | + v + [ choice == 1 ] + | + | YES + v ++-----------------------+ +| printf \'1\' | ++-----------------------+ + . + . NO (never taken) + v + [ choice == 2 ] + . + . YES (never reached) + v ++-----------------------+ +| printf \'2\' | ++-----------------------+ + . + . NO + v ++-----------------------+ +| printf \'?\' | ++-----------------------+ +``` + +### The if/else Statement + +The `if/else` structure checks conditions in order: + +```c +if (choice == 1) { +// Do something if choice is 1 +} else if (choice == 2) { +// Do something if choice is 2 +} else { +// Do something for all other values +} +``` + +### The switch Statement + +The `switch` statement is another way to handle multiple conditions: + +```c +switch (choice) { + case 1: + printf("one\r\n"); + break; + case 2: + printf("two\r\n"); + break; + default: + printf("??\r\n"); +} +``` + +**Key Differences:** + +| Feature | if/else | switch | +| ---------------- | ----------------------- | -------------------------- | +| **Condition** | Any boolean expression | Single variable comparison | +| **Values** | Ranges, complex logic | Discrete values only | +| **Fall-through** | No | Yes (without `break`) | +| **Readability** | Good for 2-3 conditions | Better for many conditions | + +--- + +## Part 3: Dynamic Conditionals + +### What Makes a Conditional "Dynamic"? + +A **dynamic conditional** is one where the condition variable changes based on runtime input: + +```c +uint8_t choice = 0; + +while (true) { + choice = getchar(); // User types a key - VALUE CHANGES! + + if (choice == '1') { + printf("1\r\n"); + } else if (choice == '2') { + printf("2\r\n"); + } else { + printf("??\r\n"); + } +} +``` + +```text + +-------------------+ + | getchar() | <----------------------------------+ + +-------------------+ | + | User types input | + v | + +-------------------+ | + | choice = input | | + +-------------------+ | + | | + v | + [ choice == '1' ] -- YES --> +-----------------------+ | + | | printf("1") | | + | NO | move servo | | + | +-----------------------+ | + v | + [ choice == '2' ] -- YES --> +-----------------------+ | + | | printf("2") | | + | NO | move servo | | + | +-----------------------+ | + v | + +-------------------+ | + | printf("??") | | + +-------------------+ | + | | + +-----------------------------------------------+ +``` + +### The getchar() Function + +`getchar()` reads a single character from the serial terminal: + +```c +uint8_t choice = getchar(); // Waits for user to type something +``` + +- Returns the ASCII value of the key pressed +- `'1'` = 0x31, `'2'` = 0x32, `'x'` = 0x78, `'y'` = 0x79 +- Blocks (waits) until a key is pressed + +--- + +## Part 4: Understanding PWM (Pulse Width Modulation) + +### What is PWM? + +**PWM** (Pulse Width Modulation) is a technique for controlling power by rapidly switching a signal on and off. The ratio of "on time" to "off time" determines the average power delivered. + +```text + PWM Signal - 50% Duty Cycle + + HIGH +-----+ +-----+ +-----+ + | | | | | | + LOW + +-----+ +-----+ +----- + |--T--| + ON OFF + + Duty Cycle = ON time / Total period = 50% +``` + +### PWM for Servo Control + +Servo motors use PWM differently - they care about the **pulse width**, not the duty cycle percentage: + +```text + Servo PWM Signal (50 Hz = 20ms period) + + 0° Position (1ms pulse): + HIGH -+ + | 1ms + LOW +----------------------------------- (19ms) ----- + + 90° Position (1.5ms pulse): + HIGH ---+ + | 1.5ms + LOW +-------------------------------- (18.5ms) ---- + + 180° Position (2ms pulse): + HIGH -----+ + | 2ms + LOW +-------------------------------- (18ms) ---- +``` + +### The Magic Numbers + +| Angle | Pulse Width | PWM Ticks (at 1MHz) | +| ----- | ----------- | ------------------- | +| 0° | 1000 µs | 1000 | +| 90° | 1500 µs | 1500 | +| 180° | 2000 µs | 2000 | + +--- + +## Part 5: PWM Timing Calculations + +### From 150 MHz to 50 Hz + +The RP2350's system clock runs at **150 MHz** (150 million cycles per second). A servo needs a **50 Hz** signal (one pulse every 20 ms). How do we bridge this gap? + +``` ext ++-----------------------+ +| System Clock | +| 150 MHz | ++-----------------------+ + | + | Divide by 150 + v ++-----------------------+ +| PWM Tick Rate | +| 1 MHz | +| (1 tick = 1 µs) | ++-----------------------+ + | + | Count to 20,000 + | Wrap at 19,999 + v ++-----------------------+ +| Servo PWM Signal | +| 50 Hz | +| (20 ms period) | ++-----------------------+ +``` + +### The Math + +**Step 1: Clock Division** +``` +PWM Tick Rate = System Clock / Divider +1,000,000 Hz = 150,000,000 Hz / 150 +``` + +**Step 2: Frame Period** +``` +Period = (Wrap Value + 1) * Tick Duration +20 ms = 20,000 ticks * 1 µs/tick +``` + +**Step 3: Pulse Width to Ticks** +``` +Ticks = Pulse Width (µs) * 1 tick/µs +1500 ticks = 1500 µs * 1 +``` + +### Worked Example: 90° Angle + +Let's calculate what happens when we command 90°: + +1. **Angle to Pulse Width:** + ``` + Pulse = MIN + (angle/180) * (MAX - MIN) + Pulse = 1000 + (90/180) * (2000 - 1000) + Pulse = 1000 + 0.5 * 1000 + Pulse = 1500 µs + ``` + +2. **Pulse to PWM Ticks:** + ``` + Level = 1500 µs * 1 tick/µs = 1500 ticks + ``` + +3. **Hardware Timing:** + - Signal HIGH for 1500 ticks (1.5 ms) + - Signal LOW for 18,500 ticks (18.5 ms) + - Total period: 20,000 ticks (20 ms) + +--- + +## Part 6: Understanding the SG90 Servo Motor + +### What is the SG90° + +The **SG90** is a small, inexpensive hobby servo motor commonly used in robotics projects: + +``` ext +[ SG90 Servo Motor ] ++-----------------------------------+ +| Motor ---> Gearbox ---> Arm | +| (0° to 180°) | ++-----------------------------------+ + | Wires + v +[ Wires ] ++-----------------------------------+ +| Orange: Signal / PWM | +| Red: VCC / 5V | +| Brown: GND / Ground | ++-----------------------------------+ +``` + +### SG90 Specifications + +| Parameter | Value | +| ----------------- | ------------------------- | +| **Voltage** | 4.8V - 6V (typically 5V) | +| **Rotation** | 0° to 180° | +| **Pulse Width** | 1000 us - 2000 us | +| **Frequency** | 50 Hz (20ms period) | +| **Stall Current** | ~650mA (can spike to 1A+) | + +### Wire Colors + +| Wire Color | Function | Connect To | +| ---------- | -------- | --------------- | +| **Brown** | GND | Ground | +| **Red** | VCC | 5V Power (VBUS) | +| **Orange** | Signal | GPIO Pin (PWM) | + +--- + +## Part 7: Power Supply Safety + +### CRITICAL WARNING + +**NEVER power the servo directly from the Pico's 3.3V pin!** + +Servos can draw over 1000mA during movement spikes. The Pico's 3.3V regulator cannot handle this and you will: + +- Cause brownouts (Pico resets) +- Damage the Pico's voltage regulator +- Potentially damage your USB port + +### Correct Power Setup + +``` ext ++-----------------+ +------------------------+ +| USB Power | ---> | VBUS 5V | ++-----------------+ +------------------------+ + | | + v v + [ Servo VCC ] [ Capacitor + ] + (Red) (1000 uF 25V) + ++-----------------+ +| Pico GND | ++-----------------+ + | + +--------------------+--------------------+ + | | + v v + [ Servo GND ] [ Capacitor - ] + (Brown) + ++-----------------+ +| Pico GPIO 6 | ---> [ Servo Signal ] (Orange) ++-----------------+ +``` + +### Why the Capacitor? + +The **1000 uF capacitor** acts as a tiny battery: + +- Absorbs sudden current demands when servo moves +- Prevents voltage drops that could reset the Pico +- Smooths out electrical noise + +--- + +## Part 8: Setting Up Your Environment + +### Prerequisites + +Before we start, make sure you have: + +1. A Raspberry Pi Pico 2 board +2. A Raspberry Pi Pico Debug Probe +3. Ghidra installed (for static analysis) +4. Python installed (for UF2 conversion) +5. A serial monitor (PuTTY, minicom, or screen) +6. An SG90 servo motor +7. A 1000 uF 25V capacitor +8. The sample projects: `0x001d_static-conditionals` and `0x0020_dynamic-conditionals` + +### Hardware Setup + +Connect your servo like this: + +| Servo Wire | Pico 2 Pin | +| --------------- | ---------- | +| Brown (GND) | GND | +| Red (VCC) | VBUS (5V) | +| Orange (Signal) | GPIO 6 | + +``` ext +Hardware Setup: + + Pico 2 Servo SG90 Capacitor (1000uF 25V) ++----------+ +-------------+ +-------------+ +| | | | | | +| GPIO 6 | -------> | Signal (Org)| | | +| | | | | | +| VBUS 5V | -------> | VCC (Red) | | | +| | | | | | | +| | +----> | | -----> | + | +| | | | | | +| GND | -------> | GND (Brn) | | | +| | | | | | | +| | +----> | | -----> | - | ++----------+ +-------------+ +-------------+ +``` + +### Project Structure + +``` +Embedded-Hacking/ ++-- 0x001d_static-conditionals/ +| +-- build/ +| | +-- 0x001d_static-conditionals.uf2 +| | +-- 0x001d_static-conditionals.bin +| +-- main/ +| | +-- 0x001d_static-conditionals.c +| +-- servo.h ++-- 0x0020_dynamic-conditionals/ +| +-- build/ +| | +-- 0x0020_dynamic-conditionals.uf2 +| | +-- 0x0020_dynamic-conditionals.bin +| +-- main/ +| | +-- 0x0020_dynamic-conditionals.c +| +-- servo.h ++-- uf2conv.py +``` + +--- + +## Part 9: Hands-On Tutorial - Static Conditionals Code + +### Step 1: Review the Source Code + +Let's examine the static conditionals code: + +**File: `0x001d_static-conditionals.c`** + +```c +#include +#include "pico/stdlib.h" +#include "servo.h" + +#define SERVO_GPIO 6 + +int main(void) { + stdio_init_all(); + + int choice = 1; // STATIC - never changes! + + servo_init(SERVO_GPIO); + + while (true) { + // if/else conditional + if (choice == 1) { + printf("1\r\n"); + } else if (choice == 2) { + printf("2\r\n"); + } else { + printf("?\r\n"); + } + + // switch/case conditional + switch (choice) { + case 1: + printf("one\r\n"); + break; + case 2: + printf("two\r\n"); + break; + default: + printf("??\r\n"); + } + + // Servo movement + servo_set_angle(0.0f); + sleep_ms(500); + servo_set_angle(180.0f); + sleep_ms(500); + } +} +``` + +### Step 2: Understand the Program Flow + +Since `choice = 1` and NEVER changes: + +``` ext ++-------------------------+ +| Start Loop Iteration | <-----------------------------------+ ++-------------------------+ | + | | + v | + [ choice == 1 ] -- TRUE --> +-------------------------+ | + | print \'1\' | | + +-------------------------+ | + | | + v | + [ switch case 1 ] | + | MATCH | + v | + +-------------------------+ | + | print \'one\' | | + +-------------------------+ | + | | + v | + +-------------------------+ | + | Move servo to 0° | | + +-------------------------+ | + | | + v | + +-------------------------+ | + | Wait 500ms | | + +-------------------------+ | + | | + v | + +-------------------------+ | + | Move servo to 180° | | + +-------------------------+ | + | | + v | + +-------------------------+ | + | Wait 500ms | ---+ + +-------------------------+ +``` + +### Step 3: Flash the Binary to Your Pico 2 + +1. Hold the BOOTSEL button on your Pico 2 +2. Plug in the USB cable (while holding BOOTSEL) +3. Release BOOTSEL - a drive called "RPI-RP2" appears +4. Drag and drop `0x001d_static-conditionals.uf2` onto the drive +5. The Pico will reboot and start running! + +### Step 4: Verify It's Working + +**Check the serial monitor (PuTTY at 115200 baud):** +``` +1 +one +1 +one +1 +one +... +``` + +**Watch the servo:** + +- It should sweep from 0° to 180° every second +- The movement is continuous and repetitive + +--- + +## Part 10: Debugging with GDB (Static Conditionals) + +### Step 5: Start OpenOCD (Terminal 1) + +Open a terminal and start OpenOCD: + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +You should see output indicating OpenOCD connected successfully to your Pico 2 via the Debug Probe. + +### Step 6: Start GDB (Terminal 2) + +Open a **new terminal** and launch GDB with the binary: + +```cmd +arm-none-eabi-gdb build\0x001d_static-conditionals.elf +``` + +### Step 7: Connect to the Remote Target + +In GDB, connect to OpenOCD: + +```gdb +target extended-remote :3333 +``` + +### Step 8: Halt the Running Binary + +Stop the processor: + +```gdb +monitor halt +``` + +### Step 9: Examine Main Function + +Disassemble around main to see the conditionals: + +```gdb +disassemble 0x10000234,+200 +``` + +Look for comparison and branch instructions that implement the if/else and switch logic. + +### Step 10: Set a Breakpoint at Main + +```gdb +break *0x10000234 +``` + +Reset and run to hit the breakpoint: + +```gdb +monitor reset halt +continue +``` + +### Step 11: Find the Comparison Instructions... Or Not! + +To see the assembly instructions as you execute them, tell GDB to display the current instruction automatically: + +```gdb +display/i $pc +nexti +``` + +Keep pressing **Enter** to repeat the `nexti` command and watch the instructions. + +**Wait, where is the `cmp` instruction?!** +You might notice that there is NO `cmp` instruction comparing our `choice` variable anywhere! Why? Because we hardcoded `int choice = 1;` at the start of our C code and never changed it. + +The C compiler is smart (even at basic optimization levels). It realized the `if (choice == 1)` condition would *always* be true, and the `else` conditions would *never* happen. Instead of wasting CPU cycles checking a condition that never changes, the compiler **optimized out the check entirely**! It just compiled the code inside the `choice == 1` block unconditionally. + +This is the defining characteristic of a "Static Conditional"—the condition is resolved at compile-time, not run-time! + +### Step 12: Examine the Printf/Puts Arguments + +If you look closely at your disassembly, you'll notice there are no `printf` calls! The compiler optimized our simple `printf` statements into `puts` (specifically `__wrap_puts` in the Pico SDK) because they didn't contain any complex formatting variables. + +When you reach a `bl <__wrap_puts>` instruction, check `r0` for the string address: + +```gdb +x/s $r0 +``` + +You should see strings like `"1\r"` or `"one\r"`. + +*(Wait, what happened to the `\n`? Because the `puts` function automatically adds a newline to the end of whatever it prints, the compiler cleverly trimmed the `\n` out of the string literal in memory to save space!)* + +### Step 13: Watch the Servo Commands + +Let's set a breakpoint on `servo_set_angle`. How do you know the address? If you look at your `main` disassembly, you can find the `bl` instruction calling it (for example, `0x10000310`). + +But hardcoded addresses change every time you recompile! Luckily, GDB is smart enough to resolve function names directly from the ELF file: + +```gdb +break servo_set_angle +continue +``` + +Check the argument passed to the function. You might assume the float is in `s0`, but check out the very first instruction of `servo_set_angle`: + +`vmov s14, r0` + +Because of how the ARM compiler handles arguments, the floating-point angle is actually passed into the function via the standard integer register `r0`. The `vmov` instruction immediately moves it from `r0` into the floating-point register `s14` so the math unit can use it! + +To see the angle, you can ask GDB to interpret the raw hex value in `r0` as a float. You can also step forward (`nexti` or `n`) to let the `vmov` execute, and then check `s14` directly! + +Here is exactly what that process looks like in your GDB terminal (assuming you hit `continue` to catch the second call where the angle is 180): + +```gdb +=> 0x10000310 : vmov s14, r0 +(gdb) print /f $r0 +$2 = 180 +(gdb) nexti +=> 0x10000314 : push {r4, r5, lr} +(gdb) info registers s14 +s14 180 (raw 0x43340000) +``` + +*(Note: On your very first breakpoint hit, `print /f $r0` will just show `0` because the first call in the C code is `servo_set_angle(0)`!)* + +### Step 14: Examine the Timing Delay + +Set a breakpoint on `sleep_ms` (using the function name, not a hardcoded address!) and check the delay value: + +```gdb +break sleep_ms +continue +info registers r0 +``` + +You should see `0x1f4` (500 decimal) for the 500ms delay. + +### Step 15: Watch the Loop Iterate + +Because this loop contains `sleep_ms(500)` calls, trying to use `nexti 100` to step forward will cause GDB to "hang" (either because it's waiting for multiple seconds of sleep to finish, or because GDB stepping interferes with the Pico's hardware timer interrupts!). + +Instead, since we already have breakpoints set on `servo_set_angle` and `sleep_ms`, just type `continue`! + +```gdb +continue +``` + +Every time you type `continue` (or press **Enter** to repeat it), you will see the CPU safely jump to the next function call. Because this is a *Static Conditional*, it will never hit a `cmp` instruction or take a different branch—it just bounces between `servo_set_angle` and `sleep_ms` forever! + +### Step 16: Exit GDB + +When done exploring: + +```gdb +quit +``` + +--- + +## Part 11: Setting Up Ghidra for Static Conditionals + +### Step 17: Start Ghidra + +Open a terminal and type: + +```cmd +ghidraRun +``` + +### Step 18: Create a New Project + +1. Click **File** -> **New Project** +2. Select **Non-Shared Project** +3. Click **Next** +4. Enter Project Name: `0x001d_static-conditionals` +5. Click **Finish** + +### Step 19: Import the Binary + +1. Open your file explorer +2. Navigate to the `0x001d_static-conditionals/build/` folder +3. **Drag and drop** the `.bin` file into Ghidra's project window + +### Step 20: Configure the Binary Format + +**Click the three dots (...) next to "Language" and:** + +1. Search for "Cortex" +2. Select **ARM Cortex 32 little endian default** +3. Click **OK** + +**Click the "Options..." button and:** + +1. Change **Block Name** to `.text` +2. Change **Base Address** to `10000000` +3. Click **OK** + +### Step 21: Analyze the Binary + +1. Double-click on the file in the project window +2. A dialog asks "Analyze now?" - Click **Yes** +3. Use default analysis options and click **Analyze** + +Wait for analysis to complete. + +--- + +## Part 12: Resolving Functions in Ghidra (Static) + +### Step 22: Navigate to Main + +1. Press `G` (Go to address) and type `10000234` +2. Right-click -> **Edit Function Signature** +3. Change to: `int main(void)` +4. Click **OK** + +### Step 23: Resolve stdio_init_all + +At address `0x10000236`: + +1. Double-click on the called function +2. Right-click -> **Edit Function Signature** +3. Change to: `bool stdio_init_all(void)` +4. Click **OK** + +### Step 24: Resolve servo_init + +Look for a function call where `r0` is loaded with `0x6` (GPIO pin 6): + +```assembly +movs r0, #0x6 ; GPIO pin 6 +bl FUN_1000027c ; servo_init +``` + +1. Right-click -> **Edit Function Signature** +2. Change to: `void servo_init(uint pin)` +3. Click **OK** + +### Step 25: Resolve puts + +Look for function calls that load string addresses into `r0`: + +```assembly +ldr r0=>DAT_10001c54 ,[DAT_10000274 ] = 00000D31h +bl FUN_10001884 undefined FUN_10001884() +``` + +**How do we know it's puts?** + +- It takes a single string argument +- The hex `0x31` is ASCII "1" +- The hex `0x0d` is carriage return `"\r"` +- We saw "1" echoed in PuTTY + +1. Right-click -> **Edit Function Signature** +2. Change to: `int puts(char *s)` +3. Click **OK** + +### Step 26: Resolve servo_set_angle + +Look for a function call (like `bl FUN_10000310`) that occurs right after the float arguments are loaded into `r0`. + +```assembly +mov r0,r5 +bl FUN_10000310 undefined FUN_10000310() +``` + +If you double-click the `FUN_10000310` label to look inside the function, you'll find a section of code checking the pulse limits: + +```assembly +cmp.w r3,#0x7d0 +it cs +mov.cs.w r3,#0x7d0 +cmp.w r3,#0x3e8 +it cc +mov.cc.w r3,#0x3e8 +``` + +These values are: + +- `0x7D0` (2000 decimal) - maximum pulse width +- `0x3E8` (1000 decimal) - minimum pulse width + +These are the servo pulse limits! + +1. Right-click -> **Edit Function Signature** +2. Change to: `void servo_set_angle(float degrees)` +3. Click **OK** + +### Step 27: Resolve sleep_ms + +Look for a function where `r0` is loaded with `0x1f4` (500 decimal): + +```assembly +mov.w r0,#0x1f4 +bl FUN_10000e20 undefined FUN_10000e20() +``` + +1. Right-click -> **Edit Function Signature** +2. Change to: `void sleep_ms(uint ms)` +3. Click **OK** + +--- + +## Part 13: Hacking Static Conditionals + +### Step 28: Open the Bytes Editor + +1. Click **Window** -> **Bytes** +2. A new panel appears showing raw hex bytes +3. Click the pencil icon to enable editing + +### Step 29: Hack #1 - Change "1" to "2" + +First, we need to find the string "1" in the binary. + +1. Go back to your `main` assembly code and look for the first `puts` call. +2. In the `ldr` instruction right before it, look for the reference next to the arrow: `r0=>DAT_10001c54`. **Double-click exactly on `DAT_10001c54`**. *(Warning: Do NOT click the reference inside the brackets like `[DAT_10000274]`, or you'll end up in the pointer table instead of the string!)* +3. The Listing and Bytes windows will automatically jump to the string's exact address! +4. Look at your **Bytes** window. You should see the byte `31` (which is ASCII for "1"). +5. Click on the `31` and type `32` to overwrite it (which is ASCII for "2"). + +### Step 30: Hack #2 - Change "one" to "fun" + +Next, let's change the word "one" to "fun": + +1. Go back to `main` and look for the second `puts` call. +2. In the `ldr` instruction, double-click the `DAT_10001c5c` reference next to the arrow (again, ignore the one in the brackets!). +3. Look at the **Bytes** window again. You'll find the bytes `6f 6e 65` (which are ASCII for "o-n-e"). +4. Click on the `6f` byte and type `66 75 6e` on your keyboard to overwrite those three bytes with "f-u-n". + +**ASCII Reference:** + +| Character | Hex | +| --------- | ---- | +| o | 0x6f | +| n | 0x6e | +| e | 0x65 | +| f | 0x66 | +| u | 0x75 | +| n | 0x6e | + +### Step 31: Hack #3 - Speed Up the Servo + +Let's change the 500ms delay to a 100ms delay. Since the compiler packed the `500` directly into a `mov.w` instruction, we can't just find it in the data section. Instead, let's use Ghidra to rewrite the instruction! + +**Method A: Bytes Window Workflow (Recommended)** +1. Ensure the Bytes window is open (**Window** -> **Bytes: 0x001d_static-conditionals.bin**). +2. Click the **pencil icon** (**Toggle Edit Mode**) in the Bytes window toolbar. +3. In the **Listing** window, locate the first `mov.w r0,#0x1f4` instruction (right before calling `sleep_ms`) and press **`C`** (**Clear Code Bytes**). +4. In the **Bytes** window at that offset, change the immediate byte from `f4` (`0x1f4`) to `64` (`0x64` = 100). +5. In the **Listing** window, click back on the address and press **`D`** (**Disassemble**). +6. Repeat for the second `mov.w r0,#0x1f4` instruction. + +**Method B: Patch Instruction** +1. Right-click the first `mov.w r0,#0x1f4` instruction and select **Patch Instruction** (or press `Ctrl+Shift+G`). +2. Delete the `#0x1f4` and type `#0x64` (which is 100 in hex). Press **Enter**! +3. Repeat this for the second `mov.w r0,#0x1f4` instruction. + +**Before:** 500ms delay (servo moves slowly) +**After:** 100ms delay (servo moves FAST!) + +### Step 32: Export and Flash + +When exporting from Ghidra, you must make sure to save the file inside your `build/` directory so the python script can find it! + +1. Click **File** -> **Export Program** +2. Set **Format** to **Raw Bytes** +3. **IMPORTANT:** Click the `...` next to the Output File field and navigate into your `0x001d_static-conditionals/build/` folder! +4. Save the file as `0x001d_static-conditionals-h.bin`. +5. Click **OK** + +Convert and flash (make sure your terminal is inside the `0x001d_static-conditionals/` folder, NOT the `build/` folder!): + +```cmd +python ..\uf2conv.py build\0x001d_static-conditionals-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +*(Troubleshooting: If you get `FileNotFoundError` for the `.bin` file, it means you didn't save the exported file into the `build/` folder! Go back to Ghidra and export it again. If you get an error that Python can't open `..\uf2conv.py`, it means you accidentally `cd`'d into the `build/` folder. Type `cd ..` to go back up one directory and run the command again!)* + +### Step 33: Verify the Hacks + +**Serial output now shows:** +``` +2 +fun +2 +fun +... +``` + +**The servo now moves 5x faster!** It's spinning back and forth like crazy! + +--- + +## Part 14: Dynamic Conditionals - The Source Code + +### Step 34: Review the Dynamic Code + +**File: `0x0020_dynamic-conditionals.c`** + +```c +#include +#include "pico/stdlib.h" +#include "servo.h" + +#define SERVO_GPIO 6 + +int main(void) { + stdio_init_all(); + + uint8_t choice = 0; // DYNAMIC - changes with user input! + + servo_init(SERVO_GPIO); + + while (true) { + choice = getchar(); // Wait for keyboard input + + if (choice == 0x31) { // '1' + printf("1\r\n"); + } else if (choice == 0x32) { // '2' + printf("2\r\n"); + } else { + printf("??\r\n"); + } + + switch (choice) { + case '1': + printf("one\r\n"); + servo_set_angle(0.0f); + sleep_ms(500); + servo_set_angle(180.0f); + sleep_ms(500); + break; + case '2': + printf("two\r\n"); + servo_set_angle(180.0f); + sleep_ms(500); + servo_set_angle(0.0f); + sleep_ms(500); + break; + default: + printf("??\r\n"); + } + } +} +``` + +### Step 35: Understand the Dynamic Behavior + +| User Types | Output | Servo Action | +| ------------- | ----------- | ------------ | +| '1' (0x31) | "1" + "one" | 0° -> 180° | +| '2' (0x32) | "2" + "two" | 180° -> 0° | +| Anything else | "??" + "??" | No movement | + +### Step 36: Flash and Test + +1. Flash `0x0020_dynamic-conditionals.uf2` +2. Open PuTTY +3. Press '1' - servo sweeps one direction +4. Press '2' - servo sweeps the other direction +5. Press 'x' - prints "??" and no movement + +--- + +## Part 15: Debugging with GDB (Dynamic Conditionals) + +### Step 37: Start OpenOCD (Terminal 1) + +Open a terminal and start OpenOCD: + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +You should see output indicating OpenOCD connected successfully to your Pico 2 via the Debug Probe. + +### Step 38: Start GDB (Terminal 2) + +Open a **new terminal** and launch GDB with the binary: + +```cmd +arm-none-eabi-gdb build\0x0020_dynamic-conditionals.elf +``` + +### Step 39: Connect to the Remote Target + +In GDB, connect to OpenOCD: + +```gdb +target extended-remote :3333 +``` + +### Step 40: Halt the Running Binary + +Stop the processor: + +```gdb +monitor halt +``` + +### Step 41: Examine Main Function + +Disassemble around main to see the dynamic conditionals: + +```gdb +disassemble 0x10000234,+250 +``` + +Look for the `bl <__wrap_getchar>` call. Because `choice` is now dynamic (based on user input), the compiler *had* to generate the comparison logic! It should look something like this: + +```assembly +bl 0x10001860 <__wrap_getchar> +uxtb r4, r0 +cmp r4, #49 @ 0x31 +beq.n 0x10000270 +cmp r4, #50 @ 0x32 +``` +There they are! The `cmp` instructions checking for `0x31` ('1') and `0x32` ('2'). + +### Step 42: Set a Breakpoint After getchar + +We want to pause execution right after we type a character so we can inspect the comparison. +Find the address of the first `cmp` instruction in your disassembly (in the example above, it's `0x1000024a`) and set a breakpoint: + +```gdb +break *0x1000024a +``` +*(Make sure you use the exact address of the `cmp` instruction from YOUR disassembly!)* + +Reset and continue: + +```gdb +monitor reset halt +continue +``` + +### Step 43: Watch the Input Value + +When you press a key in PuTTY, the breakpoint hits. Check the return value: + +```gdb +info registers r0 +``` + +If you pressed '1', you should see `0x31`. If you pressed '2', you should see `0x32`. + +### Step 44: Execute the Comparison + +Right now, GDB is paused *before* the `cmp` instruction runs (the `=>` arrow points to the instruction that will execute *next*). + +To see the result of the comparison, we must execute the `cmp` instruction by stepping forward exactly once: + +```gdb +display/i $pc +stepi +``` + +Notice that the `=>` arrow has now moved to the `beq.n` (Branch if Equal) instruction. The `cmp` instruction has just executed. + +### Step 45: Examine the Branch Decisions + +Now that the comparison has run, we can look at the CPU's condition flags to see what happened: + +```gdb +info registers xpsr +``` +*(Note: Older ARM chips call this `cpsr`, but Cortex-M chips like the Pico's RP2350 call it `xpsr`!)* + +The zero flag (`Z`) determines if the branch is taken. The flags are stored in the highest nibble (the first hex digit) of the `xpsr` register in the order `N Z C V`. + +If you typed '1', the comparison resulted in zero difference, setting the `Z` flag to 1. You should see `xpsr` start with a `6` (like `0x69000000`). `6` in binary is `0110`, which means the `Z` flag (the second bit) is 1! The `beq.n` instruction will see this flag and take the branch. + +### Step 46: Watch Different Input Paths + +Continue and press different keys to see how the program takes different branches: + +```gdb +continue +``` + +Press '2' in PuTTY, then examine registers again. + +### Step 47: Examine Servo Control + +Set a breakpoint on servo_set_angle: + +```gdb +break *0x10000280 +continue +``` + +Check the angle value: + +```gdb +info registers s0 +``` + +### Step 48: Exit GDB + +When done exploring: + +```gdb +quit +``` + +--- + +## Part 16: Setting Up Ghidra for Dynamic Conditionals + +### Step 49: Create New Project + +1. Create project: `0x0020_dynamic-conditionals` +2. Import the `.bin` file +3. Configure as ARM Cortex, base address `10000000` +4. Analyze + +### Step 50: Navigate to Main + +Press `G` and go to `10000234`. + +### Step 51: Resolve Functions + +Follow the same process: + +1. **main** at `0x10000234` -> `int main(void)` +2. **stdio_init_all** -> `bool stdio_init_all(void)` +3. **servo_init** -> `void servo_init(uint pin)` +4. **puts** -> `int puts(char *s)` +5. **servo_set_angle** -> `void servo_set_angle(float degrees)` +6. **sleep_ms** -> `void sleep_ms(uint ms)` + +### Step 52: Identify getchar + +Look for a function that: + +- Returns a value in `r0` +- That value is then compared against `0x31` ("1") + +```assembly +bl FUN_10001860 undefined FUN_10001860() +uxtb r4,r0 +cmp r4,#0x31 +beq LAB_10000270 +``` + +1. Right-click -> **Edit Function Signature** +2. Change to: `int getchar(void)` +3. Click **OK** + +### Step 53: Identify Hardware Addresses + +Double-click into `stdio_init_all` and look for hardware addresses: + +```assembly +ldr r0, =0x40070000 ; UART0 base address +``` + +Check the RP2350 datasheet Section 2.2 (Address Map): + +- `0x40070000` = UART0 + +This confirms it's a UART initialization function! + +--- + +## Part 17: Understanding Branch Instructions + +### ARM Branch Instructions + +| Instruction | Meaning | Condition | +| ----------- | ---------------------- | ------------------ | +| `b` | Branch (always) | Unconditional jump | +| `beq` | Branch if Equal | Zero flag set | +| `bne` | Branch if Not Equal | Zero flag clear | +| `bgt` | Branch if Greater Than | Signed greater | +| `blt` | Branch if Less Than | Signed less | + +### How Conditionals Become Branches + +```c +if (choice == 0x31) { + printf("1"); +} +``` + +Becomes: + +```assembly +cmp r4, #0x31 ; Compare choice to '1' +bne skip_printf ; If NOT equal, skip the printf +; ... printf code here ... +skip_printf: +``` + +``` ext ++---------------------------------+ +| cmp r4, #0x31 | +| Sets flags based on r4 - 0x31 | ++---------------------------------+ + | + v + [ beq target_address ] + | + +--------+--------+ + | | + If r4 == 0x31 If r4 != 0x31 + | | + v v ++---------------+ +----------------------------+ +| Jump to | | Continue to | +| target_address| | next instruction | ++---------------+ +----------------------------+ +``` + +--- + +## Part 18: Advanced Hacking - Creating Stealth Commands + +### The Goal + +We want to create **secret commands** that: + +1. Respond to 'x' and 'y' instead of '1' and '2' +2. Move the servo WITHOUT printing anything +3. Leave NO trace in the terminal + +### Step 54: Plan the Patches + +**Original behavior:** + +- '1' (0x31) -> prints "1" and "one", moves servo +- '2' (0x32) -> prints "2" and "two", moves servo + +**Hacked behavior:** + +- 'x' (0x78) -> moves servo SILENTLY (replacing '1') +- 'y' (0x79) -> moves servo SILENTLY (replacing '2') + +### Step 55: Change Comparison Values + +Navigate to the `main` function and find the two `cmp` instructions right after `getchar`: + +Address `1000024a` contains `cmp r4,#0x31` (`31 2c`), and address `1000024e` contains `cmp r4,#0x32` (`32 2c`). + +**Method A: Bytes Window Workflow (Recommended)** +To ensure clean disassembly and prevent assembler context conflicts: +1. Ensure the Bytes window is open (**Window** -> **Bytes: 0x001d_static-conditionals.bin**). +2. Click the **pencil icon** (**Toggle Edit Mode**) in the Bytes window toolbar to enable editing. +3. In the **Listing** window, click on address `1000024a` and press **`C`** (**Clear Code Bytes**). +4. In the **Bytes** window at offset `1000024a`, click on byte `31` and change it to **`78`** (ASCII 'x'). +5. In the **Listing** window, click back on address `1000024a` and press **`D`** (**Disassemble**). +6. In the **Listing** window, click on address `1000024e` and press **`C`** (**Clear Code Bytes**). +7. In the **Bytes** window at offset `1000024e`, click on byte `32` and change it to **`79`** (ASCII 'y'). +8. In the **Listing** window, click back on address `1000024e` and press **`D`** (**Disassemble**). + +**Method B: Patch Instruction** +1. At address `1000024a`, right-click `cmp r4,#0x31`, select **Patch Instruction**, and change it to `cmp r4,#0x78` (which is ASCII 'x'). +2. At address `1000024e`, right-click `cmp r4,#0x32`, select **Patch Instruction**, and change it to `cmp r4,#0x79` (which is ASCII 'y'). + +### Step 56: Redirect Branches to Skip Prints + +For the stealth keys, we need to jump PAST the printf calls directly to the servo code. + +**Original flow:** +``` +compare -> branch -> printf("1") -> printf("one") -> servo code +``` + +**Hacked flow:** +``` +compare 'x' -> branch -> [skip prints] -> servo code +``` + +Use **Patch Instruction** to rewrite the `beq` target addresses: + +1. At address `1000024c`, you will see `beq LAB_10000270`. + - `10000270` is the block that prints "1". We want to skip it! + - Right-click, select **Patch Instruction**, and change it to `beq 0x1000027c` (this jumps straight to the `mov r0,r6` servo code!). +2. At address `10000250`, you will see `beq LAB_1000029a`. + - `1000029a` is the block that prints "2". + - Right-click, select **Patch Instruction**, and change it to `beq 0x100002a6` (this jumps straight to the `mov r0,r5` servo code!). + +### Step 57: NOP Out Print Calls (Alternative Method) + +**NOP** (No Operation) is an instruction that does absolutely nothing. Hackers use it to "erase" code without changing the size of the binary! Since we already redirected the branches to skip the prints, this is technically redundant, but let's do it anyway just to learn the technique. + +The `bl` instruction is 32-bits (4 bytes) long. Standard Thumb `nop` instructions are only 16-bits (2 bytes) long. If you try to patch a 4-byte instruction with a 2-byte instruction, Ghidra's assembler gets very confused and corrupts the code. + +To fix this, we use the special 32-bit version of NOP: `nop.w` (Wide NOP). + +1. In the Listing view, right-click the `bl FUN_10001954` instruction at `10000272`. +2. Select **Patch Instruction**. +3. Type `nop.w` (don't forget the `.w`!) and hit Enter. +4. You will see the entire 4-byte instruction neatly get replaced by a single 32-bit NOP. +5. Repeat this for the second `bl` instruction at `10000278`. + +### Step 58: Summary of Control Flow Patches + +Here is a quick summary of the stealth command patches we just applied to the control flow: + +| Location | Original Action | Patched Action | Purpose | +| ---------- | -------------------- | -------------------- | ---------------------------- | +| `1000024a` | `cmp r4,#0x31` | `cmp r4,#0x78` | Check for 'x' instead of '1' | +| `1000024e` | `cmp r4,#0x32` | `cmp r4,#0x79` | Check for 'y' instead of '2' | +| `1000024c` | `beq LAB_10000270` | `beq LAB_1000027c` | Skip printf for 'x' | +| `10000250` | `beq LAB_1000029a` | `beq LAB_100002a6` | Skip printf for 'y' | + +### Step 59: One Final Hack - Modify the Angle + +Let's also change the servo's movement angle from 180° to 30° for fun! + +If you look back near the top of the `main` function (around address `10000242`), you'll see this instruction: +`ldr r5,[DAT_100002c4] = 43340000h` + +The compiler stored the 180.0 float value (`0x43340000`) in a literal pool at address `100002c4`. To change the angle, we just need to overwrite that raw data! + +**Original:** `0x43340000` (180.0f) +**New:** `0x41f00000` (30.0f) + +Here is how to apply the patch: + +1. Press `G` and jump to address `100002c4`. +2. In your **Bytes** window (make sure the Pencil icon is still clicked!), you will see the raw little-endian bytes: `00 00 34 43`. +3. Click on the first `00` and type `00 00 f0 41`. +4. The Listing view will instantly update to show the new `41f00000` value! + +*(For the math nerds, here is how we calculated `0x41f00000` manually using the IEEE-754 standard):* + +**Calculation for 30.0f:** +``` +30.0 = 1.875 * 2^4 +Sign = 0 +Exponent = 127 + 4 = 131 = 0x83 +Mantissa = 0.875 = 0x700000 + +Binary: 0 10000011 11100000000000000000000 +Hex: 0x41f00000 +Little-endian: 00 00 f0 41 +``` + +### Step 60: Export and Test + +When exporting, make sure to save it in your `build/` folder! + +1. Export as `0x0020_dynamic-conditionals-h.bin` inside `build/`. +2. Convert and flash (run from the `0x0020_dynamic-conditionals` directory!): + +```cmd +python ..\uf2conv.py build\0x0020_dynamic-conditionals-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +3. Flash and test: + - Press 'x' -> NO OUTPUT, but servo moves silently! + - Press 'y' -> NO OUTPUT, but servo moves silently! + - (The original '1' and '2' keys no longer work!) + +--- + +## Part 19: Summary and Review + +### What We Accomplished + +1. **Learned static vs dynamic conditionals** - Fixed vs runtime-determined values +2. **Understood if/else and switch/case** - Two ways to branch in C +3. **Mastered PWM calculations** - 150MHz to 50Hz servo signal +4. **Identified conditional branches in assembly** - beq, bne, cmp instructions +5. **Hacked string literals** - Changed "one" to "fun" +6. **Modified timing values** - Sped up servo from 500ms to 100ms +7. **Created stealth commands** - Hidden 'x' and 'y' keys +8. **NOPed out print statements** - Removed logging for stealth +9. **Redirected branch targets** - Changed program flow + +### Static vs Dynamic Summary + +```text + Static Conditionals + ------------------- + - Variable set once, never changes + - Same path taken every iteration + - Compiler may optimize out dead branches + - Example: int choice = 1; if (choice == 1) + + Dynamic Conditionals + -------------------- + - Variable changes based on input/sensors + - Different paths taken based on runtime state + - All branches must remain in binary + - Example: choice = getchar(); if (choice == '1') +``` + +### PWM Calculation Summary + +``` ext ++---------+ +-------------+ +-----------+ +--------------+ +| Angle | Formula | Pulse Width | 1us= | PWM Ticks | Servo | Servo Motion | +| degrees | ------> | µs | 1 tick | | Motion | | ++---------+ +-------------+ ------> +-----------+ ------> +--------------+ + | | | | + 0° ---------------- 1000 µs ------------- 1000 ticks --------- Fully CCW + 90° ---------------- 1500 µs ------------- 1500 ticks --------- Center + 180° ---------------- 2000 µs ------------- 2000 ticks --------- Fully CW +``` + +### Key Memory Addresses + +| Memory Address | Description | +| -------------- | ------------------------ | +| `0x10000234` | main() function | +| `0x40070000` | UART0 hardware registers | +| `0x1f4` | 500 (sleep_ms delay) | +| `0x7D0` | 2000 (max pulse width) | +| `0x3E8` | 1000 (min pulse width) | +| `0x43340000` | 180.0f (max angle) | + +--- + +--- + +## Key Takeaways + +1. **Static conditionals have fixed outcomes** - The same path always executes + +2. **Dynamic conditionals respond to input** - Different paths based on runtime state + +3. **PWM frequency = 50Hz for servos** - One pulse every 20ms + +4. **Pulse width encodes position** - 1ms=0°, 1.5ms=90°, 2ms=180° + +5. **beq = branch if equal** - Jumps when comparison matches + +6. **bne = branch if not equal** - Jumps when comparison doesn't match + +7. **NOP erases code without changing size** - `00 bf` in ARM Thumb + +8. **Branch targets can be redirected** - Change where code jumps to + +9. **IEEE-754 is needed for angles** - Floats have specific bit patterns + +10. **Stealth requires removing ALL output** - NOP out printf AND puts + +--- + +## Glossary + +| Term | Definition | +| ----------------------- | --------------------------------------------------- | +| **beq** | Branch if Equal - ARM conditional jump | +| **bne** | Branch if Not Equal - ARM conditional jump | +| **Dynamic Conditional** | Condition that changes based on runtime input | +| **Duty Cycle** | Percentage of time signal is HIGH | +| **getchar()** | C function that reads one character from input | +| **NOP** | No Operation - instruction that does nothing | +| **PWM** | Pulse Width Modulation - variable duty cycle signal | +| **SG90** | Common hobby servo motor model | +| **Static Conditional** | Condition with fixed/predetermined outcome | +| **switch/case** | C structure for multiple discrete value comparisons | +| **Wrap Value** | PWM counter maximum before reset | + +--- + +## Additional Resources + +### ASCII Reference Table + +| Character | Hex | Decimal | +| --------- | ---- | ------- | +| '0' | 0x30 | 48 | +| '1' | 0x31 | 49 | +| '2' | 0x32 | 50 | +| 'x' | 0x78 | 120 | +| 'y' | 0x79 | 121 | +| `'\r'` | 0x0d | 13 | +| `'\n'` | 0x0a | 10 | + +### IEEE-754 Common Angles + +| Angle | IEEE-754 Hex | Little-Endian Bytes | +| ----- | ------------ | ------------------- | +| 0.0 | 0x00000000 | 00 00 00 00 | +| 30.0 | 0x41f00000 | 00 00 f0 41 | +| 45.0 | 0x42340000 | 00 00 34 42 | +| 90.0 | 0x42b40000 | 00 00 b4 42 | +| 135.0 | 0x43070000 | 00 00 07 43 | +| 180.0 | 0x43340000 | 00 00 34 43 | + +### ARM Thumb NOP Encodings + +| Instruction | Encoding | Size | +| ----------- | ------------- | ------- | +| `nop` | `00 bf` | 2 bytes | +| `nop.w` | `00 f0 00 80` | 4 bytes | + +### RP2350 Key Addresses + +| Address | Peripheral | +| ------------ | ---------- | +| `0x40070000` | UART0 | +| `0x40078000` | UART1 | +| `0x40050000` | PWM | + +--- + +## Real-World Implications + +### Why Stealth Commands Matter + +The ability to create hidden commands has serious implications: + +**Legitimate Uses:** + +- Factory test modes +- Debugging interfaces +- Emergency recovery features + +**Malicious Uses:** + +- Backdoors in firmware +- Hidden surveillance features +- Unauthorized control of systems + +### Real-World Example + +Imagine a drone with hacked firmware: + +- Normal keys ('1', '2') control it visibly with logging +- Hidden keys ('x', 'y') control it with NO log entries +- An attacker could operate the drone while security monitors show nothing + +### The Nuclear Fuel Rod Analogy + +A fast-moving servo is like a nuclear fuel rod: + +- Both are small components with immense power +- Both require precise control to prevent damage +- Both can "go critical" if pushed beyond limits +- Both teach the importance of safety margins + +--- + +**Remember:** The techniques you learned today demonstrate how conditional logic can be manipulated at the binary level. Understanding these attacks helps us build more secure embedded systems. Always use your skills ethically and responsibly! + +Happy hacking! diff --git a/WEEK10/WEEK10.pdf b/WEEK10/WEEK10.pdf new file mode 100644 index 0000000..cd05955 Binary files /dev/null and b/WEEK10/WEEK10.pdf differ diff --git a/WEEK10/slides/WEEK10-IMG00.svg b/WEEK10/slides/WEEK10-IMG00.svg new file mode 100644 index 0000000..84c608d --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 10 + + +Conditionals in Embedded Systems: +Debugging and Hacking Static & Dynamic +Conditionals w/ SG90 Servo Motor PWM + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK10/slides/WEEK10-IMG01.svg b/WEEK10/slides/WEEK10-IMG01.svg new file mode 100644 index 0000000..38d8eb0 --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG01.svg @@ -0,0 +1,82 @@ + + + + +Conditionals Overview +Static vs Dynamic Decision Making + + + +What Are Conditionals? +Structures that let programs choose +different paths based on conditions + + + +Static Conditional +Value fixed at compile time + + +int choice = 1; +// never changes +if (choice == 1) +printf("1"); +// always runs +else printf("2"); +// never runs + + + +Dynamic Conditional +Value changes at runtime + + +choice = getchar(); +// user types a key +if (choice == '1') +printf("1"); +// maybe runs + + + +if/else + +Feature +Description + +Condition +Any boolean expr +Values +Ranges, complex logic +Fall-through +No +Best for +2-3 conditions + + + +switch/case + +Feature +Description + +Condition +Single variable +Values +Discrete only +Fall-through +Yes (no break) +Best for +Many conditions + diff --git a/WEEK10/slides/WEEK10-IMG02.svg b/WEEK10/slides/WEEK10-IMG02.svg new file mode 100644 index 0000000..cbd97b4 --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG02.svg @@ -0,0 +1,89 @@ + + + + +Static Conditionals +Fixed Outcome -- Same Path Every Time + + + +Static Code Pattern + +int choice = 1; +// NEVER changes +while (true) { +if (choice == 1) +printf("1"); +else if (choice == 2) +printf("2"); +// dead code +else + + + +Execution Flow + + +choice == 1? + + +YES + + +print "1" + +NO (never taken) + + + +choice == 2? + +NO (never reached) + + +print "?" + +Only ONE path ever executes! + + + +Every Loop Iteration (Always the Same) +1. if(1==1) --> TRUE +2. print "1" +3. switch(1) case 1 +4. print "one" +5. servo 0deg +6. sleep 500ms +7. servo 180deg + + + +Serial Output (Forever) + +1 +one +1 +// repeats forever + + + +Servo Motion (Forever) +0deg +--> +180deg +--> +0deg +Sweeps back and forth, 500ms each +Continuous, predictable motion + diff --git a/WEEK10/slides/WEEK10-IMG03.svg b/WEEK10/slides/WEEK10-IMG03.svg new file mode 100644 index 0000000..9a22eeb --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG03.svg @@ -0,0 +1,95 @@ + + + + +Dynamic Conditionals +Runtime Input Changes the Path + + + +Dynamic Code Pattern + +uint8_t choice = 0; +while (true) { +choice = getchar(); +// waits for keyboard input +if (choice == 0x31) +printf("1"); +else if (choice == 0x32) +printf("2"); + + + +Execution Flow + + +choice = getchar() + + + + +choice=='1'? + +YES + +servo 0-180 + + +NO + + +choice=='2'? + +YES + +servo 180-0 + + + +print "??" +Each iteration can take a DIFFERENT path + + + +getchar() Returns ASCII +'1' = 0x31 +'2' = 0x32 +'x' = 0x78 +'y' = 0x79 +Blocks until +keypress + + + +Input --> Behavior + +Key +Output +Servo + +'1' +"1" + "one" +0deg --> 180deg +'2' +"2" + "two" +180deg --> 0deg + + + +Two Projects +0x001d_static-conditionals +choice = 1 (fixed) +0x0020_dynamic-conditionals +choice = getchar() (user input) + diff --git a/WEEK10/slides/WEEK10-IMG04.svg b/WEEK10/slides/WEEK10-IMG04.svg new file mode 100644 index 0000000..a405f7e --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG04.svg @@ -0,0 +1,96 @@ + + + + +PWM Basics +Pulse Width Modulation for Servo Control + + + +What is PWM? +Rapidly switching a signal ON and OFF +Ratio of on-time to off-time controls power + + +HIGH + + + + + + + + +ON +OFF +ON +OFF + + + +Servo PWM (50Hz = 20ms period) + + +0deg (1ms pulse): + + + +1ms HIGH +19ms LOW + + +90deg (1.5ms pulse): + + + +1.5ms HIGH +18.5ms LOW + + +180deg (2ms pulse): + + + +2ms HIGH +18ms LOW + +Pulse WIDTH determines angle, not duty cycle +Total period always 20ms (50Hz) + + + +Angle to Pulse Width + +Angle +Pulse +Ticks (1MHz) + +0deg +1000us +1000 +90deg +1500us +1500 +180deg +2000us +2000 + + + +Formula +pulse = 1000 + (angle/180) x 1000 +Example for 90deg: +1000 + (90/180) x 1000 += 1500us = 1500 ticks + diff --git a/WEEK10/slides/WEEK10-IMG05.svg b/WEEK10/slides/WEEK10-IMG05.svg new file mode 100644 index 0000000..9499ded --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG05.svg @@ -0,0 +1,83 @@ + + + + +PWM Timing Chain +150MHz System Clock to 50Hz Servo Signal + + + +Clock Division + + +150 MHz Clock + +/ 150 + + + + +1 MHz PWM + +1 tick = 1us + + + +Step 1: 150,000,000 / 150 = 1,000,000 Hz +Each PWM tick = exactly 1 microsecond + +Step 2: Wrap at 20,000 ticks = 20ms = 50Hz +Wrap value = 19,999 + + + +SG90 Servo Motor + +Parameter +Value + +Voltage +4.8V - 6V (use 5V) +Rotation +0deg to 180deg +Pulse Width +1000us - 2000us +Frequency +50Hz (20ms period) + + + +Wiring to Pico 2 + + +Pico + + +SG90 + + + + +GPIO 6 = Signal (Orange) +VBUS 5V = VCC (Red) +GND = GND (Brown) +Add 1000uF capacitor on power! + + + +Power Safety +NEVER use 3.3V pin for servo! +Servos draw 650mA+ (spikes to 1A) +Use VBUS (5V from USB) with 1000uF 25V capacitor + diff --git a/WEEK10/slides/WEEK10-IMG06.svg b/WEEK10/slides/WEEK10-IMG06.svg new file mode 100644 index 0000000..bb7867d --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG06.svg @@ -0,0 +1,55 @@ + + + + +Static Source Code +0x001d_static-conditionals.c + + + +Full Source + + +#include <stdio.h> +#include "pico/stdlib.h" +#include "servo.h" +#define SERVO_GPIO 6 + +int main(void) { +stdio_init_all(); +int choice = 1; +// STATIC! +servo_init(SERVO_GPIO); + +while (true) { +if (choice == 1) +printf("1\r\n"); +else if (choice == 2) +printf("2\r\n"); +// dead code + + + +switch Block + +switch(choice) { +case 1: puts("one"); break; + + +Servo Loop + +servo_set_angle(0.0f); +sleep_ms(500); +// then 180 + diff --git a/WEEK10/slides/WEEK10-IMG07.svg b/WEEK10/slides/WEEK10-IMG07.svg new file mode 100644 index 0000000..edb9558 --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG07.svg @@ -0,0 +1,50 @@ + + + + +Dynamic Source Code +0x0020_dynamic-conditionals.c + + + +Full Source + + +#include <stdio.h> +#include "pico/stdlib.h" +#include "servo.h" +#define SERVO_GPIO 6 + +int main(void) { +stdio_init_all(); +uint8_t choice = 0; +// DYNAMIC! +servo_init(SERVO_GPIO); + +while (true) { +choice = getchar(); +// wait for input +if (choice == 0x31) +// '1' +printf("1\r\n"); +else if (choice == 0x32) +// '2' +printf("2\r\n"); + + + +switch Block (with servo control) +case '1': print "one", servo 0-->180, sleep 500ms +case '2': print "two", servo 180-->0, sleep 500ms + diff --git a/WEEK10/slides/WEEK10-IMG08.svg b/WEEK10/slides/WEEK10-IMG08.svg new file mode 100644 index 0000000..295c649 --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG08.svg @@ -0,0 +1,101 @@ + + + + +Branch Instructions +How Conditionals Become Assembly + + + +ARM Branch Instructions + +Instr +Meaning +Condition + + +b +Branch always +Always + +beq +Branch if Equal +Z flag set + +bne +Branch if != +Z flag clear + +bgt +Branch if > +Signed > + +blt +Branch if < +Signed < + + + +C --> Assembly + +C code: + +if (choice == 0x31) +printf("1"); + +Assembly: + +cmp r4, #0x31 +// compare +bne skip_printf +// skip if != + + + +Conditional Branch Flow + + +cmp r4, #0x31 + + + + +beq target_addr + + +r4==0x31: JUMP + + +r4!=0x31: continue next + +cmp sets CPU flags, branch reads them + + + +NOP (No Operation) +ARM Thumb NOP: +00 bf +2 bytes +Wide NOP: +00 f0 00 80 +Replaces 4-byte bl instruction + + + +Hacking Branches +Change branch target addr +Redirect program flow +NOP out instructions +Erase code silently + diff --git a/WEEK10/slides/WEEK10-IMG09.svg b/WEEK10/slides/WEEK10-IMG09.svg new file mode 100644 index 0000000..68d47e9 --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG09.svg @@ -0,0 +1,83 @@ + + + + +Hacking Conditionals +Strings, Timing, Stealth Commands + + + +Hack 1: Change Strings +Change "1" to "2": +0x31 +--> +0x32 +"one" to "fun": +6f 6e 65 +--> +66 75 6e + + + +Hack 2: Speed Up Servo +Change sleep_ms delay: +0x1F4 (500ms) +--> +0x064 (100ms) + + + +Hack 3: Stealth Commands +Hidden keys move servo with NO output + +Patch +Original +Hacked +Purpose + + +Compare 1 +#0x31 ('1') +#0x78 ('x') +New trigger key + +Compare 2 +#0x32 ('2') +#0x79 ('y') +New trigger key + +puts calls +bl puts +00 bf 00 bf +NOP out prints + + + +Hack 4: Change Angle +180.0f --> 30.0f: +00 00 34 43 +--> +00 00 f0 41 + + + +Stealth Result +'1','2': normal output + servo +'x','y': NO output, servo moves + + + +Workflow +Patch bytes in Ghidra --> export .bin --> convert to UF2 --> flash to Pico + diff --git a/WEEK10/slides/WEEK10-IMG10.svg b/WEEK10/slides/WEEK10-IMG10.svg new file mode 100644 index 0000000..eb92463 --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG10.svg @@ -0,0 +1,85 @@ + + + + +PWM & Servo Hacking +Conditionals, PWM, Servo, and Hacking + + + +Static vs Dynamic + +Static +choice = 1 (fixed) +Same path every iteration +Compiler may optimize + +Dynamic +choice = getchar() +Different paths at runtime + + + +PWM for Servos +150MHz / 150 = 1MHz tick +Wrap 20000 = 50Hz (20ms) +0deg=1000us 90deg=1500us 180deg=2000us +pulse = 1000 + (angle/180) x 1000 + + + +Branch Instructions +cmp r4, #0x31 +Compare +beq target +Jump if equal +bne target +Jump if not equal +NOP = 00 bf (erase code) + + + +Key Values +0x10000234 +main() +0x40070000 +UART0 +0x1F4 +500 (sleep_ms) +0x43340000 +180.0f IEEE-754 + + + +4 Hack Types Applied +String +"one"-->"fun" +Timing +500ms-->100ms +Stealth +NOP out prints +Angle +180.0f-->30.0f + + + +Projects +0x001d_static-conditionals +0x0020_dynamic-conditionals + + +IEEE-754 Angles +0.0f=00000000 90.0f=42b40000 +180.0f=43340000 30.0f=41f00000 + diff --git a/WEEK11/WEEK11-BN.md b/WEEK11/WEEK11-BN.md new file mode 100644 index 0000000..37ba25f --- /dev/null +++ b/WEEK11/WEEK11-BN.md @@ -0,0 +1,1880 @@ +# Week 11-BN: Binary Ninja Personal — Hack Structs & Functions with the NEC IR Remote (Raw `.bin`) + +*** + +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +- Build the two lesson projects with `Release` and get an `.elf` and a raw `.bin` for each +- Dump the **ELF symbol map** with `arm-none-eabi-nm` and use it as ground truth +- Load each raw `.bin` into Binary Ninja at `0x10000000` +- **Break at `main`** on live silicon, even though `main` can move between programs +- **Hack each running target live** by editing a register and redirecting a string in Binary Ninja +- **Resolve the functions in the Binary Ninja GUI** using the ELF symbol map +- See how the compiler **flattens a C struct into hard-coded immediates** and **inlines every helper function** +- **Patch** the LED pin immediates and the NEC format string, export, convert, and flash +- Understand the security lesson: **the log says one thing while the hardware does another** + +--- + +## How This Guide Works + +Each project builds two files: + +| File | What it is | How we use it | +| ---- | ---------- | ------------- | +| `.elf` | The linked image with a full symbol table and DWARF | Ground truth for every function address and signature | +| `.bin` | The raw flash image, no headers, no symbols | The image we load into Binary Ninja and reverse | + +The `.bin` is built **from** the `.elf`, so the ELF tells you exactly what is at every address. We reverse-engineer the raw `.bin` the way a real extracted firmware image is reversed. + +> **Build `Release`, not `Debug`.** Every address in this guide matches the current `Release` builds. `Release` flattens the LED struct into plain immediates and inlines every `static` helper (`make_default_leds`, `init_led_gpios`, `process_ir_key`, `poll_ir`, and in Project 2 `ir_to_led_number`, `get_led_pin`, `leds_all_off`, `blink_led`, `process_ir_led_command`, `handle_ir_key`, `poll_and_handle_ir`) into `main`. If you build `Debug`, the SDK function addresses move and the helpers stay separate, so nothing lines up. Always build `Release` for this lesson. + +The order is **dynamic first, static second**, twice — once per project: + +1. Break on the live target and prove what the code does. +2. Hack it live in the debugger and watch the behavior change. +3. Resolve the functions in Binary Ninja using the ELF symbol map. +4. Patch the bytes, export, convert, and flash. + +| Project | Prints | Also does | The hacks | +| ------- | ------ | --------- | --------- | +| `0x0023_structures` | `IR receiver on GPIO 5 ready`, then `NEC command: 0xNN` per key | lights LED1/2/3 on GPIO 16/17/18 from the flattened struct | move LED1 to GPIO 18 live; swap LED1↔LED3 pins; rename the `NEC` string | +| `0x0026_functions` | the same, plus `LED N activated on GPIO P` | blinks the mapped LED 3× then holds it | forge the decoded key live; swap LED1↔LED3 pins (log desync); rename the `NEC` string | + +> **The struct disappears.** `simple_led_ctrl_t` has six members (three `uint8_t` pins and three `bool` states), but the optimizer proves it never escapes `main`, so it is never placed in memory. `leds.led1_pin` becomes the literal `16`, `leds.led2_pin` becomes `17`, `leds.led3_pin` becomes `18`, and the `bool` states become register values. That is why you patch **immediates**, not a struct field. + +> **The functions disappear too.** Every `static` helper is inlined, so there is no `process_ir_key` or `blink_led` symbol to rename. You see their bodies directly inside `main`. The only real functions `main` calls are the SDK routines and `ir_init`/`ir_getkey`. + +> **The SVD file lives in `WEEK04`.** If you want the RP2350 peripheral register map for the SIO/GPIO side of the lab, it is `Embedded-Hacking/WEEK04/rp2350.svd` — it is **not** in `WEEK11`. + +### Background: the NEC IR remote in one paragraph + +An IR receiver on **GPIO 5** demodulates a 38 kHz carrier and presents the NEC frame as a digital mark/space train. `ir_getkey()` waits for the 9 ms leader + 4.5 ms space, samples 32 bits by timing the marks, then validates that the address and command pairs are bitwise inverses. It returns the **command byte** (`0x0C`, `0x18`, or `0x5E` for buttons 1, 2, 3) or `-1`. `main` maps that byte to one of three LEDs on **GPIO 16 (red), 17 (green), 18 (yellow)**. Because the struct is flattened, that mapping is a set of hard-coded pin numbers in the loop — exactly what we patch. + +--- + +## Part 1: Build, Flash, and Get the Symbol Map + +### Step 1: Install the toolchain + +**Windows x64** + +- Install the **Raspberry Pi Pico** extension in VS Code. It installs the ARM GNU toolchain, CMake, Ninja, and the Pico SDK. +- Install **Binary Ninja Personal** and complete its license activation. +- Install **PuTTY** for the serial monitor. + +**macOS Apple Silicon** + +```bash +brew install cmake ninja +``` + +- Install **Binary Ninja Personal** and complete its license activation. +- Install the **Arm GNU Toolchain**, or let the VS Code Pico extension manage it. + +**Linux x64** + +```bash +sudo apt install cmake ninja-build gcc-arm-none-eabi libnewlib-arm-none-eabi git python3 openocd minicom +``` + +- Install **Binary Ninja Personal** and complete its license activation. + +### Step 2: Verify your tools are the right architecture (do not skip this) + +On **macOS Apple Silicon**, the most common failure is an Intel `x86_64` tool on your `PATH`: + +``` +zsh: bad CPU type in executable: cmake +``` + +You may have **two Homebrews**: the arm64 one at `/opt/homebrew` and the Intel one at `/usr/local`. If `/usr/local/bin` wins, every `brew` tool is x86_64. Check: + +```bash +file "$(which cmake)" +file "$(which ninja)" +file "$(which arm-none-eabi-gdb)" +file "$(which arm-none-eabi-nm)" +file "$(which openocd)" +``` + +All must report `arm64`. If any is `x86_64`, put the Apple Silicon prefix first for the session and check again: + +```bash +export PATH="/opt/homebrew/bin:$PATH" +hash -r +file "$(which cmake)" +``` + +To make it permanent, add that `export` to `~/.zshrc`. Do not use Rosetta as a fix; OpenOCD and GDB are exactly the kind of programs where a translation layer produces failures that look like debugger bugs. + +**Windows x64** and **Linux x64** do not have this problem. Skip to Step 3. + +### Step 3: Build the two projects with `Release` + +Run this once inside `0x0023_structures/` and once inside `0x0026_functions/`: + +```bash +cmake -B build -G Ninja -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 -DCMAKE_BUILD_TYPE=Release +cmake --build build +``` + +**Point Binary Ninja at this repository (once).** Every console snippet below reads the repo root from `~/.embedded-hacking-repo`, so Binary Ninja never needs a database open and nothing is hardcoded. From the repo root, run once: + +**macOS / Linux:** + +```bash +pwd > ~/.embedded-hacking-repo +``` + +**Windows (PowerShell):** + +```powershell +(Get-Location).Path | Set-Content "$env:USERPROFILE\.embedded-hacking-repo" +``` + +**Then build from the Binary Ninja console**, so the whole build -> patch -> flash loop stays inside Binary Ninja. The console inherits a minimal `PATH` — on macOS just `/usr/bin:/bin:/usr/sbin:/sbin` — so it does not see Homebrew; add your package manager's `bin` first, then run plain `cmake`. + +**macOS Apple Silicon:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +os.environ["PATH"] = "/opt/homebrew/bin:" + os.environ["PATH"] # the console's PATH omits Homebrew +for name in ("0x0023_structures", "0x0026_functions"): + proj = os.path.join(root, name) + subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) + subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +**Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +for name in ("0x0023_structures", "0x0026_functions"): + proj = os.path.join(root, name) + subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) + subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +for name in ("0x0023_structures", "0x0026_functions"): + proj = os.path.join(root, name) + subprocess.run(["cmake", "-B", "build", "-G", "Ninja", "-DPICO_BOARD=pico2", + "-DPICO_PLATFORM=rp2350", "-DCMAKE_BUILD_TYPE=Release"], cwd=proj) + subprocess.run(["cmake", "--build", "build"], cwd=proj) +``` + +Each build directory now contains the pair we need: + +- `0x0023_structures/build/0x0023_structures.elf` and `.bin` — `.bin` is **16372** bytes (`0x3ff4`) +- `0x0026_functions/build/0x0026_functions.elf` and `.bin` — `.bin` is **16476** bytes (`0x405c`) + +If the ARM toolchain is not on your `PATH`, add `-DPICO_TOOLCHAIN_PATH=...`: + +| OS | Typical toolchain path | +| -- | ---------------------- | +| Windows x64 | `C:/Program Files/Arm GNU Toolchain arm-none-eabi/14.2 rel1/bin` | +| macOS Apple Silicon | `/Applications/ArmGNUToolchain/14.2.rel1/arm-none-eabi/bin` | +| Linux x64 | `/usr` | + +### Step 4: Dump the ELF symbol map + +This is the ground truth for the whole lesson. Run `arm-none-eabi-nm` on each ELF and keep the output in a terminal or a text file: + +**macOS Apple Silicon / Linux x64:** + +```bash +arm-none-eabi-nm -n --defined-only build/0x0023_structures.elf | grep -E ' [Tt] ' +arm-none-eabi-nm -n --defined-only build/0x0026_functions.elf | grep -E ' [Tt] ' +``` + +**Windows x64:** + +```powershell +arm-none-eabi-nm -n --defined-only build\0x0023_structures.elf | Select-String ' [Tt] ' +arm-none-eabi-nm -n --defined-only build\0x0026_functions.elf | Select-String ' [Tt] ' +``` + +Each line is `address type name`. The `T`/`t` type is a function. The signatures below come from the ELF's DWARF debug info queried with `arm-none-eabi-gdb -batch -ex "ptype "`, so they are exact. + +**Project 1 — `0x0023_structures` — our code and the startup chain:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | reset entry | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | calls `runtime_init`, `main`, `exit` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | copies `.data` from flash to SRAM | +| `0x100001e4` | `_init` | `void _init(void)` | runs `.init_array` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | C runtime boilerplate | +| `0x10000234` | `main` | `int main(void)` | the lesson function (struct flattened, all helpers inlined) | +| `0x100002cc` | `ir_init` | `void ir_init(uint8_t)` | the `ir.c` receiver init | +| `0x100002f4` | `ir_getkey` | `int ir_getkey(void)` | the blocking NEC decoder (timing helpers inlined) | + +**Project 1 — the GPIO, UART, stdio, and `printf` chain `main` reaches:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x100004e8` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | SDK GPIO function select | +| `0x10000524` | `gpio_set_pulls` | `void gpio_set_pulls(uint, bool, bool)` | SDK pull-up/down (used by `ir_init`) | +| `0x1000054c` | `gpio_init` | `void gpio_init(uint)` | SDK GPIO init | +| `0x10000fa8` | `sleep_ms` | `void sleep_ms(uint32_t)` | SDK millisecond delay | +| `0x1000118c` | `time_us_64` | `uint64_t time_us_64(void)` | SDK microsecond clock (NEC timing) | +| `0x100011a0` | `busy_wait_us` | `void busy_wait_us(uint64_t)` | UART timing loop | +| `0x10001220` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | SDK UART init | +| `0x100013f4` | `clock_get_hz` | `unsigned long clock_get_hz(clock_handle_t)` | UART clock lookup | +| `0x1000310c` | `exit` | `void exit(int)` | C runtime exit | +| `0x10003114` | `runtime_init` | `void runtime_init(void)` | SDK runtime init | +| `0x10003140` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | CRLF output driver | +| `0x10003250` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | buffered string output | +| `0x1000333c` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | enable the UART driver | +| `0x10003364` | `stdio_init_all` | `bool stdio_init_all(void)` | SDK serial init | +| `0x100033f4` | `__wrap_puts` | `int __wrap_puts(const char*)` | the `puts` wrapper (adjacent stdio family) | +| `0x10003430` | `__wrap_vprintf` | `int __wrap_vprintf(const char*, va_list)` | printf core | +| `0x100034f4` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | the `printf` wrapper (both prints) | +| `0x100036b0` | `stdio_uart_init` | `void stdio_uart_init(void)` | SDK UART stdio init | +| `0x100037f0` | `strlen` | `size_t strlen(const char*)` | C runtime string length | + +**Project 2 — `0x0026_functions` — our code and the startup chain:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | reset entry | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | calls `runtime_init`, `main`, `exit` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | copies `.data` from flash to SRAM | +| `0x100001e4` | `_init` | `void _init(void)` | runs `.init_array` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | C runtime boilerplate | +| `0x10000234` | `main` | `int main(void)` | the lesson function (struct flattened, all helpers inlined) | +| `0x10000318` | `ir_init` | `void ir_init(uint8_t)` | the `ir.c` receiver init | +| `0x10000340` | `ir_getkey` | `int ir_getkey(void)` | the blocking NEC decoder (timing helpers inlined) | + +**Project 2 — the GPIO, UART, stdio, and `printf` chain `main` reaches:** + +| Address | ELF symbol | Signature | Role | +| ------- | ---------- | --------- | ---- | +| `0x10000534` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | SDK GPIO function select | +| `0x10000570` | `gpio_set_pulls` | `void gpio_set_pulls(uint, bool, bool)` | SDK pull-up/down (used by `ir_init`) | +| `0x10000598` | `gpio_init` | `void gpio_init(uint)` | SDK GPIO init | +| `0x10000ff0` | `sleep_ms` | `void sleep_ms(uint32_t)` | SDK millisecond delay | +| `0x100011d4` | `time_us_64` | `uint64_t time_us_64(void)` | SDK microsecond clock (NEC timing) | +| `0x100011e8` | `busy_wait_us` | `void busy_wait_us(uint64_t)` | UART timing loop | +| `0x10001268` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | SDK UART init | +| `0x1000143c` | `clock_get_hz` | `unsigned long clock_get_hz(clock_handle_t)` | UART clock lookup | +| `0x10003154` | `exit` | `void exit(int)` | C runtime exit | +| `0x1000315c` | `runtime_init` | `void runtime_init(void)` | SDK runtime init | +| `0x10003188` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | CRLF output driver | +| `0x10003298` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | buffered string output | +| `0x10003384` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | enable the UART driver | +| `0x100033ac` | `stdio_init_all` | `bool stdio_init_all(void)` | SDK serial init | +| `0x1000343c` | `__wrap_puts` | `int __wrap_puts(const char*)` | the `puts` wrapper (adjacent stdio family) | +| `0x10003478` | `__wrap_vprintf` | `int __wrap_vprintf(const char*, va_list)` | printf core | +| `0x1000353c` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | the `printf` wrapper (all three prints) | +| `0x100036f8` | `stdio_uart_init` | `void stdio_uart_init(void)` | SDK UART stdio init | +| `0x10003838` | `strlen` | `size_t strlen(const char*)` | C runtime string length | + +> **`main` is `0x10000234` in both projects.** Both programs put `main` at the same address because the startup code and the linker layout are identical; only the body of `main` and the functions after it move. In a `Debug` build the helpers stay separate and `main` moves — another reason to build `Release`. + +> **`printf` in our source is `__wrap_printf` in the binary.** The SDK links our `printf` calls to its `__wrap_printf` wrapper, which forwards to `__wrap_vprintf`. The `__wrap_puts` symbol exists in the image (the stdio family always does), but our `printf` path does not call it. + +### Step 5: Flash Project 1 and confirm the NEC/LED behavior + +A `.bin` has no headers, so OpenOCD must be told the base address `0x10000000`. From the repository root: + +**macOS Apple Silicon / Linux x64:** + +```bash +./flash.sh 0x0023_structures/build/0x0023_structures.bin +``` + +**Windows x64 (PowerShell):** + +```powershell +.\flash.ps1 -Bin 0x0023_structures\build\0x0023_structures.bin +``` + +**Or flash from the Binary Ninja console:** + +**macOS Apple Silicon / Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x0023_structures", "build", "0x0023_structures.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("flashing in the background; log:", log) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x0023_structures", "build", "0x0023_structures.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # free the probe first +subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "flash.ps1"), "-Bin", bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT) +print("flashing in the background; log:", log) +``` + +Wait for `wrote 16372 bytes ...` and `** Verified OK **`. Open a serial monitor at **115200** baud: + +- **Windows x64:** PuTTY -> Connection type **Serial**, the Pico's COM port, speed `115200`. +- **macOS Apple Silicon:** `screen /dev/tty.usbmodem* 115200` (quit with `Ctrl-A` then `K`). +- **Linux x64:** `minicom -D /dev/ttyACM0 -b 115200`. + +On reset it prints the banner once, then a line for every button press: + +``` +IR receiver on GPIO 5 ready +NEC command: 0x0C <- button "1" -> red LED (GPIO 16) +NEC command: 0x18 <- button "2" -> green LED (GPIO 17) +NEC command: 0x5E <- button "3" -> yellow LED (GPIO 18) +``` + +### Step 6: Flash Project 2 and confirm the blink behavior + +```bash +# macOS / Linux +./flash.sh 0x0026_functions/build/0x0026_functions.bin +``` +```powershell +# Windows +.\flash.ps1 -Bin 0x0026_functions\build\0x0026_functions.bin +``` + +**Or flash from the Binary Ninja console** (same form, pointing at the Project 2 `.bin`): + +**macOS / Linux:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x0026_functions", "build", "0x0026_functions.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("flashing in the background; log:", log) +``` + +**Windows:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +bin_path = os.path.join(root, "0x0026_functions", "build", "0x0026_functions.bin") +log = os.path.join(os.path.dirname(bin_path), "flash.log") +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # free the probe first +subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "flash.ps1"), "-Bin", bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT) +print("flashing in the background; log:", log) +``` + +Wait for `wrote 16476 bytes ...`. The serial monitor shows the banner, then for each button: + +``` +IR receiver on GPIO 5 ready +NEC command: 0x0C +LED 1 activated on GPIO 16 <- red LED blinks 3x, then holds on +NEC command: 0x18 +LED 2 activated on GPIO 17 <- green LED blinks 3x, then holds on +NEC command: 0x5E +LED 3 activated on GPIO 18 <- yellow LED blinks 3x, then holds on +``` + +> **Watch the two lines together.** The `LED N activated on GPIO P` line is built from the struct's *pin constants*, which the compiler hard-coded. Later we change the pin the loop actually drives but leave the print constant alone — that is the **log desynchronization** this week is about. + +--- + +## Part 2: Load the Raw `.bin` into Binary Ninja (Project 1) + +Start from a fresh Binary Ninja state. If you already have a `.bndb` for this lesson, **close it and start over**; a stale database keeps old names and patches. + +### Step 7: Bring the raw `.bin` into Binary Ninja + +A raw `.bin` has no headers, so Binary Ninja cannot know where it belongs or what architecture it is. You must supply both. If you just double-click the `.bin`, Binary Ninja may load it at address `0x0` with a guessed architecture, and every address in this lesson will be wrong. + +1. Choose `File -> Open with Options...` (do **not** use plain `File -> Open`). +2. Select `0x0023_structures/build/0x0023_structures.bin`. +3. In the loader options, set: + - **Architecture:** `thumb2` (the ARMv7-M / ARMv8-M Thumb-2 architecture, which covers the Cortex-M33) + - **Platform:** `thumb2` + - **Base Address:** `0x10000000` (the XIP flash base) +4. Click **Open**. + +Binary Ninja analyzes the image and opens the linear view. + +**Verify the load before going further.** Press `G`, type `0x10000000`, and read the first two words: + +``` +0x10000000 0x20082000 initial stack pointer +0x10000004 0x1000015d reset vector (bit 0 = Thumb) +``` + +If you instead see data at `0x00000000`, or a vector word without bit 0 set, close the tab and repeat with `Open with Options`. The Cortex-M33 only executes Thumb-2, so `thumb2` is the only correct architecture. + +> **Console equivalent:** +> ```python +> load("0x0023_structures/build/0x0023_structures.bin", +> options={"loader.imageBase": 0x10000000, "loader.platform": "thumb2"}) +> ``` + +### Step 8: Save it as a Binary Ninja database (`.bndb`) + +Binary Ninja never writes back into the `.bin`. Your names, comments, types, and patches live in a separate **`.bndb`** database. Save one now, before you make any changes: + +1. Choose `File -> Save As...`. +2. Save it next to the image as `0x0023_structures.bndb`. +3. From now on, save with `File -> Save` (`Cmd+S` on macOS, `Ctrl+S` on Windows/Linux) whenever you rename or patch. + +| File | Role | +| ---- | ---- | +| `0x0023_structures.bin` | the raw firmware image; Binary Ninja never modifies it | +| `0x0023_structures.bndb` | your analysis database: names, types, comments, and patches | + +When you come back later, **open the `.bndb`**, not the `.bin`; that restores all your work. You export the patched image out of this view later, in Step 19. + +### Step 9: The views you will use + +- **Linear view:** the disassembly listing. You navigate, read, and patch here. +- **Graph view:** the control-flow graph of the current function. +- **Decompiler (HLIL):** the pseudo-C decompilation. +- **Hex view:** raw bytes, used for patching. +- **Function list:** the sidebar list of every detected function. + +Navigation: `G` go to address, `N` rename, `Y` set type or signature, `;` add a comment. Breakpoints are set from the GUI through the GDB MI adapter — see Step 13. + +> **macOS function keys:** the top-row `F` keys are usually mapped to system functions. Every step here uses menu paths that work without them. + +--- + +## Part 3: Dynamic — Break at `main` and Hack Live (Project 1) + +### Step 10: Start OpenOCD as a live debug server + +Make sure no other OpenOCD is running; a forgotten server holds port `3333`. + +**macOS / Linux:** + +```bash +ps aux | grep -i openocd +``` + +**Windows (PowerShell):** + +```powershell +Get-Process | Where-Object { $_.ProcessName -like '*openocd*' } +``` + +Stop any leftover server gracefully: + +**macOS / Linux:** + +```bash +pkill -TERM -f openocd +``` + +**Windows (PowerShell):** + +```powershell +Get-Process openocd -ErrorAction SilentlyContinue | Stop-Process +``` + +Start the server **parked at `main`**: + +**macOS Apple Silicon / Linux x64:** + +```bash +BP_ADDR=0x10000234 ./debug-server.sh +``` + +**Windows x64 (PowerShell):** + +```powershell +$env:BP_ADDR="0x10000234"; .\debug-server.ps1 +``` + +**Or start it from the Binary Ninja console**, freeing the probe first and launching the server in the background so the console returns immediately: + +**macOS Apple Silicon / Linux x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # stop any running server first +log = os.path.join(root, "openocd.log") +p = subprocess.Popen([os.path.join(root, "debug-server.sh")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("OpenOCD started (pid", p.pid, "); log:", log) +``` + +**Windows x64:** + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # stop any running server first +log = os.path.join(root, "openocd.log") +p = subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "debug-server.ps1")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT) +print("OpenOCD started (pid", p.pid, "); log:", log) +``` + +`Popen` returns in a few milliseconds; the server keeps running in the background. Check `openocd.log` for `Listening on port 3333`, then connect in Step 11. + +Wait for: + +``` +Info : [rp2350.dap.core0] Examination succeed +Startup breakpoint at 0x10000234 (2-byte hardware execute, one-shot). +Info : starting gdb server for rp2350.dap.core0 on 3333 +Info : Listening on port 3333 for gdb connections +``` + +> **`BP_ADDR` parks the core at `main` before any client connects.** The script arms a 2-byte hardware breakpoint and then does the startup `reset run`, so the core runs from the vector table and stops at your address with no debugger attached yet. When Binary Ninja connects a moment later, the first thing it reads is already the truth: `Stopped at 0x10000234`. + +> **This startup stop is single-use.** OpenOCD flushes breakpoints when a client connects, so this one is gone once Binary Ninja attaches — fine for `main`, which only runs once per reset. Every breakpoint after that is set from the Binary Ninja GUI (Step 13) and is repeatable. To stop at `main` again, restart the server with `BP_ADDR` and reconnect. + +> **Exactly one core.** The line must say `core0` and must **not** mention `core1`. Core1 is never started by this firmware; exposing it makes Binary Ninja read core1's reset-state registers, which are not real addresses, and OpenOCD floods the log with `Failed to read memory at 0xf0000000`. The scripts already use `USE_CORE=0`; do not change it. + +> **Windows driver note:** the Debug Probe must use the **WinUSB** driver. If OpenOCD reports `unable to open CMSIS-DAP device`, install it with [Zadig](https://zadig.akeo.ie/) (select `Debug Probe (CMSIS-DAP)` -> WinUSB). + +### Step 11: Connect Binary Ninja to the GDB server + +1. Make sure the image is open and analyzed (Part 2) and the server from Step 10 is running (parked at `main`). +2. Choose `Debugger -> Connect to Remote Process`. +3. In the **adapter** dropdown, select **GDB MI**. +4. In the **connect** settings group, set **IP Address** to `127.0.0.1` and **Port** to `3333`. +5. Set **Full GDB Executable Path** to the `arm-none-eabi-gdb` from the **Arm GNU Toolchain 14.2.rel1**. It ships for all three hosts, and the Raspberry Pi Pico VS Code extension installs that same 14.2.rel1 toolchain (including `arm-none-eabi-gdb`) on all of them: + + | OS | `arm-none-eabi-gdb` path | + | -- | ------------------------ | + | macOS Apple Silicon | `/Applications/ArmGNUToolchain/14.2.rel1/arm-none-eabi/bin/arm-none-eabi-gdb` (or the Pico extension's `~/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb`) | + | Windows x64 | `%USERPROFILE%\.pico-sdk\toolchain\14_2_Rel1\bin\arm-none-eabi-gdb.exe` (Pico extension), or `C:\Program Files (x86)\Arm GNU Toolchain arm-none-eabi\14.2 rel1\bin\arm-none-eabi-gdb.exe` | + | Linux x64 | `~/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb` (Pico extension), or the `bin/` directory of the extracted `arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi` tarball | +6. Click **Accept**. + +> **Use the GDB MI adapter.** It launches a real `arm-none-eabi-gdb --interpreter=mi2` and lets Binary Ninja drive it, so breakpoints and stepping go through real GDB — which sends the correct 2-byte breakpoint length. Verified working end to end: connect, GUI breakpoints (**Add Hardware Breakpoint...**, hardware execute), **Step Into** / **Step Over**, and register edits. + +> **Do NOT have any breakpoints set in Binary Ninja before you connect.** With the GDB MI adapter, attaching while Binary Ninja already has a breakpoint **hangs the session**. Start the server parked with `BP_ADDR` (Step 10), connect, and only add hardware breakpoints *after* the connection is up. This is a Binary Ninja bug; it is the single most common GDB MI failure. + +> **The GDB executable path matters.** Use the **14.2.rel1** build on every OS (Windows, macOS, Linux). The 13.3.rel1 build did **not** connect in testing. +> +> **This step is temporary.** Vector35 plans to ship a GDB binary with the GDB MI adapter ([Vector35/debugger#929](https://github.com/Vector35/debugger/issues/929), milestone *Langara*). Once that lands, Binary Ninja provides GDB itself and you will not need to set **Full GDB Executable Path** at all. + +> **Do not pick Corellium.** Binary Ninja's adapter dropdown also lists **Corellium**, which is for Corellium's virtual devices and expects an API token, not a local OpenOCD server. Always read the label back and confirm it says **GDB MI** before clicking **Accept**. + +> **The adapter and port are not saved in the `.bndb`.** Every time you relaunch Binary Ninja you must re-select **GDB MI**, re-enter port `3333`, and re-set the GDB path. + +The target keeps running. Open the **Registers** tab (bug icon) and confirm you see live values. `pc` inside `0x10003xxx` and `sp` just below `0x20082000` are healthy. + +> **If `pc` is `0x00000088`, `0x000000ec`, or `sp` is `0xf0000000`, the session is bad.** Restart the server, then restart Binary Ninja (a server restart while attached leaves Binary Ninja in a stale session), and connect again. + +### Step 12: Find `main` without relying on its address + +`main` can move between programs, so we do not guess it. We follow the one fixed path to it. Press `G` and go to `0x10000000`: + +``` +0x10000000 0x20082000 initial stack pointer (top of SRAM) +0x10000004 0x1000015d reset vector +``` + +Bit 0 of a vector is the Thumb bit, so `0x1000015d` means "start at `0x1000015c`". That is `_reset_handler`. Follow the reset path to `0x10000186`, `platform_entry`: + +```asm +10000186 : +10000186: 4914 ldr r1, [pc, #80] +10000188: 4788 blx r1 +1000018a: 4914 ldr r1, [pc, #80] +1000018c: 4788 blx r1 +1000018e: 4914 ldr r1, [pc, #80] +10000190: 4788 blx r1 +10000192: be00 bkpt 0x0000 +``` + +**The middle `blx` at `0x1000018c` is the call to `main`.** `platform_entry` is byte-identical in both projects, so `0x1000018c` catches `main` no matter where the linker placed it. The literal pool at `0x100001dc` holds `main | 1`; clearing bit 0 gives `0x10000234`. + +### Step 13: Set a hardware breakpoint in the GUI + +With the **GDB MI** adapter, Binary Ninja sets breakpoints through real GDB, which sends the correct 2-byte length, so you set them **in the UI**. There is no command port here. + +#### Where you can stop + +| You want to stop at | Project 1 address | How | Repeatable? | +| --- | --- | --- | --- | +| **`main`** | `0x10000234` | The server starts parked there with `BP_ADDR=0x10000234` (Step 10), so Binary Ninja is already stopped at `main` when it connects. | No — `main` runs once per reset. | +| **First `printf` (`IR receiver...`)** | `0x1000026c` | Set a hardware breakpoint in the GUI, then click **Resume**. | No — runs once before the loop. | +| **Loop `printf` (`NEC command...`)** | `0x1000027c` | Same. | Yes — fires on every key press. | +| **First `gpio_put` (`mcrr`)** | `0x1000028a` | Same — this is where LED1's pin is written. | Yes — fires on every key press. | +| **`ir_getkey` return** | `0x10000274` | Same. | Yes. | + +#### Set a breakpoint in the GUI + +1. Press `G`, type the address (for example `0x1000028a`), and press Enter. +2. Set a **hardware execution** breakpoint at that address, either way: + - `Debugger -> Add Hardware Breakpoint...` — a **hardware execute** (`HE`) breakpoint. **Use this one.** + - click the line and press `F2` (`Debugger -> Toggle Breakpoint`) — a **software** breakpoint. It will **not** work here: the code is in read-only flash, so GDB cannot install it and the core just keeps running. +3. Click **Resume**. The core is already running the loop, so the breakpoint fires on the next key press. Binary Ninja stops with the PC at the address and reports it as a **Breakpoint**. + +> **No breakpoints before you connect.** With GDB MI, a breakpoint set before the connection hangs the session (Step 11). Start parked with `BP_ADDR`, connect, *then* add breakpoints. + +> **Step Over on the raw `.bin` steps *into* calls.** The raw image has no symbol for `__wrap_printf`, `ir_getkey`, or `sleep_ms`, so **Step Over** at a `bl` behaves like **Step Into**. When the lab needs to execute the call and then stop, it moves the breakpoint to the return site and clicks **Resume** instead (Step 14 shows this). + +> **Never use Binary Ninja's Restart button.** On RP2350 it resets and halts inside the boot ROM (`pc=0x88`, `sp=0xf0000000`). To reset cleanly, restart the server with `BP_ADDR` and reconnect. + +### Step 14: HACK IT LIVE — move LED1 from GPIO 16 to GPIO 18 + +`main` loads the constant `16` into `r5` once at `0x10000240`, before the loop, and the loop's first `mcrr` at `0x1000028a` writes the LED1 state to the pin in `r5`. Because `r5` is **never reloaded inside the loop**, changing it once is sticky for every later key press. We break on that `mcrr` and change it live. + +1. Press `G`, go to `0x1000028a` (the first `mcrr`, `gpio_put(r5, ...)` for LED1). +2. Set a **hardware execute** breakpoint there: `Debugger -> Add Hardware Breakpoint...`. (Do not use `F2` — that is a software breakpoint and will not work on read-only flash.) +3. Click **Resume** in Binary Ninja, then press **"1"** on the IR remote. The `ir_getkey` call returns, the `printf` at `0x1000027c` prints `NEC command: 0x0C`, and the breakpoint fires at `0x1000028a`. +4. Open the **Registers** widget (bug icon -> **Registers**). Find `r5`. Its value is `16` (`0x10`) — LED1's pin. +5. **Set `r5` to `18` (`0x12`).** From Binary Ninja's Python console (`Plugins -> Python Console`): + ```python + dbg.set_reg_value("r5", 0x12) # LED1 now drives GPIO 18 + ``` + `dbg.set_reg_value(name, value)` writes one register (returns `True` on success). You can also right-click `r5` in the **Registers** widget, press `E` (edit), type `12`, and press Enter. The widget may not repaint the value, but the write reaches the target. +6. Remove the breakpoint at `0x1000028a` and click **Resume**. The core executes the `mcrr` with `r5 = 18`, so pressing **"1"** now lights the **yellow** LED on GPIO 18 instead of the red LED on GPIO 16. + +Because `r5` is set once and never reloaded, the change sticks for every subsequent key press until you reset. Press **"1"** again: the yellow LED lights again, while the terminal still says `NEC command: 0x0C`. + +> **`r5` is the sticky register here.** If you instead edit `r3` (the state, computed by the `clz` trick) or `r2`, the next iteration recomputes them, so the edit lasts one pass. `r5` is the pin, loaded once, so it is the one worth moving. + +### Step 14b: HACK THE STRING LIVE — change `NEC` to `HACKED` + +The text `"NEC command: 0x%02X\n"` lives in flash (`.rodata`) at `0x100038d0`, and flash is **read-only at runtime** — a debugger write there does not stick. So you cannot overwrite the text in place. Instead you redirect the pointer: at the loop `printf` call, `r0` holds the format-string address, so you point `r0` at a replacement string you place in RAM. + +1. Press `G`, go to `0x1000027c` (the loop `bl __wrap_printf`) and set a **hardware execute** breakpoint. Resume and press **"1"** on the remote. At the stop, `r0 = 0x100038d0` (the `ldr r0, [pc, #76]` at `0x1000027a` just loaded the `"NEC command: 0x%02X\n"` pointer) and `r1 = 0x0C`. +2. Put the replacement string into free RAM at `0x20080000` from the **Python console**: + ```python + dbg.write_memory(0x20080000, b"HACKED: 0x%02X\n\x00") # one %02X, same argument + ``` + `dbg.write_memory(address, bytes)` is Binary Ninja's debugger memory-write API; it returns `True` on success. Keep exactly one `%02X` so `printf` still consumes the key in `r1`. +3. Point `r0` at that string: + ```python + dbg.set_reg_value("r0", 0x20080000) + ``` + (Or right-click `r0` in the **Registers** widget, press `E`, type `20080000`, and press Enter.) +4. Move the breakpoint past the call in the GUI (remove it at `0x1000027c`, set one at `0x10000280`) and click **Resume**. The core runs `printf` with `r0` pointing at your RAM string and `r1 = 0x0C`, so this iteration prints: + ``` + HACKED: 0x0C + ``` + then stops at `0x10000280`. + +Like the pin hack, this is **one iteration only**: the loop reloads `r0` from the literal pool on every pass, so the next key prints `NEC command: ...` again. The permanent version is the static patch in Step 18b. + +### Step 15: Why the hack reverts (and why we patch next) + +Press **Resume**. The loop branches back to `0x10000270`, reloads `r0` from `0x100038d0` at `0x1000027a`, and `r5` stays at `18` only until the next reset (it is loaded once at `0x10000240`). The string edit was one iteration; the pin edit was sticky but lives only in a register. To make the behavior permanent we must patch the bytes — the static pass. + +Press **Pause** to stop the output flood. + +### Step 15b: Kill the debugger and OpenOCD + +The live hack is done. Do this **before** the static pass. + +1. In the **Debugger** sidebar, click the **X** (**Kill**) (or **`Debugger -> Kill`**) to disconnect Binary Ninja. +2. **Kill does not stop the OpenOCD process** — `debug-server.sh` started it separately, and it keeps running and holding the probe. Stop it from the Binary Ninja console: + + **macOS / Linux:** + + ```python + import subprocess + subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # stop the debug server, free the probe + ``` + + **Windows:** + + ```python + import subprocess + subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # stop the debug server, free the probe + ``` + +3. Confirm nothing is left: `pgrep -fl openocd` (macOS/Linux) prints nothing. + +From a terminal it is the same: `pkill -TERM -f openocd`, or `Get-Process openocd | Stop-Process` on Windows. + +--- + +## Part 4: Static — Resolve the Functions in Binary Ninja and Patch (Project 1) + +### Step 16: Resolve the functions in the Binary Ninja GUI + +We now name the functions in Binary Ninja using the ELF symbol map from Step 4. Binary Ninja loaded the raw `.bin` with **no symbols**, so every function shows as `sub_` — resolving means giving each one its real name and signature. + +Three keys do all the work: + +| Key | Binary Ninja action | Use it for | +| --- | --- | --- | +| `G` | Go to address | Jump to a function's address | +| `Y` | **Change Type** | Set the function's signature. The dialog shows the full prototype, so this sets the name *and* the type in one step. | +| `N` | Rename | Rename only, when you just want the name and not the type | + +For each function below: `G` to its address, then **`Y` (Change Type)** and type the prototype from the table. + +#### How to resolve a function in Binary Ninja (`Y`) + +`Y` is the **Change Type** key, and it is what actually resolves the function — it turns `void sub_10003364()` into `bool stdio_init_all(void)`. The Change Type dialog shows the full declaration (name and type), so typing the prototype sets both: + +1. `G` to the function's address. The cursor lands on the function. +2. Press **`Y`**. In the Change Type dialog, type the prototype from the table exactly — for example `bool stdio_init_all(void)` — and press Enter. + +If `Y` seems to do nothing, confirm the cursor is on the function, or right-click it and pick **Change Type...**. Binary Ninja parses what you type and silently keeps the old type if it does not parse, so glance at the header after each `Y`. + +#### Worked example: `main` + +1. Press `G`, type `0x10000234`, press Enter. The cursor lands on `sub_10000234`. +2. Press **`Y`** (Change Type), type `int main(void)`, press Enter. + +> **Binary Ninja shows `int32_t` where Ghidra shows `int`.** After you set `int main(void)`, the decompiler header may read `int32_t main(void)`. That is the same type — on this platform `int` is 32 bits and Binary Ninja's parser normalises it to `int32_t`. Do not fight it; it is not an error. + +#### Worked example: `ir_init` + +1. `G` -> `0x100002cc`. +2. `Y` -> `void ir_init(uint8_t pin)`. + +It takes a `uint8_t` pin; `main` calls it with `5` (`movs r0, #5`). It opens with `gpio_init(pin)`, then writes the direction and calls `gpio_set_pulls` for the pull-up. + +#### Worked example: `ir_getkey` + +1. `G` -> `0x100002f4`. +2. `Y` -> `int ir_getkey(void)`. + +It returns `-1` on timeout and the command byte otherwise. The NEC timing helpers (`wait_for_level`, `wait_leader`, `read_nec_bit`, `read_32_bits`, `validate_nec_frame`) are all `static` and **inlined** into it, so you will not find them as separate functions. + +#### Worked example: `gpio_init` + +1. `G` -> `0x1000054c`. +2. `Y` -> `void gpio_init(uint gpio)`. + +`main` calls it three times with `16`, `17`, `18` — the flattened struct pins. + +#### Worked example: `stdio_init_all` + +1. `G` -> `0x10003364`. +2. `Y` -> `bool stdio_init_all(void)`. + +It returns **`bool`**, not `void` — the ELF says `_Bool stdio_init_all(void)`. Our `main` ignores the return value, so the decompiler still reads cleanly. + +#### Worked example: `__wrap_printf` + +1. `G` -> `0x100034f4`. +2. `Y` -> `int __wrap_printf(const char *fmt, ...)`. Keep the `...` — `printf` is variadic. + +> **`printf` in our source is `__wrap_printf` in the binary.** The SDK links our `printf` calls to its `__wrap_printf` wrapper. + +The rest of the chain is the same two keystrokes per function (`G`, then `Y`). This is **our code plus the library functions it actually calls** — not the whole SDK. The call chain for this project: + +``` +main +├── stdio_init_all ── stdio_uart_init ── gpio_set_function, uart_init +│ │ └── uart_init ── clock_get_hz, busy_wait_us +│ ├── stdio_set_driver_enabled +│ ├── stdio_out_chars_crlf +│ └── stdio_put_string ── strlen, time_us_64 +├── gpio_init +├── ir_init ── gpio_init, gpio_set_pulls +├── ir_getkey ── time_us_64 (the NEC timing helpers are inlined) +└── __wrap_printf ── __wrap_vprintf +``` + +**Project 1 — resolve every function in that chain:** + +| Address | Rename to (`N`) | Signature (`Y`) | +| ------- | --------------- | --------------- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | +| `0x100001e4` | `_init` | `void _init(void)` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | +| **`0x10000234`** | **`main`** | **`int main(void)`** | +| `0x100002cc` | `ir_init` | `void ir_init(uint8_t)` | +| `0x100002f4` | `ir_getkey` | `int ir_getkey(void)` | +| `0x100004e8` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | +| `0x10000524` | `gpio_set_pulls` | `void gpio_set_pulls(uint, bool, bool)` | +| `0x1000054c` | `gpio_init` | `void gpio_init(uint)` | +| `0x10000fa8` | `sleep_ms` | `void sleep_ms(uint32_t)` | +| `0x1000118c` | `time_us_64` | `uint64_t time_us_64(void)` | +| `0x100011a0` | `busy_wait_us` | `void busy_wait_us(uint64_t)` | +| `0x10001220` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | +| `0x100013f4` | `clock_get_hz` | `unsigned long clock_get_hz(clock_handle_t)` | +| `0x1000310c` | `exit` | `void exit(int)` | +| `0x10003114` | `runtime_init` | `void runtime_init(void)` | +| `0x10003140` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | +| `0x10003250` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | +| `0x1000333c` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | +| `0x10003364` | `stdio_init_all` | `bool stdio_init_all(void)` | +| `0x100033f4` | `__wrap_puts` | `int __wrap_puts(const char*)` | +| `0x10003430` | `__wrap_vprintf` | `int __wrap_vprintf(const char*, va_list)` | +| `0x100034f4` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | +| `0x100036b0` | `stdio_uart_init` | `void stdio_uart_init(void)` | +| `0x100037f0` | `strlen` | `size_t strlen(const char*)` | + +> **A `void` return type may not stick — here is the fix.** Binary Ninja treats `void` as low-confidence, and its analysis can override it with an inferred type — most often `int32_t` on this 32-bit target. It is most visible on `_reset_handler` (a hand-written assembly entry that never returns normally), but it can happen to **any** function whose return type Binary Ninja thinks it can infer. +> +> Setting the full signature with `Y` reproduces the unwanted `int32_t`, and `fn.return_type = ...` fails too. What works is the **return-value** setter: +> +> ```python +> from binaryninja import ReturnValue, Type +> fn = bv.get_function_at(0x1000015c) +> if fn is not None: +> fn.return_value = ReturnValue(Type.void()) +> ``` +> +> That holds `_reset_handler` at `void` even after reanalysis. If it still will not stick, leave it — it does not affect the rest of the lesson. + +> **Shortcut — resolves name *and* type for every function.** Instead of doing `N` + `Y` by hand, paste this into Binary Ninja's Python console (`Plugins -> Python Console`). It sets each function's name and signature programmatically: +> +> ```python +> from binaryninja import Symbol, SymbolType +> # The raw .bin has no headers, so these SDK types don't exist. set_user_type() +> # re-parses each signature as C, so an undefined name raises +> # "SyntaxError: unknown type name '...'". Define them first. +> sdk = bv.parse_types_from_string(""" +> typedef unsigned int uint; +> typedef char* va_list; +> typedef unsigned long clock_handle_t; +> struct stdio_driver; +> typedef struct stdio_driver stdio_driver_t; +> struct uart_inst; +> typedef struct uart_inst uart_inst_t; +> enum gpio_function { +> GPIO_FUNC_XIP = 0, GPIO_FUNC_SPI = 1, GPIO_FUNC_UART = 2, GPIO_FUNC_I2C = 3, +> GPIO_FUNC_PWM = 4, GPIO_FUNC_SIO = 5, GPIO_FUNC_PIO0 = 6, GPIO_FUNC_PIO1 = 7, +> GPIO_FUNC_GPCK = 8, GPIO_FUNC_USB = 9, GPIO_FUNC_NULL = 0x1f, +> }; +> typedef enum gpio_function gpio_function_t; +> """) +> for name, ty in sdk.types.items(): +> bv.define_user_type(name, ty) +> +> # address: (name, signature) +> funcs = { +> 0x1000015c: ("_reset_handler", "void _reset_handler(void)"), +> 0x10000186: ("platform_entry", "void platform_entry(void)"), +> 0x1000019a: ("data_cpy", "void data_cpy(void*, void*, void*)"), +> 0x100001e4: ("_init", "void _init(void)"), +> 0x10000210: ("frame_dummy", "void frame_dummy(void)"), +> 0x10000234: ("main", "int main(void)"), +> 0x100002cc: ("ir_init", "void ir_init(uint8_t)"), +> 0x100002f4: ("ir_getkey", "int ir_getkey(void)"), +> 0x100004e8: ("gpio_set_function", "void gpio_set_function(uint, gpio_function_t)"), +> 0x10000524: ("gpio_set_pulls", "void gpio_set_pulls(uint, bool, bool)"), +> 0x1000054c: ("gpio_init", "void gpio_init(uint)"), +> 0x10000fa8: ("sleep_ms", "void sleep_ms(uint32_t)"), +> 0x1000118c: ("time_us_64", "uint64_t time_us_64(void)"), +> 0x100011a0: ("busy_wait_us", "void busy_wait_us(uint64_t)"), +> 0x10001220: ("uart_init", "uint uart_init(uart_inst_t*, uint)"), +> 0x100013f4: ("clock_get_hz", "unsigned long clock_get_hz(clock_handle_t)"), +> 0x1000310c: ("exit", "void exit(int)"), +> 0x10003114: ("runtime_init", "void runtime_init(void)"), +> 0x10003140: ("stdio_out_chars_crlf", "void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)"), +> 0x10003250: ("stdio_put_string", "int stdio_put_string(const char*, int, bool, bool)"), +> 0x1000333c: ("stdio_set_driver_enabled", "void stdio_set_driver_enabled(stdio_driver_t*, bool)"), +> 0x10003364: ("stdio_init_all", "bool stdio_init_all(void)"), +> 0x100033f4: ("__wrap_puts", "int __wrap_puts(const char*)"), +> 0x10003430: ("__wrap_vprintf", "int __wrap_vprintf(const char*, va_list)"), +> 0x100034f4: ("__wrap_printf", "int __wrap_printf(const char*, ...)"), +> 0x100036b0: ("stdio_uart_init", "void stdio_uart_init(void)"), +> 0x100037f0: ("strlen", "size_t strlen(const char*)"), +> } +> for addr, (name, sig) in funcs.items(): +> bv.define_user_symbol(Symbol(SymbolType.FunctionSymbol, addr, name)) +> f = bv.get_function_at(addr) +> if f is not None: +> f.set_user_type(sig) +> ``` +> +> SDK type names (`stdio_driver_t`, `uart_inst_t`, `gpio_function_t`, `clock_handle_t`, plus `uint`, `va_list`) are **not** in the raw `.bin`. `set_user_type` re-parses each signature as C, so an undefined name raises `SyntaxError: unknown type name '...'` and stops the loop — it is not harmless. The `sdk` block above defines them first. Standard names (`uint8_t`, `uint32_t`, `uint64_t`, `bool`, `size_t`) are built in. + +### Step 17: Read `main` in the decompiler + +Open the **Decompiler** view on `main`. The whole program is one function because every `static` helper was inlined: + +```asm +10000234
: +10000234: b508 push {r3, lr} +10000236: f003 f895 bl 10003364 +1000023a: 2010 movs r0, #16 +1000023c: f000 f986 bl 1000054c +10000240: 2510 movs r5, #16 +10000242: f04f 0401 mov.w r4, #1 +10000246: ec44 5044 mcrr 0, 4, r5, r4, cr4 +1000024a: 2011 movs r0, #17 +1000024c: f000 f97e bl 1000054c +10000250: 2311 movs r3, #17 +10000252: ec44 3044 mcrr 0, 4, r3, r4, cr4 +10000256: 2012 movs r0, #18 +10000258: f000 f978 bl 1000054c +1000025c: 2312 movs r3, #18 +1000025e: ec44 3044 mcrr 0, 4, r3, r4, cr4 +10000262: 2005 movs r0, #5 +10000264: f000 f832 bl 100002cc +10000268: 2105 movs r1, #5 +1000026a: 4816 ldr r0, [pc, #88] +1000026c: f003 f942 bl 100034f4 <__wrap_printf> +10000270: f000 f840 bl 100002f4 +10000274: 1e04 subs r4, r0, #0 +10000276: db21 blt.n 100002bc +10000278: 4621 mov r1, r4 +1000027a: 4813 ldr r0, [pc, #76] +1000027c: f003 f93a bl 100034f4 <__wrap_printf> +10000280: f1a4 030c sub.w r3, r4, #12 +10000284: fab3 f383 clz r3, r3 +10000288: 095b lsrs r3, r3, #5 +1000028a: ec43 5040 mcrr 0, 4, r5, r3, cr0 +1000028e: f1a4 0218 sub.w r2, r4, #24 +10000292: fab2 f282 clz r2, r2 +10000296: 2311 movs r3, #17 +10000298: 0952 lsrs r2, r2, #5 +1000029a: ec42 3040 mcrr 0, 4, r3, r2, cr0 +1000029e: f1a4 045e sub.w r4, r4, #94 +100002a2: fab4 f484 clz r4, r4 +100002a6: 2312 movs r3, #18 +100002a8: 0964 lsrs r4, r4, #5 +100002aa: ec44 3040 mcrr 0, 4, r3, r4, cr0 +100002ae: 200a movs r0, #10 +100002b0: f000 fe7a bl 10000fa8 +100002b4: f000 f81e bl 100002f4 +100002b8: 1e04 subs r4, r0, #0 +100002ba: dadd bge.n 10000278 +100002bc: 2001 movs r0, #1 +100002be: f000 fe73 bl 10000fa8 +100002c2: e7d5 b.n 10000270 +100002c4: 100038b0 .word 0x100038b0 +100002c8: 100038d0 .word 0x100038d0 +``` + +The decompiler reads roughly: + +```c +int32_t main(void) +{ + stdio_init_all(); + gpio_init(0x10); gpio_set_dir(0x10, 1); // led1_pin = 16 + gpio_init(0x11); gpio_set_dir(0x11, 1); // led2_pin = 17 + gpio_init(0x12); gpio_set_dir(0x12, 1); // led3_pin = 18 + ir_init(5); + __wrap_printf("IR receiver on GPIO %d ready\n", 5); + do + { + int32_t key = ir_getkey(); + if (key >= 0) + { + __wrap_printf("NEC command: 0x%02X\n", key); + mcrr(0x10, key == 0x0c); // led1_pin = 16 + mcrr(0x11, key == 0x18); // led2_pin = 17 + mcrr(0x12, key == 0x5e); // led3_pin = 18 + sleep_ms(10); + } + else + { + sleep_ms(1); + } + } while (true); +} +``` + +- **There is no struct.** `16`, `17`, `18` are immediates; `led1_state` etc. are the `clz`-computed register values. +- The `clz`/`lsrs` pair is how the compiler turns `(key == 0x0C)` into a `0`/`1` without a branch: `sub.w r3, r4, #12` sets the flags, `clz r3, r3` counts leading zeros, `lsrs r3, r3, #5` reduces it to `0` or `1`. +- `0x100002c4` and `0x100002c8` point at `"IR receiver on GPIO %d ready\n"` (`0x100038b0`) and `"NEC command: 0x%02X\n"` (`0x100038d0`) in `.rodata`. + +### Step 18: Patch 1 — swap LED1 and LED3 pins + +The original lesson swaps LED pin assignments. LED1 is the red LED on GPIO 16 and LED3 is the yellow LED on GPIO 18. Swap them so button **"1"** lights yellow and button **"3"** lights red. The two pins are hard-coded immediates: + +| Address | Instruction | Bytes before | Bytes after | Role | +| ------- | ----------- | ------------ | ----------- | ---- | +| `0x10000240` | `movs r5, #16` | `10 25` | `12 25` | LED1's pin (`r5`) 16 -> 18 | +| `0x100002a6` | `movs r3, #18` | `12 23` | `10 23` | LED3's pin (`r3`) 18 -> 16 | + +In the **Hex** view (`View -> Hex`, lock off) change the low byte of each, then right-click `main` -> `Reanalyze`. Or in the Python console: + +```python +bv.write(0x10000240, b"\x12") # movs r5, #18 (LED1 -> GPIO 18) +bv.write(0x100002a6, b"\x10") # movs r3, #16 (LED3 -> GPIO 16) +print(bv.read(0x10000240, 2).hex(" ")) # -> 12 25 +print(bv.read(0x100002a6, 2).hex(" ")) # -> 10 23 +``` + +After reanalysis the loop reads `movs r5, #18` and `movs r3, #16`, so button **"1"** drives GPIO 18 (yellow) and button **"3"** drives GPIO 16 (red). The `NEC command:` log still prints the *command byte*, so the log and the physical LED mapping no longer agree — the log desynchronization. + +### Step 18b: Patch 2 — rename the `NEC` string to `PWN` + +The format string `"NEC command: 0x%02X\n"` starts at `0x100038d0`. Its first three bytes are `4e 45 43` (`NEC`). Change them to `50 57 4e` (`PWN`), leaving the ` command: 0x%02X\n` tail untouched, so the line prints `PWN command: 0x0C`. + +**Option A — Hex view:** go to `0x100038d0` and change the three bytes `4e 45 43` to `50 57 4e`, then reanalyze. + +**Option B — Python console:** + +```python +bv.write(0x100038d0, b"PWN") +print(bv.read(0x100038d0, 20)) # -> b'PWN command: 0x%02X\n\x00' +``` + +Keep the replacement exactly three bytes. If you use a shorter string you must pad it, or `%02X` shifts and `printf` reads the wrong argument. A longer string would overwrite the ` command:` tail. + +### Step 19: Export the patched `.bin` + +```python +import os +seg = next(s for s in bv.segments if s.data_length) # the loadable image segment +data = bv.read(seg.start, seg.data_length) # base + size come from the view itself +out = os.path.join(os.path.join(root, "0x0023_structures", "build"), "0x0023_structures-h.bin") +open(out, "wb").write(data) +print(len(data), out) # -> 16372 /.../build/0x0023_structures-h.bin +``` + +Where the two numbers come from — nothing is hardcoded: + +- **`seg.start`** is the image base Binary Ninja loaded the `.bin` at (`0x10000000`), the same value you pass to `uf2conv --base`. +- **`seg.data_length`** is the segment's size in the file (`0x3ff4` = 16372). Exactly one segment carries data (the image); every peripheral and synthetic segment has `data_length == 0`, so `next(...)` picks the image. + +> **No relative path.** Binary Ninja's Python console runs with a read-only working directory (inside the app bundle), so a relative `open(...)` fails with `OSError: [Errno 30] Read-only file system`. `root` (from `~/.embedded-hacking-repo`, Step 3) is the repo, so the file is written into the project's `build/`. + +### Step 20: Convert to UF2 + +Run from the project directory: + +**macOS Apple Silicon / Linux x64:** + +```bash +python3 ../uf2conv.py 0x0023_structures-h.bin \ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +**Windows x64:** + +```cmd +python ..\uf2conv.py 0x0023_structures-h.bin ^ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +> **Or convert from the Binary Ninja console** — `chdir` to a writable directory first (the default one is read-only), then run the script: +> +> ```python +> import os, sys, runpy +> os.chdir(os.path.join(root, "0x0023_structures", "build")) # the project build dir (writable) +> sys.argv = ["uf2conv.py", "0x0023_structures-h.bin", +> "--base", "0x10000000", "--family", "0xe48bff59", "--output", "hacked.uf2"] +> runpy.run_path("../../uf2conv.py", run_name="__main__") # path to your uf2conv.py +> ``` + +### Step 21: Flash and verify the swapped LEDs + +Hold **BOOTSEL**, plug in the Pico 2, and drag `hacked.uf2` onto the **`RP2350`** drive. Or flash the `.bin` over the Debug Probe with SWD — no BOOTSEL — from the console (stop any running OpenOCD first, and use `Popen`, not `run`, so the console is not blocked): + +```python +import os, subprocess +root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() +bin_path = os.path.join(os.path.join(root, "0x0023_structures", "build"), "0x0023_structures-h.bin") +log = os.path.join(os.path.join(root, "0x0023_structures", "build"), "flash.log") +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first +p = subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) +print("flashing in the background; log:", log) +``` + +Open the serial monitor and press the buttons: + +``` +PWN command: 0x0C <- button "1" now lights the YELLOW LED (GPIO 18) +PWN command: 0x18 <- button "2" still lights the green LED (GPIO 17) +PWN command: 0x5E <- button "3" now lights the RED LED (GPIO 16) +``` + +**Two bytes swapped the LEDs and three bytes renamed the log — no source code.** + +--- + +## Part 5: Reflash Project 2 and Load It into Binary Ninja + +### Step 22: Reflash Project 2 and restart the session + +Part 4 left the Pico running the patched Project 1 image. Put the original Project 2 back and start a fresh session. + +1. Stop any running debug server so the flash script can use the probe: + + ```bash + # macOS / Linux + pkill -TERM -f openocd + ``` + ```powershell + # Windows + Get-Process openocd -ErrorAction SilentlyContinue | Stop-Process + ``` + +2. Flash the original Project 2 image: + + ```bash + # macOS / Linux + ./flash.sh 0x0026_functions/build/0x0026_functions.bin + ``` + ```powershell + # Windows + .\flash.ps1 -Bin 0x0026_functions\build\0x0026_functions.bin + ``` + + **Or do steps 1–2 from the Binary Ninja console:** + + **macOS / Linux:** + + ```python + import os, subprocess + root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) + bin_path = os.path.join(root, "0x0026_functions", "build", "0x0026_functions.bin") + log = os.path.join(os.path.dirname(bin_path), "flash.log") + subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # free the probe first + subprocess.Popen([os.path.join(root, "flash.sh"), bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) + print("flashing Project 2 in the background; log:", log) + ``` + + **Windows:** + + ```python + import os, subprocess + root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() # set once (Step 3) + bin_path = os.path.join(root, "0x0026_functions", "build", "0x0026_functions.bin") + log = os.path.join(os.path.dirname(bin_path), "flash.log") + subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # free the probe first + subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "flash.ps1"), "-Bin", bin_path], + stdout=open(log, "w"), stderr=subprocess.STDOUT) + print("flashing Project 2 in the background; log:", log) + ``` + +3. Load Project 2 and save its database — see Step 22b. + +Confirm the Pico responds to `1` / `2` / `3` again. + +### Step 22b: Load Project 2 into Binary Ninja and save the database + +Exactly like Steps 7–8, but for Project 2. **Use `File -> Open with Options...`** (not plain `File -> Open`), select `0x0026_functions/build/0x0026_functions.bin`, and set: + +- **Architecture:** `thumb2` +- **Platform:** `thumb2` +- **Base Address:** `0x10000000` + +Click **Open**. Then press `G`, type `0x10000000`, and confirm the first two words: + +``` +0x10000000 0x20082000 initial stack pointer +0x10000004 0x1000015d reset vector (bit 0 = Thumb) +``` + +If you see data at `0x00000000`, close the tab and redo it with `Open with Options`. + +Save it with `File -> Save As...` as `0x0026_functions.bndb` (next to the `.bin`). From now on open the `.bndb`, not the `.bin`; save with `Cmd+S` / `Ctrl+S` after every rename or patch. + +> **Console equivalent:** +> ```python +> load("0x0026_functions/build/0x0026_functions.bin", +> options={"loader.imageBase": 0x10000000, "loader.platform": "thumb2"}) +> ``` + +--- + +## Part 6: Dynamic — Break at `main` and Hack Live (Project 2) + +### Step 23: Break at `main` + +`main` is at `0x10000234` in this project too. Start the server parked at `main` (Step 10 form) and connect with the **GDB MI** adapter (Step 11): + +1. Restart the server parked at `main`: + + **macOS / Linux:** + + ```bash + BP_ADDR=0x10000234 ./debug-server.sh + ``` + ```powershell + # Windows + $env:BP_ADDR="0x10000234"; .\debug-server.ps1 + ``` + + **Or restart it from the Binary Ninja console:** + + **macOS / Linux:** + + ```python + import os, subprocess + root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() + log = os.path.join(root, "openocd.log") + subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # kill any running server first + subprocess.Popen([os.path.join(root, "debug-server.sh")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT, start_new_session=True) + print("OpenOCD restarted parked at main; log:", log) + ``` + + **Windows:** + + ```python + import os, subprocess + root = open(os.path.expanduser("~/.embedded-hacking-repo")).read().strip() + log = os.path.join(root, "openocd.log") + subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # kill any running server first + subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(root, "debug-server.ps1")], cwd=root, + env=dict(os.environ, BP_ADDR="0x10000234"), + stdout=open(log, "w"), stderr=subprocess.STDOUT) + print("OpenOCD restarted parked at main; log:", log) + ``` +2. Connect Binary Ninja (Step 11): adapter **GDB MI**, IP `127.0.0.1`, port `3333`. + +The target is already halted at `main` when Binary Ninja connects, and the sidebar reads `Stopped at 0x10000234`. + +### Step 24: Read `main` and find the inlined function bodies + +The whole loop is one function because every helper was inlined. The key difference from Project 1 is the extra `printf` and the blink loop that carries the LED pin in `r5`: + +```asm +10000234
: +10000234: b580 push {r7, lr} +10000236: f003 f8b9 bl 100033ac +1000023a: 2010 movs r0, #16 +1000023c: f000 f9ac bl 10000598 +10000240: f04f 0701 mov.w r7, #1 +10000244: 2310 movs r3, #16 +10000246: ec47 3044 mcrr 0, 4, r3, r7, cr4 +1000024a: 2011 movs r0, #17 +1000024c: f000 f9a4 bl 10000598 +10000250: 2311 movs r3, #17 +10000252: ec47 3044 mcrr 0, 4, r3, r7, cr4 +10000256: 2012 movs r0, #18 +10000258: f000 f99e bl 10000598 +1000025c: 2312 movs r3, #18 +1000025e: ec47 3044 mcrr 0, 4, r3, r7, cr4 +10000262: 2005 movs r0, #5 +10000264: f000 f858 bl 10000318 +10000268: 2105 movs r1, #5 +1000026a: 4828 ldr r0, [pc, #160] +1000026c: f003 f966 bl 1000353c <__wrap_printf> +10000270: f04f 0600 mov.w r6, #0 +10000274: f000 f864 bl 10000340 +10000278: 1e04 subs r4, r0, #0 +1000027a: db19 blt.n 100002b0 +1000027c: 4621 mov r1, r4 +1000027e: 4824 ldr r0, [pc, #144] +10000280: f003 f95c bl 1000353c <__wrap_printf> +10000284: 2510 movs r5, #16 +10000286: ec46 5040 mcrr 0, 4, r5, r6, cr0 +1000028a: 2311 movs r3, #17 +1000028c: ec46 3040 mcrr 0, 4, r3, r6, cr0 +10000290: 2212 movs r2, #18 +10000292: ec46 2040 mcrr 0, 4, r2, r6, cr0 +10000296: 2c0c cmp r4, #12 +10000298: d00e beq.n 100002b8 +1000029a: 2c18 cmp r4, #24 +1000029c: d02e beq.n 100002fc +1000029e: 2c5e cmp r4, #94 +100002a0: d030 beq.n 10000304 +100002a2: 200a movs r0, #10 +100002a4: f000 fea4 bl 10000ff0 +100002a8: f000 f84a bl 10000340 +100002ac: 1e04 subs r4, r0, #0 +100002ae: dae5 bge.n 1000027c +100002b0: 2001 movs r0, #1 +100002b2: f000 fe9d bl 10000ff0 +100002b6: e7dd b.n 10000274 +100002b8: f04f 0801 mov.w r8, #1 +100002bc: 2403 movs r4, #3 +100002be: ec47 5040 mcrr 0, 4, r5, r7, cr0 +100002c2: 2032 movs r0, #50 +100002c4: f000 fe94 bl 10000ff0 +100002c8: ec46 5040 mcrr 0, 4, r5, r6, cr0 +100002cc: 2032 movs r0, #50 +100002ce: f000 fe8f bl 10000ff0 +100002d2: 1e63 subs r3, r4, #1 +100002d4: f013 04ff ands.w r4, r3, #255 +100002d8: d1f1 bne.n 100002be +100002da: ec47 5040 mcrr 0, 4, r5, r7, cr0 +100002de: f1b8 0f01 cmp.w r8, #1 +100002e2: d009 beq.n 100002f8 +100002e4: f1b8 0f02 cmp.w r8, #2 +100002e8: bf14 ite ne +100002ea: 2212 movne r2, #18 +100002ec: 2211 moveq r2, #17 +100002ee: 4641 mov r1, r8 +100002f0: 4808 ldr r0, [pc, #32] +100002f2: f003 f923 bl 1000353c <__wrap_printf> +100002f6: e7d4 b.n 100002a2 +100002f8: 2210 movs r2, #16 +100002fa: e7f8 b.n 100002ee +100002fc: 461d mov r5, r3 +100002fe: f04f 0802 mov.w r8, #2 +10000302: e7db b.n 100002bc +10000304: 4615 mov r5, r2 +10000306: f04f 0803 mov.w r8, #3 +1000030a: e7d7 b.n 100002bc +1000030c: 100038f8 .word 0x100038f8 +10000310: 10003918 .word 0x10003918 +10000314: 10003930 .word 0x10003930 +``` + +What the inlining produced: + +- **`leds_all_off(&leds)`** is the three `mcrr` writes at `0x10000286`, `0x1000028c`, `0x10000292`, all using `r6 = 0` (`mov.w r6, #0` at `0x10000270`). +- **`ir_to_led_number`** is the `cmp`/`beq` chain at `0x10000296`–`0x100002a0` (`12`, `24`, `94`). +- **`get_led_pin`** is the `mov r5, r3` at `0x100002fc` (key 24 -> pin 17) and the `mov r5, r2` at `0x10000304` (key 94 -> pin 18); for key 12, `r5` keeps the `16` loaded at `0x10000284`. +- **`blink_led`** is the loop at `0x100002be`–`0x100002d8`; `r4` counts 3 down to 0, `r5` is the pin, `r7 = 1` and `r6 = 0` drive it on/off. +- **`get_led_pin` in the final print** is `movs r2, #16` at `0x100002f8` (key 1), `moveq r2, #17` at `0x100002ec` (key 2), and `movne r2, #18` at `0x100002ea` (key 3). These are the constants that will **lie** after we patch the loop pins. + +The string map, read straight from `.rodata`: + +| Literal | Points at | String | +| ------- | --------- | ------ | +| `0x1000030c` | `0x100038f8` | `"IR receiver on GPIO %d ready\n"` | +| `0x10000310` | `0x10003918` | `"NEC command: 0x%02X\n"` | +| `0x10000314` | `0x10003930` | `"LED %d activated on GPIO %d\n"` | + +### Step 25: HACK IT LIVE — forge the decoded NEC key + +`ir_getkey` returns the command byte in `r0`; `main` copies it into `r4` at `0x10000278`. We stop right after the read and overwrite `r4` so the program takes a different button's path — even though the operator pressed a different button. + +1. Press `G`, go to `0x1000027c` (the `mov r1, r4` right after the `blt.n`, inside the `key >= 0` block). Set a **hardware execute** breakpoint: `Debugger -> Add Hardware Breakpoint...`. +2. Click **Resume** and press **"1"** on the IR remote. `ir_getkey` returns, `subs r4, r0, #0` at `0x10000278` runs, and the breakpoint fires at `0x1000027c` with `r4 = 0x0C` (12). +3. **Set `r4` to `0x5E` (94)** from the Python console: + ```python + dbg.set_reg_value("r4", 0x5E) # pretend button "3" was pressed + ``` + (Or right-click `r4` in the **Registers** widget, press `E`, type `5e`, and press Enter.) +4. Remove the breakpoint at `0x1000027c` and click **Resume**. The core runs `mov r1, r4`, so the `printf` prints `NEC command: 0x5E`, the `cmp` chain takes the key-94 branch at `0x10000304`, and the Pico blinks the **yellow** LED on GPIO 18 — although you pressed **"1"**. + +> **The pin variant.** If you prefer to move the pin instead of the key, break at `0x10000286` (the first `mcrr`, after `movs r5, #16` at `0x10000284`) and set `r5 = 0x12`. LED1's blink then drives GPIO 18, but `r5` is reloaded at `0x10000284` on the next key, so it is a one-key change. The `r4` edit above is the same idea one step earlier in the pipeline. + +### Step 25b: HACK THE STRING LIVE — change `NEC` to `HACKED` + +The `"NEC command: 0x%02X\n"` format is at `0x10003918`; redirect `r0` to a RAM string at the `printf` call. + +1. Press `G`, go to `0x10000280` (the `bl __wrap_printf` on the key path) and set a **hardware execute** breakpoint. Resume and press **"1"**. At the stop, `r0 = 0x10003918` — the `ldr r0, [pc, #144]` at `0x1000027e` loaded the pointer — and `r1 = 0x0C`. +2. Write the replacement to RAM and repoint `r0`: + ```python + dbg.write_memory(0x20080000, b"HACKED: 0x%02X\n\x00") # keep one %02X + dbg.set_reg_value("r0", 0x20080000) + ``` +3. Remove the breakpoint at `0x10000280`, set one at `0x10000284`, and click **Resume**. This iteration prints: + ``` + HACKED: 0x0C + ``` + One iteration only — the loop reloads `r0` from the literal pool each pass. The permanent version is the static patch in Step 27b. + +### Step 25c: Kill the debugger and OpenOCD + +Same as Step 15b: click the **X** (**Kill**) in the **Debugger** sidebar (or **`Debugger -> Kill`**), then stop OpenOCD from the Binary Ninja console: + +**macOS / Linux:** + +```python +import subprocess +subprocess.run(["pkill", "-TERM", "-f", "openocd"]) # stop the debug server, free the probe +``` + +**Windows:** + +```python +import subprocess +subprocess.run(["taskkill", "/F", "/IM", "openocd.exe"]) # stop the debug server, free the probe +``` + +--- + +## Part 7: Static — Resolve the Functions and Patch (Project 2) + +### Step 26: Resolve the functions in the Binary Ninja GUI + +Same two keys as Step 16 — `G` to the address, then `Y` (Change Type) to set the prototype — using the Project 2 ELF symbol map from Step 4. + +#### Worked example: `main` + +1. `G` -> `0x10000234`. +2. `Y` -> `int main(void)` (Binary Ninja shows `int32_t main(void)` — the same 32-bit `int`). + +#### Worked example: `ir_init` + +1. `G` -> `0x10000318`. +2. `Y` -> `void ir_init(uint8_t pin)`. + +#### Worked example: `ir_getkey` + +1. `G` -> `0x10000340`. +2. `Y` -> `int ir_getkey(void)`. + +#### Worked example: `gpio_init` + +1. `G` -> `0x10000598`. +2. `Y` -> `void gpio_init(uint gpio)`. + +`main` calls it three times with `16`, `17`, `18` — the flattened struct pins. + +#### Worked example: `__wrap_printf` + +1. `G` -> `0x1000353c`. +2. `Y` -> `int __wrap_printf(const char *fmt, ...)`. Keep the `...` — `printf` is variadic. It forwards to `__wrap_vprintf`. + +#### Worked example: `sleep_ms` + +1. `G` -> `0x10000ff0`. +2. `Y` -> `void sleep_ms(uint32_t ms)`. + +The blink loop and the idle path both load `10` or `50` immediately before calling it. + +The call chain for this project is the same as Project 1, plus the extra `printf` and the blink loop: + +``` +main +├── stdio_init_all ── stdio_uart_init ── gpio_set_function, uart_init +│ │ └── uart_init ── clock_get_hz, busy_wait_us +│ ├── stdio_set_driver_enabled +│ ├── stdio_out_chars_crlf +│ └── stdio_put_string ── strlen, time_us_64 +├── gpio_init +├── ir_init ── gpio_init, gpio_set_pulls +├── ir_getkey ── time_us_64 (the NEC timing helpers are inlined) +├── __wrap_printf ── __wrap_vprintf (NEC line and LED line) +└── sleep_ms +``` + +**Project 2 — resolve every function in that chain:** + +| Address | Rename to (`N`) | Signature (`Y`) | +| ------- | --------------- | --------------- | +| `0x1000015c` | `_reset_handler` | `void _reset_handler(void)` | +| `0x10000186` | `platform_entry` | `void platform_entry(void)` | +| `0x1000019a` | `data_cpy` | `void data_cpy(void*, void*, void*)` | +| `0x100001e4` | `_init` | `void _init(void)` | +| `0x10000210` | `frame_dummy` | `void frame_dummy(void)` | +| **`0x10000234`** | **`main`** | **`int main(void)`** | +| `0x10000318` | `ir_init` | `void ir_init(uint8_t)` | +| `0x10000340` | `ir_getkey` | `int ir_getkey(void)` | +| `0x10000534` | `gpio_set_function` | `void gpio_set_function(uint, gpio_function_t)` | +| `0x10000570` | `gpio_set_pulls` | `void gpio_set_pulls(uint, bool, bool)` | +| `0x10000598` | `gpio_init` | `void gpio_init(uint)` | +| `0x10000ff0` | `sleep_ms` | `void sleep_ms(uint32_t)` | +| `0x100011d4` | `time_us_64` | `uint64_t time_us_64(void)` | +| `0x100011e8` | `busy_wait_us` | `void busy_wait_us(uint64_t)` | +| `0x10001268` | `uart_init` | `uint uart_init(uart_inst_t*, uint)` | +| `0x1000143c` | `clock_get_hz` | `unsigned long clock_get_hz(clock_handle_t)` | +| `0x10003154` | `exit` | `void exit(int)` | +| `0x1000315c` | `runtime_init` | `void runtime_init(void)` | +| `0x10003188` | `stdio_out_chars_crlf` | `void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)` | +| `0x10003298` | `stdio_put_string` | `int stdio_put_string(const char*, int, bool, bool)` | +| `0x10003384` | `stdio_set_driver_enabled` | `void stdio_set_driver_enabled(stdio_driver_t*, bool)` | +| `0x100033ac` | `stdio_init_all` | `bool stdio_init_all(void)` | +| `0x1000343c` | `__wrap_puts` | `int __wrap_puts(const char*)` | +| `0x10003478` | `__wrap_vprintf` | `int __wrap_vprintf(const char*, va_list)` | +| `0x1000353c` | `__wrap_printf` | `int __wrap_printf(const char*, ...)` | +| `0x100036f8` | `stdio_uart_init` | `void stdio_uart_init(void)` | +| `0x10003838` | `strlen` | `size_t strlen(const char*)` | + +> **Shortcut — resolves name *and* type for every function.** Paste this into Binary Ninja's Python console: +> +> ```python +> from binaryninja import Symbol, SymbolType +> # The raw .bin has no headers, so these SDK types don't exist. set_user_type() +> # re-parses each signature as C, so an undefined name raises +> # "SyntaxError: unknown type name '...'". Define them first. +> sdk = bv.parse_types_from_string(""" +> typedef unsigned int uint; +> typedef char* va_list; +> typedef unsigned long clock_handle_t; +> struct stdio_driver; +> typedef struct stdio_driver stdio_driver_t; +> struct uart_inst; +> typedef struct uart_inst uart_inst_t; +> enum gpio_function { +> GPIO_FUNC_XIP = 0, GPIO_FUNC_SPI = 1, GPIO_FUNC_UART = 2, GPIO_FUNC_I2C = 3, +> GPIO_FUNC_PWM = 4, GPIO_FUNC_SIO = 5, GPIO_FUNC_PIO0 = 6, GPIO_FUNC_PIO1 = 7, +> GPIO_FUNC_GPCK = 8, GPIO_FUNC_USB = 9, GPIO_FUNC_NULL = 0x1f, +> }; +> typedef enum gpio_function gpio_function_t; +> """) +> for name, ty in sdk.types.items(): +> bv.define_user_type(name, ty) +> +> # address: (name, signature) +> funcs = { +> 0x1000015c: ("_reset_handler", "void _reset_handler(void)"), +> 0x10000186: ("platform_entry", "void platform_entry(void)"), +> 0x1000019a: ("data_cpy", "void data_cpy(void*, void*, void*)"), +> 0x100001e4: ("_init", "void _init(void)"), +> 0x10000210: ("frame_dummy", "void frame_dummy(void)"), +> 0x10000234: ("main", "int main(void)"), +> 0x10000318: ("ir_init", "void ir_init(uint8_t)"), +> 0x10000340: ("ir_getkey", "int ir_getkey(void)"), +> 0x10000534: ("gpio_set_function", "void gpio_set_function(uint, gpio_function_t)"), +> 0x10000570: ("gpio_set_pulls", "void gpio_set_pulls(uint, bool, bool)"), +> 0x10000598: ("gpio_init", "void gpio_init(uint)"), +> 0x10000ff0: ("sleep_ms", "void sleep_ms(uint32_t)"), +> 0x100011d4: ("time_us_64", "uint64_t time_us_64(void)"), +> 0x100011e8: ("busy_wait_us", "void busy_wait_us(uint64_t)"), +> 0x10001268: ("uart_init", "uint uart_init(uart_inst_t*, uint)"), +> 0x1000143c: ("clock_get_hz", "unsigned long clock_get_hz(clock_handle_t)"), +> 0x10003154: ("exit", "void exit(int)"), +> 0x1000315c: ("runtime_init", "void runtime_init(void)"), +> 0x10003188: ("stdio_out_chars_crlf", "void stdio_out_chars_crlf(stdio_driver_t*, const char*, int)"), +> 0x10003298: ("stdio_put_string", "int stdio_put_string(const char*, int, bool, bool)"), +> 0x10003384: ("stdio_set_driver_enabled", "void stdio_set_driver_enabled(stdio_driver_t*, bool)"), +> 0x100033ac: ("stdio_init_all", "bool stdio_init_all(void)"), +> 0x1000343c: ("__wrap_puts", "int __wrap_puts(const char*)"), +> 0x10003478: ("__wrap_vprintf", "int __wrap_vprintf(const char*, va_list)"), +> 0x1000353c: ("__wrap_printf", "int __wrap_printf(const char*, ...)"), +> 0x100036f8: ("stdio_uart_init", "void stdio_uart_init(void)"), +> 0x10003838: ("strlen", "size_t strlen(const char*)"), +> } +> for addr, (name, sig) in funcs.items(): +> bv.define_user_symbol(Symbol(SymbolType.FunctionSymbol, addr, name)) +> f = bv.get_function_at(addr) +> if f is not None: +> f.set_user_type(sig) +> ``` + +### Step 27: Patch 1 — swap LED1 and LED3 pins + +The original lesson swaps LED 1 and LED 3. LED1 is the red LED on GPIO 16 and LED3 is the yellow LED on GPIO 18. In the loop the pins are `movs r5, #16` at `0x10000284` (LED1) and `movs r2, #18` at `0x10000290` (LED3). Swap the two immediates: + +| Address | Instruction | Bytes before | Bytes after | Role | +| ------- | ----------- | ------------ | ----------- | ---- | +| `0x10000284` | `movs r5, #16` | `10 25` | `12 25` | LED1's pin (`r5`) 16 -> 18 | +| `0x10000290` | `movs r2, #18` | `12 22` | `10 22` | LED3's pin (`r2`) 18 -> 16 | + +```python +bv.write(0x10000284, b"\x12") # movs r5, #18 (LED1 -> GPIO 18) +bv.write(0x10000290, b"\x10") # movs r2, #16 (LED3 -> GPIO 16) +print(bv.read(0x10000284, 2).hex(" ")) # -> 12 25 +print(bv.read(0x10000290, 2).hex(" ")) # -> 10 22 +``` + +Now button **"1"** blinks GPIO 18 (yellow) and button **"3"** blinks GPIO 16 (red). The `LED N activated on GPIO P` prints are **not** changed, so they still say `GPIO 16` and `GPIO 18` — **the log no longer matches the hardware.** That mismatch is the security lesson: the operator's console shows the old, expected mapping while the pins do something else. + +> **Optional consistency patch.** If you want the print to tell the truth instead, also change `movs r2, #16` at `0x100002f8` to `#18` (and the key-3 print constant `movne r2, #18` at `0x100002ea` to `#16`). For this lesson we leave them alone on purpose, so the desynchronization is visible. +> +> | Address | Instruction | Bytes before | Bytes after | Role | +> | ------- | ----------- | ------------ | ----------- | ---- | +> | `0x100002f8` | `movs r2, #16` | `10 22` | `12 22` | optional: key-1 print now says GPIO 18 | + +### Step 27b: Patch 2 — rename the `NEC` string to `PWN` + +The format string starts at `0x10003918`; change its first three bytes `4e 45 43` (`NEC`) to `50 57 4e` (`PWN`): + +```python +bv.write(0x10003918, b"PWN") +print(bv.read(0x10003918, 20)) # -> b'PWN command: 0x%02X\n\x00' +``` + +Exactly three bytes, same rule as Project 1: a shorter string must be padded, a longer one overwrites the ` command:` tail. + +### Step 28: Export, convert, and flash + +```python +import os +seg = next(s for s in bv.segments if s.data_length) # the loadable image segment +data = bv.read(seg.start, seg.data_length) # base + size from the view itself +out = os.path.join(os.path.join(root, "0x0026_functions", "build"), "0x0026_functions-h.bin") +open(out, "wb").write(data) +print(len(data), out) # -> 16476 /.../build/0x0026_functions-h.bin +``` + +`seg.data_length` is the image size (`0x405c` = 16476) read from the view — nothing hardcoded. + +**macOS Apple Silicon / Linux x64:** + +```bash +python3 ../uf2conv.py 0x0026_functions-h.bin \ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +**Windows x64:** + +```cmd +python ..\uf2conv.py 0x0026_functions-h.bin ^ + --base 0x10000000 --family 0xe48bff59 --output hacked.uf2 +``` + +Or run the conversion from the Binary Ninja console, exactly as in Step 20 (`os.chdir` to the build dir, then `runpy.run_path("../../uf2conv.py", run_name="__main__")` with `sys.argv` set to the arguments above). + +Hold **BOOTSEL**, plug in the Pico 2, drag `hacked.uf2` onto the **`RP2350`** drive. Or flash the `.bin` over the Debug Probe with SWD — no BOOTSEL — from the console, exactly as in Step 21. + +### Step 29: Verify + +Open the serial monitor: + +- press **"1"** -> the **YELLOW** LED on GPIO 18 blinks (it used to be the red LED on GPIO 16), and the terminal still prints `LED 1 activated on GPIO 16` — **wrong**, it is actually GPIO 18; +- press **"3"** -> the **RED** LED on GPIO 16 blinks (it used to be the yellow LED on GPIO 18), and the terminal still prints `LED 3 activated on GPIO 18` — **wrong**, it is actually GPIO 16; +- press **"2"** -> the green LED on GPIO 17 is unchanged; +- every `NEC command:` line now reads `PWN command:`. + +**The log says one thing, the hardware does another — with two bytes and no source code.** + +--- + +## Cheatsheet + +### Binary Ninja GUI actions + +| Action | How | +| ------ | --- | +| Go to address | `G` | +| Rename function/symbol | `N` | +| Set type or signature | `Y` | +| Add comment | `;` | +| Open Hex view | `View -> Hex` | +| Enable hex editing | Toggle the lock in the status bar | +| Reanalyze after a patch | Right-click function -> `Reanalyze` | +| Edit a register live | `dbg.set_reg_value("r4", 0x5E)` in the Python console (or right-click the register, press `E`, type hex, Enter) | +| Write debugger memory | `dbg.write_memory(0x20080000, b"HACKED: 0x%02X\n\x00")` | +| Set a breakpoint | `Debugger -> Add Hardware Breakpoint...` (hardware execute). Do **not** use `F2` — software breakpoints cannot be written to read-only flash. | +| Move a breakpoint | Remove it and set it at the new address in the GUI (command-port fallback: `rbp ` then `bp 2 hw`) | +| Confirm what is armed | The **Breakpoints** widget lists it (command-port fallback: `mdw 0xE0002000 8`, each armed breakpoint shows as ``) | +| Apply the ELF symbol map | Paste the Python snippet from Step 16 / 26 into the Python Console | + +### OpenOCD server and reset + +The server runs with `gdb_breakpoint_override hard` so that flash-writes are never attempted. Breakpoints in this lab are set in the Binary Ninja GUI through the **GDB MI** adapter (Step 13). The command-port rows below are the fallback if you use the **GDB RSP** adapter instead. + +| Action | Command | +| ------ | ------- | +| Connect to the OpenOCD prompt (fallback) | `nc 127.0.0.1 4444` (or `telnet 127.0.0.1 4444`) | +| Reset and run (command port) | `reset run` | +| Check core state (command port) | `targets` | +| Set a breakpoint in the GUI | `Debugger -> Add Hardware Breakpoint...` (hardware execute; `F2` software breakpoints do not work on flash) | +| (fallback) Add a breakpoint without the GUI | `bp 2 hw` | +| Remove one breakpoint | `rbp ` — **address only, no length, no `hw`** | +| Remove every breakpoint | `rbp all` | +| Start the server parked at `main` | macOS/Linux: `BP_ADDR=0x10000234 ./debug-server.sh` — Windows: `$env:BP_ADDR="0x10000234"; .\debug-server.ps1` (one-shot) | +| Break on the loop in a running target | set a hardware breakpoint in the GUI at the loop address, then **Resume** — repeatable | +| Make Binary Ninja stepping work | `rp2350.dap.core0 configure -rtos none` (already in the scripts) | +| Step without re-trapping | move the breakpoint off the current PC first, then **Step Into**/**Step Over** | +| Reset without desyncing Binary Ninja | **Detach**, `reset run` on the port, reconnect — never `reset run` while attached | +| Kill the debugger | click **X** in the **Debugger** sidebar, or `Debugger -> Kill` | +| Stop OpenOCD | macOS/Linux: `pkill -TERM -f openocd` — Windows: `taskkill /F /IM openocd.exe` | + +### Where you can stop + +| Stop at | Project 1 `0x0023` | Project 2 `0x0026` | +| ------- | ------------------ | ------------------ | +| `main` (once per reset) | `0x10000234` | `0x10000234` | +| `ir_getkey` return | `0x10000274` | `0x10000274` | +| Loop `printf` (`NEC command`) | `0x1000027c` | `0x10000280` | +| First `gpio_put` (`mcrr`, LED1 pin) | `0x1000028a` | `0x10000286` | +| LED3 `gpio_put` (`mcrr`) | `0x100002aa` | `0x10000292` | + +### Every address and byte we changed + +| Project | Address | Before | After | Effect | +| ------- | ------- | ------ | ----- | ------ | +| `0x0023` | `0x10000240` | `10` | `12` | LED1 pin 16 -> 18 | +| `0x0023` | `0x100002a6` | `12` | `10` | LED3 pin 18 -> 16 | +| `0x0023` | `0x100038d0` | `4e 45 43` | `50 57 4e` | prints `PWN` instead of `NEC` | +| `0x0026` | `0x10000284` | `10` | `12` | LED1 pin 16 -> 18 | +| `0x0026` | `0x10000290` | `12` | `10` | LED3 pin 18 -> 16 | +| `0x0026` | `0x100002f8` | `10` | `12` | optional: key-1 print says GPIO 18 | +| `0x0026` | `0x10003918` | `4e 45 43` | `50 57 4e` | prints `PWN` instead of `NEC` | + +### Raw image facts + +| Item | Value | +| ---- | ----- | +| Build type | `Release` | +| Load base address | `0x10000000` | +| Project 1 size | `16372` bytes (`0x3ff4`) | +| Project 2 size | `16476` bytes (`0x405c`) | +| Initial stack pointer (both) | `0x20082000` | +| Reset vector (both) | `0x1000015d` | +| Fixed `main` anchor (both) | `0x1000018c` | +| `main` (both) | `0x10000234` | +| `ir_init`, Project 1 | `0x100002cc` | +| `ir_init`, Project 2 | `0x10000318` | +| `ir_getkey`, Project 1 | `0x100002f4` | +| `ir_getkey`, Project 2 | `0x10000340` | +| Project 1 `IR receiver` string | `0x100038b0` | +| Project 1 `NEC command` string | `0x100038d0` | +| Project 2 `IR receiver` string | `0x100038f8` | +| Project 2 `NEC command` string | `0x10003918` | +| Project 2 `LED activated` string | `0x10003930` | +| RP2350 UF2 family ID | `0xe48bff59` | + +--- + +## Troubleshooting + +### Binary Ninja hangs or crashes when you connect (macOS 27) + +Three different causes have been seen on this setup; check them in this order. + +- **A breakpoint set before connecting.** With the **GDB MI** adapter, if the binary view already has a breakpoint, the session hangs. Start parked with `BP_ADDR`, connect, then add breakpoints. +- **The wrong GDB executable.** Point **Full GDB Executable Path** at the **14.2.rel1** toolchain (Step 11). The 13.3.rel1 build did not connect in testing. +- **The LLDB adapter.** A crash report with `libdebuggercore.dylib -> std::terminate() -> abort()` and `liblldb` in the stack is the **LLDB** adapter, not GDB MI. Avoid LLDB on this setup. + +If Binary Ninja hangs, force-quit it; the connect dialog has no working Cancel. The static steps (resolve, patch, export, flash) never touch the debugger and always work. + +### GDB MI hangs when you connect (a breakpoint already existed) + +With the **GDB MI** adapter, if Binary Ninja already has a breakpoint set when you connect, the session **hangs**. The working order is: + +1. Start the server parked, e.g. `BP_ADDR=0x10000234 ./debug-server.sh` (Windows: `$env:BP_ADDR="0x10000234"; .\debug-server.ps1`). +2. Connect with the **GDB MI** adapter. +3. Only *then* set hardware breakpoints in the UI. + +### Step Into / Step Over does nothing (PC never moves) + +Two causes have been seen on this target. + +1. **A breakpoint on the current PC re-traps the step.** OpenOCD's step-over-breakpoint logic fails with `Duplicate Breakpoint address` and the PC stays put. Fix: move the breakpoint off the current PC (in the GUI), then step. +2. **The `hwthread` RTOS (GDB RSP adapter only).** With the **GDB RSP** adapter, OpenOCD can log `fake step thread 0` and reply without stepping. Fix: `rp2350.dap.core0 configure -rtos none`. **GDB MI does not hit this.** + +### `zsh: bad CPU type in executable: cmake` + +An Intel `x86_64` tool is on your `PATH` on Apple Silicon. Run Step 2: `export PATH="/opt/homebrew/bin:$PATH"`, then `hash -r`. Add it to `~/.zshrc` to make it permanent. + +### My addresses do not match this guide + +You probably built `Debug`. This lesson is a `Release` build. Re-run Step 3 with `-DCMAKE_BUILD_TYPE=Release`. A `Debug` build moves the SDK functions and keeps the `static` helpers separate, so `main` is not at `0x10000234`. + +### A breakpoint never fires + +First, confirm you actually set one, and that it is a **hardware** breakpoint. `Debugger -> Add Hardware Breakpoint...` (hardware execute) should land in the **Breakpoints** widget. If nothing lands, or the core keeps running, you probably used `F2` (`Toggle Breakpoint`) — a software breakpoint cannot be written to read-only flash. Also check you are on **GDB MI**, not **GDB RSP** (the GDB RSP adapter cannot set breakpoints on this target at all). + +Then check the order and the state: + +- **Arm it only after Binary Ninja is connected.** OpenOCD flushes every breakpoint when a client attaches, so anything armed earlier is gone. This also applies to `BP_ADDR` on the startup command line. +- **Is the core running?** If it is stopped, click **Resume**. +- **Does the address get reached again?** `main` runs once per reset, so use `BP_ADDR` at startup rather than `reset run` while attached. Loop addresses such as `0x1000028a` (P1) and `0x10000286` (P2) fire on the next key press with no reset. For the `NEC` print addresses (`0x1000027c` / `0x10000280`) you must also press a remote button, because they sit inside the `if (key >= 0)` block. + +### I edit `r4` / `r5` and it reverts + +For Project 2's key forgery, `subs r4, r0, #0` reloads `r4` from `ir_getkey` on every key press, so the edit is visible for one key. For Project 1's `r5` pin edit, `r5` is loaded once at `0x10000240` and never reloaded, so it sticks until reset. For Project 2's `r5` pin edit, `movs r5, #16` at `0x10000284` reloads it on every key. The edit sticks only while the core is **genuinely stopped** at the breakpoint. + +> **The Registers widget is a snapshot, not a live view.** Binary Ninja reads the registers at each stop and shows that snapshot; it does not poll the target. A value changed outside Binary Ninja will not appear until the next stop. + +### The string hack does nothing (or prints garbage) + +Pick a RAM address that is free — `0x20080000` is safe here (well above the `.data`/`.bss` end at about `0x20000810`). Write a NUL-terminated string, and keep exactly the format specifiers the call consumes: the NEC format has one `%02X`, so the replacement must keep one `%02X`. Then set `r0`, not `r1`. + +### The patched string shifted `printf` output + +The `NEC command: 0x%02X\n` format has one `%02X`. Keep the replacement exactly three bytes (`NEC` -> `PWN`); a longer string would overwrite the ` command:` tail and a shorter one would leave a stray character. In the live hack you write a whole new NUL-terminated string to RAM, so any length is fine as long as it keeps one `%02X`. + +### The struct is not in memory — where is it? + +It is not. `Release` proved `simple_led_ctrl_t` never escapes `main`, so the compiler **flattened** it: the three `uint8_t` pins became the immediates `16`, `17`, `18` and the three `bool` states became register values. There is no `sub sp` for the struct and no memory address to inspect. You patch the immediates instead. If you need to see the struct in memory, build `Debug`, but then none of the addresses in this guide apply. + +### Project 2's `LED N activated on GPIO P` line is wrong after the patch + +That is the point. Step 27 swaps the pins the loop *drives* but leaves the print constants (`0x100002f8`, `0x100002ea`, `0x100002ec`) untouched, so the log shows the old mapping. If you want the print to match, apply the optional consistency patch in Step 27. + +### The serial capture is garbage on macOS + +Reading `/dev/cu.usbmodem*` with a bare `read()` returns garbage. Set **raw termios at 115200** first, or just use `screen /dev/cu.usbmodem* 115200`, which does it for you. + +### It worked for a second, then stopped (Binary Ninja's view desyncs) + +The main cause is **driving the core from the OpenOCD command port while Binary Ninja is connected**. If you must reset, **Detach first**, reset, then reconnect. Never leave a breakpoint on the PC you are about to step or resume from. + +### The console floods with `Failed to read memory at 0xf0000000` + +Core1 is exposed. The scripts must run with `USE_CORE=0`. Stop the server, confirm only `core0` is reported, restart, then restart Binary Ninja. + +### The decompiler still shows the old value after patching + +Right-click the function and choose `Reanalyze`. + +--- + +## Fallback: do the dynamic steps with GDB (macOS 27) + +If Binary Ninja's debugger crashes on attach on macOS 27, you can still do the live hacks with the ARM GDB from the toolchain, against the same OpenOCD server. The addresses and register values are identical to the GUI steps. + +Start the debug server (Step 10), then in a new terminal: + +``` +arm-none-eabi-gdb +``` + +At the `(gdb)` prompt for Project 1: + +``` +set architecture armv8-m.main +target extended-remote :3333 +hbreak *0x1000028a +continue +``` + +Do **not** run `monitor reset run` before `hbreak`. `0x1000028a` is inside `main`'s loop, so the breakpoint fires on the next key press with no reset. Press **"1"** on the remote, then: + +``` +info registers pc r5 # pc = 0x1000028a, r5 = 0x10 +set $r5 = 0x12 +continue +``` + +The next LED1 write drives GPIO 18 — the same temporary live hack as editing `r5` in the Binary Ninja Registers widget. For the string hack, break at `0x1000027c`, then `set {char[19]}0x20080000 = "HACKED: 0x%02X\n"` and `set $r0 = 0x20080000`. + +Project 2 is the same with the other call site and value: + +``` +hbreak *0x1000027c +continue +info registers pc r4 # r4 holds the decoded key you pressed +set $r4 = 0x5E +continue +``` + +`hbreak` sets a hardware breakpoint, which is required for read-only flash. It works from plain GDB because GDB sends the 2-byte length the Cortex-M33 comparators need. Binary Ninja's **GDB MI** adapter goes through the same GDB, so its GUI breakpoints work too. + +## Glossary + +| Term | Definition | +| ---- | ---------- | +| **`.bss`** | Section for uninitialized global variables; zeroed by startup code | +| **`.data`** | Section for initialized global variables; copied from flash to SRAM at boot | +| **`.elf`** | Linked image with the symbol table; the ground truth for addresses and names | +| **Flattening** | The optimizer replacing struct member accesses with the member's constant value; why `simple_led_ctrl_t` disappears | +| **GPIO** | General Purpose Input/Output — controllable pins on the microcontroller | +| **Hardware breakpoint** | A breakpoint serviced by the CPU comparators, required for read-only flash | +| **Inlining** | The optimizer replacing a function call with the function body; why every `static` helper disappears from `main` | +| **Literal pool** | A block of 32-bit constants that Thumb-2 code reaches with PC-relative `ldr` | +| **`mcrr`** | Move to coprocessor from two registers — how the SIO GPIO writes are encoded | +| **NEC** | A common IR protocol: 9 ms leader + 4.5 ms space, then 32 data bits (address, ~address, command, ~command) | +| **`.rodata`** | Read-only section for constants and string literals; stays in flash | +| **SIO** | Single-cycle I/O — the fast GPIO block in the RP2350, at `0xd0000000` | +| **Thumb bit** | Bit 0 of a Cortex-M function pointer; selects Thumb instruction mode | +| **UF2** | USB Flashing Format — the file format the Pico 2 bootloader accepts | +| **Vector table** | The first words of flash: initial stack pointer and exception vectors | + +--- + +**Remember:** the ELF tells you what every address is, and the `.bin` is what you actually patch. Prove the behavior dynamically, resolve the names from the ELF, then patch the bytes and flash. \ No newline at end of file diff --git a/WEEK11/WEEK11-BN.pdf b/WEEK11/WEEK11-BN.pdf new file mode 100644 index 0000000..9b53925 Binary files /dev/null and b/WEEK11/WEEK11-BN.pdf differ diff --git a/WEEK11/WEEK11-SLIDES.pdf b/WEEK11/WEEK11-SLIDES.pdf new file mode 100644 index 0000000..a21fe50 Binary files /dev/null and b/WEEK11/WEEK11-SLIDES.pdf differ diff --git a/WEEK11/WEEK11.md b/WEEK11/WEEK11.md new file mode 100644 index 0000000..4e42763 --- /dev/null +++ b/WEEK11/WEEK11.md @@ -0,0 +1,1607 @@ +# Week 11: Structures and Functions in Embedded Systems: Debugging and Hacking w/ IR Remote Control and NEC Protocol Basics + +*** +**LEGAL DISCLAIMER:** +The information, tools, and code provided in this repository and course are strictly for educational, research, and defensive purposes only. + +You are explicitly prohibited from using any materials contained herein to access, test, modify, or exploit any device, network, or system that you do not own 100% or for which you do not have explicit, documented, and legally binding authorization to interact with. + +By using this repository and course, you acknowledge and agree that: + +1. Any illegal, unauthorized, or malicious use of this information is solely your responsibility. +2. The author(s) and contributor(s) of this repository and course shall not be held liable for any damages, legal repercussions, criminal charges, or unauthorized actions resulting from the use, misuse, or abuse of the contents herein. +3. You will comply with all applicable local, state, national, and international laws regarding cybersecurity and computer fraud. + +**IF YOU DO NOT AGREE WITH THESE TERMS, DO NOT USE THIS REPOSITORY AND COURSE.** + +*** + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: + +- Understand C structures (structs) and how they organize related data +- Know how structs are represented in memory and assembly code +- Understand the NEC infrared (IR) protocol for remote control communication +- Create and use functions with parameters and return values +- Identify struct member access patterns in Ghidra +- Recognize how compilers "flatten" structs into individual operations +- Hack GPIO pin assignments to swap LED behavior +- Understand the security implications of log/behavior desynchronization +- Analyze .elf files in addition to .bin files in Ghidra + +--- + +## Part 1: Understanding C Structures (Structs) + +### What is a Struct? + +A **structure** (or **struct**) is a user-defined data type that groups related variables together under one name. Think of it like a form with multiple fields - each field can hold different types of data, but they all belong together. + +```c +// Define a struct type +typedef struct { + uint8_t led1_pin; // GPIO pin for LED 1 + uint8_t led2_pin; // GPIO pin for LED 2 + uint8_t led3_pin; // GPIO pin for LED 3 + bool led1_state; // Is LED 1 on? + bool led2_state; // Is LED 2 on? + bool led3_state; // Is LED 3 on? +} simple_led_ctrl_t; +``` + +``` ++-----------------------------------------------------------------+ +| Structure as a Container | +| | +| simple_led_ctrl_t leds | +| +-------------------------------------------------------------+| +| | led1_pin: 16 led2_pin: 17 led3_pin: 18 || +| | +--------+ +--------+ +--------+ || +| | | 16 | | 17 | | 18 | || +| | +--------+ +--------+ +--------+ || +| | || +| | led1_state: false led2_state: false led3_state: false || +| | +--------+ +--------+ +--------+ || +| | | false | | false | | false | || +| | +--------+ +--------+ +--------+ || +| +-------------------------------------------------------------+| +| | +| All 6 members live together as ONE variable called "leds" | +| | ++-----------------------------------------------------------------+ +``` + +### Why Use Structs? + +| Without Structs (Messy!) | With Structs (Clean!) | +| -------------------------- | --------------------------- | +| `uint8_t led1_pin = 16;` | `simple_led_ctrl_t leds;` | +| `uint8_t led2_pin = 17;` | `leds.led1_pin = 16;` | +| `uint8_t led3_pin = 18;` | `leds.led2_pin = 17;` | +| `bool led1_state = false;` | `leds.led3_pin = 18;` | +| `bool led2_state = false;` | `leds.led1_state = false;` | +| `bool led3_state = false;` | ... (all in one container!) | + +**Benefits of Structs:** + +1. **Organization** - Related data stays together +2. **Readability** - Code is easier to understand +3. **Maintainability** - Changes are easier to make +4. **Scalability** - Easy to add more LEDs or features +5. **Passing to Functions** - Pass one struct instead of many variables + +--- + +## Part 2: Struct Memory Layout + +### How Structs are Stored in Memory + +When you create a struct, the compiler places each member in consecutive memory locations: + +``` ++-----------------------------------------------------------------+ +| Memory Layout of simple_led_ctrl_t | +| | +| Address Member Size Value | +| ------------------------------------------------------------- | +| 0x2000000 led1_pin 1 byte 16 (0x10) | +| 0x2000001 led2_pin 1 byte 17 (0x11) | +| 0x2000002 led3_pin 1 byte 18 (0x12) | +| 0x2000003 led1_state 1 byte 0 (false) | +| 0x2000004 led2_state 1 byte 0 (false) | +| 0x2000005 led3_state 1 byte 0 (false) | +| | +| Total struct size: 6 bytes | +| | ++-----------------------------------------------------------------+ +``` + +### Accessing Struct Members + +Use the **dot operator** (`.`) to access members: + +```c +simple_led_ctrl_t leds; + +// Set values +leds.led1_pin = 16; +leds.led1_state = true; + +// Read values +printf("Pin: %d\n", leds.led1_pin); +``` + +### Pointer to Struct (Arrow Operator) + +When you have a **pointer** to a struct, use the **arrow operator** (`->`): + +```c +simple_led_ctrl_t leds; +simple_led_ctrl_t *ptr = &leds; // Pointer to the struct + +// These are equivalent: +leds.led1_pin = 16; // Using dot with struct variable +ptr->led1_pin = 16; // Using arrow with pointer +(*ptr).led1_pin = 16; // Dereferencing then dot (same thing) +``` + +``` ++-----------------------------------------------------------------+ +| Dot vs Arrow Operator | +| | +| struct_variable.member <-- Use with actual struct | +| | +| pointer_to_struct->member <-- Use with pointer to struct | +| | +| The arrow (->) is shorthand for (*pointer).member | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 3: Designated Initializers + +### Clean Struct Initialization + +C allows you to initialize struct members by name using **designated initializers**: + +```c +simple_led_ctrl_t leds = { + .led1_pin = 16, + .led2_pin = 17, + .led3_pin = 18, + .led1_state = false, + .led2_state = false, + .led3_state = false +}; +``` + +**Benefits:** + +- Clear which value goes to which member +- Order doesn't matter (can rearrange lines) +- Self-documenting code +- Easy to add new members later + +--- + +## Part 4: Understanding the NEC IR Protocol + +### What is Infrared (IR) Communication? + +**Infrared** communication uses invisible light pulses to send data. Your TV remote uses IR to send commands to your TV. The LED in the remote flashes on and off very quickly in specific patterns that represent different buttons. + +``` ++-----------------------------------------------------------------+ +| IR Communication | +| | +| Remote Control IR Receiver | +| +----------+ +----------+ | +| | Button | | | | +| | 1 | --- IR Light Pulses --- | ++ | | +| | +---+ | ~~~~~~~~~~~~> | Sensor | | +| | | Tx | | | | | +| | +---+ | +----+-----+ | +| | IR LED | | | +| +----------+ v | +| GPIO Pin | +| (Digital signal) | +| | ++-----------------------------------------------------------------+ +``` + +### The NEC Protocol + +**NEC** is one of the most common IR protocols. When you press a button, the remote sends: + +1. **Leader pulse** - 9ms HIGH, 4.5ms LOW (says "attention!") +2. **Address** - 8 bits identifying the device +3. **Address Inverse** - 8 bits (for error checking) +4. **Command** - 8 bits for the button pressed +5. **Command Inverse** - 8 bits (for error checking) + +``` ++-----------------------------------------------------------------+ +| NEC Protocol Frame | +| | +| +---------+---------+---------+---------+---------+---------+ | +| | Leader | Address | Address | Command | Command | Stop | | +| | Pulse | 8-bit | Inverse | 8-bit | Inverse | Bit | | +| | 9+4.5ms | | 8-bit | | 8-bit | | | +| +---------+---------+---------+---------+---------+---------+ | +| | +| Total: 32 bits of data (+ leader + stop) | +| | ++-----------------------------------------------------------------+ +``` + +### NEC Command Codes for Our Remote + +| Button | NEC Command Code | Hex Value | +| ------ | ---------------- | --------- | +| 1 | 0x0C | 12 | +| 2 | 0x18 | 24 | +| 3 | 0x5E | 94 | + +**Note:** Different remotes have different codes. These are specific to our example remote. + +--- + +## Part 5: Understanding Functions in C + +### What is a Function? + +A **function** is a reusable block of code that performs a specific task. Functions help organize code and avoid repetition. + +```c +// Function definition +int add_numbers(int a, int b) { + return a + b; +} + +// Function call +int result = add_numbers(5, 3); // result = 8 +``` + +### Function Components + +``` ++-----------------------------------------------------------------+ +| Anatomy of a Function | +| | +| return_type function_name ( parameters ) { | +| // function body | +| return value; | +| } | +| | +| Example: | +| +-------------------------------------------------------------+| +| | int ir_to_led_number ( int ir_command ) { || +| | --- --------------- --------------- || +| | | | | || +| | | | +-- Parameter (input) || +| | | +-- Function name || +| | +-- Return type (what it gives back) || +| | || +| | if (ir_command == 0x0C) return 1; <-- Body || +| | if (ir_command == 0x18) return 2; || +| | return 0; <-- Return value || +| | } || +| +-------------------------------------------------------------+| +| | ++-----------------------------------------------------------------+ +``` + +### Types of Functions + +| Type | Description | Example | +| ---------------------------- | ------------------------- | ---------------------------- | +| **No params, no return** | Just does something | `void leds_all_off(void)` | +| **With params, no return** | Takes input, no output | `void blink_led(pin, count)` | +| **No params, with return** | No input, gives output | `int ir_getkey(void)` | +| **With params, with return** | Takes input, gives output | `int ir_to_led_number(cmd)` | + +--- + +## Part 6: Functions with Struct Pointers + +### Passing Structs to Functions + +When passing a struct to a function, you usually pass a **pointer** to avoid copying all the data: + +```c +// Function takes a POINTER to the struct +void leds_all_off(simple_led_ctrl_t *leds) { + gpio_put(leds->led1_pin, false); // Use arrow operator! + gpio_put(leds->led2_pin, false); + gpio_put(leds->led3_pin, false); +} + +// Call with address-of operator +simple_led_ctrl_t my_leds; +leds_all_off(&my_leds); // Pass the ADDRESS of my_leds +``` + +``` ++-----------------------------------------------------------------+ +| Passing Struct by Pointer | +| | +| main() { | +| simple_led_ctrl_t leds; <-- Struct lives here | +| leds_all_off(&leds); <-- Pass ADDRESS (pointer) | +| } | | +| | | +| v | +| leds_all_off(simple_led_ctrl_t *leds) { | +| gpio_put(leds->led1_pin, false); | +| ---- | +| | | +| +-- Arrow because leds is a POINTER | +| } | +| | +| WHY use pointers? | +| - Efficient: Only 4 bytes (address) instead of entire struct | +| - Allows modification: Function can change the original | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 7: How Compilers Handle Structs + +### Struct "Flattening" in Assembly + +When the compiler converts your C code to assembly, it "flattens" struct operations into individual memory accesses: + +**C Code:** +```c +gpio_init(leds.led1_pin); // leds.led1_pin = 16 +gpio_init(leds.led2_pin); // leds.led2_pin = 17 +gpio_init(leds.led3_pin); // leds.led3_pin = 18 +``` + +**Assembly (what the compiler produces):** +```assembly +movs r0, #0x10 ; r0 = 16 (led1_pin value) +bl gpio_init ; call gpio_init(16) + +movs r0, #0x11 ; r0 = 17 (led2_pin value) +bl gpio_init ; call gpio_init(17) + +movs r0, #0x12 ; r0 = 18 (led3_pin value) +bl gpio_init ; call gpio_init(18) +``` + +``` ++-----------------------------------------------------------------+ +| Struct Flattening | +| | +| C Level (High-level abstraction): | +| +-------------------------------------------------------------+| +| | gpio_init(leds.led1_pin); || +| | gpio_init(leds.led2_pin); || +| | gpio_init(leds.led3_pin); || +| +-------------------------------------------------------------+| +| | | +| | Compiler transforms | +| v | +| Assembly Level (Flattened): | +| +-------------------------------------------------------------+| +| | movs r0, #16 ; Just the VALUE, no struct reference || +| | bl gpio_init || +| | movs r0, #17 ; Next value directly || +| | bl gpio_init || +| | movs r0, #18 ; Next value directly || +| | bl gpio_init || +| +-------------------------------------------------------------+| +| | +| The struct abstraction DISAPPEARS at the assembly level! | +| We just see individual values being loaded and used. | +| | ++-----------------------------------------------------------------+ +``` + +### Why This Matters for Reverse Engineering + +- In Ghidra, you won't always see "struct" - just individual values +- You must recognize PATTERNS (sequential values like 16, 17, 18) +- Understanding flattening helps you reconstruct the original struct + +--- + +## Part 8: Setting Up Your Environment + +### Prerequisites + +Before we start, make sure you have: + +1. A Raspberry Pi Pico 2 board +2. A Raspberry Pi Pico Debug Probe +3. Ghidra installed (for static analysis) +4. Python installed (for UF2 conversion) +5. A serial monitor (PuTTY, minicom, or screen) +6. An IR receiver module (like VS1838B) +7. An IR remote control (any NEC-compatible remote) +8. Three LEDs (red, green, yellow) with resistors +9. The sample projects: `0x0023_structures` and `0x0026_functions` + +### Hardware Setup + +**IR Receiver Wiring:** + +| IR Receiver Pin | Pico 2 Pin | +| --------------- | ---------- | +| VCC | 3.3V | +| GND | GND | +| OUT/DATA | GPIO 5 | + +**LED Wiring:** + +| LED | GPIO Pin | Resistor | +| ------ | -------- | --------- | +| Red | GPIO 16 | 220-330 ohm | +| Green | GPIO 17 | 220-330 ohm | +| Yellow | GPIO 18 | 220-330 ohm | + +``` ++-----------------------------------------------------------------+ +| Complete Wiring Diagram | +| | +| Pico 2 Components | +| +----------+ | +| | | +-------------+ | +| | GPIO 5 |--------------+ IR Receiver | | +| | | | (VS1838B) | | +| | | +------+------+ | +| | | | | +| | GPIO 16 |---[220 ohm]---(RED LED)----+ | +| | | | | +| | GPIO 17 |---[220 ohm]---(GRN LED)----+ | +| | | | | +| | GPIO 18 |---[220 ohm]---(YEL LED)----+ | +| | | | | +| | 3.3V |-------------------------+-- IR VCC | +| | | | | +| | GND |-------------------------+-- All GNDs | +| | | | +| +----------+ | +| | ++-----------------------------------------------------------------+ +``` + +### Project Structure + +``` +Embedded-Hacking/ ++-- 0x0023_structures/ +| +-- build/ +| | +-- 0x0023_structures.uf2 +| | +-- 0x0023_structures.bin +| +-- main/ +| | +-- 0x0023_structures.c +| +-- ir.h ++-- 0x0026_functions/ +| +-- build/ +| | +-- 0x0026_functions.uf2 +| | +-- 0x0026_functions.bin +| | +-- 0x0026_functions.elf +| +-- main/ +| | +-- 0x0026_functions.c +| +-- ir.h ++-- uf2conv.py +``` + +--- + +## Part 9: Hands-On Tutorial - Structures Code + +### Step 1: Review the Source Code + +Let's examine the structures code: + +**File: `0x0023_structures.c`** + +```c +#include +#include +#include "pico/stdlib.h" +#include "ir.h" + +#define IR_PIN 5 + +typedef struct { + uint8_t led1_pin; + uint8_t led2_pin; + uint8_t led3_pin; + bool led1_state; + bool led2_state; + bool led3_state; +} simple_led_ctrl_t; + +int main(void) { + stdio_init_all(); + + simple_led_ctrl_t leds = { + .led1_pin = 16, + .led2_pin = 17, + .led3_pin = 18, + .led1_state = false, + .led2_state = false, + .led3_state = false + }; + + gpio_init(leds.led1_pin); gpio_set_dir(leds.led1_pin, GPIO_OUT); + gpio_init(leds.led2_pin); gpio_set_dir(leds.led2_pin, GPIO_OUT); + gpio_init(leds.led3_pin); gpio_set_dir(leds.led3_pin, GPIO_OUT); + + ir_init(IR_PIN); + printf("IR receiver on GPIO %d ready\n", IR_PIN); + + while (true) { + int key = ir_getkey(); + if (key >= 0) { + printf("NEC command: 0x%02X\n", key); + + // Turn all off first + leds.led1_state = false; + leds.led2_state = false; + leds.led3_state = false; + + // Check NEC codes + if (key == 0x0C) leds.led1_state = true; // GPIO16 + if (key == 0x18) leds.led2_state = true; // GPIO17 + if (key == 0x5E) leds.led3_state = true; // GPIO18 + + // Apply states + gpio_put(leds.led1_pin, leds.led1_state); + gpio_put(leds.led2_pin, leds.led2_state); + gpio_put(leds.led3_pin, leds.led3_state); + + sleep_ms(10); + } else { + sleep_ms(1); + } + } +} +``` + +### Step 2: Understand the Program Flow + +``` ++-----------------------------------------------------------------+ +| Program Flow | +| | +| 1. Initialize UART (stdio_init_all) | +| 2. Create LED struct with pins 16, 17, 18 | +| 3. Initialize GPIO pins as outputs | +| 4. Initialize IR receiver on GPIO 5 | +| 5. Enter infinite loop: | +| a. Check for IR key press | +| b. If key received: | +| - Print the NEC command code | +| - Turn all LEDs off | +| - Check which button: 0x0C, 0x18, or 0x5E | +| - Turn on the matching LED | +| - Apply states to GPIO pins | +| c. Sleep briefly and repeat | +| | ++-----------------------------------------------------------------+ +``` + +### Step 3: Flash the Binary to Your Pico 2 + +1. Hold the BOOTSEL button on your Pico 2 +2. Plug in the USB cable (while holding BOOTSEL) +3. Release BOOTSEL - a drive called "RPI-RP2" appears +4. Drag and drop `0x0023_structures.uf2` onto the drive +5. The Pico will reboot and start running! + +### Step 4: Verify It's Working + +**Open PuTTY (115200 baud) and test:** + +- Press "1" on remote -> Red LED lights, terminal shows `NEC command: 0x0C` +- Press "2" on remote -> Green LED lights, terminal shows `NEC command: 0x18` +- Press "3" on remote -> Yellow LED lights, terminal shows `NEC command: 0x5E` + +--- + +## Part 10: Debugging with GDB (Structures) + +### Step 5: Start OpenOCD (Terminal 1) + +Open a terminal and start OpenOCD: + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +You should see output indicating OpenOCD connected successfully to your Pico 2 via the Debug Probe. + +### Step 6: Start GDB (Terminal 2) + +Open a **new terminal** and launch GDB with the binary: + +```cmd +arm-none-eabi-gdb build\0x0023_structures.elf +``` + +### Step 7: Connect to the Remote Target + +In GDB, connect to OpenOCD: + +```gdb +target extended-remote :3333 +``` + +### Step 8: Halt the Running Binary + +Stop the processor: + +```gdb +monitor halt +``` + +### Step 9: Examine Main Function + +Disassemble around main to see struct initialization: + +```gdb +disassemble 0x10000234,+200 +``` + +Look for the struct member initialization sequence (mov instructions with values 16, 17, 18). + +### Step 10: Set a Breakpoint at Main + +```gdb +break *0x10000234 +``` + +Reset and run to hit the breakpoint: + +```gdb +monitor reset halt +continue +``` + +### Step 11: Observe the Flattened Struct + +Instead of allocating the struct on the stack, the compiler completely **flattened and optimized** it out! There is no `sub sp` instruction for the struct. + +Examine the `main` disassembly again: + +```gdb +disassemble 0x10000234,+200 +``` + +Notice how values 16, 17, and 18 are just loaded directly into registers (`movs r0, #16`, etc.) and passed to functions. The struct abstraction is entirely gone. + +### Step 12: Watch GPIO Initialization + +Set a breakpoint on the first `gpio_init` call and watch the LED pin get initialized: + +```gdb +break *0x1000023c +continue +info registers r0 +``` + +You should see `r0 = 16`. If you set breakpoints on the subsequent calls (`0x1000024c`, `0x10000258`) and continue, you will see `17` and `18`. + +### Step 13: Examine IR Key Processing + +Because `ir_getkey` is polled in a loop and returns `-1` constantly when no key is pressed, breaking at the immediate return (`0x10000274`) will just flood you with `-1`s! + +Instead, set a breakpoint *inside* the `if (key >= 0)` block at `0x10000278`. This is right after the `blt.n` conditional branch that skips over the processing logic when no key is pressed: + +```gdb +break *0x10000278 +continue +``` + +Now press a button on the remote. The program will halt. At this point, the key value has been moved into `r4` (from the `subs r4, r0, #0` instruction earlier): + +```gdb +info registers r4 +``` + +You'll see the decimal value of the NEC code (e.g., 12 for `0x0C`, 24 for `0x18`, or 94 for `0x5E`). + +### Step 14: Watch the Conditional Checks + +Let's look at the comparisons where the code checks which button was pressed: + +```gdb +break *0x10000280 +continue +``` + +*(Note: If the program doesn't halt immediately, press a button on the remote to trigger the breakpoint!)* + +```gdb +x/8i $pc +``` + +Notice the compiler uses decimal comparisons: `cmp r4, #12` (which is `0x0c`), `cmp r4, #24` (which is `0x18`), and a subtraction trick `sub.w r4, r4, #94` (which is `0x5e`) to determine which button was pressed. + +### Step 15: Observe Inlined GPIO Operations + +The compiler even inlined the `gpio_put` calls! First, let's clear any old breakpoints so we don't accidentally stop somewhere else (like you might have seen if you hit an old breakpoint first!): + +```gdb +delete +break *0x10000298 +continue +``` + +*(Note: Again, press a button on the remote if it doesn't halt immediately!)* + +Now check the registers: + +```gdb +info registers r1 r2 r3 r4 +``` + +Depending on which button you pressed, one of the state registers will be `1` (ON) and the others will be `0` (OFF): + +- `r1` = State for the **Red** LED (GPIO 16) +- `r3` = State for the **Green** LED (GPIO 17) +- `r4` = State for the **Yellow** LED (GPIO 18) +- `r2` = `16` (`0x10`), which is the starting pin number being written to. + +*(Note: Since you are paused here, the physical LEDs haven't updated yet! They will only change after these `mcrr` instructions execute.)* + +Instead of branching to `gpio_put`, it uses direct hardware instructions (disassembled as `mcrr` on the RP2350) to write the states directly to the GPIO hardware. + +### Step 16: Exit GDB + +When done exploring: + +```gdb +quit +``` + +--- + +## Part 11: Setting Up Ghidra for Structures + +### Step 17: Start Ghidra + +Open a terminal and type: + +```cmd +ghidraRun +``` + +### Step 18: Create a New Project + +1. Click **File** -> **New Project** +2. Select **Non-Shared Project** +3. Click **Next** +4. Enter Project Name: `0x0023_structures` +5. Click **Finish** + +### Step 19: Import the Binary + +1. Navigate to the `0x0023_structures/build/` folder +2. **Drag and drop** the `.bin` file into Ghidra's project window + +### Step 20: Configure the Binary Format + +**Click the three dots (...) next to "Language" and:** + +1. Search for "Cortex" +2. Select **ARM Cortex 32 little endian default** +3. Click **OK** + +**Click the "Options..." button and:** + +1. Change **Block Name** to `.text` +2. Change **Base Address** to `10000000` +3. Click **OK** + +### Step 21: Analyze the Binary + +1. Double-click on the file in the project window +2. A dialog asks "Analyze now?" - Click **Yes** +3. Use default analysis options and click **Analyze** + +Wait for analysis to complete. + +--- + +## Part 12: Resolving Functions - Structures Project + +### Step 22: Navigate to Main + +1. Press `G` (Go to address) and type `10000234` +2. Right-click -> **Edit Function Signature** +3. Change to: `int main(void)` +4. Click **OK** + +### Step 23: Resolve stdio_init_all + +At address `0x10000236`: + +1. Double-click on the called function +2. Right-click -> **Edit Function Signature** +3. Change to: `bool stdio_init_all(void)` +4. Click **OK** + +### Step 24: Identify gpio_init from Struct Pattern + +Look for three consecutive calls with values 16, 17, 18: + +```assembly +1000023a 10 20 movs r0,#0x10 +1000023c 00 f0 8c f9 bl FUN_10000558 ; gpio_init + +1000024a 11 20 movs r0,#0x11 +1000024c 00 f0 84 f9 bl FUN_10000558 ; gpio_init + +10000256 12 20 movs r0,#0x12 +10000258 00 f0 7e f9 bl FUN_10000558 ; gpio_init +``` + +This pattern reveals the struct members! Update the function signature: + +1. Right-click on `FUN_10000558` -> **Edit Function Signature** +2. Change to: `void gpio_init(uint gpio)` +3. Click **OK** + +### Step 25: Resolve ir_init + +Look for a function call with GPIO 5: + +```assembly +10000262 05 20 movs r0,#0x5 +10000264 00 f0 38 f8 bl FUN_100002d8 ; ir_init +``` + +1. Right-click on `FUN_100002d8` -> **Edit Function Signature** +2. Change to: `void ir_init(uint pin)` +3. Click **OK** + +### Step 26: Resolve printf + +Right after ir_init, look for the "IR receiver on GPIO" string being loaded and passed to a function: + +```assembly +1000026a 19 48 ldr r0=>s_IR_receiver_on_GPIO_%d... +1000026c 03 f0 46 f9 bl FUN_100034fc ; printf +``` + +1. Right-click on `FUN_100034fc` -> **Edit Function Signature** +2. Change to: `int printf(char *format,...)` +3. Check the **Varargs** checkbox +4. Click **OK** + +### Step 27: Resolve ir_getkey + +Look for a function that returns a value checked against conditions: + +```assembly +10000270 00 f0 46 f8 bl FUN_10000300 ; Call ir_getkey +10000274 04 1e subs r4,r0,#0x0 ; Check if >= 0 +10000276 1e db blt LAB_100002b6 ; If negative, no key pressed +``` + +1. Right-click on `FUN_10000300` -> **Edit Function Signature** +2. Change to: `int ir_getkey(void)` +3. Click **OK** + +### Step 28: Resolve sleep_ms + +Look for calls with 10 (0x0A) or 1 (0x01): + +```assembly +100002a8 0a 20 movs r0,#0xa +100002aa 00 f0 81 fe bl FUN_10000fb0 ; sleep_ms +``` + +1. Right-click on `FUN_10000fb0` -> **Edit Function Signature** +2. Change to: `void sleep_ms(uint ms)` +3. Click **OK** + +--- + +## Part 13: Recognizing Struct Patterns in Assembly + +### Step 29: Identify GPIO Set Direction + +After each `gpio_init`, look for direction setting: + +```assembly +10000240 4f f0 01 04 mov.w r4,#0x1 ; direction = output +10000244 10 23 movs r3,#0x10 ; GPIO 16 +10000246 44 ec 44 30 mcrr p0,0x4,r3,r4,cr4 ; Configure GPIO direction register +``` + +This is the compiler's heavily optimized version of `gpio_set_dir(pin, GPIO_OUT)`. + +### Step 30: Map the Struct Members + +Create a mental (or written) map: + +``` ++-----------------------------------------------------------------+ +| Struct Member Mapping | +| | +| Assembly Value -> Struct Member -> Physical LED | +| ------------------------------------------------------------- | +| 0x10 (16) -> led1_pin -> Red LED | +| 0x11 (17) -> led2_pin -> Green LED | +| 0x12 (18) -> led3_pin -> Yellow LED | +| | +| NEC Code -> State Member -> Action | +| ------------------------------------------------------------- | +| 0x0C -> led1_state=true -> Red LED ON | +| 0x18 -> led2_state=true -> Green LED ON | +| 0x5E -> led3_state=true -> Yellow LED ON | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 14: Hacking Structures + +### Step 31: Patching Instructions + +To modify assembly instructions in Ghidra, you can use either the direct **Bytes Window** workflow or the GUI **Patch Instruction** dialog. Direct byte editing via the Bytes Window is the standard, bulletproof workflow across all embedded reverse engineering tasks because it avoids assembler context conflicts. + +### Step 32: Swap LED Pin Assignments + +We'll swap the red and green LED pins to reverse their behavior! Because the compiler fully flattened the struct, modifying the `gpio_init` pins won't actually change the main loop's behavior (since all three pins are initialized anyway). We must patch the hardcoded pins inside the **main loop** itself! + +**Method A: Bytes Window Workflow (Recommended)** +1. Ensure the Bytes window is open (**Window** -> **Bytes: 0x0023_structures.bin**). +2. Click the **pencil icon** (**Toggle Edit Mode**) in the Bytes window toolbar. +3. In the **Listing** window, navigate to `10000296` where the red LED pin is loaded (`movs r2,#0x10`) and press **`C`** (**Clear Code Bytes**). +4. In the **Bytes** window at offset `10000296`, change byte `10` to **`11`** (swap red to green's pin). +5. In the **Listing** window, click back on `10000296` and press **`D`** (**Disassemble**). +6. In the **Listing** window, navigate to `1000029c` where the green LED pin is loaded (`movs r2,#0x11`) and press **`C`** (**Clear Code Bytes**). +7. In the **Bytes** window at offset `1000029c`, change byte `11` to **`10`** (swap green to red's pin). +8. In the **Listing** window, click back on `1000029c` and press **`D`** (**Disassemble**). + +**Method B: Patch Instruction** +1. Navigate to `10000296`: right-click `movs r2,#0x10` -> **Patch Instruction** (or press `Ctrl+Shift+G`), change `#0x10` to `#0x11`, and press **Enter**. +2. Navigate to `1000029c`: right-click `movs r2,#0x11` -> **Patch Instruction**, change `#0x11` to `#0x10`, and press **Enter**. + +**Before:** +``` +LED 1 (0x0C) -> GPIO 16 -> Red LED +LED 2 (0x18) -> GPIO 17 -> Green LED +``` + +**After:** +``` +LED 1 (0x0C) -> GPIO 17 -> Green LED (SWAPPED!) +LED 2 (0x18) -> GPIO 16 -> Red LED (SWAPPED!) +``` + +### Step 33: Export and Flash + +1. Click **File** -> **Export Program** +2. Set **Format** to **Raw Bytes** +3. Name: `0x0023_structures-h.bin` +4. Click **OK** + +Convert and flash: + +```cmd +cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0023_structures +python ..\uf2conv.py build\0x0023_structures-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +### Step 34: Verify the Hack + +**Open PuTTY and test:** + +- Press "1" on remote -> **GREEN** LED lights (was red!) +- Terminal still shows `NEC command: 0x0C` +- Press "2" on remote -> **RED** LED lights (was green!) +- Terminal still shows `NEC command: 0x18` + +**The log says one thing, but the hardware does another!** + +--- + +## Part 15: Security Implications - Log Desynchronization + +### The Danger of Mismatched Logs + +``` ++-----------------------------------------------------------------+ +| Log vs Reality Desynchronization | +| | +| +-----------------+ +-----------------+ | +| | Terminal Log | | Physical LEDs | | +| +-----------------+ +-----------------+ | +| | NEC: 0x0C | +------- | GREEN LED on | <-- Mismatch! | +| | (expects RED) | | (not red!) | | +| +-----------------+ +-----------------+ | +| | NEC: 0x18 | +------- | RED LED on | <-- Mismatch! | +| | (expects GREEN) | | (not green!) | | +| +-----------------+ +-----------------+ | +| | +| The OPERATOR sees correct logs but WRONG physical behavior! | +| | ++-----------------------------------------------------------------+ +``` + +### Real-World Example: Stuxnet + +**Stuxnet** was a cyberweapon that: + +- Attacked Iranian nuclear centrifuges +- Made centrifuges spin at dangerous speeds +- Fed FALSE "everything normal" data to operators +- Operators saw stable readings while equipment was destroyed + +Our LED example demonstrates the same principle: + +- Logs show expected behavior +- Hardware performs different actions +- Attackers can hide malicious activity + +--- + +## Part 16: Functions Project - Advanced Code + +### Step 35: Review the Functions Code + +**File: `0x0026_functions.c`** (key functions shown) + +```c +// Map IR command to LED number +int ir_to_led_number(int ir_command) { + if (ir_command == 0x0C) return 1; + if (ir_command == 0x18) return 2; + if (ir_command == 0x5E) return 3; + return 0; +} + +// Get GPIO pin for LED number +uint8_t get_led_pin(simple_led_ctrl_t *leds, int led_num) { + if (led_num == 1) return leds->led1_pin; + if (led_num == 2) return leds->led2_pin; + if (led_num == 3) return leds->led3_pin; + return 0; +} + +// Turn off all LEDs +void leds_all_off(simple_led_ctrl_t *leds) { + gpio_put(leds->led1_pin, false); + gpio_put(leds->led2_pin, false); + gpio_put(leds->led3_pin, false); +} + +// Blink an LED +void blink_led(uint8_t pin, uint8_t count, uint32_t delay_ms) { + for (uint8_t i = 0; i < count; i++) { + gpio_put(pin, true); + sleep_ms(delay_ms); + gpio_put(pin, false); + sleep_ms(delay_ms); + } +} + +// Main command processor +int process_ir_led_command(int ir_command, simple_led_ctrl_t *leds, uint8_t blink_count) { + if (!leds || ir_command < 0) return -1; + + leds_all_off(leds); + int led_num = ir_to_led_number(ir_command); + if (led_num == 0) return 0; + + uint8_t pin = get_led_pin(leds, led_num); + blink_led(pin, blink_count, 50); + gpio_put(pin, true); + + return led_num; +} +``` + +### Step 36: Understand the Function Call Chain + +``` ++-----------------------------------------------------------------+ +| Function Call Chain | +| | +| main() | +| | | +| +--> process_ir_led_command(key, &leds, 3) | +| | | +| +--> leds_all_off(&leds) | +| | +--> gpio_put() * 3 | +| | | +| +--> ir_to_led_number(ir_command) | +| | +--> returns 1, 2, or 3 | +| | | +| +--> get_led_pin(&leds, led_num) | +| | +--> returns GPIO pin number | +| | | +| +--> blink_led(pin, 3, 50) | +| | +--> gpio_put() + sleep_ms() in loop | +| | | +| +--> gpio_put(pin, true) | +| | ++-----------------------------------------------------------------+ +``` + +### Step 37: Flash and Test + +1. Flash `0x0026_functions.uf2` to your Pico 2 +2. Open PuTTY +3. Press remote buttons: + - "1" -> Red LED blinks 3 times, then stays on + - "2" -> Green LED blinks 3 times, then stays on + - "3" -> Yellow LED blinks 3 times, then stays on + +--- + +## Part 17: Debugging with GDB (Functions) + +### Step 38: Start OpenOCD (Terminal 1) + +Open a terminal and start OpenOCD: + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +You should see output indicating OpenOCD connected successfully to your Pico 2 via the Debug Probe. + +### Step 39: Start GDB (Terminal 2) + +Open a **new terminal** and launch GDB with the binary: + +```cmd +arm-none-eabi-gdb build\0x0026_functions.elf +``` + +### Step 40: Connect to the Remote Target + +In GDB, connect to OpenOCD: + +```gdb +target extended-remote :3333 +``` + +### Step 41: Halt the Running Binary + +Stop the processor: + +```gdb +monitor halt +``` + +### Step 42: Examine the Function Layout + +Disassemble to see the multiple functions: + +```gdb +disassemble 0x10000234,+300 +``` + +You'll see multiple function prologues (push) and epilogues (pop) for the helper functions. + +### Step 43: Discover the Missing Functions + +The C code relies heavily on helper functions (`process_ir_led_command`, `leds_all_off`, `ir_to_led_number`, `blink_led`). But if you scroll through the disassembly, you'll notice a distinct lack of function prologues, epilogues, or `bl` calls to anything other than `sleep_ms` or `printf`! + +The compiler has aggressively **inlined** every single helper function into the main loop. + +Let's trace exactly how the compiler flattened this logic. First, set a breakpoint right when a key press is detected: + +```gdb +break *0x10000284 +continue +``` + +*(Press a button on your remote to trigger the breakpoint!)* + +### Step 44: Examine leds_all_off (Inlined) + +In the C code, `process_ir_led_command` starts by calling `leds_all_off(&leds)`. + +Let's look at the next few instructions starting from our breakpoint: +```gdb +x/8i $pc +``` + +You'll see: +```assembly +0x10000284 : mov r1, r4 +0x10000286 : ldr r0, [pc, #156] +0x10000288 : bl 0x10003554 <__wrap_printf> +0x1000028c : movs r5, #16 +0x1000028e : mcrr 0, 4, r5, r6, cr0 +0x10000292 : movs r3, #17 +0x10000294 : mcrr 0, 4, r3, r6, cr0 +0x10000298 : movs r2, #18 +``` +Because `r6` was set to `0` earlier, the compiler is just directly writing `0` to pins 16, 17, and 18 using `mcrr`. It bypassed the function call entirely! + +### Step 45: Examine ir_to_led_number (Inlined) + +Next, the C code calls `ir_to_led_number` and `get_led_pin`. Let's see how the compiler handled that by inspecting further down: + +```gdb +x/4i 0x1000029e +``` + +```assembly +0x1000029e : cmp r4, #12 +0x100002a0 : beq.n 0x100002c6 +0x100002a2 : cmp r4, #24 +0x100002a4 : beq.n 0x1000030a +``` +Instead of a separate function, it simply compares the button code in `r4` (`12`, `24`, `94`) and branches straight to the correct blinking logic! + +### Step 46: Watch the blink_led Loop + +Let's set a breakpoint where the blinking loop begins for Button 1 (which handles the Red LED on pin 16). + +```gdb +break *0x100002c6 +continue +``` + +Once halted, inspect the setup: +```gdb +x/6i $pc +``` + +```assembly +0x100002c6 : mov.w r8, #1 @ led_num = 1 +0x100002ca : movs r4, #3 @ blink_count = 3 +0x100002cc : mcrr 0, 4, r5, r7, cr0 @ Turn LED ON (r7=1) +0x100002d0 : movs r0, #50 @ Delay 50ms +0x100002d2 : bl 0x10001008 +0x100002d6 : mcrr 0, 4, r5, r6, cr0 @ Turn LED OFF (r6=0) +``` +The compiler placed the blink count in `r4` and handles the toggling with `mcrr` instructions surrounding `sleep_ms`. + +### Step 47: The Struct Pointer is a Lie + +If you were trying to find the `leds` struct pointer to examine the pins using `x/6xb`, you'd be looking forever. Because the compiler realized the struct is never passed to any external non-inlined functions, it **never bothered creating it in memory**. It simply kept track of the pin numbers (16, 17, 18) directly in registers during compilation! + +### Step 48: Observe the Loop Condition + +Continue execution until you hit the end of the blink loop: + +```gdb +break *0x100002e0 +continue +``` + +At this point, GDB is halted **right before** it performs the subtraction. If you check `r3` now, it will contain random leftover garbage (like `0x400b0000`) because the instruction hasn't run yet! Check `r4` (the current blink count): + +```gdb +info registers r4 +``` + +Now, step forward two instructions to let it actually do the math: + +```gdb +stepi 2 +info registers r3 r4 +``` + +```assembly +0x100002e0 : subs r3, r4, #1 +0x100002e2 : ands.w r4, r3, #255 +0x100002e6 : bne.n 0x100002cc +``` +You will now see `r3` become `2`, and `r4` become `2`! It successfully subtracted 1 from the blink count and stored it back. Since it hasn't hit 0 yet, the `bne.n` instruction will branch back to the start of the blink (`0x100002cc`) to flash the LED again! + +### Step 49: Exit GDB + +When done exploring: + +```gdb +quit +``` + +--- + +## Part 18: Analyzing .ELF Files in Ghidra + +### Step 50: Create New Ghidra Project + +1. Create project: `0x0026_functions` +2. Import the `.elf` file (NOT the .bin this time!) + +### Why Use .ELF Instead of .BIN? + +| Feature | .BIN File | .ELF File | +| -------------- | -------------------- | --------------------------- | +| **Symbols** | None | Function/variable names | +| **Sections** | Raw bytes only | .text, .data, .rodata, etc. | +| **Debug info** | None | May include debug symbols | +| **Size** | Smaller | Larger | +| **Use case** | Flashing to hardware | Analysis and debugging | + +### Step 51: Import and Analyze the .ELF + +1. Drag and drop the `.elf` file into Ghidra +2. Ghidra automatically detects ARM format! +3. Click **Yes** to analyze +4. Wait for analysis to complete + +### Step 52: Explore the Symbol Tree + +With .ELF files, you get more information: + +1. Look at the **Symbol Tree** panel +2. Expand **Functions** - you may see named functions! +3. Expand **Labels** - data labels may appear + +--- + +## Part 19: Hacking the Functions Project + +### Step 53: Find LED Pin Values + +Look for the struct initialization pattern: + +```assembly +movs r0, #0x10 ; led1_pin = 16 +movs r0, #0x11 ; led2_pin = 17 +movs r0, #0x12 ; led3_pin = 18 +``` + +### Step 54: Swap LED 1 and LED 3 + +We'll swap the red (GPIO 16) and yellow (GPIO 18) LEDs: + +**Find and patch in the .bin file:** + +Using the Bytes window in Ghidra (enable the pencil icon, press **`C`** in the Listing, change the byte in the Bytes window, and press **`D`** in the Listing) or a hex editor: +1. Change `0x10` (16) to `0x12` (18) +2. Change `0x12` (18) to `0x10` (16) + +**Before:** +``` +Button 1 -> LED 1 -> GPIO 16 -> Red +Button 3 -> LED 3 -> GPIO 18 -> Yellow +``` + +**After:** +``` +Button 1 -> LED 1 -> GPIO 18 -> Yellow (SWAPPED!) +Button 3 -> LED 3 -> GPIO 16 -> Red (SWAPPED!) +``` + +### Step 55: Export the Patched .BIN + +**Important:** Even though we analyzed the .elf, we patch the .bin! + +1. Open the original `.bin` file in Ghidra (or a hex editor) +2. Apply the patches using the Bytes window workflow +3. Export as `0x0026_functions-h.bin` + +### Step 56: Convert and Flash + +```cmd +cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0026_functions +python ..\uf2conv.py build\0x0026_functions-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +### Step 57: Verify the Hack + +**Open PuTTY and test:** + +- Press "1" -> **YELLOW** LED blinks (was red!) +- Terminal shows: `LED 1 activated on GPIO 16` (WRONG - it's actually GPIO 18!) +- Press "3" -> **RED** LED blinks (was yellow!) +- Terminal shows: `LED 3 activated on GPIO 18` (WRONG - it's actually GPIO 16!) + +**Again, logs don't match reality!** + +--- + +## Part 20: Summary and Review + +### What We Accomplished + +1. **Learned C structures** - Grouping related data together +2. **Understood struct memory layout** - How members are stored consecutively +3. **Mastered dot and arrow operators** - Accessing struct members +4. **Learned the NEC IR protocol** - How remotes communicate +5. **Understood functions with parameters** - Passing data in and out +6. **Saw struct flattening in assembly** - How compilers transform structs +7. **Analyzed .ELF files** - Getting more symbol information +8. **Hacked GPIO assignments** - Swapping LED behavior +9. **Discovered log desynchronization** - Security implications + +### Struct Operations Summary + +``` ++-----------------------------------------------------------------+ +| Struct Operations | +| | +| Definition: | +| typedef struct { | +| uint8_t pin; | +| bool state; | +| } led_t; | +| | +| Creation: | +| led_t led = { .pin = 16, .state = false }; | +| | +| Access (variable): led.pin | +| Access (pointer): ptr->pin or (*ptr).pin | +| | +| Passing to function: void func(led_t *led) | +| Calling: func(&led) | +| | ++-----------------------------------------------------------------+ +``` + +### Function Types Summary + +``` ++-----------------------------------------------------------------+ +| Function Patterns | +| | +| No params, no return: | +| void leds_all_off(void) | +| | +| With params, no return: | +| void blink_led(uint8_t pin, uint8_t count, uint32_t delay) | +| | +| No params, with return: | +| int ir_getkey(void) | +| | +| With params, with return: | +| int ir_to_led_number(int ir_command) | +| | +| With struct pointer: | +| uint8_t get_led_pin(simple_led_ctrl_t *leds, int led_num) | +| | ++-----------------------------------------------------------------+ +``` + +### Key Memory Addresses + +| Memory Address | Description | +| -------------- | ------------------------------- | +| `0x10000234` | main() function | +| `0x10` (16) | GPIO 16 - Red LED (led1_pin) | +| `0x11` (17) | GPIO 17 - Green LED (led2_pin) | +| `0x12` (18) | GPIO 18 - Yellow LED (led3_pin) | +| `0x05` | GPIO 5 - IR receiver | +| `0x0C` | NEC code for button 1 | +| `0x18` | NEC code for button 2 | +| `0x5E` | NEC code for button 3 | + +--- + +--- + +## Key Takeaways + +1. **Structs group related data** - Better organization than separate variables + +2. **Dot operator for variables, arrow for pointers** - `.` vs `->` + +3. **Designated initializers are cleaner** - `.member = value` syntax + +4. **Compilers flatten structs** - You see values, not struct names, in assembly + +5. **NEC protocol uses 8-bit commands** - 0x0C, 0x18, 0x5E for our buttons + +6. **Functions separate concerns** - Each function does one job + +7. **.ELF files contain more info than .BIN** - Symbols, sections, debug data + +8. **Log desynchronization is dangerous** - Logs can lie about real behavior + +9. **Pattern recognition is key** - Consecutive values like 16, 17, 18 reveal structs + +10. **Always patch the .bin for flashing** - .elf is for analysis only + +--- + +## Glossary + +| Term | Definition | +| -------------------------- | -------------------------------------------------- | +| **Arrow Operator (->)** | Accesses struct member through a pointer | +| **Designated Initializer** | Syntax `.member = value` for struct initialization | +| **Dot Operator (.)** | Accesses struct member from a struct variable | +| **.ELF File** | Executable and Linkable Format - contains symbols | +| **Flattening** | Compiler converting structs to individual values | +| **IR (Infrared)** | Invisible light used for remote control | +| **Log Desynchronization** | When logs don't match actual system behavior | +| **Member** | A variable inside a struct | +| **NEC Protocol** | Common IR communication standard | +| **Struct** | User-defined type grouping related variables | +| **typedef** | Creates an alias for a type | + +--- + +## Additional Resources + +### NEC IR Command Reference + +| Button | Command | Binary | +| ------ | ------- | --------- | +| 1 | 0x0C | 0000 1100 | +| 2 | 0x18 | 0001 1000 | +| 3 | 0x5E | 0101 1110 | + +### GPIO Pin Quick Reference + +| GPIO | Default Function | Our Usage | +| ---- | ---------------- | ----------- | +| 5 | General I/O | IR Receiver | +| 16 | General I/O | Red LED | +| 17 | General I/O | Green LED | +| 18 | General I/O | Yellow LED | + +### Struct Size Calculation + +| Type | Size (bytes) | +| ---------- | ------------ | +| `uint8_t` | 1 | +| `bool` | 1 | +| `uint16_t` | 2 | +| `uint32_t` | 4 | +| `int` | 4 | +| `float` | 4 | +| `pointer` | 4 (on ARM32) | + +--- + +## Real-World Implications + +### What You've Learned in This Course + +Over these weeks, you've built skills that few people possess: + +1. **Hardware fundamentals** - GPIO, I2C, PWM, IR protocols +2. **Reverse engineering** - Ghidra, disassembly, function identification +3. **Binary patching** - Modifying compiled code +4. **Security awareness** - Understanding vulnerabilities + +### The Power and Responsibility + +The techniques you've learned can be used for: + +**Good:** + +- Security research +- Debugging proprietary systems +- Understanding how things work +- Career in cybersecurity + +**Danger:** + +- Unauthorized system access +- Sabotage of critical infrastructure +- Fraud and deception + +**Always use your skills ethically and legally!** + +### Keep Learning + +This is just the beginning: + +- Explore more complex protocols (SPI, CAN bus) +- Learn dynamic analysis with debuggers +- Study cryptographic implementations +- Practice on CTF challenges + +--- + +**Congratulations on completing this course! You now have the curiosity, persistence, and skills that embedded systems engineers and security researchers thrive on. Keep experimenting, documenting, and sharing your work. The world needs more builders and defenders like you!** + +Happy hacking! :) diff --git a/WEEK11/WEEK11.pdf b/WEEK11/WEEK11.pdf new file mode 100644 index 0000000..01c037d Binary files /dev/null and b/WEEK11/WEEK11.pdf differ diff --git a/WEEK11/slides/WEEK11-IMG00.svg b/WEEK11/slides/WEEK11-IMG00.svg new file mode 100644 index 0000000..0469f90 --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 11 + + +Structures and Functions in +Embedded Systems: Debugging and Hacking +w/ IR Remote Control & NEC Protocol + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK11/slides/WEEK11-IMG01.svg b/WEEK11/slides/WEEK11-IMG01.svg new file mode 100644 index 0000000..6701ba4 --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG01.svg @@ -0,0 +1,70 @@ + + + + +C Structures (Structs) +Grouping Related Data Together + + + +What is a Struct? +A user-defined type that groups +related variables under one name +Like a form with multiple fields +-- each field holds different data + + + +Struct Definition + +typedef struct { +uint8_t led1_pin; +uint8_t led2_pin; +uint8_t led3_pin; +bool led1_state; +bool led2_state; +bool led3_state; +} simple_led_ctrl_t; + + + +Why Use Structs? +1. +Organization +Related data stays together +2. +Readability +Code easier to understand +3. +Scalability +Easy to add more features +4. +Pass to Functions + + + +simple_led_ctrl_t leds + +pin1: 16 + +pin2: 17 + +pin3: 18 + +state1: 0 + +state2: 0 + +state3: 0 + diff --git a/WEEK11/slides/WEEK11-IMG02.svg b/WEEK11/slides/WEEK11-IMG02.svg new file mode 100644 index 0000000..6157da6 --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG02.svg @@ -0,0 +1,85 @@ + + + + +Struct Memory Layout +How Structs Are Stored and Accessed + + + +Memory Layout (6 bytes total) + +Address +Member +Size +Value + + +0x20000000 +led1_pin +1 byte +16 (0x10) + +0x20000001 +led2_pin +1 byte +17 (0x11) + +0x20000002 +led3_pin +1 byte +18 (0x12) + +0x20000003 +led1_state +1 byte +0 (false) + +0x20000004 +led2_state +1 byte +0 (false) + +0x20000005 +led3_state +1 byte +0 (false) + + + +Dot Operator ( . ) +Use with struct variable + +leds.led1_pin = 16; +leds.led1_state = true; + + + +Arrow Operator ( -> ) +Use with pointer to struct + +ptr->led1_pin = 16; +// same as (*ptr).led1_pin + + + +Designated Initializers + +simple_led_ctrl_t leds = { +.led1_pin = 16, .led2_pin = 17, .led3_pin = 18 +}; +Clear which value goes +to which member +Order doesn't matter + diff --git a/WEEK11/slides/WEEK11-IMG03.svg b/WEEK11/slides/WEEK11-IMG03.svg new file mode 100644 index 0000000..c44ccd0 --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG03.svg @@ -0,0 +1,88 @@ + + + + +NEC IR Protocol +Infrared Remote Control Communication + + + +How IR Works +Remote sends invisible light pulses +IR receiver on GPIO 5 reads signal +IR LED flashes in specific patterns +Each pattern = different button + + + +NEC Protocol Frame (32 bits) + + +Leader + +Address + +Addr Inv + +Command + +Cmd Inv + +Stop + +9ms+4.5ms +8-bit +8-bit check +8-bit +8-bit check + +Leader says "attention!", address identifies device, command is the button pressed + + + +NEC Command Codes + +Button +NEC Code +LED + + +1 +0x0C +Red (GP16) + +2 +0x18 +Green (GP17) + +3 +0x5E +Yellow (GP18) + + + +Hardware Wiring +GPIO 5 +IR Receiver (VS1838B) +GPIO 16 +Red LED + 220 ohm +GPIO 17 +Green LED + 220 ohm +GPIO 18 +Yellow LED + 220 ohm + + + +Projects: 0x0023_structures and 0x0026_functions + diff --git a/WEEK11/slides/WEEK11-IMG04.svg b/WEEK11/slides/WEEK11-IMG04.svg new file mode 100644 index 0000000..719946b --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG04.svg @@ -0,0 +1,91 @@ + + + + +Functions in C +Reusable Blocks of Code + + + +Anatomy of a Function + +int ir_to_led_number(int ir_command) { + +^^^ +^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^ +ret +function name +parameter + + +if (ir_command == 0x0C) return 1; +// body + return value + + + +Function Types + +Type +Example + + +No params, no ret +leds_all_off() + +Params, no return +blink_led(..) + +No params, return +ir_getkey() + +Params + return +ir_to_led_num() + +Struct pointer +get_led_pin() + + + +Key Functions + +ir_to_led_number(cmd) +Maps NEC code to LED 1/2/3 + +get_led_pin(leds, num) +Returns GPIO pin for LED + +blink_led(pin, cnt, ms) +Blinks LED cnt times + + + +Function Call Chain + +main() +--> +process_ir_led_command() + +1. leds_all_off() +Turn all LEDs off + +2. ir_to_led_number() +Map NEC to LED + +3. get_led_pin() +Get GPIO pin + +4. blink_led() +Blink + stay on + diff --git a/WEEK11/slides/WEEK11-IMG05.svg b/WEEK11/slides/WEEK11-IMG05.svg new file mode 100644 index 0000000..9058acc --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG05.svg @@ -0,0 +1,66 @@ + + + + +Struct Pointers in Functions +Passing Data Efficiently + + + +Why Pass by Pointer? +Efficient +4 bytes (address) not 6 +Modifiable +Function can change original +Standard +Embedded systems practice + + + +Arrow Operator +leds->led1_pin +Same as (*leds).led1_pin +Use -> when leds is a pointer + + + +leds_all_off() + +void leds_all_off( +simple_led_ctrl_t *leds) { +gpio_put(leds->led1_pin, 0); +gpio_put(leds->led2_pin, 0); + + + +blink_led() + +void blink_led(uint8_t pin, +uint8_t count, uint32_t ms){ +gpio_put(pin, true); +sleep_ms(ms); + + + +process_ir_led_command() -- Main Command Processor + +int process_ir_led_command(int cmd, +simple_led_ctrl_t *leds, uint8_t blink_count) { +leds_all_off(leds); +// turn all off first +int num = ir_to_led_number(cmd); +// map NEC to LED +blink_led(get_led_pin(leds, num), +// blink then stay on + diff --git a/WEEK11/slides/WEEK11-IMG06.svg b/WEEK11/slides/WEEK11-IMG06.svg new file mode 100644 index 0000000..84c7359 --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG06.svg @@ -0,0 +1,57 @@ + + + + +Structures Source Code +0x0023_structures.c + + + +Full Source + + +#include <stdio.h> +#include "pico/stdlib.h" +#include "ir.h" + +typedef struct { +uint8_t led1_pin, led2_pin, led3_pin; +bool led1_state, led2_state, led3_state; +} simple_led_ctrl_t; + +int main(void) { +stdio_init_all(); +simple_led_ctrl_t leds = { +.led1_pin=16, .led2_pin=17, .led3_pin=18 +}; + +gpio_init(leds.led1_pin); +// init 16, 17, 18 +ir_init(5); +// IR on GPIO 5 +while (true) { +// main loop + + + +Main Loop Flow +ir_getkey() +--> +check NEC code +--> +set state +--> +gpio_put() +0x0C=LED1(red) 0x18=LED2(green) 0x5E=LED3(yellow) + diff --git a/WEEK11/slides/WEEK11-IMG07.svg b/WEEK11/slides/WEEK11-IMG07.svg new file mode 100644 index 0000000..514e9f0 --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG07.svg @@ -0,0 +1,73 @@ + + + + +Struct Flattening +How Compilers Transform Structs + + + +C Code (High Level) + +gpio_init(leds.led1_pin); +// leds.led1_pin = 16 +gpio_init(leds.led2_pin); +// leds.led2_pin = 17 + + + +Assembly (Flattened) + +movs r0, #0x10 +// 16 +bl gpio_init +movs r0, #0x11 +// 17 +bl gpio_init + + + +The Key Insight +Struct abstraction DISAPPEARS +at assembly level +You see individual values (16, 17, 18) not struct names + + + +Struct Member Mapping + +Assembly +Struct Member +Physical +NEC Code + + +0x10 (16) +led1_pin +Red LED +0x0C + +0x11 (17) +led2_pin +Green LED +0x18 + +0x12 (18) +led3_pin +Yellow LED +0x5E + +Sequential values (16,17,18) reveal the struct pattern +Recognize patterns to reconstruct original structs in Ghidra + diff --git a/WEEK11/slides/WEEK11-IMG08.svg b/WEEK11/slides/WEEK11-IMG08.svg new file mode 100644 index 0000000..8eb24ae --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG08.svg @@ -0,0 +1,77 @@ + + + + +Hacking Structures +Swapping GPIO Pin Assignments + + + +Swap LED 1 and LED 2 +Find gpio_init values: +0x10 (16) +--> +0x11 (17) +0x11 (17) +--> +0x10 (16) +Swap the two byte values + + + +Result After Hack + +Before: +Btn 1 --> GPIO 16 --> Red +Btn 2 --> GPIO 17 --> Green + +After: +SWAPPED! + + + +Before (Normal) +Btn 1 (0x0C) --> GPIO 16 +Red +Btn 2 (0x18) --> GPIO 17 +Green +Log and LED match correctly + + +After (Hacked) +Btn 1 (0x0C) --> GPIO 17 +Green! +Btn 2 (0x18) --> GPIO 16 +Red! +Log says RED but GREEN lights + + + +Log Desynchronization + +Terminal Log: +NEC command: 0x0C +(expects Red) + +Physical LED: +GREEN LED on +MISMATCH! + +Operator sees correct logs but WRONG behavior + + + +Stuxnet: +False "normal" data to operators, equipment destroyed + diff --git a/WEEK11/slides/WEEK11-IMG09.svg b/WEEK11/slides/WEEK11-IMG09.svg new file mode 100644 index 0000000..e6ae7a2 --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG09.svg @@ -0,0 +1,73 @@ + + + + +.ELF vs .BIN Analysis +Ghidra Analysis of 0x0026_functions + + + +.ELF vs .BIN Comparison + +Feature +.BIN File +.ELF File + + +Symbols +None +Function names + +Sections +Raw bytes only +.text .data .rodata + +Debug info +None +May include debug + +Use case +Flash to device +Analysis + debug + + + +Importing .BIN +Manual setup required: +ARM Cortex 32 little endian +Block: .text +Base: 10000000 +No function names + + +Importing .ELF +Auto-detected by Ghidra: +ARM format recognized +Sections auto-loaded +Symbol tree populated +Named functions visible + + + +Important Rule +Analyze the .ELF +for symbol information +Patch the .BIN +for flashing + + + +Export Workflow +Patch .bin in Ghidra --> uf2conv.py --> flash to Pico 2 + diff --git a/WEEK11/slides/WEEK11-IMG10.svg b/WEEK11/slides/WEEK11-IMG10.svg new file mode 100644 index 0000000..3906d05 --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG10.svg @@ -0,0 +1,85 @@ + + + + +Structs & IR Protocol +Structs, Functions, IR, and Hacking + + + +C Structures +Group related data together +Dot (.) for variables +Arrow (->) for pointers +Designated init: .pin = 16 + + + +NEC IR Protocol +32-bit frame: addr + cmd +0x0C +Button 1 +0x18 +Button 2 +0x5E +Button 3 + + + +Functions +Reusable blocks, one job each +ir_to_led_number() +get_led_pin() / blink_led() +process_ir_led_command() + + + +Assembly Flattening +Structs vanish in assembly +Only see values: 0x10 0x11 0x12 +Pattern recognition is key +.ELF has symbols, .BIN doesn't + + + +Key Values +0x10000234 +main() +GPIO 5 +IR receiver +GPIO 16/17/18 +Red/Green/Yellow + + + +Hacking Techniques +GPIO swap +0x10 <--> 0x11 +Log desync +Logs lie! +Stuxnet +Same concept + + + +Projects +0x0023_structures +0x0026_functions + + + +Key Takeaway +Patch bytes, mislead logs +hardware does what YOU say + diff --git a/datasheets/DDI0553B_y_armv8m_arm.pdf b/datasheets/DDI0553B_y_armv8m_arm.pdf new file mode 100644 index 0000000..00f7e8d Binary files /dev/null and b/datasheets/DDI0553B_y_armv8m_arm.pdf differ diff --git a/datasheets/aapcs32.pdf b/datasheets/aapcs32.pdf new file mode 100644 index 0000000..3238f72 Binary files /dev/null and b/datasheets/aapcs32.pdf differ diff --git a/datasheets/advnote132.pdf b/datasheets/advnote132.pdf new file mode 100644 index 0000000..60cc1bf Binary files /dev/null and b/datasheets/advnote132.pdf differ diff --git a/datasheets/arm_cortex_m33_trm_100230_0100_03_en.pdf b/datasheets/arm_cortex_m33_trm_100230_0100_03_en.pdf new file mode 100644 index 0000000..9ad17d1 Binary files /dev/null and b/datasheets/arm_cortex_m33_trm_100230_0100_03_en.pdf differ diff --git a/datasheets/raspberry-pi-pico-c-sdk.pdf b/datasheets/raspberry-pi-pico-c-sdk.pdf new file mode 100644 index 0000000..1bd374f Binary files /dev/null and b/datasheets/raspberry-pi-pico-c-sdk.pdf differ diff --git a/datasheets/rp2350-datasheet.pdf b/datasheets/rp2350-datasheet.pdf new file mode 100644 index 0000000..9006fba Binary files /dev/null and b/datasheets/rp2350-datasheet.pdf differ diff --git a/debug-server.ps1 b/debug-server.ps1 new file mode 100644 index 0000000..1f01f90 --- /dev/null +++ b/debug-server.ps1 @@ -0,0 +1,279 @@ +<# +.SYNOPSIS + Start OpenOCD as a live GDB server for the RP2350 via the Pico Debug Probe. + +.DESCRIPTION + Starts OpenOCD in the foreground as a long-running GDB server. It exposes + rp2350.dap.core0 on 127.0.0.1:3333 and leaves the core RUNNING, so that a + debugger (Binary Ninja, or plain GDB) can attach to a target that is already + executing and therefore has sane registers. + + This script is deliberately NOT flash.ps1. flash.ps1 programs flash and + exits; this one claims the probe and stays up so you can single-step, read + memory, and set breakpoints. Do not run both at once -- exactly one process + may own the debug probe. + + The script ends with "reset run" rather than OpenOCD's default halt. This is + the single most important line in the file; see "Why reset run" below. + +.PARAMETER OCD + Optional. Directory containing openocd.exe AND its scripts\ directory. + Overrides the PICO_OPENOCD environment variable. + +.ENVIRONMENT + PICO_OPENOCD Directory containing openocd.exe AND its scripts\ directory. + Default: $env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev + The path is used for BOTH the executable and the -s scripts + argument. This must be an OpenOCD build matching your machine + architecture (x64). + ADAPTER_SPEED SWD clock in kHz. Default: 24000. + This is a read-mostly debug session, so a fast clock is fine + and makes stepping noticeably smoother. Drop it (10000 or + lower) if the link is flaky or you are using long dupont + wires instead of the probe's own connector. + USE_CORE Which cores to expose to the client. Default: 0 (core0 only). + Accepted values: + 0 core0 only <- use this with Binary Ninja + 1 core1 only <- rarely useful + SMP both cores as hwthreads <- see "Why USE_CORE=0" + Do not change this to SMP when driving Binary Ninja. The + explanation below is the whole reason this default is 0. + BP_ADDR OPTIONAL address to halt at during the startup run, as 0x + prefixed hex, e.g. 0x10000234 for main. Unset by default, + which is the normal "attach to a running target" behaviour. + Arms a 2-byte hardware execute breakpoint just before the + final "reset run", so the core runs from the vector table + and stops there on its own, with no client attached yet. + See "Why BP_ADDR is one-shot" below for the important + limitation. Arming a length of 2 is mandatory: the Cortex-M33 + comparators are halfword-based and reject anything else. + +.NOTES + Requirements: + * Pico Debug Probe (or any CMSIS-DAP / SWD adapter) connected to the target. + * OpenOCD with the rp2350 target script installed. See PICO_OPENOCD. + * Exactly one process may own the debug probe. + * A firmware image already programmed into flash (use flash.ps1 first). + * The Debug Probe must use the WinUSB driver. If OpenOCD reports + "unable to open CMSIS-DAP device", install it with Zadig + (https://zadig.akeo.ie/), selecting "Debug Probe (CMSIS-DAP)" -> WinUSB. + + Why USE_CORE=0 (core1 must be hidden from the client) + The RP2350 has two Cortex-M33 cores. Core1 does not start on its own: + nothing in a Pico SDK application releases it from reset unless the program + explicitly does so. If you expose it anyway (USE_CORE=SMP), its registers + read back as meaningless reset defaults: + + core1: pc 0x000000ec sp 0xf0000000 xpsr 0x09000000 lr 0x00000147 + core0: pc 0x1000320c sp 0x20081f38 xpsr 0xa9000000 lr 0x100030ab + + Binary Ninja's register widget renders a memory preview for every register, + which means it treats each register value as an ADDRESS and reads it. The + core1 values above are not real addresses. Each read data-aborts, and + OpenOCD responds by tearing down and re-establishing the SWD debug port, + logging a pair of lines per fault: + + Error: Failed to read memory at 0xf0000000 + Info : SWD DPIDR 0x4c013477 + + That becomes a self-sustaining loop of roughly 450 fault-and-recover cycles + every two seconds, which makes the session unusable. It looks exactly like a + broken debugger or a broken firmware. It is neither: it is a configuration + mismatch, and hiding core1 removes it completely. + + Why reset run (the target must be released, not halted) + On RP2350, halting during reset stops the core at the boot ROM stub BEFORE + the stack pointer is loaded: + + xPSR: 0xf9000000 pc: 0x00000088 msp: 0xf0000000 lr: 0xffffffff + + Those values are garbage for every core, including core0. Attaching in that + window triggers the identical fault storm described above. Ending this + script with "reset run" means the core starts from the vector table and is + executing normally by the time you attach, so registers read back correct. + + Consequence for the client: ATTACH WHILE THE TARGET IS RUNNING. Do not + press Reset/Restart in Binary Ninja -- it performs reset-halt and puts you + back in the garbage window. If you must reset, send "reset run" over the + OpenOCD telnet port (4444) instead. See WEEK04\WEEK04-BN.md. + + Why BP_ADDR is one-shot (read this before relying on it) + A breakpoint armed here DOES fire during the startup "reset run" and halts + the core at your address, so the server comes up parked there and your + debugger can simply attach and look at it. That part works. + + What you do NOT get is a reusable breakpoint. As soon as any GDB client + connects, OpenOCD unconditionally flushes every breakpoint it is holding: + + Info : accepting 'gdb' connection on tcp/3333 + Debug: breakpoints.c:328 breakpoint_remove_all_internal(): + [rp2350.dap.core0] Delete all breakpoints + + So by the time Binary Ninja is up, the comparator is gone -- reading + 0xE0002000 shows zeros, not your address. Consequences: + + * The startup stop is single-use. You cannot Resume and re-catch the + same address. + * You cannot use BP_ADDR to stop in a loop that is already running, + because the core only passes that point once per reset. + * main (0x10000234) is a good BP_ADDR value precisely because it is + reached exactly once, right after reset. + + To arm anything further, or to re-arm main, do it from the OpenOCD command + port AFTER your debugger has connected -- the order is the whole trick: + + nc 127.0.0.1 4444 + > bp 0x1000023e 2 hw + > reset run + + Arming before the client connects does not work (flushed above), and in + plain GDB the equivalent "hbreak" is cleared by "detach" -- both were + verified by reading the FPB comparator registers back. + + Related Binary Ninja bug: Binary Ninja's own breakpoints cannot be used on + this target at all. It sends Z0,,1 -- a 1-byte packet -- and + OpenOCD answers "only breakpoints of two bytes length supported". Both + Toggle Breakpoint (F2) and Add Hardware Breakpoint (F3) fail, at every + address, and the dialog's Size field is disabled so there is no UI way + around it. gdb_breakpoint_override hard does not change this. Hence the + command-port workflow described above. + + Why the remaining OpenOCD flags are set + gdb_breakpoint_override hard + Force every client breakpoint onto the Cortex-M33 hardware comparators + (the RP2350 has 8 breakpoints and 4 watchpoints). Without this, a + client may try to write a BKPT instruction into flash at 0x10000000, + which is read-only XIP memory, and the write fails. + gdb_memory_map disable + Stops the client probing the entire 32 MiB flash map on connect. Pure + Raspberry Pi guidance for suppressing spurious "Failed to read memory" + reports during target discovery. + cortex_m reset_config sysresetreq + The Cortex-M33 in the RP2350 has no VECTRESET. Without this, OpenOCD + warns on every reset: + VECTRESET is not supported on this Cortex-M core, using SYSRESETREQ + instead + NOTE: this MUST be the generic "cortex_m" command, not + "rp2350.dap.core1 cortex_m ...". With USE_CORE=0 the core1 target does + not exist, so a core1-scoped command aborts OpenOCD before "init" runs, + and the script exits silently having printed nothing useful. + adapter speed + See ADAPTER_SPEED above. + +.EXAMPLE + .\debug-server.ps1 + +.EXAMPLE + $env:ADAPTER_SPEED=10000; .\debug-server.ps1 + +.EXAMPLE + $env:PICO_OPENOCD="C:\openocd\bin"; .\debug-server.ps1 + +.EXAMPLE + $env:BP_ADDR="0x10000234"; .\debug-server.ps1 + Comes up halted at main (one-shot; see "Why BP_ADDR is one-shot"). + +.NOTES + Exit status: + * propagated from OpenOCD. A clean shutdown via Ctrl-C exits 0; an + OpenOCD configuration error exits non-zero. + + Related scripts: + flash.ps1 one-shot raw .bin programmer (exits when done) + + See also: + WEEK04\WEEK04-BN.md the full walkthrough this script belongs to +#> + +[CmdletBinding()] +param( + [Parameter()] + [string]$OCD +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# --- configuration --------------------------------------------------------- + +if (-not $OCD) { $OCD = $env:PICO_OPENOCD } +if (-not $OCD) { $OCD = "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev" } + +$SPEED = if ($env:ADAPTER_SPEED) { $env:ADAPTER_SPEED } else { "24000" } +$USE_CORE = if ($env:USE_CORE) { $env:USE_CORE } else { "0" } +$BP_ADDR = if ($env:BP_ADDR) { $env:BP_ADDR.Trim() } else { "" } + +if ($BP_ADDR -and $BP_ADDR -notmatch '^0x[0-9a-fA-F]+$') { + Write-Error "BP_ADDR must be 0x-prefixed hex, e.g. 0x10000234 (got '$BP_ADDR')" + exit 1 +} + +if (-not (Test-Path "$OCD\openocd.exe")) { + Write-Error "OpenOCD not found at $OCD\openocd.exe (set PICO_OPENOCD or -OCD to the directory containing openocd.exe)" + exit 1 +} + +# --- announce, so the operator can verify intent before the target is touched -- + +Write-Host "Starting OpenOCD GDB server on 127.0.0.1:3333 using $OCD\openocd.exe" +Write-Host "SWD adapter speed: $SPEED kHz" +Write-Host "Cores exposed to GDB (USE_CORE): $USE_CORE" +Write-Host "Target will be reset and released (reset run) - attach while it is running." + +if ($BP_ADDR) { + Write-Host "Startup breakpoint at $BP_ADDR (2-byte hardware execute, one-shot)." + Write-Host " It fires during this startup reset run. Any client connecting later" + Write-Host " causes OpenOCD to delete it, so arm further breakpoints on port 4444" + Write-Host " after attaching: bp 2 hw" +} + +# --- start ----------------------------------------------------------------- +# +# Order matters: USE_CORE must be set BEFORE -f target/rp2350.cfg is read, +# because the target script branches on it when creating the DAP targets. +# +# "reset run" is last so it happens after init and after the target is +# examined, releasing the core rather than halting it. See .NOTES for why. +# +# Any BP_ADDR breakpoint is inserted after "init" (the target must exist before +# a comparator can be programmed) but before "reset run" (so the core is already +# armed when it starts). The length must be 2: Cortex-M33 comparators reject +# other widths. + +$ocdArgs = @( + "-s", "$OCD\scripts", + "-f", "interface/cmsis-dap.cfg", + "-c", "set USE_CORE $USE_CORE", + "-f", "target/rp2350.cfg", + # Drop the hwthread RTOS the RP2350 target script attaches to core0. + # + # target/rp2350.cfg creates core0 with "-rtos hwthread", which registers a + # fake RTOS whose "current thread" is coreid+1 = 1. Binary Ninja single-steps + # with the GDB packet "vCont;s" and no thread id, i.e. thread 0. OpenOCD's + # gdb_server sees rtos->current_thread (1) != thread_id (0) and takes its + # "fake step" path, replying with a stop without ever stepping the core: + # + # gdb_server.c gdb_handle_vcont_packet(): fake step thread 0 + # + # The result is that Step Into / Step Over in Binary Ninja does nothing: the + # PC never moves. Clearing the RTOS removes the mismatch so the step is real. + # Harmless for single-core use, which is all this lab does (USE_CORE=0). + # The core's name differs between OpenOCD builds (rp2350.dap.core0 vs rp2350.cm0 + # in the Pico SDK's Windows build), so look it up instead of hard-coding it. + "-c", "[lindex [target names] 0] configure -rtos none", + "-c", "adapter speed $SPEED", + "-c", "gdb_memory_map disable", + "-c", "gdb_breakpoint_override hard", + "-c", "cortex_m reset_config sysresetreq", + "-c", "init" +) + +if ($BP_ADDR) { + $ocdArgs += @("-c", "bp $BP_ADDR 2 hw") +} + +$ocdArgs += @("-c", "reset run") + +& "$OCD\openocd.exe" @ocdArgs + +exit $LASTEXITCODE diff --git a/debug-server.sh b/debug-server.sh new file mode 100755 index 0000000..4246d8d --- /dev/null +++ b/debug-server.sh @@ -0,0 +1,259 @@ +#!/usr/bin/env bash +# +# debug-server.sh - start OpenOCD as a live GDB server for the RP2350 via the Pico Debug Probe. +# +# Synopsis: +# ./debug-server.sh +# +# Description: +# Starts OpenOCD in the foreground as a long-running GDB server. It exposes +# rp2350.dap.core0 on 127.0.0.1:3333 and leaves the core RUNNING, so that a +# debugger (Binary Ninja, or plain GDB) can attach to a target that is already +# executing and therefore has sane registers. +# +# This script is deliberately NOT flash.sh. flash.sh programs flash and exits; +# this one claims the probe and stays up so you can single-step, read memory, +# and set breakpoints. Do not run both at once -- exactly one process may own +# the debug probe. +# +# The script ends with "reset run" rather than OpenOCD's default halt. This is +# the single most important line in the file; see "Why reset run" below. +# +# Requirements: +# - Pico Debug Probe (or any CMSIS-DAP / SWD adapter) connected to the target. +# - OpenOCD with the rp2350 target script installed. See PICO_OPENOCD. +# - Exactly one process may own the debug probe. +# - A firmware image already programmed into flash (use flash.sh first). +# +# Environment variables: +# PICO_OPENOCD Directory containing the openocd binary AND its scripts/ +# directory. Default: $HOME/.pico-sdk/openocd/0.12.0+dev +# macOS note: an OpenOCD on PATH is frequently the x86_64 +# Homebrew build, which will not run under Rosetta on some +# setups and cannot talk to the ARM64 firmware tooling. The +# Pico SDK ships an arm64 build; point this at it explicitly. +# ADAPTER_SPEED SWD clock in kHz. Default: 24000. +# This is a read-mostly debug session, so a fast clock is fine +# and makes stepping noticeably smoother. Drop it (10000 or +# lower) if the link is flaky or you are using long dupont +# wires instead of the probe's own connector. +# BP_ADDR OPTIONAL address to halt at during the startup run, as 0x +# prefixed hex, e.g. 0x10000234 for main. Unset by default, +# which is the normal "attach to a running target" behavior. +# Arms a 2-byte hardware execute breakpoint just before the +# final "reset run", so the core runs from the vector table +# and stops there on its own, with no client attached yet. +# See "Why BP_ADDR is one-shot" below for the important +# limitation. Arming a length of 2 is mandatory: the Cortex-M33 +# comparators are halfword-based and reject anything else. +# USE_CORE Which cores to expose to the client. Default: 0 (core0 only). +# Accepted values: +# 0 core0 only <- use this with Binary Ninja +# 1 core1 only <- rarely useful +# SMP both cores as hwthreads <- see "Why USE_CORE=0" +# Do not change this to SMP when driving Binary Ninja. The +# explanation below is the whole reason this default is 0. +# +# Why USE_CORE=0 (core1 must be hidden from the client) +# The RP2350 has two Cortex-M33 cores. Core1 does not start on its own: nothing +# in a Pico SDK application releases it from reset unless the program +# explicitly does so. If you expose it anyway (USE_CORE=SMP), its registers +# read back as meaningless reset defaults: +# +# core1: pc 0x000000ec sp 0xf0000000 xpsr 0x09000000 lr 0x00000147 +# core0: pc 0x1000320c sp 0x20081f38 xpsr 0xa9000000 lr 0x100030ab +# +# Binary Ninja's register widget renders a memory preview for every register, +# which means it treats each register value as an ADDRESS and reads it. The +# core1 values above are not real addresses. Each read data-aborts, and +# OpenOCD responds by tearing down and re-establishing the SWD debug port, +# logging a pair of lines per fault: +# +# Error: Failed to read memory at 0xf0000000 +# Info : SWD DPIDR 0x4c013477 +# +# That becomes a self-sustaining loop of roughly 450 fault-and-recover cycles +# every two seconds, which makes the session unusable. It looks exactly like a +# broken debugger or a broken firmware. It is neither: it is a configuration +# mismatch, and hiding core1 removes it completely. +# +# Why reset run (the target must be released, not halted) +# On RP2350, halting during reset stops the core at the boot ROM stub BEFORE +# the stack pointer is loaded: +# +# xPSR: 0xf9000000 pc: 0x00000088 msp: 0xf0000000 lr: 0xffffffff +# +# Those values are garbage for every core, including core0. Attaching in that +# window triggers the identical fault storm described above. Ending this +# script with "reset run" means the core starts from the vector table and is +# executing normally by the time you attach, so registers read back correct. +# +# Consequence for the client: ATTACH WHILE THE TARGET IS RUNNING. Do not +# press Reset/Restart in Binary Ninja -- it performs reset-halt and puts you +# back in the garbage window. If you must reset, send "reset run" over the +# OpenOCD telnet port (4444) instead: +# +# nc 127.0.0.1 4444 then type: reset run +# +# ...or use debug-server.sh's restart instructions in WEEK04/WEEK04-BN.md. +# +# Why BP_ADDR is one-shot (read this before relying on it) +# A breakpoint armed here DOES fire during the startup "reset run" and halts +# the core at your address, so the server comes up parked there and your +# debugger can simply attach and look at it. That part works. +# +# What you do NOT get is a reusable breakpoint. As soon as any GDB client +# connects, OpenOCD unconditionally flushes every breakpoint it is holding: +# +# Info : accepting 'gdb' connection on tcp/3333 +# Debug: breakpoints.c:328 breakpoint_remove_all_internal(): +# [rp2350.dap.core0] Delete all breakpoints +# +# So by the time Binary Ninja is up, the comparator is gone -- reading +# 0xE0002000 shows zeros, not your address. Consequences: +# +# * The startup stop is single-use. You cannot Resume and re-catch the +# same address. +# * You cannot use BP_ADDR to stop in a loop that is already running, +# because the core only passes that point once per reset. +# * main (0x10000234) is a good BP_ADDR value precisely because it is +# reached exactly once, right after reset. +# +# To arm anything further, or to re-arm main, do it from the OpenOCD command +# port AFTER your debugger has connected -- the order is the whole trick: +# +# nc 127.0.0.1 4444 +# > bp 0x1000023e 2 hw +# > reset run +# +# Arming before the client connects does not work (flushed above), and in +# plain GDB the equivalent "hbreak" is cleared by "detach" -- both were +# verified by reading the FPB comparator registers back. +# +# Related Binary Ninja bug: Binary Ninja's own breakpoints cannot be used on +# this target at all. It sends Z0,,1 -- a 1-byte packet -- and OpenOCD +# answers "only breakpoints of two bytes length supported". Both Toggle +# Breakpoint (F2) and Add Hardware Breakpoint (F3) fail, at every address, and +# the dialog's Size field is disabled so there is no UI way around it. +# gdb_breakpoint_override hard does not change this. Hence the command-port +# workflow described above. +# +# Why the remaining OpenOCD flags are set +# gdb_breakpoint_override hard +# Force every client breakpoint onto the Cortex-M33 hardware comparators +# (the RP2350 has 8 breakpoints and 4 watchpoints). Without this, a client +# may try to write a BKPT instruction into flash at 0x10000000, which is +# read-only XIP memory, and the write fails. +# gdb_memory_map disable +# Stops the client probing the entire 32 MiB flash map on connect. Pure +# Raspberry Pi guidance for suppressing spurious "Failed to read memory" +# reports during target discovery. +# cortex_m reset_config sysresetreq +# The Cortex-M33 in the RP2350 has no VECTRESET. Without this, OpenOCD +# warns on every reset: +# VECTRESET is not supported on this Cortex-M core, using SYSRESETREQ +# instead +# NOTE: this MUST be the generic "cortex_m" command, not +# "rp2350.dap.core1 cortex_m ...". With USE_CORE=0 the core1 target does +# not exist, so a core1-scoped command aborts OpenOCD before "init" runs, +# and the script exits silently having printed nothing useful. +# adapter speed +# See ADAPTER_SPEED above. +# +# Examples: +# ./debug-server.sh +# ADAPTER_SPEED=10000 ./debug-server.sh +# PICO_OPENOCD=/opt/homebrew/bin ./debug-server.sh +# BP_ADDR=0x10000234 ./debug-server.sh +# Comes up halted at main (one-shot; see "Why BP_ADDR is one-shot"). +# +# Exit status: +# * propagated from OpenOCD. A clean shutdown via Ctrl-C exits 0; an +# OpenOCD configuration error exits non-zero. +# +# Related scripts: +# flash.sh / flash.ps1 one-shot raw .bin programmer (exits when done) +# +# See also: +# WEEK04/WEEK04-BN.md the full walkthrough this script belongs to + +set -euo pipefail + +# --- configuration --------------------------------------------------------- + +OCD="${PICO_OPENOCD:-$HOME/.pico-sdk/openocd/0.12.0+dev}" +SPEED="${ADAPTER_SPEED:-24000}" +USE_CORE="${USE_CORE:-0}" +BP_ADDR="${BP_ADDR:-}" + +if [ -n "$BP_ADDR" ] && ! printf '%s' "$BP_ADDR" | grep -qiE '^0x[0-9a-f]+$'; then + echo "error: BP_ADDR must be 0x-prefixed hex, e.g. 0x10000234 (got '$BP_ADDR')" >&2 + exit 1 +fi + +if [ ! -x "$OCD/openocd" ]; then + echo "error: OpenOCD not found at $OCD/openocd" >&2 + echo " set PICO_OPENOCD=/path/to/openocd (the directory containing the openocd binary)" >&2 + exit 1 +fi + +# --- announce, so the operator can verify intent before the target is touched -- + +echo "Starting OpenOCD GDB server on 127.0.0.1:3333 using $OCD/openocd" +echo "SWD adapter speed: ${SPEED} kHz" +echo "Cores exposed to GDB (USE_CORE): ${USE_CORE}" +echo "Target will be reset and released (reset run) - attach while it is running." + +if [ -n "$BP_ADDR" ]; then + echo "Startup breakpoint at ${BP_ADDR} (2-byte hardware execute, one-shot)." + echo " It fires during this startup reset run. Any client connecting later" + echo " causes OpenOCD to delete it, so arm further breakpoints on port 4444" + echo " after attaching: bp 2 hw" +fi + +# --- start ----------------------------------------------------------------- +# +# Order matters: USE_CORE must be set BEFORE -f target/rp2350.cfg is read, +# because the target script branches on it when creating the DAP targets. +# +# "reset run" is last so it happens after init and after the target is +# examined, releasing the core rather than halting it. See the header for why. +# +# Any BP_ADDR breakpoint is inserted after "init" (the target must exist before +# a comparator can be programmed) but before "reset run" (so the core is already +# armed when it starts). The length must be 2: Cortex-M33 comparators reject +# other widths. + +ocd_args=( + -s "$OCD/scripts" + -f interface/cmsis-dap.cfg + -c "set USE_CORE ${USE_CORE}" + -f target/rp2350.cfg + # Drop the hwthread RTOS the RP2350 target script attaches to core0. + # + # target/rp2350.cfg creates core0 with "-rtos hwthread", which registers a + # fake RTOS whose "current thread" is coreid+1 = 1. Binary Ninja single-steps + # with the GDB packet "vCont;s" and no thread id, i.e. thread 0. OpenOCD's + # gdb_server sees rtos->current_thread (1) != thread_id (0) and takes its + # "fake step" path, replying with a stop without ever stepping the core: + # + # gdb_server.c gdb_handle_vcont_packet(): fake step thread 0 + # + # The result is that Step Into / Step Over in Binary Ninja does nothing: the + # PC never moves. Clearing the RTOS removes the mismatch so the step is real. + # Harmless for single-core use, which is all this lab does (USE_CORE=0). + -c "rp2350.dap.core0 configure -rtos none" + -c "adapter speed ${SPEED}" + -c "gdb_memory_map disable" + -c "gdb_breakpoint_override hard" + -c "cortex_m reset_config sysresetreq" + -c "init" +) + +if [ -n "$BP_ADDR" ]; then + ocd_args+=(-c "bp ${BP_ADDR} 2 hw") +fi + +ocd_args+=(-c "reset run") + +exec "$OCD/openocd" "${ocd_args[@]}" diff --git a/flash.ps1 b/flash.ps1 new file mode 100644 index 0000000..74790de --- /dev/null +++ b/flash.ps1 @@ -0,0 +1,132 @@ +<# +.SYNOPSIS + Program a raw .bin into RP2350 XIP flash via the Pico Debug Probe and OpenOCD. + +.DESCRIPTION + Writes a headerless raw binary to the RP2350's external XIP flash starting at + physical address 0x10000000, verifies the written bytes by reading them back, + then releases the core so the freshly programmed firmware runs. + + A raw .bin carries no address information, no entry point and no section + table, so the load address MUST be supplied out of band. On the RP2350 the + only correct value is 0x10000000: that is where the boot ROM jumps after + pulling the reset vector out of the on-chip XIP window. Flashing anywhere + else produces a board that enumerates over USB and then does nothing. + + The target must already be running the stock RP2350 bootrom (the default + state after power-up, or after any BOOTSEL + UF2 reflash). OpenOCD attaches + to the bootrom over SWD, halts it, programs flash, verifies, and resets. + + This is the macOS / Linux equivalent of flash.sh. The two must stay in + lockstep: same base address, same verify, same conservative SWD clock. + +.PARAMETER Bin + Mandatory. Path to the raw .bin image to program. + +.ENVIRONMENT + PICO_OPENOCD Directory containing openocd.exe AND its scripts\ directory. + Default: $env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev + The path is used for BOTH the executable and the -s scripts + argument. + ADAPTER_SPEED SWD clock in kHz. Default: 5000. + 5000 kHz is deliberately conservative. This is a write path, + not a read-only debug session, and a marginal USB cable or + long dupont run produces spurious verify failures at higher + clocks. Raise it (24000) for read-only work; if + "Error: target not halted" or verify mismatches appear, + lower it to 1000. + +.EXAMPLE + .\flash.ps1 -Bin 0x0005_intro-to-variables\build\0x0005_intro-to-variables.bin + +.EXAMPLE + $env:ADAPTER_SPEED=1000; .\flash.ps1 -Bin build\hacked.bin + +.EXAMPLE + $env:PICO_OPENOCD="C:\openocd\bin"; .\flash.ps1 -Bin build\hacked.bin + +.NOTES + Requirements: + * Pico Debug Probe (or any CMSIS-DAP / SWD adapter) connected to the target. + * OpenOCD with the rp2350 target script installed. + * The Debug Probe must use the WinUSB driver. If OpenOCD reports + "unable to open CMSIS-DAP device", install it with Zadig + (https://zadig.akeo.ie/), selecting "Debug Probe (CMSIS-DAP)" -> WinUSB. + * Exactly one process may own the debug probe. Close any other OpenOCD, + GDB, or IDE debug session first. + + Exit status: + 0 flash written, verified, and core released + 1 input file missing or OpenOCD not found + * any other status is propagated from OpenOCD, so a failed verify or a + write error is visible to the caller rather than being swallowed. + + Related scripts: + debug-server.ps1 long-running GDB server for live debugging with + Binary Ninja. Use that instead of this script when you + need to single-step. + + See also: + WEEK04\WEEK04-BN.md the full walkthrough this script belongs to +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string]$Bin +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# --- argument validation --------------------------------------------------- + +if (-not (Test-Path -PathType Leaf $Bin)) { + Write-Error "file not found: $Bin" + Write-Host "build it first: cmake -B build -G Ninja -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 ; cmake --build build" + exit 1 +} + +# --- toolchain resolution -------------------------------------------------- + +$OCD = if ($env:PICO_OPENOCD) { $env:PICO_OPENOCD } else { "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev" } +if (-not (Test-Path "$OCD\openocd.exe")) { + Write-Error "OpenOCD not found at $OCD\openocd.exe (set PICO_OPENOCD to the directory containing openocd.exe)" + exit 1 +} + +$SPEED = if ($env:ADAPTER_SPEED) { $env:ADAPTER_SPEED } else { "5000" } + +# --- program --------------------------------------------------------------- + +# OpenOCD flag notes: +# -f interface/cmsis-dap.cfg the Pico Debug Probe is a CMSIS-DAP v1 device +# -f target/rp2350.cfg RP2350 dual Cortex-M33 + RP2350B0-style DAP; +# sets USE_CORE=SMP by default, which is fine here +# because we never hand uninitialised core1 +# registers to a debugger. For live debugging use +# debug-server.ps1, which forces USE_CORE=0. +# -c "program BIN 0x10000000 verify reset exit" +# program the write +# 0x10000000 base address (see .DESCRIPTION) +# verify read back and compare every byte; a mismatch aborts +# reset reset the core so the new image starts at its vectors +# exit release the probe and return to the shell +# +# The adapter speed is deliberately lower here than in debug-server.ps1; see +# ADAPTER_SPEED in .ENVIRONMENT. +Write-Host "Flashing $Bin -> 0x10000000 using $OCD\openocd.exe (SWD $SPEED kHz)" + +# OpenOCD parses -c strings as Tcl, which eats backslashes ("\build" -> backspace+"uild"). +# Hand it an absolute path with forward slashes, braced so spaces survive. +$BinTcl = (Resolve-Path $Bin).Path -replace '\\', '/' + +& "$OCD\openocd.exe" ` + -s "$OCD\scripts" ` + -f interface/cmsis-dap.cfg ` + -f target/rp2350.cfg ` + -c "adapter speed $SPEED" ` + -c "program {$BinTcl} 0x10000000 verify reset exit" + +exit $LASTEXITCODE diff --git a/flash.sh b/flash.sh new file mode 100755 index 0000000..97ee9c5 --- /dev/null +++ b/flash.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash +# +# flash.sh - program a raw .bin into RP2350 XIP flash via the Pico Debug Probe + OpenOCD. +# +# Synopsis: +# ./flash.sh +# +# Description: +# Writes a headerless raw binary to the RP2350's external XIP flash starting at +# physical address 0x10000000, then verifies the written bytes by reading them +# back, then releases the core so the freshly programmed firmware runs. +# +# A raw .bin carries no address information, no entry point and no section +# table, so the load address MUST be supplied out of band. On the RP2350 the +# only correct value is 0x10000000: that is where the boot ROM jumps after +# pulling the reset vector out of the on-chip XIP window. Flashing anywhere +# else produces a board that enumerates over USB and then does nothing. +# +# The target must already be running the stock RP2350 bootrom (the default +# state after power-up or after any BOOTSEL+UF2 reflash). OpenOCD attaches to +# the bootrom via SWD, halts it, programs flash, verifies, and resets. +# +# Requirements: +# - Pico Debug Probe (or any CMSIS-DAP / SWD adapter) connected to the target. +# - OpenOCD with the rp2350 target script installed. The Pico SDK ships one; +# see PICO_OPENOCD below. +# - Exactly one process may own the debug probe. Close any other OpenOCD, +# GDB, or IDE debug session first. +# +# Environment variables: +# PICO_OPENOCD Directory containing the openocd binary AND its scripts/ +# directory. Default: $HOME/.pico-sdk/openocd/0.12.0+dev +# The path is used for BOTH the executable and -s scripts. +# ADAPTER_SPEED SWD clock in kHz. Default: 5000. +# 5000 kHz is deliberately conservative. This is a write path, +# not a read-only debug session, and a marginal USB cable or +# long dupont run will produce spurious verify failures at +# higher clocks. Raise it (24000) for read-only work; if +# "Error: target not halted" or verify mismatches appear, +# lower it to 1000. +# +# Examples: +# ./flash.sh 0x0005_intro-to-variables/build/0x0005_intro-to-variables.bin +# ADAPTER_SPEED=1000 ./flash.sh build/hacked.bin +# PICO_OPENOCD=/opt/homebrew/bin ./flash.sh build/hacked.bin +# +# Exit status: +# 0 flash written, verified, and core released +# 1 bad usage, missing input file, or OpenOCD not found +# * any other status is propagated from OpenOCD, so a failed verify or a +# write error is visible to the caller rather than being swallowed. +# +# Related scripts: +# debug-server.sh / debug-server.ps1 long-running GDB server for live +# debugging with Binary Ninja +# +# See also: +# WEEK04/WEEK04-BN.md the full walkthrough this script belongs to + +set -euo pipefail + +# --- argument validation --------------------------------------------------- + +BIN="${1:-}" +if [ -z "$BIN" ]; then + echo "usage: $0 " >&2 + exit 1 +fi + +if [ ! -f "$BIN" ]; then + echo "error: file not found: $BIN" >&2 + echo " build it first: cmake -B build -G Ninja -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 && cmake --build build" >&2 + exit 1 +fi + +# --- toolchain resolution -------------------------------------------------- + +OCD="${PICO_OPENOCD:-$HOME/.pico-sdk/openocd/0.12.0+dev}" +if [ ! -x "$OCD/openocd" ]; then + echo "error: OpenOCD not found at $OCD/openocd" >&2 + echo " set PICO_OPENOCD=/path/to/openocd (the directory containing the openocd binary)" >&2 + exit 1 +fi + +SPEED="${ADAPTER_SPEED:-5000}" + +# --- program --------------------------------------------------------------- + +# OpenOCD flag notes: +# -f interface/cmsis-dap.cfg the Pico Debug Probe is a CMSIS-DAP v1 device +# -f target/rp2350.cfg RP2350 dual Cortex-M33 + RP2350B0-style DAP; +# sets USE_CORE=SMP by default, which is fine here +# because we never hand uninitialised core1 +# registers to a debugger. For live debugging use +# debug-server.sh, which forces USE_CORE=0. +# -c "program BIN 0x10000000 verify reset exit" +# program the write +# 0x10000000 base address (see header) +# verify read back and compare every byte; a mismatch aborts +# reset reset the core so the new image starts at its vectors +# exit release the probe and return to the shell +# +# The adapter speed is deliberately lower here than in debug-server.sh; see +# ADAPTER_SPEED in the header. +echo "Flashing $BIN -> 0x10000000 using $OCD/openocd (SWD ${SPEED} kHz)" +"$OCD/openocd" \ + -s "$OCD/scripts" \ + -f interface/cmsis-dap.cfg \ + -f target/rp2350.cfg \ + -c "adapter speed ${SPEED}" \ + -c "program $BIN 0x10000000 verify reset exit" diff --git a/hardware/EHP2.fzz b/hardware/EHP2.fzz new file mode 100644 index 0000000..149e6b1 Binary files /dev/null and b/hardware/EHP2.fzz differ diff --git a/hardware/EHP2_bb.png b/hardware/EHP2_bb.png new file mode 100644 index 0000000..3b81e76 Binary files /dev/null and b/hardware/EHP2_bb.png differ diff --git a/hardware/dp.png b/hardware/dp.png new file mode 100644 index 0000000..92079cd Binary files /dev/null and b/hardware/dp.png differ diff --git a/hardware/pico-2-r4-pinout.svg b/hardware/pico-2-r4-pinout.svg new file mode 100644 index 0000000..be839ba --- /dev/null +++ b/hardware/pico-2-r4-pinout.svg @@ -0,0 +1,1816 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2 + + + 39 + + + DEBUG + + + 1 + + + LED + + + USB + + + BOOTSEL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5bed409 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,94 @@ +# Python standards for this repository. +# +# Scope note: only the small number of first-party helper scripts at the repo +# root are linted. Firmware projects under 0x*/ are C/C++/ASM and are not +# Python, and vendored trees (pico-sdk, build/) are excluded explicitly. +# +# Run: +# ruff check . +# ruff format . +# ruff check --fix . + +[project] +name = "embedded-hacking-tools" +version = "1.0.0" +description = "Host-side tooling for the Embedded Hacking course" +requires-python = ">=3.10" + +[tool.ruff] +target-version = "py310" +line-length = 88 +# Scope: the first-party host-side helpers at the repo root only. The lesson +# trees below are per-lesson material that predates this config and are +# intentionally not linted; migrate a file to the repo root, or run ruff on it +# directly with an explicit path, when you touch it. +extend-exclude = [ + "0x*", + "WEEK*", + "**/build", + "pico-sdk", + "**/pico-sdk", + "node_modules", +] + +[tool.ruff.lint] +# E/W pycodestyle - formatting-independent style +# F pyflakes - unused imports, undefined names, f-string gaps +# I isort - import ordering +# UP pyupgrade - modern syntax for the declared target version +# B bugbear - real bug patterns +# SIM simplify - redundant code +# C4 comprehensions - needless comprehension / generator misuse +# RET return - inconsistent returns +# PTH pathlib - use pathlib instead of os.path +# ARG unused-arguments - dead parameters +# D pydocstyle - docstring presence and convention +# ANN annotations - type annotations +# TRY tryceratops - correct exception handling +# PIE misc - misc lints +# RUF ruff-specific - modern idioms and correctness rules +select = [ + "E", + "F", + "I", + "UP", + "B", + "SIM", + "C4", + "RET", + "PTH", + "ARG", + "D", + "ANN", + "TRY", + "PIE", + "RUF", +] + +ignore = [ + # The UF2 block format is a byte-level binary format; several conversions + # legitimately deal in loose byte slices and magic integers. + "E501", + # C901 complexity: convert_from_uf2 is a linear decoder over a block stream + # and reads far better as one pass than split into helpers. + "C901", + # Legacy CLI scripts print progress to stdout as their primary output. + "T201", +] + +[tool.ruff.lint.per-file-ignores] +# Tutorial scripts are run directly and use a permissive argparse style. +"uf2conv.py" = ["D100"] + +[tool.ruff.lint.pydocstyle] +convention = "google" + +[tool.ruff.lint.flake8-annotations] +# Accept `-> None` omission only on __init__; require annotations elsewhere. +suppress-dummy-args = false +mypy-init-return = true + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +line-ending = "lf" diff --git a/textbook/Embedded-Hacking.pdf b/textbook/Embedded-Hacking.pdf new file mode 100644 index 0000000..af0b640 Binary files /dev/null and b/textbook/Embedded-Hacking.pdf differ diff --git a/uf2conv.py b/uf2conv.py new file mode 100644 index 0000000..c5a832f --- /dev/null +++ b/uf2conv.py @@ -0,0 +1,861 @@ +#!/usr/bin/env python3 +r"""Convert firmware images between Intel HEX, raw BIN, and Raspberry Pi UF2. + +This is the course's single canonical copy of the converter. Every week that +patches a binary refers to the copy at the repository root: + + python ../uf2conv.py build/image-h.bin --base 0x10000000 \\ + --family 0xe48bff59 --output build/hacked.uf2 + +It descends from the upstream Raspberry Pi ``uf2conv.py``, with the following +changes made for this course: + +* Full type annotations and Google-style docstrings on every public callable. +* ``pathlib`` instead of ``os.path``, and explicit ``except`` clauses. +* Latent crash bugs in the upstream error paths fixed (see + :func:`convert_from_uf2`). +* Module globals replaced with an explicit :class:`ConverterContext`, so the + base address and family ID are threaded through rather than mutated behind + the caller's back. + +UF2 format, for reference +------------------------- +A UF2 file is a flat sequence of 512-byte blocks. Each block is:: + + offset size field + 0x00 4 magicStart0 = 0x0A324655 ("UF2\\n") + 0x04 4 magicStart1 = 0x9E5D5157 (arbitrary constant) + 0x08 4 flags + 0x0C 4 targetAddr + 0x10 4 payloadSize (max 476) + 0x14 4 blockNo + 0x18 4 numBlocks + 0x1C 4 familyID + 0x20 476 data + 0x1FC 4 magicEnd = 0x0AB16F30 + +Flags that matter here: + +* ``0x00000001`` -- "do not flash"; this block is a no-flash marker. +* ``0x00002000`` -- "family ID present"; ``familyID`` is meaningful. + +For the RP2350 the family ID is ``0xe48bff59``, and it is what tells the +boot ROM that this UF2 targets RP2350 hardware rather than, say, an RP2040 or +a Pico W. + +See Also: +-------- +* ``uf2families.json`` -- the family-name to family-ID table, loaded from the + directory containing this file. +* ``flash.sh`` / ``flash.ps1`` -- program a raw ``.bin`` over SWD instead. +""" + +from __future__ import annotations + +import argparse +import json +import os +import re +import struct +import subprocess +import sys +from dataclasses import dataclass +from pathlib import Path +from time import sleep + +# --------------------------------------------------------------------------- +# UF2 format constants +# --------------------------------------------------------------------------- + +#: First magic word, ``"UF2\n"`` read as a little-endian uint32. +UF2_MAGIC_START0 = 0x0A324655 +#: Second magic word. The value is arbitrary but must be constant. +UF2_MAGIC_START1 = 0x9E5D5157 +#: Trailing magic word closing every 512-byte block. +UF2_MAGIC_END = 0x0AB16F30 + +#: Size of one UF2 block in bytes. +UF2_BLOCK_SIZE = 512 +#: Offset of the 32-byte block header within a block. +UF2_HEADER_SIZE = 32 +#: Largest payload a single block may carry (476 = 512 - 32 header - 4 magic). +UF2_MAX_PAYLOAD = UF2_BLOCK_SIZE - UF2_HEADER_SIZE - 4 + +#: Flag bit marking a block the bootloader must NOT program. +UF2_FLAG_NOFLASH = 0x00000001 +#: Flag bit announcing that ``familyID`` carries a meaningful value. +UF2_FLAG_FAMILY_ID = 0x00002000 + +#: Name of the marker file the boot ROM exposes on its flash-drive volume. +INFO_FILE = "INFO_UF2.TXT" + +#: Struct format for the 32-byte UF2 block header. +_HEADER_STRUCT = struct.Struct("UF2 + conversion of RP2040-era images, and the wrong behaviour for + RP2350, which is why the course always passes ``--family``. + """ + + appstartaddr: int | None = DEFAULT_BASE_ADDRESS + familyid: int = DEFAULT_FAMILY_ID + + +# --------------------------------------------------------------------------- +# Format detection +# --------------------------------------------------------------------------- + + +def is_uf2(buf: bytes) -> bool: + """Report whether a buffer begins with the UF2 start magic. + + Args: + buf: Raw file contents. + + Returns: + ``True`` if the first two uint32 words are the UF2 start magic. + """ + if len(buf) < 8: + return False + word0, word1 = _HEADER_STRUCT.unpack_from(buf, 0)[:2] + return word0 == UF2_MAGIC_START0 and word1 == UF2_MAGIC_START1 + + +def is_hex(buf: bytes) -> bool: + """Report whether a buffer looks like an Intel HEX file. + + A file qualifies when it decodes as UTF-8, begins with a record marker + ``:``, and contains only characters legal in Intel HEX records. + + Args: + buf: Raw file contents. + + Returns: + ``True`` if the buffer should be treated as Intel HEX. + """ + try: + text = buf[:30].decode("utf-8") + except UnicodeDecodeError: + return False + if not text.startswith(":"): + return False + return re.match(rb"^[:0-9a-fA-F\r\n]+$", buf) is not None + + +# --------------------------------------------------------------------------- +# UF2 -> BIN +# --------------------------------------------------------------------------- + + +def convert_from_uf2(buf: bytes, ctx: ConverterContext) -> bytes: + """Decode a UF2 file back into a flat binary image. + + Blocks are visited in file order. Gaps between consecutive block addresses + are filled with zero words, and any trailing gap is dropped so that the + result ends at the last byte actually programmed. + + A UF2 file may legitimately interleave blocks from several families (this + is how a multicore image ships both cores in one download). When more than + one family is present and the caller did not constrain the family with + ``--family``, the conversion is ambiguous, so the payload is emptied and + ``appstartaddr`` reset to ``0``. + + Args: + buf: Complete UF2 file contents. + ctx: Conversion state; updated in place with the discovered base + address. + + Returns: + The decoded payload. + + Raises: + Uf2Error: If a block declares an oversized payload, the blocks are out + of order, or the implied padding exceeds 10 MiB. + + Note: + The upstream Raspberry Pi version of this function crashes on its own + error paths: it builds the message with ``"..." + ptr`` where ``ptr`` + is an ``int``, which raises :class:`TypeError` and masks the real + problem. The messages here use f-strings so the diagnostic survives. + """ + numblocks = len(buf) // UF2_BLOCK_SIZE + curraddr: int | None = None + currfamilyid: int | None = None + families_found: dict[int, int] = {} + prev_flag: int | None = None + all_flags_same = True + outp: list[bytes] = [] + + for blockno in range(numblocks): + ptr = blockno * UF2_BLOCK_SIZE + block = buf[ptr : ptr + UF2_BLOCK_SIZE] + ( + magic0, + magic1, + flags, + target_addr, + datalen, + _blockno, + _numblocks, + block_family, + ) = _HEADER_STRUCT.unpack_from(block, 0) + + if magic0 != UF2_MAGIC_START0 or magic1 != UF2_MAGIC_START1: + print(f"Skipping block at {ptr:#x}; bad magic") + continue + + if flags & UF2_FLAG_NOFLASH: + continue + + if datalen > UF2_MAX_PAYLOAD: + msg = f"Invalid UF2 data size at {ptr:#x}: {datalen}" + raise Uf2Error(msg) + + if flags & UF2_FLAG_FAMILY_ID and currfamilyid is None: + currfamilyid = block_family + + # A new contiguous run starts when the address jumps or the family + # changes. appstartaddr tracks the start of the current run, which is + # what makes the reported start address the image's true base rather + # than the address of its last block. + if curraddr is None or ( + flags & UF2_FLAG_FAMILY_ID and block_family != currfamilyid + ): + currfamilyid = block_family + curraddr = target_addr + if ctx.familyid == DEFAULT_FAMILY_ID or ctx.familyid == block_family: + ctx.appstartaddr = target_addr + + padding = target_addr - curraddr + if padding < 0: + msg = f"Block out of order at {ptr:#x}: {target_addr:#x} < {curraddr:#x}" + raise Uf2Error(msg) + if padding > 10 * 1024 * 1024: + msg = f"More than 10M of padding needed at {ptr:#x}" + raise Uf2Error(msg) + if padding % 4 != 0: + msg = f"Non-word padding size at {ptr:#x}: {padding}" + raise Uf2Error(msg) + + while padding > 0: + padding -= 4 + outp.append(b"\x00\x00\x00\x00") + + if ctx.familyid == DEFAULT_FAMILY_ID or ( + flags & UF2_FLAG_FAMILY_ID and ctx.familyid == block_family + ): + outp.append( + block[UF2_HEADER_SIZE : UF2_HEADER_SIZE + datalen], + ) + + curraddr = target_addr + datalen + + if flags & UF2_FLAG_FAMILY_ID: + existing = families_found.get(block_family) + if existing is None or existing > target_addr: + families_found[block_family] = target_addr + + if prev_flag is None: + prev_flag = flags + elif prev_flag != flags: + all_flags_same = False + + if blockno == numblocks - 1: + _print_uf2_header_info(families_found, all_flags_same, flags) + if len(families_found) > 1 and ctx.familyid == DEFAULT_FAMILY_ID: + outp = [] + ctx.appstartaddr = 0x0 + + return b"".join(outp) + + +def _print_uf2_header_info( + families_found: dict[int, int], + all_flags_same: bool, + flags: int, +) -> None: + """Print the family summary block that follows a UF2->BIN conversion. + + Args: + families_found: Mapping of family ID to the lowest target address seen + for that family. + all_flags_same: Whether every block carried an identical flag word. + flags: The flag word from the final block, used for display. + """ + print("--- UF2 File Header Info ---") + families = load_families() + name_by_id = {value: key for key, value in families.items()} + for family_hex, address in families_found.items(): + short_name = name_by_id.get(family_hex, "") + print(f"Family ID is {short_name}, hex value is {family_hex:#010x}") + print(f"Target Address is {address:#010x}") + if all_flags_same: + print(f"All block flag values consistent, {flags:#06x}") + else: + print("Flags were not all the same") + print("----------------------------") + + +# --------------------------------------------------------------------------- +# BIN -> UF2 +# --------------------------------------------------------------------------- + + +def convert_to_uf2(file_content: bytes, ctx: ConverterContext) -> bytes: + """Wrap a raw binary in UF2 blocks. + + The binary is split into 256-byte payloads; the final block is zero-padded. + Each block's ``targetAddr`` is the byte offset within the image plus + :attr:`ConverterContext.appstartaddr`, so for RP2350 firmware built at + ``0x10000000`` the caller passes ``--base 0x10000000``. + + Args: + file_content: The raw image. + ctx: Conversion state supplying the base address and family ID. + + Returns: + The UF2 file contents, always a whole number of 512-byte blocks. + """ + datapadding = bytes(UF2_BLOCK_SIZE - 256 - UF2_HEADER_SIZE - 4) + numblocks = (len(file_content) + 255) // 256 + flags = UF2_FLAG_FAMILY_ID if ctx.familyid else 0x0 + base = ctx.appstartaddr or 0 + + blocks: list[bytes] = [] + for blockno in range(numblocks): + ptr = 256 * blockno + chunk = file_content[ptr : ptr + 256] + header = _HEADER_STRUCT.pack( + UF2_MAGIC_START0, + UF2_MAGIC_START1, + flags, + ptr + base, + 256, + blockno, + numblocks, + ctx.familyid, + ) + payload = chunk + bytes(256 - len(chunk)) + block = header + payload + datapadding + struct.pack(" bytes: + """Render a binary as a C array initialiser for embedding in firmware. + + Args: + file_content: The raw image. + + Returns: + UTF-8 bytes of a C translation unit fragment. + """ + parts = [ + f"const unsigned long bindata_len = {len(file_content)};\n", + "const unsigned char bindata[] __attribute__((aligned(16))) = {", + ] + for index, value in enumerate(file_content): + if index % 16 == 0: + parts.append("\n") + parts.append(f"{value:#04x}, ") + parts.append("\n};\n") + return "".join(parts).encode() + + +# --------------------------------------------------------------------------- +# Intel HEX -> UF2 +# --------------------------------------------------------------------------- + + +@dataclass +class Block: + """A single 256-byte-aligned region of an image under construction. + + Attributes: + addr: Base address of the block, always 256-byte aligned. + data: The block's bytes, initialised to ``default_data``. + """ + + addr: int + data: bytearray + + def __init__(self, addr: int, default_data: int = 0xFF) -> None: + """Initialise an erased 256-byte block. + + Args: + addr: 256-byte-aligned base address of the block. + default_data: Fill byte; ``0xFF`` matches erased flash. + """ + self.addr = addr + self.data = bytearray([default_data] * 256) + + def encode(self, blockno: int, numblocks: int, ctx: ConverterContext) -> bytes: + """Serialise this block into a 512-byte UF2 block. + + Args: + blockno: Index of this block within the file. + numblocks: Total number of blocks in the file. + ctx: Conversion state supplying the family ID. + + Returns: + Exactly 512 bytes. + """ + flags = UF2_FLAG_FAMILY_ID if ctx.familyid else 0x0 + out = _HEADER_STRUCT.pack( + UF2_MAGIC_START0, + UF2_MAGIC_START1, + flags, + self.addr, + 256, + blockno, + numblocks, + ctx.familyid, + ) + out += self.data[0:256] + out += bytes(UF2_BLOCK_SIZE - 4 - len(out)) + out += struct.pack(" bytes: + """Assemble Intel HEX records into a UF2 file. + + Only record types 0x00 (data), 0x01 (EOF), 0x02 (extended segment address) + and 0x04 (extended linear address) are handled; any other type is ignored, + matching the upstream behaviour. + + Args: + text: Decoded Intel HEX file contents. + ctx: Conversion state; ``appstartaddr`` is set to the first data + address seen. + + Returns: + The UF2 file contents. + """ + ctx.appstartaddr = None + upper = 0 + currblock: Block | None = None + blocks: list[Block] = [] + + for line in text.split("\n"): + if not line.startswith(":"): + continue + record = [int(line[i : i + 2], 16) for i in range(1, len(line) - 1, 2)] + rec_type = record[3] + if rec_type == 4: + upper = ((record[4] << 8) | record[5]) << 16 + elif rec_type == 2: + upper = ((record[4] << 8) | record[5]) << 4 + elif rec_type == 1: + break + elif rec_type == 0: + addr = upper + ((record[1] << 8) | record[2]) + if ctx.appstartaddr is None: + ctx.appstartaddr = addr + index = 4 + while index < len(record) - 1: + if currblock is None or currblock.addr & ~0xFF != addr & ~0xFF: + currblock = Block(addr & ~0xFF) + blocks.append(currblock) + currblock.data[addr & 0xFF] = record[index] + addr += 1 + index += 1 + + numblocks = len(blocks) + return b"".join(blocks[i].encode(i, numblocks, ctx) for i in range(numblocks)) + + +# --------------------------------------------------------------------------- +# Drive discovery +# --------------------------------------------------------------------------- + + +def _iter_candidate_dirs() -> list[Path]: + """Return the directories that may contain mounted removable volumes.""" + if sys.platform == "win32": + return [] + + searchpaths = [Path("/mnt"), Path("/media")] + if sys.platform == "darwin": + searchpaths = [Path("/Volumes")] + elif sys.platform == "linux": + user = environ_user() + if user: + searchpaths += [Path("/media") / user, Path("/run/media") / user] + sudo_user = os.environ.get("SUDO_USER") + if sudo_user: + searchpaths += [ + Path("/media") / sudo_user, + Path("/run/media") / sudo_user, + ] + return searchpaths + + +def environ_user() -> str | None: + """Return the current user name from the environment. + + Tried under several variable names so the search works on macOS, Linux and + Windows shells alike. + + Returns: + The user name, or ``None`` if it cannot be determined. + """ + for var in ("USER", "USERNAME", "LOGNAME"): + value = os.environ.get(var) + if value: + return value + return None + + +def get_drives() -> list[Path]: + """Find mounted Pico boot drives. + + A drive qualifies when it contains the ``INFO_UF2.TXT`` marker the boot ROM + writes when it presents the flash as a USB mass-storage device. + + Returns: + Every qualifying mount point. + """ + drives: list[Path] = [] + + if sys.platform == "win32": + command = ( + "(Get-Volume | Where-Object { $_.FileSystemLabel -match 'RP2350|RPI-RP2' })" + ".DriveLetter" + ) + try: + raw = subprocess.check_output( + ["powershell", "-Command", command], + text=True, + ) + except (subprocess.CalledProcessError, FileNotFoundError, OSError): + return [] + for letter in raw.split(): + if len(letter) == 1 and letter.isalpha(): + drives.append(Path(f"{letter.upper()}:\\")) + return drives + + for rootpath in _iter_candidate_dirs(): + if not rootpath.is_dir(): + continue + try: + entries = list(rootpath.iterdir()) + except OSError: + continue + for entry in entries: + if not entry.is_dir(): + continue + if (entry / INFO_FILE).is_file(): + drives.append(entry) + return drives + + +def board_id(path: Path) -> str: + """Read the ``Board-ID`` field from a boot drive's info file. + + Args: + path: Mount point of the boot drive. + + Returns: + The board ID string, for example ``RP2350``. + + Raises: + Uf2Error: If the info file is missing or has no ``Board-ID`` field. + """ + info = path / INFO_FILE + if not info.is_file(): + msg = f"Not a Pico boot drive: {path} has no {INFO_FILE}" + raise Uf2Error(msg) + match = re.search(r"Board-ID: ([^\r\n]*)", info.read_text()) + if match is None: + msg = f"No Board-ID field in {info}" + raise Uf2Error(msg) + return match.group(1) + + +def list_drives() -> None: + """Print every connected Pico boot drive and its board ID.""" + for drive in get_drives(): + try: + print(drive, board_id(drive)) + except Uf2Error as exc: + print(f"{drive} ") + + +def write_file(name: Path | str, buf: bytes) -> None: + """Write a buffer to disk and report the size written. + + Args: + name: Destination path. + buf: Bytes to write. + """ + path = Path(name) + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(buf) + print(f"Wrote {len(buf)} bytes to {path}") + + +def load_families() -> dict[str, int]: + """Load the family-name to family-ID table. + + ``uf2families.json`` is read from the directory containing this script, so + the converter works regardless of the caller's working directory. + + Returns: + Mapping of upper-case short name to family ID. + + Raises: + Uf2Error: If the JSON file is missing or malformed. + """ + pathname = Path(__file__).resolve().parent / "uf2families.json" + try: + raw_families = json.loads(pathname.read_text()) + except FileNotFoundError as exc: + msg = f"Required family table not found: {pathname}" + raise Uf2Error(msg) from exc + except json.JSONDecodeError as exc: + msg = f"Malformed family table {pathname}: {exc}" + raise Uf2Error(msg) from exc + + return {fam["short_name"]: int(fam["id"], 0) for fam in raw_families} + + +# --------------------------------------------------------------------------- +# CLI +# --------------------------------------------------------------------------- + + +def _build_parser() -> argparse.ArgumentParser: + """Construct the command-line argument parser. + + Returns: + A configured :class:`argparse.ArgumentParser`. + """ + parser = argparse.ArgumentParser( + prog="uf2conv.py", + description="Convert firmware between Intel HEX, raw BIN and UF2.", + epilog=( + "Course example (RP2350 raw image built for XIP flash):\n" + " python ../uf2conv.py build/image-h.bin " + "--base 0x10000000 \\\n" + " --family 0xe48bff59 --output build/hacked.uf2\n" + ), + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument( + "input", + metavar="INPUT", + nargs="?", + help="input file (HEX, BIN or UF2); omit when using --list", + ) + parser.add_argument( + "-b", + "--base", + default=hex(DEFAULT_BASE_ADDRESS), + help=( + "base address of the application for BIN input " + f"(default: {hex(DEFAULT_BASE_ADDRESS)}; use 0x10000000 for RP2350 XIP)" + ), + ) + parser.add_argument( + "-f", + "--family", + default=hex(DEFAULT_FAMILY_ID), + help=( + "family ID as a number or a name from uf2families.json " + f"(default: {hex(DEFAULT_FAMILY_ID)}; RP2350 is 0xe48bff59)" + ), + ) + parser.add_argument( + "-o", + "--output", + metavar="FILE", + help='write output to FILE; defaults to "flash.uf2" or "flash.bin"', + ) + parser.add_argument( + "-d", + "--device", + dest="device_path", + help="select a specific device path to flash (reserved; unused)", + ) + parser.add_argument( + "-l", + "--list", + action="store_true", + help="list connected Pico boot drives and exit", + ) + parser.add_argument( + "-c", + "--convert", + action="store_true", + help="convert only; do not deploy to a mounted boot drive", + ) + parser.add_argument( + "-D", + "--deploy", + action="store_true", + help="deploy the input file unchanged; do not convert", + ) + parser.add_argument( + "-w", + "--wait", + action="store_true", + help="poll for a boot drive to appear instead of failing immediately", + ) + parser.add_argument( + "-C", + "--carray", + action="store_true", + help="emit a C array initialiser instead of a UF2 file", + ) + parser.add_argument( + "-i", + "--info", + action="store_true", + help="print UF2 header information and exit without converting", + ) + return parser + + +def _resolve_family(spec: str) -> int: + """Resolve a ``--family`` value to a numeric family ID. + + Args: + spec: Either a family short name (case-insensitive) or an integer + literal such as ``0xe48bff59``. + + Returns: + The numeric family ID. + + Raises: + Uf2Error: If the value is neither a known name nor a valid integer. + """ + families = load_families() + if spec.upper() in families: + return families[spec.upper()] + try: + return int(spec, 0) + except ValueError as exc: + msg = "Family ID needs to be a number or one of: " + ", ".join(families) + raise Uf2Error(msg) from exc + + +def _wait_for_drive() -> Path | None: + """Poll for a boot drive to be mounted. + + Returns: + The first drive found, or ``None`` if the wait was interrupted. + """ + print("Waiting for drive to deploy...") + while True: + drives = get_drives() + if drives: + return drives[0] + sleep(0.1) + + +def main(argv: list[str] | None = None) -> int: + """Run the command-line converter. + + Args: + argv: Argument list, defaulting to :data:`sys.argv` ``[1:]``. + + Returns: + ``0`` on success, ``1`` on a user or input error. + """ + parser = _build_parser() + args = parser.parse_args(argv) + + ctx = ConverterContext() + try: + ctx.familyid = _resolve_family(args.family) + ctx.appstartaddr = int(args.base, 0) + except (Uf2Error, ValueError) as exc: + print(exc, file=sys.stderr) + return 1 + + if args.list: + list_drives() + return 0 + + if not args.input: + print("Need an input file (or use --list)", file=sys.stderr) + return 1 + + source = Path(args.input) + if not source.is_file(): + print(f"error: file not found: {source}", file=sys.stderr) + return 1 + + inpbuf = source.read_bytes() + from_uf2 = is_uf2(inpbuf) + ext = "uf2" + + try: + if args.deploy: + outbuf = inpbuf + elif from_uf2 and not args.info: + outbuf = convert_from_uf2(inpbuf, ctx) + ext = "bin" + elif from_uf2 and args.info: + outbuf = b"" + convert_from_uf2(inpbuf, ctx) + elif is_hex(inpbuf): + outbuf = convert_from_hex_to_uf2(inpbuf.decode("utf-8"), ctx) + elif args.carray: + outbuf = convert_to_carray(inpbuf) + ext = "h" + else: + outbuf = convert_to_uf2(inpbuf, ctx) + except Uf2Error as exc: + print(f"error: {exc}", file=sys.stderr) + return 1 + + if not args.deploy and not args.info: + print( + f"Converted to {ext}, output size: {len(outbuf)}, " + f"start address: {ctx.appstartaddr or 0:#x}", + ) + + if (args.convert or ext != "uf2") and args.output is None: + args.output = f"flash.{ext}" + if args.output: + write_file(args.output, outbuf) + + if ext == "uf2" and not args.convert and not args.info: + drives = get_drives() + if not drives: + if args.wait: + found = _wait_for_drive() + drives = [found] if found else [] + elif not args.output: + print("error: no drive to deploy", file=sys.stderr) + return 1 + for drive in drives: + print(f"Flashing {drive} ({board_id(drive)})") + write_file(drive / "NEW.UF2", outbuf) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/uf2families.json b/uf2families.json new file mode 100644 index 0000000..39373c4 --- /dev/null +++ b/uf2families.json @@ -0,0 +1,22 @@ +[ + { + "short_name": "RP2040", + "id": "0xe48bff56", + "description": "Raspberry Pi RP2040" + }, + { + "short_name": "RP2350-ARM-S", + "id": "0xe48bff59", + "description": "Raspberry Pi RP2350, ARM, Secure" + }, + { + "short_name": "RP2350-ARM-NS", + "id": "0xe48bff5a", + "description": "Raspberry Pi RP2350, ARM, Non-Secure" + }, + { + "short_name": "RP2350-RISCV", + "id": "0xe48bff5b", + "description": "Raspberry Pi RP2350, RISC-V" + } +] \ No newline at end of file