diff --git a/.github/workflows/choco_packages.config b/.github/workflows/choco_packages.config index d443f751c..116f47f86 100644 --- a/.github/workflows/choco_packages.config +++ b/.github/workflows/choco_packages.config @@ -2,5 +2,4 @@ - diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 502e18c14..a224dde61 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -7,8 +7,13 @@ env: jobs: build: - if: github.repository_owner == 'raspberrypi' + # Prevent running twice for PRs from same repo + if: github.repository_owner == 'raspberrypi' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name) runs-on: [self-hosted, Linux, X64] + strategy: + fail-fast: false + matrix: + board: ["pico", "pico2_w"] steps: - name: Clean workspace @@ -37,24 +42,25 @@ jobs: # Some projects don't allow in-source building, so create a separate build directory # We'll use this as our working directory for all subsequent commands working-directory: ${{github.workspace}}/pico-examples - run: cmake -E make_directory ${{github.workspace}}/pico-examples/build + run: cmake -E make_directory ${{github.workspace}}/pico-examples/build.${{ matrix.board }} - name: Configure CMake # Use a bash shell so we can use the same syntax for environment variable # access regardless of the host operating system shell: bash - working-directory: ${{github.workspace}}/pico-examples/build + working-directory: ${{github.workspace}}/pico-examples/build.${{ matrix.board }} # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: PICO_SDK_PATH=../../pico-sdk cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE + # Set WIFI_SSID and WIFI_PASSWORD to build all of the wireless examples + run: PICO_SDK_PATH=../../pico-sdk cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DPICO_BOARD=${{ matrix.board }} -DWIFI_SSID=TestSSID -DWIFI_PASSWORD=TestPassword - name: Get core count id: core_count run : cat /proc/cpuinfo | grep processor | wc -l - name: Build - working-directory: ${{github.workspace}}/pico-examples/build + working-directory: ${{github.workspace}}/pico-examples/build.${{ matrix.board }} shell: bash # Execute the build. You can specify a specific target with "--target " run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) diff --git a/.github/workflows/multi-gcc.yml b/.github/workflows/multi-gcc.yml index 526e4f040..373b54297 100644 --- a/.github/workflows/multi-gcc.yml +++ b/.github/workflows/multi-gcc.yml @@ -41,141 +41,331 @@ jobs: run: cmake -E make_directory ${{github.workspace}}/pico-examples/build - name: GCC 6.2.1 Debug - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-6_2-2016q4 -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - - name: GCC 6.2.1 Release - if: always() - shell: bash - run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-6_2-2016q4 -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) +# - name: GCC 6.2.1 Release +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-6_2-2016q4 -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - name: GCC 6.3.1 Debug - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-6-2017-q2-update -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - - name: GCC 6.3.1 Release - if: always() - shell: bash - run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-6-2017-q2-update -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) +# - name: GCC 6.3.1 Release +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-6-2017-q2-update -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - name: GCC 7.2.1 Debug - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-7-2017-q4-major -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - - name: GCC 7.2.1 Release - if: always() - shell: bash - run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-7-2017-q4-major -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) +# - name: GCC 7.2.1 Release +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-7-2017-q4-major -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - name: GCC 7.3.1 Debug - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-7-2018-q2-update -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - - name: GCC 7.3.1 Release - if: always() - shell: bash - run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-7-2018-q2-update -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) +# - name: GCC 7.3.1 Release +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-7-2018-q2-update -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - name: GCC 8.2.1 Debug - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-8-2018-q4-major -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - - name: GCC 8.2.1 Release - if: always() - shell: bash - run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-8-2018-q4-major -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) +# - name: GCC 8.2.1 Release +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-8-2018-q4-major -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - name: GCC 8.3.1 Debug - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-8-2019-q3-update -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - - name: GCC 8.3.1 Release - if: always() - shell: bash - run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-8-2019-q3-update -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) +# - name: GCC 8.3.1 Release +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-8-2019-q3-update -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - name: GCC 9.2.1 Debug - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-9-2019-q4-major -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - - name: GCC 9.2.1 Release - if: always() - shell: bash - run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-9-2019-q4-major -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) +# - name: GCC 9.2.1 Release +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-9-2019-q4-major -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - name: GCC 9.3.1 Debug - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-9-2020-q2-update -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - - name: GCC 9.3.1 Release - if: always() - shell: bash - run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-9-2020-q2-update -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) +# - name: GCC 9.3.1 Release +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-9-2020-q2-update -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - name: GCC 10.2.1 Debug - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-10-2020-q4-major -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - - name: GCC 10.2.1 Release - if: always() - shell: bash - run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-10-2020-q4-major -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) +# - name: GCC 10.2.1 Release +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-10-2020-q4-major -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - name: GCC 10.3.1 Debug - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-10.3-2021.10 -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - name: GCC 10.3.1 Release - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-none-eabi-10.3-2021.10 -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - name: GCC 11.2.1 Debug - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-11.2-2022.02-x86_64-arm-none-eabi -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - name: GCC 11.2.1 Release - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/gcc-arm-11.2-2022.02-x86_64-arm-none-eabi -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - name: GCC 11.3.1 Debug - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-11.3.rel1-x86_64-arm-none-eabi -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - name: GCC 11.3.1 Release - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-11.3.rel1-x86_64-arm-none-eabi -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - name: GCC 12.2.1 Debug - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-12.2.rel1-x86_64-arm-none-eabi -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - name: GCC 12.2.1 Release - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-12.2.rel1-x86_64-arm-none-eabi -DPICO_BOARD=pico_w; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + - name: GCC 12.3.1 Debug Pico W + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_BOARD=pico_w -DPICO_PLATFORM=rp2040 -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: GCC 12.3.1 Debug RP2350 + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_PLATFORM=rp2350 -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + +# - name: GCC 12.3.1 Release Pico W +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_BOARD=pico_w -DPICO_PLATFORM=rp2040 -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) +# +# - name: GCC 12.3.1 Release RP2350 +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_PLATFORM=rp2350 -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: GCC 13.2.1 Debug Pico W + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_BOARD=pico_w -DPICO_PLATFORM=rp2040 -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: GCC 13.2.1 Debug RP2350 + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_PLATFORM=rp2350 -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + +# - name: GCC 13.2.1 Release Pico W +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_BOARD=pico_w -DPICO_PLATFORM=rp2040 -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) +# +# - name: GCC 13.2.1 Release RP2350 +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_PLATFORM=rp2350 -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: GCC 13.3.1 Debug Pico W + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_BOARD=pico_w -DPICO_PLATFORM=rp2040 -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: GCC 13.3.1 Debug RP2350 + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_PLATFORM=rp2350 -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + +# - name: GCC 13.3.1 Release Pico W +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_BOARD=pico_w -DPICO_PLATFORM=rp2040 -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) +# +# - name: GCC 13.3.1 Release RP2350 +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_PLATFORM=rp2350 -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: LLVM 14.0.0 Debug Pico W + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_BOARD=pico_w -DPICO_PLATFORM=rp2040 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVMEmbeddedToolchainForArm-14.0.0; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: LLVM 14.0.0 Debug RP2350 + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_PLATFORM=rp2350 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVMEmbeddedToolchainForArm-14.0.0; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + +# - name: LLVM 14.0.0 Release Pico W +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_BOARD=pico_w -DPICO_PLATFORM=rp2040 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVMEmbeddedToolchainForArm-14.0.0; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) +# +# - name: LLVM 14.0.0 Release RP2350 +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_PLATFORM=rp2350 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVMEmbeddedToolchainForArm-14.0.0; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: RISCV GCC 14.1.0 Debug RP2350 RISCV + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_PLATFORM=rp2350-riscv -DPICO_TOOLCHAIN_PATH=/opt/riscv/riscv32-unknown-elf-gcc-14.1.0; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: RISCV GCC 14.1.0 Release RP2350 RISCV + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_PLATFORM=rp2350-riscv -DPICO_TOOLCHAIN_PATH=/opt/riscv/riscv32-unknown-elf-gcc-14.1.0; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: GCC 14.2.1 Debug Pico W + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_BOARD=pico_w -DPICO_PLATFORM=rp2040 -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: GCC 14.2.1 Debug RP2350 + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_PLATFORM=rp2350 -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + +# - name: GCC 14.2.1 Release Pico W +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_BOARD=pico_w -DPICO_PLATFORM=rp2040 -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) +# +# - name: GCC 14.2.1 Release RP2350 +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_PLATFORM=rp2350 -DPICO_TOOLCHAIN_PATH=/opt/arm/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: LLVM 15.0.2 Debug Pico W + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_BOARD=pico_w -DPICO_PLATFORM=rp2040 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVMEmbeddedToolchainForArm-15.0.2; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: LLVM 15.0.2 Debug RP2350 + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_PLATFORM=rp2350 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVMEmbeddedToolchainForArm-15.0.2; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + +# - name: LLVM 15.0.2 Release Pico W +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_BOARD=pico_w -DPICO_PLATFORM=rp2040 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVMEmbeddedToolchainForArm-15.0.2; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) +# +# - name: LLVM 15.0.2 Release RP2350 +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_PLATFORM=rp2350 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVMEmbeddedToolchainForArm-15.0.2; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: LLVM 16.0.0 Debug Pico W + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_BOARD=pico_w -DPICO_PLATFORM=rp2040 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVMEmbeddedToolchainForArm-16.0.0; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: LLVM 16.0.0 Debug RP2350 + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_PLATFORM=rp2350 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVMEmbeddedToolchainForArm-16.0.0; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + +# - name: LLVM 16.0.0 Release Pico W +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_BOARD=pico_w -DPICO_PLATFORM=rp2040 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVMEmbeddedToolchainForArm-16.0.0; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) +# +# - name: LLVM 16.0.0 Release RP2350 +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_PLATFORM=rp2350 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVMEmbeddedToolchainForArm-16.0.0; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: LLVM 17.0.1 Debug Pico W + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_BOARD=pico_w -DPICO_PLATFORM=rp2040 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVMEmbeddedToolchainForArm-17.0.1; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: LLVM 17.0.1 Debug RP2350 + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_PLATFORM=rp2350 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVMEmbeddedToolchainForArm-17.0.1; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + +# - name: LLVM 17.0.1 Release Pico W +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_BOARD=pico_w -DPICO_PLATFORM=rp2040 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVMEmbeddedToolchainForArm-17.0.1; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) +# +# - name: LLVM 17.0.1 Release RP2350 +# if: ${{ !cancelled() }} +# shell: bash +# run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_PLATFORM=rp2350 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVMEmbeddedToolchainForArm-17.0.1; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: LLVM 18.1.3 Debug Pico W + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_BOARD=pico_w -DPICO_PLATFORM=rp2040 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVM-ET-Arm-18.1.3-Linux-x86_64; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: LLVM 18.1.3 Debug RP2350 + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_PLATFORM=rp2350 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVM-ET-Arm-18.1.3-Linux-x86_64; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: LLVM 18.1.3 Release Pico W + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_BOARD=pico_w -DPICO_PLATFORM=rp2040 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVM-ET-Arm-18.1.3-Linux-x86_64; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + + - name: LLVM 18.1.3 Release RP2350 + if: ${{ !cancelled() }} + shell: bash + run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_PLATFORM=rp2350 -DPICO_COMPILER=pico_arm_clang -DPICO_TOOLCHAIN_PATH=/opt/arm/LLVM-ET-Arm-18.1.3-Linux-x86_64; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) + - name: Native Debug - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Debug -DPICO_PLATFORM=host; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) - name: Native Release - if: always() + if: ${{ !cancelled() }} shell: bash run: cd ${{github.workspace}}/pico-examples; mkdir -p build; rm -rf build/*; cd build; PICO_SDK_PATH=../../pico-sdk cmake ../ -DCMAKE_BUILD_TYPE=Release -DPICO_PLATFORM=host; make --output-sync=target --no-builtin-rules --no-builtin-variables -j$(nproc) diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml new file mode 100644 index 000000000..6196a08e4 --- /dev/null +++ b/.github/workflows/pr-check.yml @@ -0,0 +1,28 @@ +name: Make sure PRs target the develop branch + +on: + pull_request_target: + +# By default, pull_request_target gets write permissions to the repo - this prevents that +permissions: + pull-requests: write + +jobs: + check-branch: + if: github.event.pull_request.base.ref == 'master' + runs-on: ubuntu-latest + steps: + - name: Add comment + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: 'Please do not submit against `master`, use `develop` instead' + }) + - name: Throw error + run: | + echo "::error title=wrong-branch::Please do not submit against 'master', use 'develop' instead" + exit 1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 597cb3e57..a76866c0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,8 +9,8 @@ project(pico_examples C CXX ASM) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) -if (PICO_SDK_VERSION_STRING VERSION_LESS "2.1.0") - message(FATAL_ERROR "Raspberry Pi Pico SDK version 2.1.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}") +if (PICO_SDK_VERSION_STRING VERSION_LESS "2.2.0") + message(FATAL_ERROR "Raspberry Pi Pico SDK version 2.2.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}") endif() set(PICO_EXAMPLES_PATH ${PROJECT_SOURCE_DIR}) @@ -20,6 +20,11 @@ if (NOT DEFINED PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS) set(PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS 3000) endif() +# Allow using the example keys, as this is the examples repository +if (NOT DEFINED PICO_ALLOW_EXAMPLE_KEYS) + set(PICO_ALLOW_EXAMPLE_KEYS 1) +endif() + # Initialize the SDK pico_sdk_init() @@ -66,6 +71,7 @@ add_subdirectory(cmake) add_subdirectory(dcp) add_subdirectory(divider) add_subdirectory(dma) +add_subdirectory(encrypted) add_subdirectory(flash) add_subdirectory(gpio) add_subdirectory(hstx) @@ -80,6 +86,7 @@ add_subdirectory(pwm) add_subdirectory(reset) add_subdirectory(rtc) add_subdirectory(spi) +add_subdirectory(status_led) add_subdirectory(system) add_subdirectory(timer) add_subdirectory(uart) diff --git a/README.md b/README.md index 5a436a348..f43a7339a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Getting started -See [Getting Started with the Raspberry Pi Pico](https://rptl.io/pico-get-started) and the README in the [pico-sdk](https://github.com/raspberrypi/pico-sdk) for information +See [Getting started with Raspberry Pi Pico-series](https://rptl.io/pico-get-started) and the README in the [pico-sdk](https://github.com/raspberrypi/pico-sdk) for information on getting up and running. ##### Notes on different boards and platforms (RP2040 / RP2350) @@ -24,13 +24,13 @@ Information on which examples are not being built is displayed during the CMake ### First Examples -App| Description | Link to prebuilt UF2 ----|----------------------------------------------------------------------------|--- -[hello_serial](hello_world/serial) | The obligatory Hello World program for Pico (Output over serial version) | -[hello_usb](hello_world/usb) | The obligatory Hello World program for Pico (Output over USB version) | https://rptl.io/pico-hello-usb -[blink](blink) | Blink a LED on and off. Works on both boards with regular LEDs and boards like Pico W where the led is connected via the Wi-Fi chip | https://rptl.io/pico-blink -[blink_simple](blink_simple) | Blink a LED on and off. Does not work on boards like Pico W where the led is connected via the Wi-Fi chip. | https://rptl.io/pico-blink -[picow_blink](pico_w/wifi/blink) | Blinks the on-board LED on boards like Pico W where the led is connected via the Wi-Fi chip. | http://rptl.io/pico-w-blink +App| Description | Link to prebuilt UF2 +---|---|--- +[hello_serial](hello_world/serial) | The obligatory Hello World program for Pico (output over serial version). | +[hello_usb](hello_world/usb) | The obligatory Hello World program for Pico (output over USB version). | https://rptl.io/pico-hello-usb +[blink](blink) | Blink a LED on and off. Works on both boards with regular LEDs and boards like Pico W where the LED is connected via the Wi-Fi chip. | https://rptl.io/pico-blink +[blink_simple](blink_simple) | Blink a LED on and off. Does not work on boards like Pico W where the LED is connected via the Wi-Fi chip. | +[picow_blink](pico_w/wifi/blink) | Blinks the on-board LED on boards like Pico W where the LED is connected via the Wi-Fi chip. | http://rptl.io/pico-w-blink ### ADC @@ -48,13 +48,17 @@ App|Description App|Description ---|--- -[blink_any](binary_info/blink_any) | Uses `bi_ptr` variables to create a configurable blink binary - see the separate [README](binary_info/README.md) for more details -[hello_anything](binary_info/hello_anything) | Uses `bi_ptr` variables to create a configurable hello_world binary - see the separate [README](binary_info/README.md) for more details +[blink_any](binary_info/blink_any) | Uses `bi_ptr` variables to create a configurable blink binary - see the separate [README](binary_info/README.md) for more details. +[hello_anything](binary_info/hello_anything) | Uses `bi_ptr` variables to create a configurable hello_world binary - see the separate [README](binary_info/README.md) for more details. ### Bootloaders (RP235x Only) + +These examples all produce multiple UF2s - a bootloader UF2, and then a separate UF2 of the program that the bootloader will load and boot. To load them onto a device with empty flash, first load the bootloader UF2, then reset to BOOTSEL mode and load the program UF2. This ordering is required because the bootloaders contain embedded partition tables - see section 5.10.6 in the RP2350 datasheet for more details on those. + App|Description ---|--- -[enc_bootloader](bootloaders/encrypted) | A bootloader which decrypts binaries from flash into SRAM. See the separate [README](bootloaders/encrypted/README.md) for more information +[enc_bootloader](bootloaders/encrypted) | A bootloader which decrypts binaries from flash into SRAM. See the separate [README](bootloaders/encrypted/README.md) for more information. +[uart_boot](bootloaders/uart) | A bootloader which boots a separate RP2350 using the UART boot interface. See section 5.8 in the datasheet for more details, including the wiring requirements. ### Clocks @@ -69,7 +73,7 @@ App|Description App|Description ---|--- -[build_variants](cmake/build_variants) | Builds two version of the same app with different configurations +[build_variants](cmake/build_variants) | Builds two versions of the same app with different configurations. ### DCP (RP235x Only) @@ -86,11 +90,18 @@ App|Description [channel_irq](dma/channel_irq) | Use an IRQ handler to reconfigure a DMA channel, in order to continuously drive data through a PIO state machine. [sniff_crc](dma/sniff_crc) | Use the DMA engine's 'sniff' capability to calculate a CRC32 on a data buffer. +### Encrypted (RP235x Only) + +App|Description +---|--- +[hello_encrypted](encrypted/hello_encrypted) | Create a self-decrypting binary, using the hardened decryption stage. This should be secure against side channel attacks. +[hello_encrypted_mbedtls](encrypted/hello_encrypted) | Create a self-decrypting binary, using the MbedTLS decryption stage. This is not secure against side channel attacks, so is fast but provides limited protection. + ### HSTX (RP235x Only) App|Description ---|--- -[dvi_out_hstx_encoder](hstx/dvi_out_hstx_encoder) | Use the HSTX to output a DVI signal with 3:3:2 RGB +[dvi_out_hstx_encoder](hstx/dvi_out_hstx_encoder) | Use the HSTX to output a DVI signal with 3:3:2 RGB. ### Flash @@ -101,7 +112,8 @@ App|Description [program](flash/program) | Erase a flash sector, program one flash page, and read back the data. [xip_stream](flash/xip_stream) | Stream data using the XIP stream hardware, which allows data to be DMA'd in the background whilst executing code from flash. [ssi_dma](flash/ssi_dma) | DMA directly from the flash interface (continuous SCK clocking) for maximum bulk read performance. -[runtime_flash_permissions](flash/runtime_flash_permissions) | Demonstrates adding partitions at runtime to change the flash permissions +[runtime_flash_permissions](flash/runtime_flash_permissions) | Demonstrates adding partitions at runtime to change the flash permissions. +[partition_info](flash/partition_info) | Extract and enumerate partition information (address ranges, permissions, IDs, and names) from the partition table. ### FreeRTOS @@ -109,7 +121,9 @@ These examples require you to set the `FREERTOS_KERNEL_PATH` to point to the Fre App|Description ---|--- -[hello_freertos](freertos/hello_freertos) | Examples that demonstrate how run FreeRTOS and tasks on 1 or 2 cores. +[hello_freertos_one_core](freertos/hello_freertos) | Demonstrates how to run FreeRTOS and tasks on one core. +[hello_freertos_two_cores](freertos/hello_freertos) | Demonstrates how to run FreeRTOS and tasks on two cores. +[hello_freertos_static_allocation](freertos/hello_freertos) | Demonstrates how to run FreeRTOS on two cores with static RAM allocation. ### GPIO @@ -135,15 +149,15 @@ App|Description [bmp280_i2c](i2c/bmp280_i2c) | Read and convert temperature and pressure data from a BMP280 sensor, attached to an I2C bus. [lcd_1602_i2c](i2c/lcd_1602_i2c) | Display some text on a generic 16x2 character LCD display, via I2C. [lis3dh_i2c](i2c/lis3dh_i2c) | Read acceleration and temperature value from a LIS3DH sensor via I2C -[mcp9808_i2c](i2c/mcp9808_i2c) | Read temperature, set limits and raise alerts when limits are surpassed. +[mcp9808_i2c](i2c/mcp9808_i2c) | Read temperature from a MCP9808 sensor, set limits and raise alerts when limits are surpassed. [mma8451_i2c](i2c/mma8451_i2c) | Read acceleration from a MMA8451 accelerometer and set range and precision for the data. [mpl3115a2_i2c](i2c/mpl3115a2_i2c) | Interface with an MPL3115A2 altimeter, exploring interrupts and advanced board features, via I2C. [mpu6050_i2c](i2c/mpu6050_i2c) | Read acceleration and angular rate values from a MPU6050 accelerometer/gyro, attached to an I2C bus. -[ssd1306_i2c](i2c/ssd1306_i2c) | Convert and display a bitmap on a 128x32 or 128x64 SSD1306-driven OLED display +[ssd1306_i2c](i2c/ssd1306_i2c) | Convert and display a bitmap on a 128x32 or 128x64 SSD1306-driven OLED display. [pa1010d_i2c](i2c/pa1010d_i2c) | Read GPS location data, parse and display data via I2C. -[pcf8523_i2c](i2c/pcf8523_i2c) | Read time and date values from a real time clock. Set current time and alarms on it. +[pcf8523_i2c](i2c/pcf8523_i2c) | Read time and date values from a PCF8523 real time clock. Set current time and alarms on it. [ht16k33_i2c](i2c/ht16k33_i2c) | Drive a 4 digit 14 segment LED with an HT16K33. -[slave_mem_i2c](i2c/slave_mem_i2c) | i2c slave example where the slave implements a 256 byte memory +[slave_mem_i2c](i2c/slave_mem_i2c) | i2c slave example where the slave implements a 256 byte memory. [slave_mem_i2c_burst](i2c/slave_mem_i2c) | i2c slave example where the slave implements a 256 byte memory. This version inefficiently writes each byte in a separate call to demonstrate read and write burst mode. ### Interpolator @@ -157,7 +171,7 @@ App|Description App|Description ---|--- [hello_multicore](multicore/hello_multicore) | Launch a function on the second core, printf some messages on each core, and pass data back and forth through the mailbox FIFOs. -[multicore_fifo_irqs](multicore/multicore_fifo_irqs) | On each core, register and interrupt handler for the mailbox FIFOs. Show how the interrupt fires when that core receives a message. +[multicore_fifo_irqs](multicore/multicore_fifo_irqs) | On each core, register an interrupt handler for the mailbox FIFOs. Show how the interrupt fires when that core receives a message. [multicore_runner](multicore/multicore_runner) | Set up the second core to accept, and run, any function pointer pushed into its mailbox FIFO. Push in a few pieces of code and get answers back. [multicore_doorbell](multicore/multicore_doorbell) | Claims two doorbells for signaling between the cores. Counts how many doorbell IRQs occur on the second core and uses doorbells to coordinate exit. @@ -171,7 +185,7 @@ App|Description App|Description ---|--- -[blinky](picoboard/blinky) | Blink "hello, world" in Morse code on Pico's LED +[blinky](picoboard/blinky) | Blink "hello, world" in Morse code on Pico's LED. [button](picoboard/button) | Use Pico's BOOTSEL button as a regular button input, by temporarily suspending flash access. ### Pico Networking @@ -182,8 +196,8 @@ App|Description ---|--- [picow_access_point](pico_w/wifi/access_point) | Starts a WiFi access point, and fields DHCP requests. [picow_blink](pico_w/wifi/blink) | Blinks the on-board LED (which is connected via the WiFi chip). -[picow_blink_slow_clock](pico_w/wifi/blink) | Blinks the on-board LED (which is connected via the WiFi chip) with a slower system clock to show how to reconfigure communication with the WiFi chip at run time under those circumstances -[picow_blink_fast_clock](pico_w/wifi/blink) | Blinks the on-board LED (which is connected via the WiFi chip) with a faster system clock to show how to reconfigure communication with the WiFi chip at build time under those circumstances +[picow_blink_slow_clock](pico_w/wifi/blink) | Blinks the on-board LED (which is connected via the WiFi chip) with a slower system clock to show how to reconfigure communication with the WiFi chip at run time under those circumstances. +[picow_blink_fast_clock](pico_w/wifi/blink) | Blinks the on-board LED (which is connected via the WiFi chip) with a faster system clock to show how to reconfigure communication with the WiFi chip at build time under those circumstances. [picow_iperf_server](pico_w/wifi/iperf) | Runs an "iperf" server for WiFi speed testing. [picow_ntp_client](pico_w/wifi/ntp_client) | Connects to an NTP server to fetch and display the current time. [picow_tcp_client](pico_w/wifi/tcp_client) | A simple TCP client. You can run [python_test_tcp_server.py](pico_w/wifi/python_test_tcp/python_test_tcp_server.py) for it to connect to. @@ -192,26 +206,27 @@ App|Description [picow_tls_verify](pico_w/wifi/tls_client) | Demonstrates how to make a HTTPS request using TLS with certificate verification. [picow_wifi_scan](pico_w/wifi/wifi_scan) | Scans for WiFi networks and prints the results. [picow_udp_beacon](pico_w/wifi/udp_beacon) | A simple UDP transmitter. -[picow_httpd](pico_w/wifi/httpd) | Runs a LWIP HTTP server test app -[picow_http_client](pico_w/wifi/http_client) | Demonstrates how to make http and https requests -[picow_http_client_verify](pico_w/wifi/http_client) | Demonstrates how to make a https request with server authentication -[picow_mqtt_client](pico_w/wifi/mqtt) | Demonstrates how to implement a MQTT client application +[picow_httpd](pico_w/wifi/httpd) | Runs a LWIP HTTP server test app. +[picow_http_client](pico_w/wifi/http_client) | Demonstrates how to make http and https requests. +[picow_http_client_verify](pico_w/wifi/http_client) | Demonstrates how to make a https request with server authentication. +[picow_mqtt_client](pico_w/wifi/mqtt) | Demonstrates how to implement a MQTT client application. +[picow_ota_update](pico_w/wifi/ota_update) | A minimal OTA update server (RP235x only). See the separate [README](pico_w/wifi/ota_update/README.md) for more details. -#### FreeRTOS examples +#### FreeRTOS Networking These are examples of integrating Wi-Fi networking under FreeRTOS, and require you to set the `FREERTOS_KERNEL_PATH` to point to the FreeRTOS Kernel. See https://github.com/FreeRTOS/FreeRTOS-Kernel App|Description ---|--- -[picow_freertos_iperf_server_nosys](pico_w/wifi/freertos/iperf) | Runs an "iperf" server for WiFi speed testing under FreeRTOS in NO_SYS=1 mode. The LED is blinked in another task -[picow_freertos_iperf_server_sys](pico_w/wifi/freertos/iperf) | Runs an "iperf" server for WiFi speed testing under FreeRTOS in NO_SYS=0 (i.e. full FreeRTOS integration) mode. The LED is blinked in another task +[picow_freertos_iperf_server_nosys](pico_w/wifi/freertos/iperf) | Runs an "iperf" server for WiFi speed testing under FreeRTOS in NO_SYS=1 mode. The LED is blinked in another task. +[picow_freertos_iperf_server_sys](pico_w/wifi/freertos/iperf) | Runs an "iperf" server for WiFi speed testing under FreeRTOS in NO_SYS=0 (i.e. full FreeRTOS integration) mode. The LED is blinked in another task. [picow_freertos_ping_nosys](pico_w/wifi/freertos/ping) | Runs the lwip-contrib/apps/ping test app under FreeRTOS in NO_SYS=1 mode. [picow_freertos_ping_sys](pico_w/wifi/freertos/ping) | Runs the lwip-contrib/apps/ping test app under FreeRTOS in NO_SYS=0 (i.e. full FreeRTOS integration) mode. The test app uses the lwIP _socket_ API in this case. [picow_freertos_ntp_client_socket](pico_w/wifi/freertos/ntp_client_socket) | Connects to an NTP server using the LwIP Socket API with FreeRTOS in NO_SYS=0 (i.e. full FreeRTOS integration) mode. [pico_freertos_httpd_nosys](pico_w/wifi/freertos/httpd) | Runs a LWIP HTTP server test app under FreeRTOS in NO_SYS=1 mode. [pico_freertos_httpd_sys](pico_w/wifi/freertos/httpd) | Runs a LWIP HTTP server test app under FreeRTOS in NO_SYS=0 (i.e. full FreeRTOS integration) mode. -[picow_freertos_http_client_sys](pico_w/wifi/freertos/http_client) | Demonstrates how to make a https request in NO_SYS=0 (i.e. full FreeRTOS integration) +[picow_freertos_http_client_sys](pico_w/wifi/freertos/http_client) | Demonstrates how to make a https request in NO_SYS=0 (i.e. full FreeRTOS integration) mode. ### Pico Bluetooth @@ -234,7 +249,7 @@ App|Description [picow_bt_example_audio_duplex](https://github.com/bluekitchen/btstack/tree/master/example/audio_duplex.c) | Audio Driver - Forward Audio from Source to Sink. [picow_bt_example_avrcp_browsing_client](https://github.com/bluekitchen/btstack/tree/master/example/avrcp_browsing_client.c) | AVRCP Browsing - Browse Media Players and Media Information. [picow_bt_example_dut_mode_classic](https://github.com/bluekitchen/btstack/tree/master/example/dut_mode_classic.c) | Testing - Enable Device Under Test (DUT.c) Mode for Classic. -[picow_bt_example_gap_dedicated_bonding](https://github.com/bluekitchen/btstack/tree/master/example/gap_dedicated_bonding.c) | GAP bonding +[picow_bt_example_gap_dedicated_bonding](https://github.com/bluekitchen/btstack/tree/master/example/gap_dedicated_bonding.c) | GAP bonding. [picow_bt_example_gap_inquiry](https://github.com/bluekitchen/btstack/tree/master/example/gap_inquiry.c) | GAP Classic Inquiry. [picow_bt_example_gap_le_advertisements](https://github.com/bluekitchen/btstack/tree/master/example/gap_le_advertisements.c) | GAP LE Advertisements Scanner. [picow_bt_example_gap_link_keys](https://github.com/bluekitchen/btstack/tree/master/example/gap_link_keys.c) | GAP Link Key Management (Classic.c). @@ -276,41 +291,44 @@ App|Description [picow_bt_example_spp_streamer](https://github.com/bluekitchen/btstack/tree/master/example/spp_streamer.c) | Performance - Stream Data over SPP (Server.c). [picow_bt_example_ublox_spp_le_counter](https://github.com/bluekitchen/btstack/blob/master/example/ublox_spp_le_counter.c) | LE u-blox SPP-like Heartbeat Server. -Some Standalone Bluetooth examples (without all the common example build infrastructure) are also available: +Some standalone Bluetooth examples (without all the common example build infrastructure) are also available: App|Description ---|--- -[picow_ble_temp_sensor](pico_w/bt/standalone) | Reads from the on board temperature sensor and sends notifications via BLE -[picow_ble_temp_sensor_with_wifi](pico_w/bt/standalone) | Same as above but also connects to Wi-Fi and starts an "iperf" server -[picow_ble_temp_reader](pico_w/bt/standalone) | Connects to one of the above "sensors" and reads the temperature +[picow_ble_temp_sensor](pico_w/bt/standalone) | Reads from the on board temperature sensor and sends notifications via BLE. +[picow_ble_temp_sensor_with_wifi](pico_w/bt/standalone) | Same as above but also connects to Wi-Fi and starts an "iperf" server. +[picow_ble_temp_reader](pico_w/bt/standalone) | Connects to one of the above "sensors" and reads the temperature. ### PIO App|Description ---|--- [hello_pio](pio/hello_pio) | Absolutely minimal example showing how to control an LED by pushing values into a PIO FIFO. -[apa102](pio/apa102) | Rainbow pattern on on a string of APA102 addressable RGB LEDs. -[clocked_input](pio/clocked_input) | Shift in serial data, sampling with an external clock. -[differential_manchester](pio/differential_manchester) | Send and receive differential Manchester-encoded serial (BMC). -[hub75](pio/hub75) | Display an image on a 128x64 HUB75 RGB LED matrix. -[i2c](pio/i2c) | Scan an I2C bus. -[ir_nec](pio/ir_nec) | Sending and receiving IR (infra-red) codes using the PIO. -[logic_analyser](pio/logic_analyser) | Use PIO and DMA to capture a logic trace of some GPIOs, whilst a PWM unit is driving them. -[manchester_encoding](pio/manchester_encoding) | Send and receive Manchester-encoded serial. -[onewire](pio/onewire)| A library for interfacing to 1-Wire devices, with an example for the DS18B20 temperature sensor. +[pio_apa102](pio/apa102) | Rainbow pattern on a string of APA102 addressable RGB LEDs. +[pio_clocked_input](pio/clocked_input) | Shift in serial data, sampling with an external clock. +[pio_differential_manchester](pio/differential_manchester) | Send and receive differential Manchester-encoded serial (BMC). +[pio_hub75](pio/hub75) | Display an image on a 128x64 HUB75 RGB LED matrix. +[pio_i2c_bus_scan](pio/i2c) | Scan an I2C bus. +[pio_ir_loopback](pio/ir_nec) | Sending and receiving IR (infra-red) codes using the PIO. +[pio_logic_analyser](pio/logic_analyser) | Use PIO and DMA to capture a logic trace of some GPIOs, whilst a PWM unit is driving them. +[pio_manchester_encoding](pio/manchester_encoding) | Send and receive Manchester-encoded serial. +[pio_onewire](pio/onewire)| A library for interfacing to 1-Wire devices, with an example for the DS18B20 temperature sensor. [pio_blink](pio/pio_blink) | Set up some PIO state machines to blink LEDs at different frequencies, according to delay counts pushed into their FIFOs. -[pwm](pio/pwm) | Pulse width modulation on PIO. Use it to gradually fade the brightness of an LED. -[spi](pio/spi) | Use PIO to erase, program and read an external SPI flash chip. A second example runs a loopback test with all four CPHA/CPOL combinations. -[squarewave](pio/squarewave) | Drive a fast square wave onto a GPIO. This example accesses low-level PIO registers directly, instead of using the SDK functions. -[squarewave_div_sync](pio/squarewave) | Generates a square wave on three GPIOs and synchronises the divider on all the state machines -[st7789_lcd](pio/st7789_lcd) | Set up PIO for 62.5 Mbps serial output, and use this to display a spinning image on a ST7789 serial LCD. -[quadrature_encoder](pio/quadrature_encoder) | A quadrature encoder using PIO to maintain counts independent of the CPU. -[quadrature_encoder_substep](pio/quadrature_encoder_substep) | High resolution speed measurement using a standard quadrature encoder -[uart_rx](pio/uart_rx) | Implement the receive component of a UART serial port. Attach it to the spare Arm UART to see it receive characters. -[uart_tx](pio/uart_tx) | Implement the transmit component of a UART serial port, and print hello world. -[ws2812](pio/ws2812) | Examples of driving WS2812 addressable RGB LEDs. -[addition](pio/addition) | Add two integers together using PIO. Only around 8 billion times slower than Cortex-M0+. -[uart_pio_dma](pio/uart_pio_dma) | Send and receive data from a UART implemented using the PIO and DMA +[pio_pwm](pio/pwm) | Pulse width modulation on PIO. Use it to gradually fade the brightness of an LED. +[pio_quadrature_encoder](pio/quadrature_encoder) | A quadrature encoder using PIO to maintain counts independent of the CPU. +[pio_quadrature_encoder_substep](pio/quadrature_encoder_substep) | High resolution speed measurement using a standard quadrature encoder. +[pio_spi_flash](pio/spi) | Use PIO to erase, program and read an external SPI flash chip. +[pio_spi_loopback](pio/spi) | Use PIO to run a loopback test with all four CPHA/CPOL combinations. +[pio_squarewave](pio/squarewave) | Drive a fast square wave onto a GPIO. This example accesses low-level PIO registers directly, instead of using the SDK functions. +[pio_squarewave_div_sync](pio/squarewave) | Generates a square wave on three GPIOs and synchronises the divider on all the state machines. +[pio_st7789_lcd](pio/st7789_lcd) | Set up PIO for 62.5 Mbps serial output, and use this to display a spinning image on a ST7789 serial LCD. +[pio_uart_dma](pio/uart_dma) | Send and receive data from a UART implemented using the PIO and DMA. +[pio_uart_rx](pio/uart_rx) | Implement the receive component of a UART serial port. Attach it to the spare Arm UART to see it receive characters. +[pio_uart_rx_intr](pio/uart_rx) | Implement the receive component of a UART serial port with an interrupt for received characters. Attach it to the spare Arm UART to see it receive characters. +[pio_uart_tx](pio/uart_tx) | Implement the transmit component of a UART serial port, and print hello world. +[pio_ws2812](pio/ws2812) | Example of driving a string of WS2812 addressable RGB LEDs. +[pio_ws2812_parallel](pio/ws2812) | Examples of driving multiple strings of WS2812 addressable RGB LEDs efficiently. +[pio_addition](pio/addition) | Add two integers together using PIO. Only around 8 billion times slower than Cortex-M0+. ### PWM @@ -326,7 +344,7 @@ App|Description ---|--- [hello_reset](reset/hello_reset) | Perform a hard reset on some peripherals, then bring them back up. -### RTC +### RTC (RP2040 Only) App|Description ---|--- @@ -338,30 +356,37 @@ App|Description App|Description ---|--- -[hello_sha256](sha/sha256) | Demonstrates how to use the pico_sha256 library to calculate a checksum using the hardware in rp2350 -[mbedtls_sha256](sha/mbedtls_sha256) | Demonstrates using the SHA-256 hardware acceleration in mbedtls +[hello_sha256](sha/sha256) | Demonstrates how to use the pico_sha256 library to calculate a checksum using the hardware in RP2350. +[mbedtls_sha256](sha/mbedtls_sha256) | Demonstrates using the SHA-256 hardware acceleration in MbedTLS. ### SPI App|Description ---|--- [bme280_spi](spi/bme280_spi) | Attach a BME280 temperature/humidity/pressure sensor via SPI. -[mpu9250_spi](spi/mpu9250_spi) | Attach a MPU9250 accelerometer/gyoscope via SPI. +[mpu9250_spi](spi/mpu9250_spi) | Attach a MPU9250 accelerometer/gyroscope via SPI. [spi_dma](spi/spi_dma) | Use DMA to transfer data both to and from the SPI simultaneously. The SPI is configured for loopback. [spi_flash](spi/spi_flash) | Erase, program and read a serial flash device attached to one of the SPI controllers. [spi_master_slave](spi/spi_master_slave) | Demonstrate SPI communication as master and slave. -[max7219_8x7seg_spi](spi/max7219_8x7seg_spi) | Attaching a Max7219 driving an 8 digit 7 segment display via SPI -[max7219_32x8_spi](spi/max7219_32x8_spi) | Attaching a Max7219 driving an 32x8 LED display via SPI +[max7219_8x7seg_spi](spi/max7219_8x7seg_spi) | Attaching a Max7219 driving an 8 digit 7 segment display via SPI. +[max7219_32x8_spi](spi/max7219_32x8_spi) | Attaching a Max7219 driving an 32x8 LED display via SPI. + +### Status LED + +App|Description +---|--- +[status_blink](status_led/status_blink) | Blink the onboard LED using the status_led API. +[color_blink](status_led/color_blink) | Blink the onboard colored (WS2812) LED using the colored_status_led API if supported by the board. ### System App|Description ---|--- -[boot_info](system/boot_info) | Demonstrate how to read and interpret sys info boot info. +[boot_info](system/boot_info) | Demonstrate how to read and interpret sys info boot info (RP235x only). [hello_double_tap](system/hello_double_tap) | An LED blink with the `pico_bootsel_via_double_reset` library linked. This enters the USB bootloader when it detects the system being reset twice in quick succession, which is useful for boards with a reset button but no BOOTSEL button. [rand](system/rand) | Demonstrate how to use the pico random number functions. [narrow_io_write](system/narrow_io_write) | Demonstrate the effects of 8-bit and 16-bit writes on a 32-bit IO register. -[unique_board_id](system/unique_board_id) | Read the 64 bit unique ID from external flash, which serves as a unique identifier for the board. +[unique_board_id](system/unique_board_id) | Read the 64 bit unique ID from external flash (RP2040) or OTP (RP235x), which serves as a unique identifier for the board. ### Timer @@ -376,7 +401,7 @@ App|Description App|Description ---|--- [hello_uart](uart/hello_uart) | Print some text from one of the UART serial ports, without going through `stdio`. -[lcd_uart](uart/lcd_uart) | Display text and symbols on a 16x02 RGB LCD display via UART +[lcd_uart](uart/lcd_uart) | Display text and symbols on a 16x02 RGB LCD display via UART. [uart_advanced](uart/uart_advanced) | Use some other UART features like RX interrupts, hardware control flow, and data formats other than 8n1. ### Universal @@ -386,16 +411,16 @@ These require you to set `PICO_ARM_TOOLCHAIN_PATH` and `PICO_RISCV_TOOLCHAIN_PAT App|Description ---|--- -[blink](universal/CMakeLists.txt#L126) | Same as the [blink](blink) example, but universal. +[blink_universal](universal/CMakeLists.txt#L126) | Same as the [blink](blink) example, but universal. [hello_universal](universal/hello_universal) | The obligatory Hello World program for Pico (USB and serial output). On RP2350 it will reboot to the other architecture after every 10 prints. -[nuke_universal](universal/CMakeLists.txt#L132) | Same as the [nuke](flash/nuke) example, but universal. On RP2350 runs as a packaged SRAM binary, so is written to flash and copied to SRAM by the bootloader +[nuke_universal](universal/CMakeLists.txt#L132) | Same as the [nuke](flash/nuke) example, but universal. On RP2350 runs as a packaged SRAM binary, so it is written to flash and copied to SRAM by the bootloader. ### USB Device #### TinyUSB Examples Most of the USB device examples come directly from the TinyUSB device examples directory [here](https://github.com/hathach/tinyusb/tree/master/examples/device). -Those that are supported on RP2040 devices are automatically included as part of the pico-examples +Those that are supported on RP2040 and RP2350 devices are automatically included as part of the pico-examples build as targets named `tinyusb_dev_`, e.g. https://github.com/hathach/tinyusb/tree/master/examples/device/hid_composite is built as `tinyusb_dev_hid_composite`. @@ -423,18 +448,18 @@ At the time of writing, these examples are available: Whilst these examples ably demonstrate how to use TinyUSB in device mode, their `CMakeLists.txt` is set up in a way tailored to how TinyUSB builds their examples within their source tree. -For a better example of how to configure `CMakeLists.txt` for using TinyUSB in device mode with the Raspberry Pi SDK +For a better example of how to configure `CMakeLists.txt` for using TinyUSB in device mode with the Raspberry Pi Pico SDK see below: #### SDK build example App|Description ---|--- -[dev_hid_composite](usb/device/dev_hid_composite) | A copy of the TinyUSB device example with the same name, but with a CMakeLists.txt which demonstrates how to add a dependency on the TinyUSB device libraries with the Raspberry Pi Pico SDK +[dev_hid_composite](usb/device/dev_hid_composite) | A copy of the TinyUSB device example with the same name, but with a CMakeLists.txt which demonstrates how to add a dependency on the TinyUSB device libraries with the Raspberry Pi Pico SDK. #### Low Level example App|Description ---|--- -[dev_lowlevel](usb/device/dev_lowlevel) | A USB Bulk loopback implemented with direct access to the USB hardware (no TinyUSB) +[dev_lowlevel](usb/device/dev_lowlevel) | A USB Bulk loopback implemented with direct access to the USB hardware (no TinyUSB). #### Custom CDC with SDK stdio @@ -447,7 +472,7 @@ App|Description ### USB Host All the USB host examples come directly from the TinyUSB host examples directory [here](https://github.com/hathach/tinyusb/tree/master/examples/host). -Those that are supported on RP2040 devices are automatically included as part of the pico-examples +Those that are supported on RP2040 and RP2350 devices are automatically included as part of the pico-examples build as targets named `tinyusb_host_`, e.g. https://github.com/hathach/tinyusb/tree/master/examples/host/cdc_msc_hid is built as `tinyusb_host_cdc_msc_hid`. @@ -457,12 +482,12 @@ At the time of writing, there is only one host example available: ### USB Dual Mode -USB Dual Mode uses PIO as a USB host controller and the RP2040 USB device controller as a device controller. All the USB dual examples come directly from the TinyUSB dual examples directory [here](https://github.com/hathach/tinyusb/tree/master/examples/dual). -Those that are supported on RP2040 devices are automatically included as part of the pico-examples +USB Dual Mode uses PIO as a USB host controller and the RP2040/RP2350 USB controller as a device controller. All the USB dual examples come directly from the TinyUSB dual examples directory [here](https://github.com/hathach/tinyusb/tree/master/examples/dual). +Those that are supported on RP2040 and RP2350 devices are automatically included as part of the pico-examples build as targets named `tinyusb_dual_`, e.g. https://github.com/hathach/tinyusb/tree/master/examples/dual/host_hid_to_device_cdc is built as `tinyusb_dual_host_hid_to_device_cdc`. -At the time of writing, there is only one dual example available: +At the time of writing, there is only one dual mode example available: - tinyusb_dual_host_hid_to_device_cdc diff --git a/adc/read_vsys/CMakeLists.txt b/adc/read_vsys/CMakeLists.txt index 67535bd72..28f28089c 100644 --- a/adc/read_vsys/CMakeLists.txt +++ b/adc/read_vsys/CMakeLists.txt @@ -1,14 +1,4 @@ -add_library(power_status_adc INTERFACE) -target_sources(power_status_adc INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/power_status.c - ) -target_include_directories(power_status_adc INTERFACE - ${CMAKE_CURRENT_LIST_DIR} - ) -target_link_libraries(power_status_adc INTERFACE - hardware_adc - hardware_gpio - ) +add_subdirectory(power_status) add_executable(read_vsys read_vsys.c diff --git a/adc/read_vsys/power_status/CMakeLists.txt b/adc/read_vsys/power_status/CMakeLists.txt new file mode 100644 index 000000000..bf334b037 --- /dev/null +++ b/adc/read_vsys/power_status/CMakeLists.txt @@ -0,0 +1,11 @@ +add_library(power_status_adc INTERFACE) +target_sources(power_status_adc INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/power_status.c + ) +target_include_directories(power_status_adc INTERFACE + ${CMAKE_CURRENT_LIST_DIR} + ) +target_link_libraries(power_status_adc INTERFACE + hardware_adc + hardware_gpio + ) diff --git a/adc/read_vsys/power_status.c b/adc/read_vsys/power_status/power_status.c similarity index 100% rename from adc/read_vsys/power_status.c rename to adc/read_vsys/power_status/power_status.c diff --git a/adc/read_vsys/power_status.h b/adc/read_vsys/power_status/power_status.h similarity index 100% rename from adc/read_vsys/power_status.h rename to adc/read_vsys/power_status/power_status.h diff --git a/bootloaders/CMakeLists.txt b/bootloaders/CMakeLists.txt index fff349887..b8148217c 100644 --- a/bootloaders/CMakeLists.txt +++ b/bootloaders/CMakeLists.txt @@ -1,10 +1,7 @@ +add_subdirectory_exclude_platforms(uart host rp2040) + if (TARGET pico_mbedtls) - # older clang seem to have a segment overlap issue that confuses picotool - if (PICO_C_COMPILER_IS_CLANG AND CMAKE_C_COMPILER_VERSION VERSION_LESS "17.0.0") - message("Skipping encrypted bootloader example on LLVM/Clang version < 17; please use GCC or newer LLVM/Clang") - else() - add_subdirectory_exclude_platforms(encrypted host rp2040 rp2350-riscv) - endif() + add_subdirectory_exclude_platforms(encrypted host rp2040 rp2350-riscv) else() # Assume picotool has no signing support, if no pico_mbedtls available message("Skipping encrypted bootloader example as pico_mbedtls unavailable") diff --git a/bootloaders/encrypted/CMakeLists.txt b/bootloaders/encrypted/CMakeLists.txt index f29f0efe2..6ad33f055 100644 --- a/bootloaders/encrypted/CMakeLists.txt +++ b/bootloaders/encrypted/CMakeLists.txt @@ -1,26 +1,17 @@ # Encrypted Bootloader add_executable(enc_bootloader enc_bootloader.c - aes.S + mbedtls_aes.c ) -# Add command to update otp.json if privateaes.bin changes -add_custom_command(OUTPUT ${CMAKE_CURRENT_LIST_DIR}/otp.json - COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_LIST_DIR}/update-key.cmake" - DEPENDS ${CMAKE_CURRENT_LIST_DIR}/privateaes.bin) -# Copy that otp.json file to build directory -add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/otp.json - COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_LIST_DIR}/otp.json" "${CMAKE_CURRENT_BINARY_DIR}/otp.json" - DEPENDS ${CMAKE_CURRENT_LIST_DIR}/otp.json) -add_custom_target(otp_json DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/otp.json) -add_dependencies(enc_bootloader otp_json) - # pull in common dependencies -target_link_libraries(enc_bootloader pico_stdlib pico_rand) +target_link_libraries(enc_bootloader pico_stdlib pico_rand pico_mbedtls) # use stack guards, as AES variables are written near the stack target_compile_definitions(enc_bootloader PRIVATE PICO_USE_STACK_GUARDS=1) +target_include_directories(enc_bootloader PRIVATE ${CMAKE_CURRENT_LIST_DIR}) + # set as no_flash binary pico_set_binary_type(enc_bootloader no_flash) @@ -49,9 +40,6 @@ endfunction() # create linker script to run from 0x20070000 add_linker_script(enc_bootloader "0x20070000" "64k") -# configure otp output -pico_set_otp_key_output_file(enc_bootloader ${CMAKE_CURRENT_BINARY_DIR}/otp.json) - # sign, hash, and clear SRAM pico_sign_binary(enc_bootloader ${CMAKE_CURRENT_LIST_DIR}/private.pem) pico_hash_binary(enc_bootloader) @@ -62,7 +50,10 @@ pico_embed_pt_in_binary(enc_bootloader ${CMAKE_CURRENT_LIST_DIR}/enc-pt.json) # create absolute uf2, and package in flash pico_set_uf2_family(enc_bootloader "absolute") -pico_package_uf2_output(enc_bootloader 0x10000000) +pico_package_uf2_output(enc_bootloader) + +# optionally enable USB output in addition to UART +# pico_enable_stdio_usb(enc_bootloader 1) # create map/bin/hex/uf2 file etc. pico_add_extra_outputs(enc_bootloader) @@ -86,13 +77,19 @@ pico_set_binary_type(hello_serial_enc no_flash) # create linker script to ensure it doesn't overwrite the bootloader at 0x20070000 add_linker_script(hello_serial_enc "0x20000000" "448k") +# configure otp output +pico_set_otp_key_output_file(hello_serial_enc ${CMAKE_CURRENT_BINARY_DIR}/otp.json) + # sign, hash, and encrypt pico_sign_binary(hello_serial_enc ${CMAKE_CURRENT_LIST_DIR}/private.pem) pico_hash_binary(hello_serial_enc) -pico_encrypt_binary(hello_serial_enc ${CMAKE_CURRENT_LIST_DIR}/privateaes.bin) +pico_encrypt_binary(hello_serial_enc ${CMAKE_CURRENT_LIST_DIR}/privateaes.bin ${CMAKE_CURRENT_LIST_DIR}/ivsalt.bin) # package uf2 in flash -pico_package_uf2_output(hello_serial_enc 0x10000000) +pico_package_uf2_output(hello_serial_enc) + +# optionally enable USB output in addition to UART +# pico_enable_stdio_usb(hello_serial_enc 1) # create map/bin/hex/uf2 file etc. pico_add_extra_outputs(hello_serial_enc) diff --git a/bootloaders/encrypted/README.md b/bootloaders/encrypted/README.md index f079d9469..f29f5194f 100644 --- a/bootloaders/encrypted/README.md +++ b/bootloaders/encrypted/README.md @@ -1,15 +1,34 @@ -Replace private.pem and privateaes.bin with your own keys - your signing key must be for the _secp256k1_ curve, in PEM format. You can create a .PEM file with: +For security you **must** replace private.pem and privateaes.bin with your own keys, and ivsalt.bin with your own per-device salt. Make sure you **don't lose your keys and salts**, else you may not be able to update the code on your device. + +This bootloader uses MbedTLS for decryption, so it is not secure against side channel attacks and therefore only offers limited protection against physical attackers. + +Your signing key must be for the _secp256k1_ curve, in PEM format. You can create a .PEM file with: ```bash openssl ecparam -name secp256k1 -genkey -out private.pem ``` -The AES key is just be a 32 byte binary file - you can create one with +The AES key is stored in a 32 byte binary file - you can create one with ```bash dd if=/dev/urandom of=privateaes.bin bs=1 count=32 ``` +or in Powershell 7 +```powershell +[byte[]] $(Get-SecureRandom -Maximum 256 -Count 32) | Set-Content privateaes.bin -AsByteStream +``` + +The IV salt is just a 16 byte binary file - you can create it the same way, replacing `32` with `16` and `privateaes.bin` with `ivsalt.bin` in the commands above. + +You will need to program your OTP using the `otp.json` file generated by the build in your build folder +NOTE: This will enable secure boot on your device, so only correctly signed binaries can then run, and will also lock down the OTP pages the AES key and IV salt are stored in. +```bash +picotool otp load otp.json +``` + +> For more information on security see chapter 10 of the [RP2350 datasheet](https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf), and for information on how to sign other binaries to run on a secure chip see section 5.10 + Then either drag & drop the UF2 files to the device in order (enc_bootloader first, then hello_serial_enc) waiting for a reboot in-between, or run ```bash picotool load enc_bootloader.uf2 diff --git a/bootloaders/encrypted/aes.S b/bootloaders/encrypted/aes.S deleted file mode 100644 index feccaae68..000000000 --- a/bootloaders/encrypted/aes.S +++ /dev/null @@ -1,1698 +0,0 @@ -.syntax unified -.cpu cortex-m33 -.thumb - -#include "hardware/platform_defs.h" -#include "hardware/regs/addressmap.h" -#include "hardware/regs/sha256.h" -#include "hardware/rcp.h" - -#include "config.h" - -.global delay -.global aes_start -.global aes_end -.global flush_reg -.global isr_systick -.extern systick_data - -.global gen_lut_inverse -.global gen_lut_sbox -.if NEED_INV_ROUNDS -.global gen_lut_inv_sbox -.endif - -.if INCLUDE_ENCRYPT_CBC -.global cbc_encrypt_s -.endif -.if INCLUDE_DECRYPT_CBC -.global cbc_decrypt_s -.endif -.if INCLUDE_CRYPT_CTR -.global ctr_crypt_s -.endif - -.global remap -.global gen_rand -.global init_key - -.global rkey_s -.global lut_a,lut_a_map -.global lut_b,lut_b_map -.global rstate - -@ RCP macros - -#define CTAG0 0x2a -#define CTAG1 0x2b -#define CTAG2 0x2c -#define CTAG3 0x2d -#define CTAG4 0x2e -#define CTAG5 0x30 -#define CTAG6 0x31 -#define CTAG7 0x32 -#define CTAG8 0x33 -#define CTAG9 0x34 -#define CTAG10 0x35 -#define CTAG11 0x36 -#define CTAG12 0x37 -#define CTAG13 0x38 -#define CTAG14 0x39 -#define CTAG15 0x3a -#define CTAG16 0x3b -#define CTAG17 0x3c - -.macro SET_COUNT n -.if RC_COUNT -.if RC_JITTER - rcp_count_set \n -.else - rcp_count_set_nodelay \n -.endif -.endif -.endm - -.macro CHK_COUNT n -.if RC_COUNT -.if RC_JITTER - rcp_count_check \n -.else - rcp_count_check_nodelay \n -.endif -.endif -.endm - -.macro GET_CANARY rx,tag -.if RC_CANARY -.if RC_JITTER - rcp_canary_get \rx,\tag -.else - rcp_canary_get_nodelay \rx,\tag -.endif -.endif -.endm - -.macro CHK_CANARY rx,tag -.if RC_CANARY -.if RC_JITTER - rcp_canary_check \rx,\tag -.else - rcp_canary_check_nodelay \rx,\tag -.endif -.endif -.endm - -.macro GET_CANARY_NJ rx,tag @ with no jitter even if you ask for it (otherwise slows down gen_rand a lot) -.if RC_CANARY - rcp_canary_get_nodelay \rx,\tag -.endif -.endm - -.macro CHK_CANARY_NJ rx,tag @ with no jitter even if you ask for it -.if RC_CANARY - rcp_canary_check_nodelay \rx,\tag -.endif -.endm - -.section .stack.aes -@ Regardless of configuration the code uses a single 256-entry LUT. If both -@ encryption and decryption are enabled then this is a table of inverses -@ of GF(2⁸) field elements, from which both the S-box and inverse S-box -@ functions can be derived; otherwise it can be a simple inverse S-box -@ table. -@ In either case the LUT is represented as two shares, lut_a and lut_b, -@ whose values must be EORed. Furthermore, the contents of each share are -@ scambled according to a 4-byte "map". The map comprises two bytes that -@ are EORed into the addressing of the share, and two bytes that are -@ EORed into the data read back from the share. Performing a lookup -@ of a value x involves computing -@ lut_a[x ^ a₀ ^ a₁] ^ c₀ ^ c₁ ^ lut_b[x ^ b₀ ^ b₁] ^ d₀ ^ d₁ -@ where a₀, a₁, c₀ and c₁ are the "map" of the lut_a share and -@ b₀, b₁, d₀ and d₁ are the "map" of the lut_b share. -@ In practice the result of a lookup is itself represented in two -@ shares, namely -@ lut_a[x ^ a₀ ^ a₁] ^ c₀ ^ d₀ and -@ lut_b[x ^ b₀ ^ b₁] ^ c₁ ^ d₁ -lut_a: @ LUT share A -.space 256 -lut_a_map: @ the current scrambling of lut_a; not particularly secret since it can be deduced from the contents of lut_a and lut_b -.space 4 -.space 4 @ align to multiple of 8 -lut_b: @ LUT share B -.space 256 -lut_b_map: -.space 4 -.space 4 @ align to multiple of 8 -rkey_s: @ round key shares -.if RK_ROR -.space 600 -.else -.space 480 -.endif -.if CT_BPERM -ctr_scratch: @ scratch area for CTR code to use when "decrypting" out-of-range blocks -.space 16 -.endif -rstate: @ SHA random state, to be initialised to TRNG bytes; zeroth byte must be initialised to zero -.space 16 - -.section .text.aes,"ax",%progbits - -.thumb_func -aes_start: - nop - -.if GEN_RAND_SHA -.balign 4 -.thumb_func -@ random numbers using SHA256 hardware -@ preserves r1-r3 -gen_rand: - GET_CANARY_NJ r0,CTAG1 - push {r0-r3,r14} - ldr r0,=#SHA256_BASE -4: - ldr r2,=#rstate - ldrb r1,[r2] @ get word counter from bottom byte of rstate[] (offset into SUM registers) - subs r3,r1,#4 @ decrement it to previous SUM register - ble 1f @ if the offset was 4 or less we have run out of SUM register values -.if SHA256_SUM0_OFFSET!=8 -.err -.endif -2: - ldr r0,[r0,r1] @ read value from SUM register: note that this relies on SHA256_SUM0_OFFSET==8 - strb r3,[r2] @ save updated SUM register offset in bottom byte of rstate[] - pop {r1} - CHK_CANARY_NJ r1,CTAG1 - pop {r1-r3,r15} - -1: - movs r3,#SHA256_SUM6_OFFSET+1 - strb r3,[r2] @ reset word counter: the +1 is compensated for later - movw r1,#(1<r5-r6->r7->r4 etc. in constant time -@ r0: b0..1: rotate amount -@ returns r0 value required to undo -@ preserves r2 - and r1,r0,#2 - rsbs r1,r1,#0 @ 0 or fffffffe depending on b1 of r0 - uadd8 r1,r1,r1 @ set/clear all GE flags according to b1 of r0: set if rotate of two places is required - mov r1,r4 - sel r4,r6,r4 - sel r6,r1,r6 - mov r1,r5 - sel r5,r7,r5 - sel r7,r1,r7 - mov r1,r8 - sel r8,r10,r8 - sel r10,r1,r10 - mov r1,r9 - sel r9,r11,r9 - sel r11,r1,r11 - and r1,r0,#1 - rsbs r1,r1,#0 @ 0 or ffffffff depending on b0 of r0 - uadd8 r1,r1,r1 @ set/clear all GE flags according to b0 of r0: set if rotate of one place is required - mov r1,r4 - sel r4,r5,r4 - sel r5,r6,r5 - sel r6,r7,r6 - sel r7,r1,r7 - mov r1,r8 - sel r8, r9 ,r8 - sel r9, r10 ,r9 - sel r10,r11,r10 - sel r11,r1 ,r11 - rsbs r0,r0,#0 @ generate control value for inverse operation - bx r14 -.endif - -.if IK_SHUFREAD -@ randomly shuffle an array n bytes long, n≤65536 a power of 2, by performing k random exchanges, k>0 -@ r0: array pointer p -@ r1: n -@ r2: k -@ does not need to be a subroutine!!! -array_shuf: - push {r4-r6,r14} - mov r4,r0 - subs r5,r1,#1 @ mask for random number generation - mov r6,r2 -1: - bl gen_rand - and r1,r5,r0,lsr#16 - and r0,r5,r0 @ r0,r1 are two random numbers 0..n-1 - ldrb r2,[r4,r0] - ldrb r3,[r4,r1] - strb r3,[r4,r0] - strb r2,[r4,r1] - subs r6,r6,#1 - bne 1b - pop {r4-r6,r15} -.endif - -@ "refresh" shares of rkeys by random eor into both shares of each word -.if RK_ROR -@ and randomly change rotate amount on each word of each share -.endif -@ preserves r0-r11 -.balign 4 -ref_round_keys_s: - push {r14} - GET_CANARY r14,CTAG4 - push {r0-r11,r14} - ldr r0,=rkey_s - mov r1,#15 @ there are 15 expanded keys -1: -.if RK_ROR - ldmia r0,{r2-r11} - push {r0-r1} - - bl gen_rand @ xra=random extra rotates for share A - usub8 r6,r6,r0 @ ra-=xra bytewise - rors r2,r2,r0 @ a=ror(a,xra) - rev16 r0,r0 @ byte order 2301, i.e. B1 at the bottom - rors r3,r3,r0 @ a=ror(a,xra) - rev r0,r0 @ byte order 1032, i.e. B2 at the bottom - rors r4,r4,r0 @ a=ror(a,xra) - rev16 r0,r0 @ byte order 0123, i.e. B3 at the bottom - rors r5,r5,r0 @ a=ror(a,xra) - - bl gen_rand @ xrb=random extra rotates for share B - usub8 r11,r11,r0 @ rb-=xrb bytewise - rors r7,r7,r0 @ b=ror(b,xrb) - rev16 r0,r0 - rors r8,r8,r0 @ b=ror(b,xrb) - rev r0,r0 - rors r9,r9,r0 @ b=ror(b,xrb) - rev16 r0,r0 - rors r10,r10,r0 @ b=ror(b,xrb) - usub8 r1,r6,r11 @ ra-rb bytewise - - bl gen_rand @ xab=extra exclusive OR into shares - eors r2,r2,r0 @ a^=xab - rors r0,r0,r1 @ ror(xab,ra-rb) - eors r7,r7,r0 @ b^=ror(xab,ra-rb) - rev16 r1,r1 - - bl gen_rand @ xab - eors r3,r3,r0 @ a^=xab - rors r0,r0,r1 @ ror(xab,ra-rb) - eors r8,r8,r0 @ b^=ror(xab,ra-rb) - rev r1,r1 - - bl gen_rand @ xab - eors r4,r4,r0 @ a^=xab - rors r0,r0,r1 @ ror(xab,ra-rb) - eors r9,r9,r0 @ b^=ror(xab,ra-rb) - rev16 r1,r1 - - bl gen_rand @ xab - eors r5,r5,r0 @ a^=xab - rors r0,r0,r1 @ ror(xab,ra-rb) - eors r10,r10,r0 @ b^=ror(xab,ra-rb) - - pop {r0-r1} - stmia r0!,{r2-r11} -.else - ldmia r0,{r4-r11} @ EOR random data into the shares - push {r0-r1} - bl gen_rand - eor r4,r4,r0 - eor r8,r8,r0 - bl gen_rand - eor r5,r5,r0 - eor r9,r9,r0 - bl gen_rand - eor r6,r6,r0 - eor r10,r10,r0 - bl gen_rand - eor r7,r7,r0 - eor r11,r11,r0 - pop {r0-r1} - stmia r0!,{r4-r11} -.endif - subs r1,r1,#1 - bne 1b - pop {r0-r11,r14} - CHK_CANARY r14,CTAG4 - pop {r15} - -@ switch from non-shared to shared state -.balign 4 -ns_to_s: - push {r14} - GET_CANARY r14,CTAG5 - push {r0-r3,r14} - bl gen_rand - mov r8,r0 - bl gen_rand - mov r9,r0 - bl gen_rand - mov r10,r0 - bl gen_rand - mov r11,r0 - eors r4,r4,r8 - eors r5,r5,r9 - eors r6,r6,r10 - eors r7,r7,r11 - pop {r0-r3,r14} - CHK_CANARY r14,CTAG5 - pop {r15} - -.if NEED_ROUNDS -.balign 4 -.thumb_func -shift_rows_s: -@ first "rotate" the two most-significant bytes of the state by two registers -@ slightly faster (but not shorter?) with ubfx/bfi - eors r0,r4,r6 @ ta=state[0]^state[2]; ta&=0xffff0000; state[0]^=ta; state[2]^=ta; - lsrs r0,r0,#16 - lsls r0,r0,#16 - eors r4,r4,r0 - eors r6,r6,r0 - eors r0,r5,r7 @ ta=state[1]^state[3]; ta&=0xffff0000; state[1]^=ta; state[3]^=ta; - lsrs r0,r0,#16 - lsls r0,r0,#16 - eors r5,r5,r0 - eors r7,r7,r0 -@ next "rotate" the two odd-significance bytes of the state by one register - eors r1,r7,r4 @ tb=state[3]^state[0]; tb&=0xff00ff00; - ands r1,r1,#0xff00ff00 - eors r0,r4,r5 @ ta=state[0]^state[1]; ta&=0xff00ff00; state[0]^=ta; - ands r0,r0,#0xff00ff00 - eors r4,r4,r0 - eors r0,r5,r6 @ ta=state[1]^state[2]; ta&=0xff00ff00; state[1]^=ta; - ands r0,r0,#0xff00ff00 - eors r5,r5,r0 - eors r0,r6,r7 @ ta=state[2]^state[3]; ta&=0xff00ff00; state[2]^=ta; - ands r0,r0,#0xff00ff00 - eors r6,r6,r0 - eors r7,r7,r1 @ state[3]^=tb; -@ repeat for other share - eors r0,r8,r10 @ ta=state[0]^state[2]; ta&=0xffff0000; state[0]^=ta; state[2]^=ta; - lsrs r0,r0,#16 - lsls r0,r0,#16 - eors r8,r8,r0 - eors r10,r10,r0 - eors r0,r9,r11 @ ta=state[1]^state[3]; ta&=0xffff0000; state[1]^=ta; state[3]^=ta; - lsrs r0,r0,#16 - lsls r0,r0,#16 - eors r9,r9,r0 - eors r11,r11,r0 - - eors r1,r11,r8 @ tb=state[3]^state[0]; tb&=0xff00ff00; - ands r1,r1,#0xff00ff00 - eors r0,r8,r9 @ ta=state[0]^state[1]; ta&=0xff00ff00; state[0]^=ta; - ands r0,r0,#0xff00ff00 - eors r8,r8,r0 - eors r0,r9,r10 @ ta=state[1]^state[2]; ta&=0xff00ff00; state[1]^=ta; - ands r0,r0,#0xff00ff00 - eors r9,r9,r0 - eors r0,r10,r11 @ ta=state[2]^state[3]; ta&=0xff00ff00; state[2]^=ta; - ands r0,r0,#0xff00ff00 - eors r10,r10,r0 - eors r11,r11,r1 @ state[3]^=tb; - bx r14 -.endif - -.if NEED_INV_ROUNDS -.balign 4 -.thumb_func -inv_shift_rows_s: -@ first half is the same as shift_rows; halves could be done in opposite order for tail chain - eors r0,r4,r6 @ ta=state[0]^state[2]; ta&=0xffff0000; state[0]^=ta; state[2]^=ta; - lsrs r0,r0,#16 - lsls r0,r0,#16 - eors r4,r4,r0 - eors r6,r6,r0 - eors r0,r5,r7 @ ta=state[1]^state[3]; ta&=0xffff0000; state[1]^=ta; state[3]^=ta; - lsrs r0,r0,#16 - lsls r0,r0,#16 - eors r5,r5,r0 - eors r7,r7,r0 - - eors r1,r7,r4 @ tb=state[3]^state[0]; tb&=0xff00ff00; - ands r1,r1,#0xff00ff00 - eors r0,r6,r7 @ ta=state[2]^state[3]; ta&=0xff00ff00; state[3]^=ta; - ands r0,r0,#0xff00ff00 - eors r7,r7,r0 - eors r0,r5,r6 @ ta=state[1]^state[2]; ta&=0xff00ff00; state[2]^=ta; - ands r0,r0,#0xff00ff00 - eors r6,r6,r0 - eors r0,r4,r5 @ ta=state[0]^state[1]; ta&=0xff00ff00; state[1]^=ta; - ands r0,r0,#0xff00ff00 - eors r5,r5,r0 - eors r4,r4,r1 @ state[0]^=tb; - - eors r0,r8,r10 @ ta=state[0]^state[2]; ta&=0xffff0000; state[0]^=ta; state[2]^=ta; - lsrs r0,r0,#16 - lsls r0,r0,#16 - eors r8,r8,r0 - eors r10,r10,r0 - eors r0,r9,r11 @ ta=state[1]^state[3]; ta&=0xffff0000; state[1]^=ta; state[3]^=ta; - lsrs r0,r0,#16 - lsls r0,r0,#16 - eors r9,r9,r0 - eors r11,r11,r0 - - eors r1,r11,r8 @ tb=state[3]^state[0]; tb&=0xff00ff00; - ands r1,r1,#0xff00ff00 - eors r0,r10,r11 @ ta=state[2]^state[3]; ta&=0xff00ff00; state[3]^=ta; - ands r0,r0,#0xff00ff00 - eors r11,r11,r0 - eors r0,r9,r10 @ ta=state[1]^state[2]; ta&=0xff00ff00; state[2]^=ta; - ands r0,r0,#0xff00ff00 - eors r10,r10,r0 - eors r0,r8,r9 @ ta=state[0]^state[1]; ta&=0xff00ff00; state[1]^=ta; - ands r0,r0,#0xff00ff00 - eors r9,r9,r0 - eors r8,r8,r1 @ state[0]^=tb; - bx r14 -.endif - -@ multiply polynomial over GF(2⁸) by c(x) = 0x03x³ + 0x01x² + 0x01x + 0x02 modulo x⁴+1 -@ r0x00 is a register holding 0x00000000; r0x1b is a register holding 0x1b1b1b1b -.macro mixcol rx,rt,ru,r0x00,r0x1b - @ let rx=(a,b,c,d) - uadd8 \rt,\rx,\rx @ MSB of each byte into the GE flags - sel \ru,\r0x1b,\r0x00 @ get bytewise correction for bytewise field multiplication by 2 - eors \rt,\rt,\ru @ (2a,2b,2c,2d) - - eors \ru,\rt,\rx @ (3a,3b,3c,3d) - eors \rt,\rt,\rx,ror#24 @ (2a+b,2b+c,2c+d,2d+a) - eors \rt,\rt,\rx,ror#16 @ (2a+b+c,2b+c+d,2c+d+a,2d+a+b) - eors \rx,\rt,\ru,ror#8 @ (2a+b+c+3d,2b+c+d+3a,2c+d+a+3b,2d+a+b+3c) -.endm - -@ multiply polynomial over GF(2⁸) by d(x) = 0x0Bx³ + 0x0Dx² + 0x09x + 0x0E modulo x⁴+1; c(x)d(x)=1 modulo x⁴+1 -.macro invmixcol rx,rt,ru,rv,rw,r0x00,r0x1b -@ !!! can probably save some registers, e.g. allow trashing of r0x00, r0x1b -@ can possibly also simplify slightly with refactorisation - uadd8 \rt,\rx,\rx @ field multiplication by 2 as above - sel \rw,\r0x1b,\r0x00 - eors \rt,\rt,\rw @ 2x - uadd8 \ru,\rt,\rt - sel \rw,\r0x1b,\r0x00 - eors \ru,\ru,\rw @ 4x - uadd8 \rv,\ru,\ru - sel \rw,\r0x1b,\r0x00 - eors \rv,\rv,\rw @ 8x - - eors \rx,\rx,\rv @ 9x - eors \rw,\rx,\rt @ 11x - eors \rw,\rw,\rx,ror#16 @ 11x ^ 9x ROL #16 - eors \rx,\rx,\ru @ 13x - eors \rw,\rw,\rx,ror#8 @ 11x ^ 9x ROL #16 ^ 13x ROL #24 - eors \rt,\rt,\ru @ 6x - eors \rt,\rt,\rv @ 14x - eors \rx,\rt,\rw,ror#8 @ 14x ^ 9x ROL #8 ^ 13x ROL #16 ^ 11x ROL #24 -.endm - -.if NEED_ROUNDS -.balign 4 -.thumb_func -mix_cols_s: - mov r2,#0x00000000 - mov r3,#0x1b1b1b1b - mixcol r4 ,r0,r1,r2,r3 @ apply mixcol to each state word - mixcol r5 ,r0,r1,r2,r3 - mixcol r6 ,r0,r1,r2,r3 - mixcol r7 ,r0,r1,r2,r3 - mixcol r8 ,r0,r1,r2,r3 - mixcol r9 ,r0,r1,r2,r3 - mixcol r10,r0,r1,r2,r3 - mixcol r11,r0,r1,r2,r3 - bx r14 -.endif - -.if NEED_INV_ROUNDS -.balign 4 -.thumb_func -inv_mix_cols_s: - push {r14} - GET_CANARY r14,CTAG6 - push {r14} - mov r12,#0x00000000 - mov r14,#0x1b1b1b1b - invmixcol r4 ,r0,r1,r2,r3,r12,r14 @ apply invmixcol to each state word - invmixcol r5 ,r0,r1,r2,r3,r12,r14 - invmixcol r6 ,r0,r1,r2,r3,r12,r14 - invmixcol r7 ,r0,r1,r2,r3,r12,r14 - invmixcol r8 ,r0,r1,r2,r3,r12,r14 - invmixcol r9 ,r0,r1,r2,r3,r12,r14 - invmixcol r10,r0,r1,r2,r3,r12,r14 - invmixcol r11,r0,r1,r2,r3,r12,r14 - pop {r14} - CHK_CANARY r14,CTAG6 - pop {r15} -.endif - -.if SBOX_VIA_INV -@ bytewise EOR-convolution with constant 0x1f -.macro conv_0x1f rx,rt,ru - eors \rt,\rx,\rx,ror#31 @ t=x^ROL(x,1); - eors \rt,\rt,\rt,ror#30 @ t=t^ROL(t,2); - eors \rt,\rt,\rx,ror#28 @ t=t^ROL(x,4); @ convolution with byte boundaries "trashed" - ands \ru,\rx,#0xf0f0f0f0 @ u=x&0xf0f0f0f0; - eors \ru,\ru,\ru,ror#31 @ u=u^ROL(u,1); - eors \ru,\ru,\ru,ror#30 @ u=u^ROL(u,2); - ands \ru,\ru,#0x87878787 @ u=u&0x87878787; @ compensation for trashing - eors \ru,\ru,\ru,ror#24 @ u=u^ROL(u,8); - eors \rx,\rt,\ru,ror#7 @ t^=ROR(u,7); @ with trashing fixed -.endm - -@ bytewise EOR-convolution with constant 0x4a -.macro conv_0x4a rx,rt,ru - eors \rt,\rx,\rx,ror#30 @ t=x^ROL(x,2); - eors \rt,\rt,\rx,ror#27 @ t=t^ROL(x,5); - ands \ru,\rx,#0xf8f8f8f8 @ u=x&0xf8f8f8f8; - eors \ru,\ru,\ru,ror#29 @ u=u^ROL(u,3); - ands \ru,\ru,#0xc7c7c7c7 @ u=u&0xc7c7c7c7; - eors \ru,\ru,\ru,ror#24 @ u=u^ROL(u,8); - eors \rt,\rt,\ru,ror#6 @ t^=ROR(u,6); - ands \ru,\rt,#0x80808080 @ t=rorbytes(t,7); - uadd8 \rt,\rt,\rt - orrs \rx,\rt,\ru,lsr#7 -.endm - -.balign 4 -.thumb_func -map_sbox_s: - push {r14} - GET_CANARY r14,CTAG7 - push {r14} - bl lutmap_state_s @ the S-box function is an inverse followed by an affine transformation: - conv_0x1f r4 ,r0,r1 @ see https://en.wikipedia.org/wiki/Rijndael_S-box - conv_0x1f r5 ,r0,r1 - conv_0x1f r6 ,r0,r1 - conv_0x1f r7 ,r0,r1 - conv_0x1f r8 ,r0,r1 - conv_0x1f r9 ,r0,r1 - conv_0x1f r10,r0,r1 - conv_0x1f r11,r0,r1 - eor r4 ,r4 ,#0xcacacaca @ scramble the shares slightly: 0x63=0xca^0xa9 etc. - eor r5 ,r5 ,#0xf5f5f5f5 - eor r6 ,r6 ,#0x0c0c0c0c - eor r7 ,r7 ,#0xa2a2a2a2 - eor r8 ,r8 ,#0xa9a9a9a9 - eor r9 ,r9 ,#0x96969696 - eor r10,r10,#0x6f6f6f6f - eor r11,r11,#0xc1c1c1c1 - pop {r14} - CHK_CANARY r14,CTAG7 - pop {r15} - -.if NEED_INV_ROUNDS -.balign 4 -.thumb_func -inv_map_sbox_s: - push {r14} - GET_CANARY r14,CTAG8 - push {r14} @ similarly, the inverse S-box is an affine transformation followed by an inverse - conv_0x4a r4 ,r0,r1 - conv_0x4a r5 ,r0,r1 - conv_0x4a r6 ,r0,r1 - conv_0x4a r7 ,r0,r1 - conv_0x4a r8 ,r0,r1 - conv_0x4a r9 ,r0,r1 - conv_0x4a r10,r0,r1 - conv_0x4a r11,r0,r1 - eor r4 ,r4 ,#0xd1d1d1d1 @ scramble the shares slightly: 0x05=0xd1^0xd4 etc. - eor r5 ,r5 ,#0x94949494 - eor r6 ,r6 ,#0xfcfcfcfc - eor r7 ,r7 ,#0x3a3a3a3a - eor r8 ,r8 ,#0xd4d4d4d4 - eor r9 ,r9 ,#0x91919191 - eor r10,r10,#0xf9f9f9f9 - eor r11,r11,#0x3f3f3f3f - bl lutmap_state_s - pop {r14} - CHK_CANARY r14,CTAG8 - pop {r15} -.endif - -.else - -.balign 4 -.thumb_func -gen_lut_sbox: -@ set both lut_a and lut_b to the S-box table -@ returns r0=lut_a+256, r1=lut_b+256 - push {r14} - GET_CANARY r14,CTAG9 - push {r14} @ similarly, the inverse S-box is an affine transformation followed by an inverse - bl gen_lut_inverse @ first generate the table of inverses in lut_a - mov r14,#256 -1: - ldrb r2,[r0] - eors r3,r2,r2,lsl#1 @ convolve byte with 0x1f - eors r3,r3,r3,lsl#2 - eors r3,r3,r2,lsl#4 - eors r2,r3,r3,lsr#8 - eor r2,r2,#0x63 @ and add 0x63 - strb r2,[r0],#1 - strb r2,[r1],#1 - subs r14,r14,#1 - bne 1b - pop {r14} - CHK_CANARY r14,CTAG9 - pop {r15} - -.if NEED_INV_ROUNDS -.balign 4 -.thumb_func -gen_lut_inv_sbox: -@ set lut_a to the inverse S-box table - push {r14} - GET_CANARY r14,CTAG10 - push {r14} - bl gen_lut_sbox @ get the forwards S-box - sub r0,r0,#256 - sub r1,r1,#256 - mov r2,#0 -1: - ldrb r3,[r1],#1 @ get y=S-box(x)... - strb r2,[r0,r3] @ ... and store x at location y - adds r2,r2,#1 - cmp r2,#255 - bls 1b - pop {r14} - CHK_CANARY r14,CTAG10 - pop {r15} -.endif -.endif - -@ if we are using direct S-box lookup then [inv_]map_sbox_s is the same as lutmap_state_s -.if !SBOX_VIA_INV -.balign 4 -.thumb_func -map_sbox_s: -.if NEED_INV_ROUNDS -.thumb_func -inv_map_sbox_s: -.endif -.endif - -@ map all bytes of the state through the LUT -.balign 4 -lutmap_state_s: - push {r14} - GET_CANARY r14,CTAG11 - push {r14} - ldr r12,=lut_a - ldr r14,=lut_b - mov r0,#0x8000 @ "counter" for bytes of state mapped -1: - ldr r3,[r12,#0x100] @ lut_a_map - eor r1,r4,r3 @ share A of x ^ share A of lut_a address map - eor r1,r1,r8 @ ^ share B of x - eor r1,r1,r3,ror#8 @ ^ share B of lut_a address map - uxtb r1,r1 - ldrb r1,[r12,r1] @ look up in lut_a - eor r1,r1,r3,ror#16 @ ^ share A of lut_a data map - ldr r3,[r14,#0x100] @ lut_b_map - eor r1,r1,r3,ror#24 @ ^ share B of lut_b data map, generating share A of the result - - eor r2,r4,r3 @ share A of x ^ share A of lut_b address map - eor r2,r2,r8 @ ^ share B of x - eor r2,r2,r3,ror#8 @ ^ share B of lut_b address map - uxtb r2,r2 - ldrb r2,[r14,r2] @ look up in lut_b - eor r2,r2,r3,ror#16 @ ^ share A of lut_b data map - ldr r3,[r12,#0x100] @ lut_a_map - eor r2,r2,r3,ror#24 @ ^ share B of lut_a data map, generating share B of the result - - lsrs r4,#8 @ shift share A of state down one byte... - orrs r4,r4,r5,lsl#24 - lsrs r5,#8 - orrs r5,r5,r6,lsl#24 - lsrs r6,#8 - orrs r6,r6,r7,lsl#24 - lsrs r7,#8 - orrs r7,r7,r1,lsl#24 @ and insert share A of mapped byte - - lsrs r8,#8 @ shift share B of state down one byte... - orrs r8,r8,r9,lsl#24 - lsrs r9,#8 - orrs r9,r9,r10,lsl#24 - lsrs r10,#8 - orrs r10,r10,r11,lsl#24 - lsrs r11,#8 - orrs r11,r11,r2,lsl#24 @ and insert share B of mapped byte - - lsrs r0,#1 @ count 16 iterations - bne 1b - pop {r14} - CHK_CANARY r14,CTAG11 - pop {r15} - -@ perform one EOR step in round key generation -@ !!! can we introduce some more randomness into the shares here? -.balign 4 -grk_s_step: - ldmia r0!,{r5-r7,r12} @ from last round key_a but one - eors r5,r5,r4 - eors r6,r6,r5 - eors r7,r7,r6 - eors r12,r12,r7 - stmia r1!,{r5-r7,r12} - mov r4,r12 -.if RK_ROR - movs r12,#0 - str r12,[r0],#4 - str r12,[r1],#4 -.endif - ldmia r0!,{r9-r11,r12} @ from last round key_a but one - eors r9,r9,r8 - eors r10,r10,r9 - eors r11,r11,r10 - eors r12,r12,r11 - stmia r1!,{r9-r11,r12} - mov r8,r12 -.if RK_ROR - movs r12,#0 - str r12,[r0],#4 - str r12,[r1],#4 -.endif - bx r14 - -.macro jitter rx -.if IK_JITTER - rors \rx,\rx,#1 - bcc \@f -\@: -.else -@ nothing -.endif -.endm - -.balign 4 -.thumb_func -init_key: -@ r0: rkeys_s -@ r1: raw key data (32 bytes) -.if RK_ROR -@ rkeys_s is a 40*15=600-byte region -@ each of the 15 round keys is represented as two 4-word regions rka[0..3] and rkb[0..3], each of which is followed by a word containing -@ four byte-wide rotate values ra[i] and rb[i] -@ such that rk[i]=(rka[i] ROR ra[i])^(rkb[i] ROR rb[i]) gives the round keys -@ rotations always operate mod 32, so we do not bother to mask the rotate amounts to 5 bits -.else -@ rkeys_s is a 32*15=480-byte region -@ each of the 15 round keys is represented as two 4-word regions rka[0..3] and rkb[0..3] -@ such that rk[i]=rka[i]^rkb[i] gives the round keys -.endif - GET_CANARY r12,CTAG12 - push {r4-r12,r14} -.if IK_JITTER - push {r0,r1} - bl gen_rand - mov r12,r0 - pop {r0,r1} -.endif - jitter r12 - mov r4,r0 - mov r5,r1 -.if IK_SHUFREAD - SET_COUNT 73 - add r6,r4,#128 @ use 64 bytes of temporary space at r0+128 for buf - mov r7,#0 -1: - bl gen_rand - and r0,r0,#0x1f - strb r0,[r6,#32] @ buf contains each number 0..31 and 32 more random numbers in that range - strb r7,[r6],#1 @ so each number at least once... - adds r7,r7,#1 - cmp r7,#32 - bne 1b - CHK_COUNT 73 - add r0,r4,#128 - mov r10,r0 - movs r1,#64 - movs r2,#200 - bl array_shuf @ ... in a random order - mov r11,#63 - CHK_COUNT 74 -.else - mov r6,#31 -.endif -1: - SET_COUNT 104 - jitter r12 -.if IK_SHUFREAD - ldrb r6,[r10,r11] @ now process the raw key bytes in the order given by buf, some more than once -.endif - lsrs r8,r6,#4 -.if RK_ROR - add r7,r6,r8,lsl#3 - add r7,r7,r8,lsl#4 @ 0..15 -> 0..15, 16..31 -> 40..55 -.else - add r7,r6,r8,lsl#4 @ 0..15 -> 0..15, 16..31 -> 32..47 -.endif - ldrb r9,[r5,r6] @ fetch key byte - bl gen_rand @ make random shares of round key 0 - CHK_COUNT 104 - eor r9,r9,r0 - strb r9,[r4,r7] -.if RK_ROR - adds r7,#20 -.else - adds r7,#16 -.endif - strb r0,[r4,r7] -.if IK_SHUFREAD - subs r11,r11,#1 -.else - subs r6,r6,#1 -.endif - CHK_COUNT 105 - bpl 1b - CHK_COUNT 106 - mov r0,r4 -.if RK_ROR - movs r1,#0 - str r1,[r0,#16] - str r1,[r0,#36] -.endif -@ now generate the other round keys - movs r2,#1 @ round constant -.if RK_ROR - add r1,r0,#80 - ldr r4,[r0,#52] @ last word from previous round key_a - ldr r8,[r0,#72] @ last word from previous round key_b -.else - add r1,r0,#64 - ldr r4,[r0,#44] @ last word from previous round key_a - ldr r8,[r0,#60] @ last word from previous round key_b -.endif - CHK_COUNT 107 -1: - SET_COUNT 42 - rors r4,r4,#8 - rors r8,r8,#8 - push {r0-r3} -.if IK_JUNK - bl gen_rand @ put some junk in r5-r7, r9-r11 - mov r5,r0 - bl gen_rand - mov r6,r0 - bl gen_rand - mov r7,r0 - bl gen_rand - mov r9,r0 - bl gen_rand - mov r10,r0 - bl gen_rand - mov r11,r0 -.endif - CHK_COUNT 42 -.if IK_REMAP - bl remap -.endif - CHK_COUNT 43 -.if IK_PERM - bl gen_rand - bl vperm - push {r0} - bl gen_rand - bl hperm - push {r0} - bl map_sbox_s @ this actually maps all of r4..r7, r8..r11 - i.e., trashes r5, r6, r7, r9, r10, r11 - pop {r0} - bl hperm - pop {r0} - bl vperm -.else - bl map_sbox_s @ this actually maps all of r4..r7, r8..r11 - i.e., trashes r5, r6, r7, r9, r10, r11 -.endif - CHK_COUNT 44 - pop {r0-r3} - eors r4,r4,r2 @ round constant - bl grk_s_step - CHK_COUNT 45 - lsls r2,#1 @ step round constant - cmp r2,#0x40 @ done? - bhi 2f - push {r0-r2} - bl map_sbox_s @ this actually maps all of r4..r7, r8..r11 - i.e., trashes r5, r6, r7, r9, r10, r11 - CHK_COUNT 46 - pop {r0-r2} - bl grk_s_step - CHK_COUNT 47 - b 1b -2: - CHK_COUNT 46 - pop {r4-r12,r14} - CHK_CANARY r12,CTAG12 - bx r14 - -@ add the round key shares pointed to by r12 into the state shares -.balign 4 -addrkey_s: - push {r14} - GET_CANARY r14,CTAG13 - push {r0-r3,r14} -.if RK_ROR - ldmia r12!,{r0-r3,r14} @ share A of round key + ROR data - rors r0,r0,r14 @ ROR first word - eors r4,r4,r0 @ add to state - rev16 r0,r14 @ move byte 1 of ROR data into byte 0 - rors r1,r1,r0 - eors r5,r5,r1 - rev r0,r0 @ move byte 2 of ROR data into byte 0 - rors r2,r2,r0 - eors r6,r6,r2 - rev16 r0,r0 @ move byte 3 of ROR data into byte 0 - rors r3,r3,r0 - eors r7,r7,r3 -.else - ldmia r12!,{r0-r3} @ share A of round key - eors r4,r4,r0 - eors r5,r5,r1 - eors r6,r6,r2 - eors r7,r7,r3 -.endif -.if RK_ROR - ldmia r12!,{r0-r3,r14} @ share B of round key + ROR data - rors r0,r0,r14 @ ROR first word - eors r8,r8,r0 @ etc., as above - rev16 r0,r14 - rors r1,r1,r0 - eors r9,r9,r1 - rev r0,r0 - rors r2,r2,r0 - eors r10,r10,r2 - rev16 r0,r0 - rors r3,r3,r0 - eors r11,r11,r3 -.else - ldmia r12!,{r0-r3} @ share B of round key - eors r8 ,r8 ,r0 - eors r9 ,r9 ,r1 - eors r10,r10,r2 - eors r11,r11,r3 -.endif - pop {r0-r3,r14} - CHK_CANARY r14,CTAG13 - pop {r15} - -.if NEED_ROUNDS - -@ perform encryption rounds -@ r4-r7, r8-r11: state -@ preserves r0-r3,r12 -.balign 4 -rounds_s: - push {r14} - GET_CANARY r14,CTAG14 - push {r0-r3,r12,r14} - mov r2,#0 @ round counter -1: - ldr r12,=rkey_s - add r12,r12,r2,lsl#5 @ pointer to key shares for this round -.if RK_ROR - add r12,r12,r2,lsl#3 -.endif - bl addrkey_s -.if ST_VPERM - bl gen_rand - bl vperm @ V shuffle -.endif - push {r0,r2} @ save round count -.if ST_HPERM - bl gen_rand - bl hperm @ H shuffle - push {r0} -.endif - bl map_sbox_s -.if ST_HPERM - pop {r0} - bl hperm @ undo H shuffle -.endif - bl shift_rows_s - ldr r2,[r13,#4] @ increment round counter on stack - adds r2,r2,#1 - str r2,[r13,#4] - cmp r2,#14 - beq 2f @ break from loop? (last round has no mix_cols) - bl mix_cols_s - pop {r0,r2} -.if ST_VPERM - bl vperm @ undo V shuffle -.endif - b 1b -2: -@ bl inv_mix_cols_s @ or could skip in last round above - pop {r0,r2} -.if ST_VPERM - bl vperm @ undo V shuffle -.endif -.if RK_ROR - ldr r12,=rkey_s+14*40 @ final round key shares -.else - ldr r12,=rkey_s+14*32 @ final round key shares -.endif - bl addrkey_s - pop {r0-r3,r12,r14} - CHK_CANARY r14,CTAG14 - pop {r15} -.endif - -.if NEED_INV_ROUNDS -@ perform decryption rounds -@ r4-r7, r8-r11: state -@ preserves r0-r2 -.balign 4 -inv_rounds_s: - push {r14} - GET_CANARY r14,CTAG15 - push {r0-r2,r14} -.if RK_ROR - ldr r12,=rkey_s+14*40 @ final round key shares -.else - ldr r12,=rkey_s+14*32 @ final round key shares -.endif - bl addrkey_s - mov r2,#13 @ round counter - push {r2} -.if ST_VPERM - bl gen_rand - bl vperm @ V shuffle - push {r0} -.endif - b 2f @ into middle of loop (last round has no mix_cols) -1: - push {r2} -.if ST_VPERM - bl gen_rand - bl vperm @ V shuffle - push {r0} -.endif - bl inv_mix_cols_s -2: - bl inv_shift_rows_s -.if ST_HPERM - bl gen_rand - bl hperm @ H shuffle - push {r0} -.endif - bl inv_map_sbox_s -.if ST_HPERM - pop {r0} - bl hperm @ undo H shuffle -.endif -.if ST_VPERM - pop {r0} - bl vperm @ undo V shuffle -.endif - pop {r2} - ldr r12,=rkey_s - add r12,r12,r2,lsl#5 @ pointer to key shares for this round -.if RK_ROR - add r12,r12,r2,lsl#3 -.endif - bl addrkey_s - subs r2,r2,#1 - bpl 1b - pop {r0-r2,r14} - CHK_CANARY r14,CTAG15 - pop {r15} -.endif - -.if INCLUDE_ENCRYPT_CBC -.balign 4 -.thumb_func -@ encrypt data in place -@ r0: ivec -@ r1: buf -@ r2: number of blocks -@ this implementation does not scramble the shares properly; consider a better implementation -@ if security is required in encryption -cbc_encrypt_s: - push {r14} - GET_CANARY r14,CTAG16 - push {r4-r11,r14} - ldmia r0,{r4-r7} @ load iv into share a -2: - ldmia r1,{r8-r11} @ load plaintext into share b - bl rounds_s - eor r4,r4,r8 @ convert shared to non-shared - eor r5,r5,r9 - eor r6,r6,r10 - eor r7,r7,r11 - stmia r1!,{r4-r7} - subs r2,r2,#1 - bne 2b - pop {r4-r11,r14} - CHK_CANARY r14,CTAG16 - pop {r15} -.endif - -.if INCLUDE_DECRYPT_CBC -.balign 4 -.thumb_func -@ decrypt data in place -@ r0: ivec -@ r1: buf -@ r2: number of blocks -@ return -@ r0=0 OK -@ r0=1: fault detected -@ could be simplified to use more ldmia:s at the cost of another 8 words of stack -cbc_decrypt_s: - push {r14} - GET_CANARY r14,CTAG17 - push {r4-r11,r14} - ldmia r0,{r4-r7} @ load IV - bl ns_to_s - push {r4-r11} @ IV shares on the stack -2: - bl remap - bl ref_round_keys_s @ refresh the round keys - ldmia r1,{r4-r7} @ load the ciphertext - bl ns_to_s @ convert to shares - bl inv_rounds_s @ do decryption rounds - -.if ROUND_TRIP_TEST - -@ compute plaintext {r4-r7}^{r8-r11}^{SP[0..3]}^{SP[4..7]} -@ as shares {r4-r7}^{SP[0..3]}, {r8-r11}^{SP[4..7]} - ldrd r0,r3,[r13,#0] - eor r0,r0,r4 - eor r3,r3,r5 - strd r0,r3,[r13,#0] - ldrd r0,r3,[r13,#8] - eor r0,r0,r6 - eor r3,r3,r7 - strd r0,r3,[r13,#8] - ldrd r0,r3,[r13,#16] - eor r0,r0,r8 - eor r3,r3,r9 - strd r0,r3,[r13,#16] - ldrd r0,r3,[r13,#24] - eor r0,r0,r10 - eor r3,r3,r11 - strd r0,r3,[r13,#24] @ plaintext_s now on the stack - bl rounds_s @ restore original ciphertext (or we could have saved it) - - ldmia r1!,{r0,r3} @ reload actual ciphertext and compare to check for faults - eors r0,r0,r4 - eors r0,r0,r8 - bne 1f @ mismatch? could repeat this bne or add other protection against its being skipped - eors r3,r3,r5 - eors r3,r3,r9 - bne 1f - ldmia r1!,{r0,r3} - eors r0,r0,r6 - eors r0,r0,r10 - bne 1f - eors r3,r3,r7 - eors r3,r3,r11 - bne 1f - subs r1,r1,#16 - - pop {r0,r3} @ now EOR plaintext shares on stack to recover non-shared plaintext - ldr r14,[sp,#8] - eors r0,r0,r14 - ldr r14,[sp,#12] - eors r3,r3,r14 - stmia r1!,{r0,r3} @ overwrite ciphertext with plaintext - - pop {r0,r3} - ldr r14,[sp,#8] - eors r0,r0,r14 - ldr r14,[sp,#12] - eors r3,r3,r14 - stmia r1!,{r0,r3} @ overwrite ciphertext with plaintext - - add r13,#16 @ first share of plaintext has now been popped; skip the other share - -.else - -@ compute plaintext {r4-r7}^{r8-r11}^{SP[0..3]}^{SP[4..7]} -@ as shares {r4-r7}^{SP[0..3]}, {r8-r11}^{SP[4..7]} - pop {r0,r3} - eor r4,r0,r4 - eor r5,r3,r5 - pop {r0,r3} - eor r6,r0,r6 - eor r7,r3,r7 - pop {r0,r3} - eor r8,r0,r8 - eor r9,r3,r9 - pop {r0,r3} - eor r10,r0,r10 - eor r11,r3,r11 @ now plaintext_s in r4-r11 - eor r8,r8,r4 @ convert to non-shared - eor r9,r9,r5 - eor r10,r10,r6 - eor r11,r11,r7 @ now plaintext_ns in r8-r11 - ldmia r1,{r4-r7} @ ciphertext_ns in r4-r7 - stmia r1!,{r8-r11} @ overwrite ciphertext_ns with plaintext_ns - bl ns_to_s @ convert non-shared ciphertext to shared - -.endif - - push {r4-r11} @ push ciphertext_s, replacing iv or previous ciphertext_s on stack - subs r2,r2,#1 @ count the blocks - bne 2b - add r13,#32 - mov r0,#0 @ return OK status - pop {r4-r11,r14} - CHK_CANARY r14,CTAG17 - pop {r15} - -.if ROUND_TRIP_TEST -1: -@ fault here - rcp_panic -.endif -.endif - -.if INCLUDE_CRYPT_CTR -.balign 4 -.thumb_func -@ de/encrypt data in place -@ r0: ivec -@ r1: buf -@ r2: n, number of blocks, n>0 -.if CT_BPERM -@ In AES-CTR each block can be independently en/decrypted as the encryption only depends on -@ the IV, the key, and the block number. We can therefore process them in any order. Hence -@ we generate all the residues mod u=2^k such that u≥n in a pseudo-random order using a linear conguential -@ generator (x_i+1 = a x_i + c mod u), and process the blocks in that order. We choose -@ x_0 and a randomly (subject to a=5 mod 8), as well as adding an overall random offset -@ to the sequence, which is equivalent to choosing a random c. -@ -@ For residues greater than or equal to n we "decrypt" an area of scratch -@ memory, taking the same time as a real decryption. The inefficiency -@ due to rounding up the number of blocks processed to the next power of -@ two is a factor of 2 in the worst case. -@ q.v. https://en.wikipedia.org/wiki/Linear_congruential_generator#m_a_power_of_2,_c_%E2%89%A0_0 -.endif -ctr_crypt_s: - GET_CANARY r3,CTAG0 - SET_COUNT 171 -.if CT_BPERM - push {r0,r1,r3,r4-r11,r14} - mvn r4,#0 - subs r5,r2,#1 @ make sure we generate optimal mask for n an exact power of 2 - clz r5,r5 - lsrs r4,r4,r5 @ mask m=2^k-1 s.t. m≥n - orrs r4,r4,#7 @ m≥7 - bl gen_rand - bic r5,r0,#7 - adds r5,r5,#5 @ multiplier a, randomly initialised, but make sure it is 5 mod 8 - bl gen_rand - mov r7,r0 @ initial block pointer x₀, randomly initialised - bl gen_rand - mov r8,r0 @ sequence offset, randomly initialised: this is equivalent to choosing a random c - mov r6,r4 -.else - push {r0,r3,r4-r11,r14} - movs r12,#0 -.endif - CHK_COUNT 171 -1: - SET_COUNT 129 -.if CT_BPERM - add r12,r7,r8 @ add sequence offset - and r12,r12,r4 @ get block pointer mod 2^k - cmp r12,r2 @ set C if beyond end of buffer - sbcs r3,r3,r3 @ r3==0xffffffff in buffer, 0x00000000 past end - uadd8 r3,r3,r3 @ set/clear all GE flags if in buffer/past end - ldr r1,[r13,#4] @ get buffer address from stack - add r1,r1,r12,lsl#4 @ calculate address of block - ldr r3,=ctr_scratch - sel r1,r1,r3 @ if beyond end of buffer, just process scratch area - ldr r0,[r13] @ get IV address from stack - push {r4-r8,r12} -.else - ldr r0,[r13] @ get IV address from stack - push {r12} -.endif - CHK_COUNT 129 -@ It is not clear if the following addition of the block number in r12 to the IV can usefully -@ be done in terms of shares. Instead we do an addition and subtraction whose overall effect -@ is the same, and which provides a small degree of masking. The IV is not a secret anyway. - ldmia r0,{r4-r7} @ load IV - rev r7,r7 @ prepare for byte-big-endian, bit-little-endian (!) addition - rev r6,r6 - rev r5,r5 - rev r4,r4 - bl gen_rand - bic r8,r0,#0x80000000 @ only 31 bits so we don't get any overflows in the following - add r9,r8,r12 @ "masked" block number - adds r7,r7,r9 @ 128-bit addition - adcs r6,r6,#0 - adcs r5,r5,#0 - adcs r4,r4,#0 - subs r7,r7,r8 @ 128-bit subtraction, unmasking block number - sbcs r6,r6,r8,asr#31 - sbcs r5,r5,r8,asr#31 - sbcs r4,r4,r8,asr#31 - rev r7,r7 - rev r6,r6 - rev r5,r5 - rev r4,r4 - CHK_COUNT 130 - bl remap @ shuffle the LUts - CHK_COUNT 131 - bl ref_round_keys_s @ refresh the round keys - CHK_COUNT 132 - bl ns_to_s @ convert IV+x to shares - CHK_COUNT 133 - bl rounds_s @ forward AES rounds on IV+x - CHK_COUNT 134 - ldr r3,[r1] @ decrypt ciphertext - eors r3,r3,r4 - eors r3,r3,r8 - str r3,[r1] - ldr r3,[r1,#4] - eors r3,r3,r5 - eors r3,r3,r9 - str r3,[r1,#4] - ldr r3,[r1,#8] - eors r3,r3,r6 - eors r3,r3,r10 - str r3,[r1,#8] - ldr r3,[r1,#12] - eors r3,r3,r7 - eors r3,r3,r11 - str r3,[r1,#12] - CHK_COUNT 135 -.if CT_BPERM - pop {r4-r8,r12} - muls r7,r7,r5 @ LCG step: x<-ax+1 - adds r7,r7,#1 - subs r6,r6,#1 - CHK_COUNT 136 - bcs 1b - pop {r0,r1,r3,r4-r11,r14} -.else - pop {r12} - adds r1,r1,#16 - add r12,r12,#1 - cmp r12,r2 - CHK_COUNT 136 - bne 1b - pop {r0,r3,r4-r11,r14} -.endif - CHK_COUNT 137 - CHK_CANARY r3,CTAG0 - bx r14 -.endif - -.ltorg - -.thumb_func -aes_end: - nop - -@@@@@@@@@@@@@@@@@@@@@@@@@ test functions @@@@@@@@@@@@@@@@@@@@@@@@@ - -@ .global test_v - -@ .section .text.test_v,"ax",%progbits -@ .macro fn -@ ldr.n r0,=0x12345678 -@ ldr.n r0,=0xedcba987 -@ .endm -@ .macro tenfn -@ fn -@ fn -@ fn -@ fn -@ fn -@ fn -@ fn -@ fn -@ fn -@ fn -@ .endm -@ .macro hundredfn -@ tenfn -@ tenfn -@ tenfn -@ tenfn -@ tenfn -@ tenfn -@ tenfn -@ tenfn -@ tenfn -@ tenfn -@ .endm -@ -@ .thumb_func -@ test_v: -@ .balign 4 -@ 1: -@ hundredfn -@ b 1b -@ bx r14 -@ .ltorg - -@ switch from shared to non-shared state -@ s_to_ns: -@ eor r4,r4,r8 -@ eor r5,r5,r9 -@ eor r6,r6,r10 -@ eor r7,r7,r11 -@ bx r14 - -.section .text.debugging,"ax",%progbits - -.thumb_func -delay: -.if CHIPW - subs r0,r0,#3 @ we are clocked approximately three times slower -.else - subs r0,r0,#1 -.endif - bcs delay - bx r14 - -.thumb_func -flush_reg: -@ put known values into r0-r3,r12 - mov r0, #0x80808080 - mov r1, #0x81818181 - mov r2, #0x82828282 - mov r3, #0x83838383 - mov r12,#0x8c8c8c8c - bx r14 - -.thumb_func -isr_systick: - mov.w r2,#0xd0000000 @ set GPIO24 - mov.w r3,#0x01000000 - str r3,[r2,#24] - ldr r0,=systick_data - - ldr r1,[r0] - adds r1,r1,#1 - stmia r0!,{r1} - ldr r1,[r13,#0] @ r0..r2 - ldr r2,[r13,#4] - ldr r3,[r13,#8] - stmia r0!,{r1-r3} - ldr r1,[r13,#12] @ r3 - stmia r0!,{r1,r4-r11} - ldr r1,[r13,#16] @ r12 - ldr r3,[r13,#28] @ RETPSR - ubfx r2,r3,#9,#1 @ SPREALIGN - add r2,r13,r2,lsl#2 @ add 4 to SP if SPREALIGN set in RETPSR - add r2,r2,#0x68 @ r13 - stmia r0!,{r1-r2} - - ldr r1,[r13,#20] @ r14 - ldr r2,[r13,#24] @ ReturnAddress -@ RETPSR still in r3 - stmia r0!,{r1-r3} - - ldr r0,=0xe000e010 - mov r1,#5 - str r1,[r0] @ write to CSR - mov.w r2,#0xd0000000 - mov.w r3,#0x01000000 - str r3,[r2,#32] @ clear GPIO24 - bx r14 \ No newline at end of file diff --git a/bootloaders/encrypted/config.h b/bootloaders/encrypted/config.h deleted file mode 100644 index 0a39cedf4..000000000 --- a/bootloaders/encrypted/config.h +++ /dev/null @@ -1,127 +0,0 @@ -#pragma once - -#ifndef CM_PROFILE -#define CM_PROFILE 0 -#endif - -#define DEBUG 0 // for use in debugging with serial output (timing not repeatable) -#define CHIPW 0 // change clock to 48MHz for use with CW hardware -#define SYSTICK_IMAP 0 // use SYSTICK to get a map of instruction execution (set DEBUG to 0 to get useful timings) -#define INCLUDE_ENCRYPT_CBC 0 // include code to perform encryption in CBC mode? -#define INCLUDE_DECRYPT_CBC 0 // include code to perform decryption in CBC mode? -#define INCLUDE_CRYPT_CTR 1 // include code to perform de/encryption in CTR mode? -#define ROUND_TRIP_TEST 0 // do the glitch detection test in CBC mode where we re-encrypt each block and compare against original ciphertext? -#define SBOX_VIA_INV 1 // compute (inverse) S-box values via a table of field inverses rather than via a direct table? -#define GEN_RAND_SHA 0 // use SHA256 hardware to generate random numbers (disable for Qemu testing) - -#if ROUND_TRIP_TEST && !SBOX_VIA_INV -#error Sorry, if you want to do the round-trip test then SBOX_VIA_INV must also be set -#endif - -#if CM_PROFILE==0 - -#define RANDOMIZE 0 // new random seed on each reset? -#define RC_CANARY 0 // use rcp_canary feature -#define RC_JITTER 0 // use random-delay versions of RCP instructions -#define RC_COUNT 0 // use rcp_count feature -#define IK_SHUFREAD 0 // read key bytes in random order? -#define IK_JUNK 0 // add some random distraction in init_key? -#define IK_PERM 0 // permute bytes (and possibly distraction bytes) in round key generation? -#define IK_REMAP 0 // remap S-box in round key generation? -#define IK_JITTER 0 // jitter timing in init_key? -#define RK_ROR 0 // store round keys with random RORs? -#define ST_HPERM 0 // insert random horizontal permutations in state during de/encryption? -#define ST_VPERM 0 // insert random vertical permutations in state during de/encryption? -#define CT_BPERM 0 // process blocks in a random order in counter mode? - -#elif CM_PROFILE==1 - -#define RANDOMIZE 1 // new random seed on each reset? -#define RC_CANARY 0 // use rcp_canary feature -#define RC_JITTER 0 // use random-delay versions of RCP instructions -#define RC_COUNT 0 // use rcp_count feature -#define IK_SHUFREAD 1 // read key bytes in random order? -#define IK_JUNK 1 // add some random distraction in init_key? -#define IK_PERM 1 // permute bytes (and possibly distraction bytes) in round key generation? -#define IK_REMAP 1 // remap S-box in round key generation? -#define IK_JITTER 0 // jitter timing in init_key? -#define RK_ROR 1 // store round keys with random RORs? -#define ST_HPERM 0 // insert random horizontal permutations in state during de/encryption? -#define ST_VPERM 0 // insert random vertical permutations in state during de/encryption? -#define CT_BPERM 0 // process blocks in a random order in counter mode? - -#elif CM_PROFILE==2 - -#define RANDOMIZE 1 // new random seed on each reset? -#define RC_CANARY 0 // use rcp_canary feature -#define RC_JITTER 0 // use random-delay versions of RCP instructions -#define RC_COUNT 0 // use rcp_count feature -#define IK_SHUFREAD 0 // read key bytes in random order? -#define IK_JUNK 0 // add some random distraction in init_key? -#define IK_PERM 0 // permute bytes (and possibly distraction bytes) in round key generation? -#define IK_REMAP 0 // remap S-box in round key generation? -#define IK_JITTER 0 // jitter timing in init_key? -#define RK_ROR 0 // store round keys with random RORs? -#define ST_HPERM 1 // insert random horizontal permutations in state during de/encryption? -#define ST_VPERM 1 // insert random vertical permutations in state during de/encryption? -#define CT_BPERM 0 // process blocks in a random order in counter mode? - -#elif CM_PROFILE==3 - -#define RANDOMIZE 1 // new random seed on each reset? -#define RC_CANARY 0 // use rcp_canary feature -#define RC_JITTER 0 // use random-delay versions of RCP instructions -#define RC_COUNT 0 // use rcp_count feature -#define IK_SHUFREAD 0 // read key bytes in random order? -#define IK_JUNK 0 // add some random distraction in init_key? -#define IK_PERM 0 // permute bytes (and possibly distraction bytes) in round key generation? -#define IK_REMAP 0 // remap S-box in round key generation? -#define IK_JITTER 0 // jitter timing in init_key? -#define RK_ROR 0 // store round keys with random RORs? -#define ST_HPERM 0 // insert random horizontal permutations in state during de/encryption? -#define ST_VPERM 0 // insert random vertical permutations in state during de/encryption? -#define CT_BPERM 1 // process blocks in a random order in counter mode? - -#elif CM_PROFILE==4 - -#define RANDOMIZE 1 // new random seed on each reset? -#define RC_CANARY 0 // use rcp_canary feature -#define RC_JITTER 0 // use random-delay versions of RCP instructions -#define RC_COUNT 0 // use rcp_count feature -#define IK_SHUFREAD 0 // read key bytes in random order? -#define IK_JUNK 0 // add some random distraction in init_key? -#define IK_PERM 0 // permute bytes (and possibly distraction bytes) in round key generation? -#define IK_REMAP 0 // remap S-box in round key generation? -#define IK_JITTER 1 // jitter timing in init_key? -#define RK_ROR 0 // store round keys with random RORs? -#define ST_HPERM 0 // insert random horizontal permutations in state during de/encryption? -#define ST_VPERM 0 // insert random vertical permutations in state during de/encryption? -#define CT_BPERM 0 // process blocks in a random order in counter mode? - -#elif CM_PROFILE==5 - -#define RANDOMIZE 1 // new random seed on each reset? -#define RC_CANARY 1 // use rcp_canary feature -#define RC_JITTER 1 // use random-delay versions of RCP instructions -#define RC_COUNT 1 // use rcp_count feature -#define IK_SHUFREAD 1 // read key bytes in random order? -#define IK_JUNK 1 // add some random distraction in init_key? -#define IK_PERM 1 // permute bytes (and possibly distraction bytes) in round key generation? -#define IK_REMAP 1 // remap S-box in round key generation? -#define IK_JITTER 1 // jitter timing in init_key? -#define RK_ROR 1 // store round keys with random RORs? -#define ST_HPERM 1 // insert random horizontal permutations in state during de/encryption? -#define ST_VPERM 1 // insert random vertical permutations in state during de/encryption? -#define CT_BPERM 1 // process blocks in a random order in counter mode? - -#endif - -#if RC_COUNT && (INCLUDE_ENCRYPT_CBC || INCLUDE_DECRYPT_CBC) -#error Sorry, RC_COUNT is only tested in CTR mode -#endif - -// derived values -#define NEED_ROUNDS (INCLUDE_ENCRYPT_CBC || (INCLUDE_DECRYPT_CBC && ROUND_TRIP_TEST) || INCLUDE_CRYPT_CTR) -#define NEED_INV_ROUNDS (INCLUDE_DECRYPT_CBC) -#define NEED_HPERM (IK_PERM || ST_HPERM) -#define NEED_VPERM (IK_PERM || ST_VPERM) \ No newline at end of file diff --git a/bootloaders/encrypted/enc-pt.json b/bootloaders/encrypted/enc-pt.json index 9b7a86d3b..9c5c3a17e 100644 --- a/bootloaders/encrypted/enc-pt.json +++ b/bootloaders/encrypted/enc-pt.json @@ -34,4 +34,4 @@ "link": ["a", 0] } ] -} \ No newline at end of file +} diff --git a/bootloaders/encrypted/enc_bootloader.c b/bootloaders/encrypted/enc_bootloader.c index 1df509101..f87394983 100644 --- a/bootloaders/encrypted/enc_bootloader.c +++ b/bootloaders/encrypted/enc_bootloader.c @@ -13,56 +13,102 @@ #include "hardware/structs/otp.h" #include "hardware/structs/qmi.h" #include "hardware/structs/xip_ctrl.h" +#include "hardware/clocks.h" +#include "hardware/xosc.h" +#include "hardware/structs/rosc.h" +#include "hardware/pll.h" + +#define OTP_KEY_PAGE 29 + +extern void decrypt(uint8_t* key4way, uint8_t* IV_OTPsalt, uint8_t* IV_public, uint8_t(*buf)[16], int nblk); + +// These just have to be higher than the actual frequency, to prevent overclocking unused peripherals +#define ROSC_HZ 300*MHZ +#define OTHER_CLK_DIV 30 + + +void runtime_init_clocks(void) { + // Disable resus that may be enabled from previous software + clocks_hw->resus.ctrl = 0; + + uint32_t rosc_div = 2; // default divider 2 + uint32_t rosc_drive = 0x7777; // default drives of 0b111 (0x7) + + // Bump up ROSC speed to ~110MHz + rosc_hw->freqa = 0; // reset the drive strengths + rosc_hw->div = rosc_div | ROSC_DIV_VALUE_PASS; // set divider + // Increment the freqency range one step at a time - this is safe provided the current config is not TOOHIGH + // because ROSC_CTRL_FREQ_RANGE_VALUE_MEDIUM | ROSC_CTRL_FREQ_RANGE_VALUE_HIGH == ROSC_CTRL_FREQ_RANGE_VALUE_HIGH + static_assert((ROSC_CTRL_FREQ_RANGE_VALUE_LOW | ROSC_CTRL_FREQ_RANGE_VALUE_MEDIUM) == ROSC_CTRL_FREQ_RANGE_VALUE_MEDIUM); + static_assert((ROSC_CTRL_FREQ_RANGE_VALUE_MEDIUM | ROSC_CTRL_FREQ_RANGE_VALUE_HIGH) == ROSC_CTRL_FREQ_RANGE_VALUE_HIGH); + hw_set_bits(&rosc_hw->ctrl, ROSC_CTRL_FREQ_RANGE_VALUE_MEDIUM); + hw_set_bits(&rosc_hw->ctrl, ROSC_CTRL_FREQ_RANGE_VALUE_HIGH); + + // Enable rosc randomisation + rosc_hw->freqa = (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) | + rosc_drive | ROSC_FREQA_DS1_RANDOM_BITS | ROSC_FREQA_DS0_RANDOM_BITS; // enable randomisation + + // Not used with FREQ_RANGE_VALUE_HIGH, but should still be set to the maximum drive + rosc_hw->freqb = (ROSC_FREQB_PASSWD_VALUE_PASS << ROSC_FREQB_PASSWD_LSB) | + ROSC_FREQB_DS7_LSB | ROSC_FREQB_DS6_LSB | ROSC_FREQB_DS5_LSB | ROSC_FREQB_DS4_LSB; + + // CLK SYS = ROSC directly, as it's running slowly enough + clock_configure_int_divider(clk_sys, + CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX, + CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_ROSC_CLKSRC, + ROSC_HZ, // this doesn't have to be accurate + 1); + + // CLK_REF = ROSC / OTHER_CLK_DIV - this isn't really used, so just needs to be set to a low enough frequency + clock_configure_int_divider(clk_ref, + CLOCKS_CLK_REF_CTRL_SRC_VALUE_ROSC_CLKSRC_PH, + 0, + ROSC_HZ, + OTHER_CLK_DIV); + + + // Everything else should run from PLL USB, so we can use UART and USB for output + xosc_init(); + pll_init(pll_usb, PLL_USB_REFDIV, PLL_USB_VCO_FREQ_HZ, PLL_USB_POSTDIV1, PLL_USB_POSTDIV2); + + // CLK USB = PLL USB 48MHz / 1 = 48MHz + clock_configure_undivided(clk_usb, + 0, // No GLMUX + CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, + USB_CLK_HZ); + + // CLK ADC = PLL USB 48MHz / 1 = 48MHz + clock_configure_undivided(clk_adc, + 0, // No GLMUX + CLOCKS_CLK_ADC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, + USB_CLK_HZ); + + // CLK PERI = PLL USB 48MHz / 1 = 48MHz. Used as reference clock for UART and SPI serial. + clock_configure_undivided(clk_peri, + 0, + CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, + USB_CLK_HZ); + + // CLK_HSTX = PLL USB 48MHz / 1 = 48MHz. Transmit bit clock for the HSTX peripheral. + clock_configure_undivided(clk_hstx, + 0, + CLOCKS_CLK_HSTX_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, + USB_CLK_HZ); +} -#include "config.h" - -extern void flush_reg(); -volatile uint32_t systick_data[18]; // count, R0-R15,RETPSR - -extern void remap(); -extern uint32_t gen_rand(); -extern void init_key(uint8_t *rk_s, uint8_t *key); -extern void gen_lut_inverse(); -extern void gen_lut_sbox(); -extern void gen_lut_inv_sbox(); -extern int ctr_crypt_s(uint8_t*iv,uint8_t*buf,int nblk); - -extern uint8_t rkey_s[480]; -extern uint8_t lut_a[256]; -extern uint8_t lut_b[256]; -extern uint32_t lut_a_map; -extern uint32_t lut_b_map; -extern uint32_t rstate[4]; - -static void init_lut_map() { - int i; - for(i=0;i<256;i++) lut_b[i]=gen_rand()&0xff, lut_a[i]^=lut_b[i]; - lut_a_map=0; - lut_b_map=0; - remap(); +// The function lock_key() is called from decrypt() after key initialisation is complete and before decryption begins. +// That is a suitable point to lock the OTP area where key information is stored. +void lock_key() { + otp_hw->sw_lock[OTP_KEY_PAGE] = 0xf; + otp_hw->sw_lock[OTP_KEY_PAGE + 1] = 0xf; } + static __attribute__((aligned(4))) uint8_t workarea[4 * 1024]; int main() { stdio_init_all(); - #if RANDOMIZE - get_rand_128((rng_128_t*)rstate); // fill rstate with 128 bits of random data - #else - rstate[0]=1223352428; - rstate[1]=1223352428; - rstate[2]=0x41414141; - rstate[3]=0x41414141; - #endif - - // reset the RNG - reset_block(RESETS_RESET_SHA256_BITS); - unreset_block(RESETS_RESET_SHA256_BITS); - rstate[0]&=0xffffff00; // bottom byte must be zero - - printf("Rstate at address %x\n", rstate); - printf("Entered bootloader code\n"); int rc; rc = rom_load_partition_table(workarea, sizeof(workarea), false); @@ -74,15 +120,13 @@ int main() { boot_info_t info; printf("Getting boot info\n"); rc = rom_get_boot_info(&info); - uint32_t flash_update_base = 0; printf("Boot Type %x\n", info.boot_type); if (info.boot_type == BOOT_TYPE_FLASH_UPDATE) { - flash_update_base = info.reboot_params[0]; - printf("Flash Update Base %x\n", flash_update_base); + printf("Flash Update Base %x\n", info.reboot_params[0]); } - rc = rom_pick_ab_partition(workarea, sizeof(workarea), 0, flash_update_base); + rc = rom_pick_ab_partition_during_update((uint32_t*)workarea, sizeof(workarea), 0); if (rc < 0) { printf("Partition Table A/B choice failed %d - resetting\n", rc); reset_usb_boot(0, 0); @@ -92,31 +136,35 @@ int main() { rc = rom_get_partition_table_info((uint32_t*)workarea, 0x8, PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_SINGLE_PARTITION | (boot_partition << 24)); - uint32_t data_start_addr; - uint32_t data_end_addr; + uint32_t data_start_addr = 0; + uint32_t data_end_addr = 0; + uint32_t data_max_size = 0; if (rc != 3) { printf("No boot partition - assuming bin at start of flash\n"); data_start_addr = 0; data_end_addr = 0x70000; // must fit into 0x20000000 -> 0x20070000 + data_max_size = data_end_addr - data_start_addr; } else { uint16_t first_sector_number = (((uint32_t*)workarea)[1] & PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_BITS) >> PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_LSB; uint16_t last_sector_number = (((uint32_t*)workarea)[1] & PICOBIN_PARTITION_LOCATION_LAST_SECTOR_BITS) >> PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB; data_start_addr = first_sector_number * 0x1000; data_end_addr = (last_sector_number + 1) * 0x1000; + data_max_size = data_end_addr - data_start_addr; - printf("Partition Start %x, End %x\n", data_start_addr, data_end_addr); + printf("Partition Start %x, End %x, Max Size %x\n", data_start_addr, data_end_addr, data_max_size); } printf("Decrypting the chosen image\n"); uint32_t first_mb_start = 0; + bool first_mb_start_found = false; uint32_t first_mb_end = 0; uint32_t last_mb_start = 0; - for (uint16_t i=0; i <= 0x1000; i += 4) { + for (uint16_t i=0; i < 0x1000; i += 4) { if (*(uint32_t*)(XIP_BASE + data_start_addr + i) == 0xffffded3) { printf("Found first block start\n"); first_mb_start = i; - } - if (*(uint32_t*)(XIP_BASE + data_start_addr + i) == 0xab123579) { + first_mb_start_found = true; + } else if (first_mb_start_found && (*(uint32_t*)(XIP_BASE + data_start_addr + i) == 0xab123579)) { printf("Found first block end\n"); first_mb_end = i + 4; last_mb_start = *(uint32_t*)(XIP_BASE + data_start_addr + i-4) + first_mb_start; @@ -124,6 +172,12 @@ int main() { } } + if (last_mb_start > data_max_size) { + // todo - harden this check + printf("ERROR: Encrypted binary is too big for it's partition - resetting\n"); + reset_usb_boot(0, 0); + } + if (*(uint32_t*)(XIP_BASE + data_start_addr + last_mb_start) == 0xffffded3) { printf("Found last block start where expected\n"); } else { @@ -144,15 +198,6 @@ int main() { reset_usb_boot(0, 0); } - printf("OTP Valid Keys %x\n", otp_hw->key_valid); - - printf("Unlocking\n"); - for (int i=0; i<4; i++) { - uint32_t key_i = ((i*2+1) << 24) | ((i*2+1) << 16) | - (i*2 << 8) | i*2; - otp_hw->crt_key_w[i] = key_i; - } - uint8_t iv[16]; data_start_addr += first_mb_end; memcpy(iv, (void*)(XIP_BASE + data_start_addr), sizeof(iv)); @@ -171,20 +216,17 @@ int main() { for (int i=0; i < 4; i++) printf("%08x\n", *(uint32_t*)(SRAM_BASE + i*4)); - flush_reg(); - #if !SBOX_VIA_INV - gen_lut_sbox(); - #else - gen_lut_inverse(); - #endif - init_lut_map(); // Read key directly from OTP - guarded reads will throw a bus fault if there are any errors uint16_t* otp_data = (uint16_t*)OTP_DATA_GUARDED_BASE; - init_key(rkey_s, (uint8_t*)&(otp_data[(OTP_CMD_ROW_BITS & 0x780)])); - otp_hw->sw_lock[30] = 0xf; - flush_reg(); - ctr_crypt_s(iv, (void*)SRAM_BASE, data_size/16); - flush_reg(); + + decrypt( + (uint8_t*)&(otp_data[OTP_KEY_PAGE * 0x40]), + (uint8_t*)&(otp_data[(OTP_KEY_PAGE + 2) * 0x40]), + iv, (void*)SRAM_BASE, data_size/16 + ); + + // Lock the IV salt + otp_hw->sw_lock[OTP_KEY_PAGE + 2] = 0xf; printf("Post decryption image begins with\n"); for (int i=0; i < 4; i++) @@ -192,7 +234,7 @@ int main() { printf("Chaining into %x, size %x\n", SRAM_BASE, data_size); - stdio_deinit_all(); + stdio_uart_deinit(); // stdio_usb_deinit doesn't work here, so only deinit UART rc = rom_chain_image( workarea, @@ -201,7 +243,7 @@ int main() { data_size ); - stdio_init_all(); + stdio_uart_init(); printf("Shouldn't return from ROM call %d\n", rc); reset_usb_boot(0, 0); diff --git a/bootloaders/encrypted/ivsalt.bin b/bootloaders/encrypted/ivsalt.bin new file mode 100644 index 000000000..fb9ef50b8 --- /dev/null +++ b/bootloaders/encrypted/ivsalt.bin @@ -0,0 +1 @@ +���x��%�^��=T�Č \ No newline at end of file diff --git a/bootloaders/encrypted/mbedtls_aes.c b/bootloaders/encrypted/mbedtls_aes.c new file mode 100644 index 000000000..9f19c9b4d --- /dev/null +++ b/bootloaders/encrypted/mbedtls_aes.c @@ -0,0 +1,73 @@ +#include +#include "pico/stdlib.h" + +extern void lock_key(); + +int mb_aes_crypt_ctr_xor(mbedtls_aes_context *ctx, + size_t length, + unsigned char iv0[16], + unsigned char nonce_xor[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output) +{ + int c; + int ret = 0; + size_t n = 0; + uint32_t counter = 0; + + assert(length == (uint32_t)length); + + while (length--) { + if (n == 0) { + for (int i = 16; i > 0; i--) { + nonce_xor[i-1] = iv0[i-1]; + if (i - (int)(16 - sizeof(counter)) > (int)0) { + nonce_xor[i-1] ^= (unsigned char)(counter >> ((16-i)*8)); + } + } + + ret = mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, nonce_xor, stream_block); + if (ret != 0) { + break; + } + counter++; + } + c = *input++; + *output++ = (unsigned char) (c ^ stream_block[n]); + + n = (n + 1) & 0x0F; + } + + return ret; +} + +void decrypt(uint8_t* key4way, uint8_t* IV_OTPsalt, uint8_t* IV_public, uint8_t(*buf)[16], int nblk) { + mbedtls_aes_context aes; + + uint32_t aes_key[8]; + uint32_t* key4waywords = (uint32_t*)key4way; + // Key is stored as a 4-way share of each word, ie X[0] = A[0] ^ B[0] ^ C[0] ^ D[0], stored as A[0], B[0], C[0], D[0] + for (int i=0; i < count_of(aes_key); i++) { + int skip = (i/4)*16; // skip every other 16 words (64 bytes), due to the FIB workaround + aes_key[i] = key4waywords[i*4 + skip] + ^ key4waywords[i*4 + 1 + skip] + ^ key4waywords[i*4 + 2 + skip] + ^ key4waywords[i*4 + 3 + skip]; + } + + uint8_t iv[16]; + for (int i=0; i < sizeof(iv); i++) { + iv[i] = IV_OTPsalt[i] ^ IV_public[i]; + } + + int len = nblk * 16; + + mbedtls_aes_setkey_enc(&aes, (uint8_t*)aes_key, 256); + + lock_key(); + + uint8_t xor_working_block[16] = {0}; + uint8_t stream_block[16] = {0}; + mb_aes_crypt_ctr_xor(&aes, len, (uint8_t*)iv, xor_working_block, stream_block, (uint8_t*)buf, (uint8_t*)buf); +} diff --git a/bootloaders/encrypted/mbedtls_config.h b/bootloaders/encrypted/mbedtls_config.h new file mode 100644 index 000000000..7b1c073c1 --- /dev/null +++ b/bootloaders/encrypted/mbedtls_config.h @@ -0,0 +1,9 @@ +#ifndef _MBEDTLS_CONFIG_H +#define _MBEDTLS_CONFIG_H + +#define MBEDTLS_HAVE_ASM +#define MBEDTLS_AES_C +#define MBEDTLS_AES_ROM_TABLES +#define MBEDTLS_CIPHER_MODE_CTR + +#endif diff --git a/bootloaders/encrypted/otp.json b/bootloaders/encrypted/otp.json deleted file mode 100644 index f86a9e019..000000000 --- a/bootloaders/encrypted/otp.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "30:0" : - { - "ecc" : true, - "value" : - [ - "0x00", - "0x01", - "0x02", - "0x03", - "0x04", - "0x05", - "0x06", - "0x07", - "0x08", - "0x09", - "0x0a", - "0x0b", - "0x0c", - "0x0d", - "0x0e", - "0x0f", - "0x00", - "0x10", - "0x20", - "0x30", - "0x40", - "0x50", - "0x60", - "0x70", - "0x80", - "0x90", - "0xa0", - "0xb0", - "0xc0", - "0xd0", - "0xe0", - "0xf0" - ] - }, - "OTP_DATA_KEY1" : [ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7 ], - "OTP_DATA_KEY1_VALID" : "0x010101", - "OTP_DATA_KEY2" : [ 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0 ], - "OTP_DATA_KEY2_VALID" : "0x010101", - "PAGE30_LOCK0" : "0x4a4a4a" -} \ No newline at end of file diff --git a/bootloaders/encrypted/privateaes.bin b/bootloaders/encrypted/privateaes.bin index 0122f8a2c..533d79266 100644 Binary files a/bootloaders/encrypted/privateaes.bin and b/bootloaders/encrypted/privateaes.bin differ diff --git a/bootloaders/encrypted/update-key.cmake b/bootloaders/encrypted/update-key.cmake deleted file mode 100644 index a14c90c7c..000000000 --- a/bootloaders/encrypted/update-key.cmake +++ /dev/null @@ -1,23 +0,0 @@ -if (CMAKE_VERSION VERSION_LESS 3.19) - # Check if keyfile is not the default, and print warning - file(READ ${CMAKE_CURRENT_LIST_DIR}/privateaes.bin key_file HEX) - if (NOT ${key_file} STREQUAL "000102030405060708090a0b0c0d0e0f00102030405060708090a0b0c0d0e0f0") - message(WARNING - "Encrypted bootloader AES key not updated in otp.json file, as CMake version is < 3.19" - " - you will need to change the key in otp.json manually and re-run the build" - ) - endif() -else() - # Read the JSON file. - file(READ ${CMAKE_CURRENT_LIST_DIR}/otp.json json_string) - # Read the key file - file(READ ${CMAKE_CURRENT_LIST_DIR}/privateaes.bin key_file HEX) - - # adds '0x' prefix, comma suffix, and quotes for every byte - string(REGEX REPLACE "([0-9a-f][0-9a-f])" "\"0x\\1\", " key_file ${key_file}) - set(key_file_json "[${key_file}]") - - string(JSON json_string SET ${json_string} "30:0" "value" ${key_file_json}) - - file(WRITE ${CMAKE_CURRENT_LIST_DIR}/otp.json ${json_string}) -endif() diff --git a/bootloaders/uart/CMakeLists.txt b/bootloaders/uart/CMakeLists.txt new file mode 100644 index 000000000..a0470902a --- /dev/null +++ b/bootloaders/uart/CMakeLists.txt @@ -0,0 +1,38 @@ +add_executable(uart_boot + uart_boot.c + ) + +# pull in common dependencies +target_link_libraries(uart_boot pico_stdlib hardware_flash) + +# add partition table +pico_embed_pt_in_binary(uart_boot ${CMAKE_CURRENT_LIST_DIR}/uart-pt.json) + +# create absolute UF2, as it's a bootloader so shouldn't go in a partition +pico_set_uf2_family(uart_boot "absolute") + +# create map/bin/hex file etc. +pico_add_extra_outputs(uart_boot) + +# add url via pico_set_program_url +example_auto_set_url(uart_boot) + + +# Create separate binary to be loaded onto other device +add_executable(uart_binary + uart_binary.c + ) + +# pull in common dependencies +target_link_libraries(uart_binary pico_stdlib) + +pico_set_binary_type(uart_binary no_flash) + +# package uf2 in flash +pico_package_uf2_output(uart_binary) + +# create map/bin/hex/uf2 file etc. +pico_add_extra_outputs(uart_binary) + +# call pico_set_program_url to set path to example on github, so users can find the source for an example via picotool +example_auto_set_url(uart_binary) diff --git a/bootloaders/uart/uart-pt.json b/bootloaders/uart/uart-pt.json new file mode 100644 index 000000000..c42cc1098 --- /dev/null +++ b/bootloaders/uart/uart-pt.json @@ -0,0 +1,23 @@ +{ + "version": [1, 0], + "unpartitioned": { + "families": ["absolute"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + } + }, + "partitions": [ + { + "start": "128K", + "size": "32K", + "families": ["rp2350-arm-s", "rp2350-riscv"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + } + } + ] +} diff --git a/bootloaders/uart/uart_binary.c b/bootloaders/uart/uart_binary.c new file mode 100644 index 000000000..e3f2f535b --- /dev/null +++ b/bootloaders/uart/uart_binary.c @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico/stdlib.h" +#include "hardware/uart.h" +#include "hardware/structs/pads_qspi.h" +#include "hardware/structs/io_qspi.h" + +#ifndef LED_DELAY_MS +#define LED_DELAY_MS 500 +#endif + +// Initialize the GPIO for the LED +void pico_led_init(void) { +#ifdef PICO_DEFAULT_LED_PIN + // A device like Pico that uses a GPIO for the LED will define PICO_DEFAULT_LED_PIN + // so we can use normal GPIO functionality to turn the led on and off + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); +#endif +} + +// Turn the LED on or off +void pico_set_led(bool led_on) { +#if defined(PICO_DEFAULT_LED_PIN) + // Just set the GPIO on or off + gpio_put(PICO_DEFAULT_LED_PIN, led_on); +#endif +} + +// Set function for QSPI GPIO pin +void qspi_gpio_set_function(uint gpio, gpio_function_t fn) { + // Set input enable on, output disable off + hw_write_masked(&pads_qspi_hw->io[gpio], + PADS_QSPI_GPIO_QSPI_SD2_IE_BITS, + PADS_QSPI_GPIO_QSPI_SD2_IE_BITS | PADS_QSPI_GPIO_QSPI_SD2_OD_BITS + ); + // Zero all fields apart from fsel; we want this IO to do what the peripheral tells it. + // This doesn't affect e.g. pullup/pulldown, as these are in pad controls. + io_qspi_hw->io[gpio].ctrl = fn << IO_QSPI_GPIO_QSPI_SD2_CTRL_FUNCSEL_LSB; + + // Remove pad isolation now that the correct peripheral is in control of the pad + hw_clear_bits(&pads_qspi_hw->io[gpio], PADS_QSPI_GPIO_QSPI_SD2_ISO_BITS); +} + +int main() { + pico_led_init(); + + // SD2 is QSPI GPIO 3, SD3 is QSPI GPIO 4 + qspi_gpio_set_function(3, GPIO_FUNC_UART_AUX); + qspi_gpio_set_function(4, GPIO_FUNC_UART_AUX); + + uart_init(uart0, 1000000); + + while (true) { + uart_puts(uart0, "Hello, world\n"); + pico_set_led(true); + sleep_ms(LED_DELAY_MS); + pico_set_led(false); + sleep_ms(LED_DELAY_MS); + } +} diff --git a/bootloaders/uart/uart_boot.c b/bootloaders/uart/uart_boot.c new file mode 100644 index 000000000..a9dfb5f7c --- /dev/null +++ b/bootloaders/uart/uart_boot.c @@ -0,0 +1,232 @@ +#include +#include +#include "pico/stdlib.h" +#include "hardware/uart.h" +#include "pico/bootrom.h" +#include "boot/picobin.h" +#include "hardware/flash.h" + +// UART defines for uart boot +#define UART_ID uart1 + +// Use pins 4 and 5 for uart boot +#define UART_TX_PIN 4 +#define UART_RX_PIN 5 + +// Use pin 3 for the RUN pin on the other chip +#define RUN_PIN 3 + + +void reset_chip() { + // Toggle run pin + gpio_put(RUN_PIN, false); + sleep_ms(1); + gpio_put(RUN_PIN, true); +} + + +void uart_boot() { + uint knocks = 0; + char in = 0; + while (true) { + // Send the knock sequence + uart_putc_raw(UART_ID, 0x56); + uart_putc_raw(UART_ID, 0xff); + uart_putc_raw(UART_ID, 0x8b); + uart_putc_raw(UART_ID, 0xe4); + uart_putc_raw(UART_ID, 'n'); + + if (uart_is_readable_within_us(UART_ID, 1000)) { + in = uart_getc(UART_ID); + if (in != 'n') { + printf("Incorrect response - resetting\n"); + reset_chip(); + return; + } + printf("%c\n", in); + break; + } else { + if (knocks > 10) { + printf("No response - resetting\n"); + reset_chip(); + return; + } + printf("No response - knocking again\n"); + knocks++; + } + } + + printf("Boot starting\n"); + + // Get partition location in flash + const int buf_words = (16 * 4) + 1; // maximum of 16 partitions, each with maximum of 4 words returned, plus 1 + uint32_t* buffer = malloc(buf_words * 4); + + int ret = rom_get_partition_table_info(buffer, buf_words, PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_SINGLE_PARTITION | (0 << 24)); + hard_assert(buffer[0] == (PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_SINGLE_PARTITION)); + hard_assert(ret == 3); + + uint32_t location_and_permissions = buffer[1]; + uint32_t start_addr = XIP_BASE + ((location_and_permissions & PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_BITS) >> PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_LSB) * FLASH_SECTOR_SIZE; + uint32_t end_addr = XIP_BASE + (((location_and_permissions & PICOBIN_PARTITION_LOCATION_LAST_SECTOR_BITS) >> PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB) + 1) * FLASH_SECTOR_SIZE; + printf("Start %08x, end %08x\n", start_addr, end_addr); + + free(buffer); + + printf("Writing binary\n"); + uint32_t time_start = time_us_32(); + uint32_t current_addr = start_addr; + while (current_addr < end_addr) { + uart_putc_raw(UART_ID, 'w'); + char *buf = (char*)current_addr; + int i; + for (i = 0; i < 32; i++) { + uart_putc_raw(UART_ID, buf[i]); + } + if (!uart_is_readable_within_us(UART_ID, 500)) { + // Detect hangs and reset the chip + printf("Write has hung - resetting\n"); + reset_chip(); + return; + } + in = uart_getc(UART_ID); + if (in != 'w') { + printf("Incorrect response - resetting\n"); + reset_chip(); + return; + } + current_addr += i; + } + + uint32_t time_end = time_us_32(); + printf("Write took %dus\n", time_end - time_start); + printf("Write complete - resetting pointer\n"); + + uart_putc_raw(UART_ID, 'c'); + if (!uart_is_readable_within_us(UART_ID, 500)) { + // Detect hangs and reset the chip + printf("Clear has hung - resetting\n"); + reset_chip(); + return; + } + in = uart_getc(UART_ID); + printf("%c\n", in); + if (in != 'c') { + printf("Incorrect response - resetting\n"); + reset_chip(); + return; + } + + printf("Verifying binary\n"); + time_start = time_us_32(); + current_addr = start_addr; + while (current_addr < end_addr) { + uart_putc_raw(UART_ID, 'r'); + char *buf = (char*)current_addr; + if (!uart_is_readable_within_us(UART_ID, 500)) { + // Detect hangs and reset the chip + printf("Verify has hung - resetting\n"); + reset_chip(); + return; + } + int i = 0; + while (uart_is_readable_within_us(UART_ID, 10) && i < 32) { + in = uart_getc(UART_ID); + if (in != buf[i]) { + printf("Verify has incorrect data at 0x%08x - resetting\n", current_addr - start_addr + SRAM_BASE); + reset_chip(); + return; + } + i++; + } + if (i != 32) { + printf("Verify has incorrect data size - resetting\n"); + reset_chip(); + return; + } + in = uart_getc(UART_ID); + if (in != 'r') { + printf("Incorrect response - resetting\n"); + reset_chip(); + return; + } + current_addr += i; + } + + time_end = time_us_32(); + printf("Verify took %dus\n", time_end - time_start); + printf("Verify complete - executing\n"); + + uart_putc_raw(UART_ID, 'x'); + if (!uart_is_readable_within_us(UART_ID, 500)) { + // Detect hangs and reset the chip + printf("Execute has hung - resetting\n"); + reset_chip(); + return; + } + in = uart_getc(UART_ID); + printf("%c\n", in); + if (in != 'x') { + printf("Incorrect response - resetting\n"); + reset_chip(); + return; + } +} + + +int main() +{ + stdio_init_all(); + + // Set up our UART for booting the other device + uart_init(UART_ID, 1000000); + gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART); + gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART); + + // Set up run pin + gpio_init(RUN_PIN); + gpio_set_dir(RUN_PIN, GPIO_OUT); + + // Reset chip + reset_chip(); + + int attempts = 0; + char splash[] = "RP2350"; + char hello[] = "Hello, world"; + + while (true) { + char buf[500] = {0}; + int i = 0; + while (uart_is_readable(UART_ID) && i < sizeof(buf)) { + char in = uart_getc(UART_ID); + printf("%c", in); + buf[i] = in; + i++; + } + if (i > 0) { + printf(" ...Read done\n"); + } + char *ptr = memchr(buf, splash[0], sizeof(buf)); + if (ptr && strncmp(ptr, splash, sizeof(splash) - 1) == 0) { + printf("Splash found\n"); + uart_boot(); + attempts = 0; + } else { + ptr = memchr(buf, hello[0], sizeof(buf)); + if (ptr && strncmp(ptr, hello, sizeof(hello) - 1) == 0) { + printf("Device is running\n"); + attempts = 0; + } else { + if (attempts > 3) { + printf("Device not running - attempting reset\n"); + reset_chip(); + attempts = 0; + } else { + printf("Device not running - waiting\n"); + attempts++; + } + } + } + sleep_ms(1000); + } +} diff --git a/encrypted/CMakeLists.txt b/encrypted/CMakeLists.txt new file mode 100644 index 000000000..c7346d4ea --- /dev/null +++ b/encrypted/CMakeLists.txt @@ -0,0 +1,6 @@ +if (TARGET pico_mbedtls) + add_subdirectory_exclude_platforms(hello_encrypted host rp2040 rp2350-riscv) +else() + # Assume picotool has no signing support, if no pico_mbedtls available + message("Skipping encrypted example as pico_mbedtls unavailable") +endif () diff --git a/encrypted/hello_encrypted/CMakeLists.txt b/encrypted/hello_encrypted/CMakeLists.txt new file mode 100644 index 000000000..a627c4d25 --- /dev/null +++ b/encrypted/hello_encrypted/CMakeLists.txt @@ -0,0 +1,95 @@ +# Example encrypted binary - this should be secure against side channel attacks +add_executable(hello_encrypted + hello_encrypted.c + secret.S + ) + +# include directory containing secret.txt +target_include_directories(hello_encrypted PRIVATE ${CMAKE_CURRENT_LIST_DIR}) + +# add dependency on secret.txt +set_property(SOURCE secret.S APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/secret.txt) + +# pull in common dependencies +target_link_libraries(hello_encrypted pico_stdlib) + +# enable stdio_usb and stdio_uart +pico_enable_stdio_uart(hello_encrypted 1) +pico_enable_stdio_usb(hello_encrypted 1) + +# set as no_flash binary +pico_set_binary_type(hello_encrypted no_flash) + +# set version (optional) +pico_set_binary_version(hello_encrypted MAJOR 7 MINOR 3) + +# set tbyb (optional) +# target_compile_definitions(hello_encrypted PRIVATE PICO_CRT0_IMAGE_TYPE_TBYB=1) + +# configure otp output +pico_set_otp_key_output_file(hello_encrypted ${CMAKE_CURRENT_BINARY_DIR}/otp.json) + +# sign, hash, and encrypt +pico_sign_binary(hello_encrypted ${CMAKE_CURRENT_LIST_DIR}/private.pem) +pico_hash_binary(hello_encrypted) +pico_encrypt_binary(hello_encrypted + ${CMAKE_CURRENT_LIST_DIR}/privateaes.bin + ${CMAKE_CURRENT_LIST_DIR}/ivsalt.bin + EMBED) + +# package uf2 in flash +pico_package_uf2_output(hello_encrypted) + +# create map/bin/hex/uf2 file etc. +pico_add_extra_outputs(hello_encrypted) + +# add url via pico_set_program_url +example_auto_set_url(hello_encrypted) + + +# Example encrypted binary using MbedTLS - this is faster, but not secure against side channel attacks +add_executable(hello_encrypted_mbedtls + hello_encrypted.c + secret.S + ) + +# include directory containing secret.txt +target_include_directories(hello_encrypted_mbedtls PRIVATE ${CMAKE_CURRENT_LIST_DIR}) + +# pull in common dependencies +target_link_libraries(hello_encrypted_mbedtls pico_stdlib) + +# enable stdio_usb and stdio_uart +pico_enable_stdio_uart(hello_encrypted_mbedtls 1) +pico_enable_stdio_usb(hello_encrypted_mbedtls 1) + +# set as no_flash binary +pico_set_binary_type(hello_encrypted_mbedtls no_flash) + +# set version (optional) +pico_set_binary_version(hello_encrypted_mbedtls MAJOR 7 MINOR 3) + +# set tbyb (optional) +# target_compile_definitions(hello_encrypted_mbedtls PRIVATE PICO_CRT0_IMAGE_TYPE_TBYB=1) + +# configure otp output +pico_set_otp_key_output_file(hello_encrypted_mbedtls ${CMAKE_CURRENT_BINARY_DIR}/otp_mbedtls.json) + +# sign, hash, and encrypt using MbedTLS +pico_sign_binary(hello_encrypted_mbedtls ${CMAKE_CURRENT_LIST_DIR}/private.pem) +pico_hash_binary(hello_encrypted_mbedtls) +pico_encrypt_binary(hello_encrypted_mbedtls + ${CMAKE_CURRENT_LIST_DIR}/privateaes.bin + ${CMAKE_CURRENT_LIST_DIR}/ivsalt.bin + EMBED + MBEDTLS + OTP_KEY_PAGE 29) + +# package uf2 in flash +pico_package_uf2_output(hello_encrypted_mbedtls) + +# create map/bin/hex/uf2 file etc. +pico_add_extra_outputs(hello_encrypted_mbedtls) + +# add url via pico_set_program_url +example_auto_set_url(hello_encrypted_mbedtls) diff --git a/encrypted/hello_encrypted/README.md b/encrypted/hello_encrypted/README.md new file mode 100644 index 000000000..bf7a8a03e --- /dev/null +++ b/encrypted/hello_encrypted/README.md @@ -0,0 +1,28 @@ +For security you **must** replace private.pem and privateaes.bin with your own keys, and ivsalt.bin with your own per-device salt. Make sure you **don't lose your keys and salts**, else you may not be able to update the code on your device. + +Your signing key must be for the _secp256k1_ curve, in PEM format. You can create a .PEM file with: + +```bash +openssl ecparam -name secp256k1 -genkey -out private.pem +``` + +The AES key is stored in a 32 byte binary file - you can create one with + +```bash +dd if=/dev/urandom of=privateaes.bin bs=1 count=32 +``` + +or in Powershell 7 +```powershell +[byte[]] $(Get-SecureRandom -Maximum 256 -Count 32) | Set-Content privateaes.bin -AsByteStream +``` + +The IV salt is just a 16 byte binary file - you can create it the same way, replacing `32` with `16` and `privateaes.bin` with `ivsalt.bin` in the commands above. + +You will need to program your OTP using the `otp.json` file generated by the build in your build folder +NOTE: This will enable secure boot on your device, so only correctly signed binaries can then run, and will also lock down the OTP pages the AES key and IV salt are stored in. +```bash +picotool otp load otp.json +``` + +> For more information on security see chapter 10 of the [RP2350 datasheet](https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf), and for information on how to sign other binaries to run on a secure chip see section 5.10 diff --git a/encrypted/hello_encrypted/hello_encrypted.c b/encrypted/hello_encrypted/hello_encrypted.c new file mode 100644 index 000000000..ff578bdcc --- /dev/null +++ b/encrypted/hello_encrypted/hello_encrypted.c @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include "pico/stdlib.h" +#include "pico/bootrom.h" +#include "hardware/sync.h" + +int main() { + enable_interrupts(); + stdio_init_all(); + +#if PICO_CRT0_IMAGE_TYPE_TBYB + boot_info_t boot_info = {}; + int ret = rom_get_boot_info(&boot_info); + if (ret) { + // BOOT_TBYB_AND_UPDATE_FLAG_BUY_PENDING will always be set, but check anyway + if (boot_info.tbyb_and_update_info & BOOT_TBYB_AND_UPDATE_FLAG_BUY_PENDING) { + // Need to check flash_update_base is set to see if this is a TBYB update + uint32_t flash_update_base = boot_info.reboot_params[0]; + if (flash_update_base) { + printf("Perform self-check... "); + if (1 == 1) { // replace this with your actual self-check function + printf("passed\n"); + } else { + printf("failed - looping forever\n"); + while (true) sleep_ms(1000); + } + } + uint32_t buf_size = flash_update_base ? 4096 : 0; + uint8_t* buffer = flash_update_base ? malloc(buf_size) : NULL; + int ret = rom_explicit_buy(buffer, buf_size); + assert(ret == 0); + if (buffer) free(buffer); + } + } +#endif + extern char secret_data[]; + + while (true) { + printf("Hello, world!\n"); + printf("I'm a self-decrypting binary\n"); + printf("My secret is...\n"); + sleep_ms(1000); + printf(secret_data); + sleep_ms(10000); + } +} diff --git a/encrypted/hello_encrypted/ivsalt.bin b/encrypted/hello_encrypted/ivsalt.bin new file mode 100644 index 000000000..fb9ef50b8 --- /dev/null +++ b/encrypted/hello_encrypted/ivsalt.bin @@ -0,0 +1 @@ +���x��%�^��=T�Č \ No newline at end of file diff --git a/encrypted/hello_encrypted/private.pem b/encrypted/hello_encrypted/private.pem new file mode 100644 index 000000000..bf777d897 --- /dev/null +++ b/encrypted/hello_encrypted/private.pem @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BgUrgQQACg== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHQCAQEEIAXAdiilH8wT07TESUzWPt+BY9+NcchvYU3xbnpK+CBNoAcGBSuBBAAK +oUQDQgAEYYJtMQFGW4AB94tU3u/Qir5sRcYjBYMqCa+8gxsYd9OwMS3dqWKsnVBz +dyy7bFWdJzXDMb9o20xRRd57Q9xSYw== +-----END EC PRIVATE KEY----- diff --git a/encrypted/hello_encrypted/privateaes.bin b/encrypted/hello_encrypted/privateaes.bin new file mode 100644 index 000000000..533d79266 --- /dev/null +++ b/encrypted/hello_encrypted/privateaes.bin @@ -0,0 +1 @@ +�U&a�n�X�� :қ���u������r��� \ No newline at end of file diff --git a/encrypted/hello_encrypted/secret.S b/encrypted/hello_encrypted/secret.S new file mode 100644 index 000000000..0014c0d6e --- /dev/null +++ b/encrypted/hello_encrypted/secret.S @@ -0,0 +1,5 @@ +.section .rodata +.global secret_data +secret_data: +.incbin "secret.txt" +.byte 0 \ No newline at end of file diff --git a/encrypted/hello_encrypted/secret.txt b/encrypted/hello_encrypted/secret.txt new file mode 100644 index 000000000..351db192a --- /dev/null +++ b/encrypted/hello_encrypted/secret.txt @@ -0,0 +1 @@ +TODO: Put a funny secret here diff --git a/flash/CMakeLists.txt b/flash/CMakeLists.txt index 22de94038..fe03dcba9 100644 --- a/flash/CMakeLists.txt +++ b/flash/CMakeLists.txt @@ -5,6 +5,7 @@ if (TARGET hardware_flash) add_subdirectory_exclude_platforms(ssi_dma "rp2350.*") add_subdirectory_exclude_platforms(xip_stream) add_subdirectory_exclude_platforms(runtime_flash_permissions rp2040) + add_subdirectory_exclude_platforms(partition_info rp2040) else() message("Skipping flash examples as hardware_flash is unavailable on this platform") -endif() \ No newline at end of file +endif() diff --git a/flash/partition_info/CMakeLists.txt b/flash/partition_info/CMakeLists.txt new file mode 100644 index 000000000..4c178a465 --- /dev/null +++ b/flash/partition_info/CMakeLists.txt @@ -0,0 +1,17 @@ +add_executable(partition_info partition_info.c uf2_family_ids.c) + +target_link_libraries(partition_info PRIVATE + pico_stdlib + pico_bootrom + hardware_flash + boot_uf2_headers + ) + +# add a partition table +pico_embed_pt_in_binary(partition_info ${CMAKE_CURRENT_LIST_DIR}/pt.json) + +# create map/bin/hex/uf2 file etc. +pico_add_extra_outputs(partition_info) + +# add url via pico_set_program_url +example_auto_set_url(partition_info) diff --git a/flash/partition_info/partition_info.c b/flash/partition_info/partition_info.c new file mode 100644 index 000000000..9691ad3a9 --- /dev/null +++ b/flash/partition_info/partition_info.c @@ -0,0 +1,212 @@ +/** + * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include +#include +#include "pico/stdlib.h" +#include "pico/bootrom.h" +#include "boot/picobin.h" +#include "hardware/flash.h" +#include "uf2_family_ids.h" + +#define PART_LOC_FIRST(x) ( ((x) & PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_BITS) >> PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_LSB ) +#define PART_LOC_LAST(x) ( ((x) & PICOBIN_PARTITION_LOCATION_LAST_SECTOR_BITS) >> PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB ) + +#define PARTITION_LOCATION_AND_FLAGS_SIZE 2 +#define PARTITION_ID_SIZE 2 +#define PARTITION_NAME_MAX 127 // name length is indicated by 7 bits +#define PARTITION_TABLE_FIXED_INFO_SIZE (4 + PARTITION_TABLE_MAX_PARTITIONS * (PARTITION_LOCATION_AND_FLAGS_SIZE + PARTITION_ID_SIZE)) + +/* + * Stores partition table information and data read status + */ +typedef struct { + uint32_t table[PARTITION_TABLE_FIXED_INFO_SIZE]; + uint32_t fields; + bool has_partition_table; + int partition_count; + uint32_t unpartitioned_space_first_sector; + uint32_t unpartitioned_space_last_sector; + uint32_t flags_and_permissions; + int current_partition; + size_t pos; + int status; +} pico_partition_table_t; + +/* + * Stores information on each partition + */ +typedef struct { + uint32_t first_sector; + uint32_t last_sector; + uint32_t flags_and_permissions; + bool has_id; + uint64_t partition_id; + bool has_name; + char name[PARTITION_NAME_MAX + 1]; + uint32_t extra_family_id_count; + uint32_t extra_family_ids[PARTITION_EXTRA_FAMILY_ID_MAX]; +} pico_partition_t; + + +/* + * Read the partition table information. + * + * See the RP2350 datasheet 5.1.2, 5.4.8.16 for flags and structures that can be specified. + */ +int read_partition_table(pico_partition_table_t *pt) { + // Reads fixed size fields + uint32_t flags = PT_INFO_PT_INFO | PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_PARTITION_ID; + int rc = rom_get_partition_table_info(pt->table, sizeof(pt->table), flags); + if (rc < 0) { + pt->partition_count = 0; + pt->status = rc; + return rc; + } + + size_t pos = 0; + pt->fields = pt->table[pos++]; + assert(pt->fields == flags); + pt->partition_count = pt->table[pos] & 0x000000FF; + pt->has_partition_table = pt->table[pos] & 0x00000100; + pos++; + uint32_t location = pt->table[pos++]; + pt->unpartitioned_space_first_sector = PART_LOC_FIRST(location); + pt->unpartitioned_space_last_sector = PART_LOC_LAST(location); + pt->flags_and_permissions = pt->table[pos++]; + pt->current_partition = 0; + pt->pos = pos; + pt->status = 0; + + return 0; +} + +/* + * Extract each partition information + */ +bool read_next_partition(pico_partition_table_t *pt, pico_partition_t *p) { + if (pt->current_partition >= pt->partition_count) { + return false; + } + + size_t pos = pt->pos; + uint32_t location = pt->table[pos++]; + p->first_sector = PART_LOC_FIRST(location); + p->last_sector = PART_LOC_LAST(location); + p->flags_and_permissions = pt->table[pos++]; + p->has_name = p->flags_and_permissions & PICOBIN_PARTITION_FLAGS_HAS_NAME_BITS; + p->has_id = p->flags_and_permissions & PICOBIN_PARTITION_FLAGS_HAS_ID_BITS; + + if (p->has_id) { + uint32_t id_low = pt->table[pos++]; + uint32_t id_high = pt->table[pos++]; + p->partition_id = ((uint64_t)id_high << 32) | id_low; + } else { + p->partition_id = 0; + } + pt->pos = pos; + + p->extra_family_id_count = (p->flags_and_permissions & PICOBIN_PARTITION_FLAGS_ACCEPTS_NUM_EXTRA_FAMILIES_BITS) + >> PICOBIN_PARTITION_FLAGS_ACCEPTS_NUM_EXTRA_FAMILIES_LSB; + if (p->extra_family_id_count | p->has_name) { + // Read variable length fields + uint32_t extra_family_ids_and_name[PARTITION_EXTRA_FAMILY_ID_MAX + (((PARTITION_NAME_MAX + 1) / sizeof(uint32_t)) + 1)]; + uint32_t flags = PT_INFO_SINGLE_PARTITION | PT_INFO_PARTITION_FAMILY_IDS | PT_INFO_PARTITION_NAME; + int rc = rom_get_partition_table_info(extra_family_ids_and_name, sizeof(extra_family_ids_and_name), + (pt->current_partition << 24 | flags)); + if (rc < 0) { + pt->status = rc; + return false; + } + size_t pos_ = 0; + uint32_t __attribute__((unused)) fields = extra_family_ids_and_name[pos_++]; + assert(fields == flags); + for (size_t i = 0; i < p->extra_family_id_count; i++, pos_++) { + p->extra_family_ids[i] = extra_family_ids_and_name[pos_]; + } + + if (p->has_name) { + uint8_t *name_buf = (uint8_t *)&extra_family_ids_and_name[pos_]; + uint8_t name_length = *name_buf++ & 0x7F; + memcpy(p->name, name_buf, name_length); + p->name[name_length] = '\0'; + } + } + if (!p->has_name) + p->name[0] = '\0'; + + pt->current_partition++; + return true; +} + +int main() { + stdio_init_all(); + + pico_partition_table_t pt; + int rc; + rc = read_partition_table(&pt); + if (rc != 0) { + panic("rom_get_partition_table_info returned %d", pt.status); + } + if (!pt.has_partition_table) { + printf("there is no partition table\n"); + } else if (pt.partition_count == 0) { + printf("the partition table is empty\n"); + } + + uf2_family_ids_t *family_ids = uf2_family_ids_new(pt.flags_and_permissions); + char *str_family_ids = uf2_family_ids_join(family_ids, ", "); + printf("un-partitioned_space: S(%s%s) NSBOOT(%s%s) NS(%s%s) uf2 { %s }\n", + (pt.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_S_R_BITS ? "r" : ""), + (pt.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_S_W_BITS ? "w" : ""), + (pt.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NSBOOT_R_BITS ? "r" : ""), + (pt.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NSBOOT_W_BITS ? "w" : ""), + (pt.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NS_R_BITS ? "r" : ""), + (pt.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NS_W_BITS ? "w" : ""), + str_family_ids); + free(str_family_ids); + uf2_family_ids_free(family_ids); + + if (pt.partition_count == 0) { + return 0; + } + printf("partitions:\n"); + pico_partition_t p; + while (read_next_partition(&pt, &p)) { + printf("%3d:", pt.current_partition - 1); + + printf(" %08x->%08x S(%s%s) NSBOOT(%s%s) NS(%s%s)", + p.first_sector * FLASH_SECTOR_SIZE, (p.last_sector + 1) * FLASH_SECTOR_SIZE, + (p.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_S_R_BITS ? "r" : ""), + (p.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_S_W_BITS ? "w" : ""), + (p.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NSBOOT_R_BITS ? "r" : ""), + (p.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NSBOOT_W_BITS ? "w" : ""), + (p.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NS_R_BITS ? "r" : ""), + (p.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NS_W_BITS ? "w" : "")); + if (p.has_id) { + printf(", id=%016llx", p.partition_id); + } + if (p.has_name) { + printf(", \"%s\"", p.name); + } + + // print UF2 family ID + family_ids = uf2_family_ids_new(p.flags_and_permissions); + for (size_t i = 0; i < p.extra_family_id_count; i++) { + uf2_family_ids_add_extra_family_id(family_ids, p.extra_family_ids[i]); + } + str_family_ids = uf2_family_ids_join(family_ids, ", "); + printf(", uf2 { %s }", str_family_ids); + free(str_family_ids); + uf2_family_ids_free(family_ids); + + printf("\n"); + } + if (pt.status != 0) { + panic("rom_get_partition_table_info returned %d", pt.status); + } + + return 0; +} diff --git a/flash/partition_info/pt.json b/flash/partition_info/pt.json new file mode 100644 index 000000000..ae68abac7 --- /dev/null +++ b/flash/partition_info/pt.json @@ -0,0 +1,47 @@ +{ + "version": [1, 0], + "unpartitioned": { + "families": ["absolute"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + } + }, + "partitions": [ + { + "name": "Firmware", + "id": 1, + "start": 0, + "size": "512K", + "families": ["rp2350-arm-ns", "rp2350-arm-s", "rp2350-riscv", "0x12345678"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + } + }, + { + "name": "Data", + "id": 2, + "size": "512K", + "families": ["data"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + } + }, + { + "name": "Read only Data", + "id": 3, + "size": "512K", + "families": ["data"], + "permissions": { + "secure": "r", + "nonsecure": "r", + "bootloader": "r" + } + } + ] +} diff --git a/flash/partition_info/uf2_family_ids.c b/flash/partition_info/uf2_family_ids.c new file mode 100644 index 000000000..c8feb27cd --- /dev/null +++ b/flash/partition_info/uf2_family_ids.c @@ -0,0 +1,83 @@ +#include "uf2_family_ids.h" + +#define UF2_FAMILY_ID_HEX_SIZE (2 + 8 * 2 + 1) + +static void _add(uf2_family_ids_t *ids, const char *str) { + ids->items = realloc(ids->items, (ids->count + 1) * sizeof(char *)); + ids->items[ids->count] = strdup(str); + if (ids->items[ids->count] == NULL) { + perror("strdup"); + return; + } + ids->count++; +} + +static void _add_default_families(uf2_family_ids_t *ids, uint32_t flags) { + if (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_ABSOLUTE_BITS) + _add(ids, "absolute"); + if (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_RP2040_BITS) + _add(ids, "rp2040"); + if (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_RP2350_ARM_S_BITS) + _add(ids, "rp2350-arm-s"); + if (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_RP2350_ARM_NS_BITS) + _add(ids, "rp2350-arm-ns"); + if (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_RP2350_RISCV_BITS) + _add(ids, "rp2350-riscv"); + if (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_DATA_BITS) + _add(ids, "data"); +} + +uf2_family_ids_t *uf2_family_ids_new(uint32_t flags) { + uf2_family_ids_t *ids = malloc(sizeof(uf2_family_ids_t)); + ids->count = 0; + ids->items = NULL; + _add_default_families(ids, flags); + return ids; +} + +char *uf2_family_ids_join(const uf2_family_ids_t *ids, const char *sep) { + size_t total_length = 0; + size_t sep_length = strlen(sep); + + for (size_t i = 0; i < ids->count; i++) { + total_length += strlen(ids->items[i]); + if (i < ids->count - 1) + total_length += sep_length; + } + + char *result = calloc(1, total_length + 1); + if (!result) { + perror("calloc"); + return NULL; + } + + result[0] = '\0'; + for (size_t i = 0; i < ids->count; i++) { + strcat(result, ids->items[i]); + if (i < ids->count - 1) + strcat(result, sep); + } + + return result; +} + +void uf2_family_ids_free(uf2_family_ids_t *ids) { + for (size_t i = 0; i < ids->count; i++) { + free(ids->items[i]); + } + free(ids->items); + free(ids); +} + +void uf2_family_ids_add_extra_family_id(uf2_family_ids_t *ids, uint32_t family_id) { + char hex_id[UF2_FAMILY_ID_HEX_SIZE]; + switch (family_id) { + case CYW43_FIRMWARE_FAMILY_ID: + _add(ids, "cyw43-firmware"); + break; + default: + sprintf(hex_id, "0x%08x", family_id); + _add(ids, hex_id); + break; + } +} diff --git a/flash/partition_info/uf2_family_ids.h b/flash/partition_info/uf2_family_ids.h new file mode 100644 index 000000000..8e47c1a2a --- /dev/null +++ b/flash/partition_info/uf2_family_ids.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include + +#include "pico/stdlib.h" +#include "boot/picobin.h" +#include "boot/uf2.h" + + +#define PARTITION_EXTRA_FAMILY_ID_MAX 3 + +typedef struct { + size_t count; + char **items; +} uf2_family_ids_t; + + +uf2_family_ids_t *uf2_family_ids_new(uint32_t flags); +char *uf2_family_ids_join(const uf2_family_ids_t *ids, const char *sep); +void uf2_family_ids_free(uf2_family_ids_t *ids); + +void uf2_family_ids_add_extra_family_id(uf2_family_ids_t *ids, uint32_t family_id); diff --git a/freertos/FreeRTOSConfig_examples_common.h b/freertos/FreeRTOSConfig_examples_common.h index 88d4ac94f..af0ec3686 100755 --- a/freertos/FreeRTOSConfig_examples_common.h +++ b/freertos/FreeRTOSConfig_examples_common.h @@ -70,8 +70,12 @@ #define configMESSAGE_BUFFER_LENGTH_TYPE size_t /* Memory allocation related definitions. */ +#ifndef configSUPPORT_STATIC_ALLOCATION #define configSUPPORT_STATIC_ALLOCATION 0 +#endif +#ifndef configSUPPORT_DYNAMIC_ALLOCATION #define configSUPPORT_DYNAMIC_ALLOCATION 1 +#endif #define configTOTAL_HEAP_SIZE (128*1024) #define configAPPLICATION_ALLOCATED_HEAP 0 diff --git a/freertos/hello_freertos/CMakeLists.txt b/freertos/hello_freertos/CMakeLists.txt index 1da0ce773..a96d34a25 100644 --- a/freertos/hello_freertos/CMakeLists.txt +++ b/freertos/hello_freertos/CMakeLists.txt @@ -1,42 +1,74 @@ -set(TARGET_NAME hello_freertos1) -add_executable(${TARGET_NAME} +# Example running FreeRTOS on 1 core +add_executable(hello_freertos_one_core hello_freertos.c ) -target_include_directories(${TARGET_NAME} PRIVATE +target_include_directories(hello_freertos_one_core PRIVATE ${CMAKE_CURRENT_LIST_DIR}/.. ) -target_link_libraries(${TARGET_NAME} PRIVATE +# Linking to FreeRTOS-Kernel-Heap4 means we use a dynamic heap for allocations +target_link_libraries(hello_freertos_one_core PRIVATE pico_async_context_freertos FreeRTOS-Kernel-Heap4 pico_stdlib ) +# Set the nunber of cores to 1. +# This defaults to 2 in FreeRTOSConfig_examples_common.h if not defined in here +target_compile_definitions(hello_freertos_one_core PRIVATE + configNUMBER_OF_CORES=1 + ) if(PICO_CYW43_SUPPORTED) # For led support on pico_w - target_link_libraries(${TARGET_NAME} PRIVATE + target_link_libraries(hello_freertos_one_core PRIVATE pico_cyw43_arch_none - ) + ) endif() -target_compile_definitions(${TARGET_NAME} PRIVATE - configNUMBER_OF_CORES=1 - ) -pico_add_extra_outputs(${TARGET_NAME}) +pico_add_extra_outputs(hello_freertos_one_core) -set(TARGET_NAME hello_freertos2) -add_executable(${TARGET_NAME} +# Example running FreeRTOS on 2 cores +add_executable(hello_freertos_two_cores hello_freertos.c ) -target_include_directories(${TARGET_NAME} PRIVATE +target_include_directories(hello_freertos_two_cores PRIVATE ${CMAKE_CURRENT_LIST_DIR}/.. ) -target_link_libraries(${TARGET_NAME} PRIVATE +# Linking to FreeRTOS-Kernel-Heap4 to use a dynamic heap for allocations +target_link_libraries(hello_freertos_two_cores PRIVATE pico_async_context_freertos FreeRTOS-Kernel-Heap4 pico_stdlib ) if(PICO_CYW43_SUPPORTED) # For led support on pico_w - target_link_libraries(${TARGET_NAME} PRIVATE + target_link_libraries(hello_freertos_two_cores PRIVATE pico_cyw43_arch_none + ) +endif() +pico_add_extra_outputs(hello_freertos_two_cores) + +# Example running FreeRTOS on 2 cores with static RAM allocation +add_executable(hello_freertos_static_allocation + hello_freertos.c ) +target_include_directories(hello_freertos_static_allocation PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/.. + ) +# Linking to FreeRTOS-Kernel-Static to use static memory instead of a dynamic heap for allocations +target_link_libraries(hello_freertos_static_allocation PRIVATE + pico_async_context_freertos + FreeRTOS-Kernel-Static + pico_stdlib + ) +# Change the configuration to just use static RAM allocation +# If configSUPPORT_DYNAMIC_ALLOCATION is left undefined it will default to 1 +# If configSUPPORT_STATIC_ALLOCATION is left undefined it will default to 0 +target_compile_definitions(hello_freertos_static_allocation PRIVATE + configSUPPORT_STATIC_ALLOCATION=1 + configSUPPORT_DYNAMIC_ALLOCATION=0 + ) +if(PICO_CYW43_SUPPORTED) + # For led support on pico_w + target_link_libraries(hello_freertos_static_allocation PRIVATE + pico_cyw43_arch_none + ) endif() -pico_add_extra_outputs(${TARGET_NAME}) +pico_add_extra_outputs(hello_freertos_static_allocation) diff --git a/freertos/hello_freertos/hello_freertos.c b/freertos/hello_freertos/hello_freertos.c index 77a3c8490..af500155e 100755 --- a/freertos/hello_freertos/hello_freertos.c +++ b/freertos/hello_freertos/hello_freertos.c @@ -48,10 +48,14 @@ static async_context_freertos_t async_context_instance; // Create an async context -static async_context_t *example_async_context(void) { +static async_context_t *create_async_context(void) { async_context_freertos_config_t config = async_context_freertos_default_config(); config.task_priority = WORKER_TASK_PRIORITY; // defaults to ASYNC_CONTEXT_DEFAULT_FREERTOS_TASK_PRIORITY config.task_stack_size = WORKER_TASK_STACK_SIZE; // defaults to ASYNC_CONTEXT_DEFAULT_FREERTOS_TASK_STACK_SIZE +#if configSUPPORT_STATIC_ALLOCATION + static StackType_t async_context_freertos_task_stack[WORKER_TASK_STACK_SIZE]; + config.task_stack = async_context_freertos_task_stack; +#endif if (!async_context_freertos_init(&async_context_instance, &config)) return NULL; return &async_context_instance.core; @@ -59,7 +63,7 @@ static async_context_t *example_async_context(void) { #if USE_LED // Turn led on or off -static void pico_set_led(bool led_on) { +static void set_led(bool led_on) { #if defined PICO_DEFAULT_LED_PIN gpio_put(PICO_DEFAULT_LED_PIN, led_on); #elif defined(CYW43_WL_GPIO_LED_PIN) @@ -68,20 +72,20 @@ static void pico_set_led(bool led_on) { } // Initialise led -static void pico_init_led(void) { +static void init_led(void) { #if defined PICO_DEFAULT_LED_PIN gpio_init(PICO_DEFAULT_LED_PIN); gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); #elif defined(CYW43_WL_GPIO_LED_PIN) hard_assert(cyw43_arch_init() == PICO_OK); - pico_set_led(false); // make sure cyw43 is started + set_led(false); // make sure cyw43 is started #endif } void blink_task(__unused void *params) { bool on = false; printf("blink_task starts\n"); - pico_init_led(); + init_led(); while (true) { #if configNUMBER_OF_CORES > 1 static int last_core_id = -1; @@ -90,7 +94,7 @@ void blink_task(__unused void *params) { printf("blink task is on core %d\n", last_core_id); } #endif - pico_set_led(on); + set_led(on); on = !on; #if LED_BUSY_WAIT @@ -122,13 +126,20 @@ static void do_work(async_context_t *context, async_at_time_worker_t *worker) { async_at_time_worker_t worker_timeout = { .do_work = do_work }; void main_task(__unused void *params) { - async_context_t *context = example_async_context(); + async_context_t *context = create_async_context(); // start the worker running async_context_add_at_time_worker_in_ms(context, &worker_timeout, 0); #if USE_LED // start the led blinking +#if configSUPPORT_STATIC_ALLOCATION + static StackType_t blink_stack[BLINK_TASK_STACK_SIZE]; + static StaticTask_t blink_buf; + xTaskCreateStatic(blink_task, "BlinkThread", BLINK_TASK_STACK_SIZE, NULL, BLINK_TASK_PRIORITY, blink_stack, &blink_buf); +#else + static_assert(configSUPPORT_DYNAMIC_ALLOCATION, ""); xTaskCreate(blink_task, "BlinkThread", BLINK_TASK_STACK_SIZE, NULL, BLINK_TASK_PRIORITY, NULL); -#endif +#endif // configSUPPORT_STATIC_ALLOCATION +#endif // USE_LED int count = 0; while(true) { #if configNUMBER_OF_CORES > 1 @@ -146,11 +157,19 @@ void main_task(__unused void *params) { void vLaunch( void) { TaskHandle_t task; +#if configSUPPORT_STATIC_ALLOCATION + static StackType_t main_stack[MAIN_TASK_STACK_SIZE]; + static StaticTask_t main_buf; + task = xTaskCreateStatic(main_task, "MainThread", MAIN_TASK_STACK_SIZE, NULL, MAIN_TASK_PRIORITY, main_stack, &main_buf); +#else + static_assert(configSUPPORT_DYNAMIC_ALLOCATION, ""); xTaskCreate(main_task, "MainThread", MAIN_TASK_STACK_SIZE, NULL, MAIN_TASK_PRIORITY, &task); - +#endif // configSUPPORT_STATIC_ALLOCATION #if configUSE_CORE_AFFINITY && configNUMBER_OF_CORES > 1 // we must bind the main task to one core (well at least while the init is called) vTaskCoreAffinitySet(task, 1); +#else + (void)task; #endif /* Start the tasks and timer running. */ diff --git a/i2c/lis3dh_i2c/lis3dh_i2c.c b/i2c/lis3dh_i2c/lis3dh_i2c.c index 1a0a40f91..6d280315b 100644 --- a/i2c/lis3dh_i2c/lis3dh_i2c.c +++ b/i2c/lis3dh_i2c/lis3dh_i2c.c @@ -10,7 +10,7 @@ #include "pico/binary_info.h" #include "hardware/i2c.h" -/* Example code to talk to a LIS3DH Mini GPS module. +/* Example code to talk to a LIS3DH Triple Axis Accelerometer This example reads data from all 3 axes of the accelerometer and uses an auxiliary ADC to output temperature values. @@ -22,7 +22,7 @@ GND (physical pin 38) -> GND on LIS3DH board */ -// By default this device is on bus address 0x18 +// By default this device is on bus address 0x18. If this doesn't work, try 0x19. const int ADDRESS = 0x18; const uint8_t CTRL_REG_1 = 0x20; diff --git a/i2c/mpl3115a2_i2c/CMakeLists.txt b/i2c/mpl3115a2_i2c/CMakeLists.txt index db9a5d7b6..cc816832e 100644 --- a/i2c/mpl3115a2_i2c/CMakeLists.txt +++ b/i2c/mpl3115a2_i2c/CMakeLists.txt @@ -2,6 +2,15 @@ add_executable(mpl3115a2_i2c mpl3115a2_i2c.c ) +target_include_directories(mpl3115a2_i2c PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_link_libraries(mpl3115a2_i2c + pico_stdlib + hardware_i2c +) + # pull in common dependencies and additional i2c hardware support target_link_libraries(mpl3115a2_i2c pico_stdlib hardware_i2c) diff --git a/i2c/mpl3115a2_i2c/README.adoc b/i2c/mpl3115a2_i2c/README.adoc index 6ad129b3a..7750b6caf 100644 --- a/i2c/mpl3115a2_i2c/README.adoc +++ b/i2c/mpl3115a2_i2c/README.adoc @@ -4,7 +4,7 @@ This example code shows how to interface the Raspberry Pi Pico to an MPL3115A2 a The board used in this example https://www.adafruit.com/product/1893[comes from Adafruit], but any MPL3115A2 breakouts should work similarly. -The MPL3115A2 makes available two ways of reading its temperature and pressure data. The first is known as polling, where the Pico will continuously read data out of a set of auto-incrementing registers which are refreshed with new data every so often. The second, which this example will demonstrate, uses a 160-byte first-in-first-out (FIFO) queue and configurable interrupts to tell the Pico when to read data. More information regarding when the interrupts can be triggered available https://www.nxp.com/docs/en/data-sheet/MPL3115A2.pdf[in the datasheet]. This example waits for the 32 sample FIFO to overflow, detects this via an interrupt pin, and then averages the 32 samples taken. The sensor is configured to take a sample every second. +The MPL3115A2 makes available two ways of reading its temperature and pressure data. The first is known as polling, where the Pico will continuously read data out of a set of auto-incrementing registers which are refreshed with new data every so often. The second, which this example will demonstrate, uses a 160-byte first-in-first-out (FIFO) queue and configurable interrupts to tell the Pico when to read data. More information regarding when the interrupts can be triggered is available at https://www.nxp.com/docs/en/data-sheet/MPL3115A2.pdf[in the datasheet]. This example waits for the 32 sample FIFO to overflow, detects this via an interrupt pin, and then averages the 32 samples taken. The sensor is configured to take a sample every second. Bit math is used to convert the temperature and altitude data from the raw bits collected in the registers. Take the temperature calculation as an example: it is a 12-bit signed number with 8 integer bits and 4 fractional bits. First, we read the 2 8-bit registers and store them in a buffer. Then, we concatenate them into one unsigned 16-bit integer starting with the OUT_T_MSB register, thus making sure that the last bit of this register is aligned with the MSB in our 16 bit unsigned integer so it is correctly interpreted as the signed bit when we later cast this to a signed 16-bit integer. Finally, the entire number is converted to a float implicitly when we multiply it by 1/2^8 to shift it 8 bits to the right of the decimal point. Though only the last 4 bits of the OUT_T_LSB register hold data, this does not matter as the remaining 4 are held at zero and "disappear" when we shift the decimal point left by 8. Similar logic is applied to the altitude calculation. diff --git a/i2c/mpl3115a2_i2c/mpl3115a2_i2c.c b/i2c/mpl3115a2_i2c/mpl3115a2_i2c.c index d09fc1d40..a1ec4a665 100644 --- a/i2c/mpl3115a2_i2c/mpl3115a2_i2c.c +++ b/i2c/mpl3115a2_i2c/mpl3115a2_i2c.c @@ -9,6 +9,7 @@ #include "pico/binary_info.h" #include "hardware/gpio.h" #include "hardware/i2c.h" +#include "mpl3115a2_i2c.h" /* Example code to talk to an MPL3115A2 altimeter sensor via I2C @@ -42,6 +43,11 @@ #define MPL3115A2_OFF_T _u(0x2C) #define MPL3115A2_OFF_H _u(0x2D) +/*** Sea-level pressure registers ***/ +#define MPL3115A2_BAR_IN_MSB _u(0x14) +#define MPL3115A2_BAR_IN_LSB _u(0x15) + + #define MPL3115A2_FIFO_DISABLED _u(0x00) #define MPL3115A2_FIFO_STOP_ON_OVERFLOW _u(0x80) #define MPL3115A2_FIFO_SIZE 32 @@ -56,12 +62,24 @@ volatile uint8_t fifo_data[MPL3115A2_FIFO_SIZE * MPL3115A2_DATA_BATCH_SIZE]; volatile bool has_new_data = false; -struct mpl3115a2_data_t { - // Q8.4 fixed point - float temperature; - // Q16.4 fixed-point - float altitude; -}; + +/*** Sea-level pressure functions ***/ +// Set sea-level pressure in hectopascals (hPa) +void mpl3115a2_set_sealevel_pressure(float hPa) { + uint16_t bars = (uint16_t)(hPa * 50); // Convert hPa to BAR_IN value (2 Pa/LSB) + uint8_t buf[] = {MPL3115A2_BAR_IN_MSB, (bars >> 8) & 0xFF, bars & 0xFF}; + i2c_write_blocking(i2c_default, ADDR, buf, 3, false); +} + +// Get current sea-level pressure setting in hPa +float mpl3115a2_get_sealevel_pressure() { + uint8_t reg = MPL3115A2_BAR_IN_MSB; + uint8_t buf[2]; + i2c_write_blocking(i2c_default, ADDR, ®, 1, true); + i2c_read_blocking(i2c_default, ADDR, buf, 2, false); + uint16_t bars = (buf[0] << 8) | buf[1]; + return (float)bars / 50.0f; // Convert back to hPa +} void copy_to_vbuf(uint8_t buf1[], volatile uint8_t buf2[], uint buflen) { for (size_t i = 0; i < buflen; i++) { @@ -109,6 +127,9 @@ void mpl3115a2_init() { buf[0] = MPL3115A2_CTRLREG5, buf[1] = 0x40; i2c_write_blocking(i2c_default, ADDR, buf, 2, false); + /*** Default sea-level pressure (1013.25 hPa) ***/ + mpl3115a2_set_sealevel_pressure(1013.25f); + // set p, t and h offsets here if needed // eg. 2's complement number: 0xFF subtracts 1 meter //buf[0] = MPL3115A2_OFF_H, buf[1] = 0xFF; @@ -132,7 +153,6 @@ void gpio_callback(uint gpio, __unused uint32_t events) { // FIFO overflow interrupt // watermark bits set to 0 in F_SETUP reg, so only possible event is an overflow // otherwise, we would read F_STATUS to confirm it was an overflow - printf("FIFO overflow!\n"); // drain the fifo mpl3115a2_read_fifo(fifo_data); // read status register to clear interrupt bit @@ -186,6 +206,9 @@ int main() { mpl3115a2_init(); + // Uncomment to overwrite default sea-level pressure: + // mpl3115a2_set_sealevel_pressure(1020.0f); // Local weather pressure + gpio_set_irq_enabled_with_callback(INT1_PIN, GPIO_IRQ_LEVEL_LOW, true, &gpio_callback); while (1) { @@ -200,6 +223,7 @@ int main() { } printf("%d sample average -> t: %.4f C, h: %.4f m\n", MPL3115A2_FIFO_SIZE, tsum / MPL3115A2_FIFO_SIZE, hsum / MPL3115A2_FIFO_SIZE); + mpl3115a2_get_sealevel_pressure(); // Show current setting has_new_data = false; } sleep_ms(10); diff --git a/i2c/mpl3115a2_i2c/mpl3115a2_i2c.h b/i2c/mpl3115a2_i2c/mpl3115a2_i2c.h new file mode 100644 index 000000000..ee75fda47 --- /dev/null +++ b/i2c/mpl3115a2_i2c/mpl3115a2_i2c.h @@ -0,0 +1,30 @@ +// mpl3115a2.h +#ifndef _MPL3115A2_H +#define _MPL3115A2_H + +#include "pico/stdlib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct mpl3115a2_data_t { + // Q8.4 fixed point + float temperature; + // Q16.4 fixed-point + float altitude; +}; + +void mpl3115a2_init(void); +void mpl3115a2_read_fifo(volatile uint8_t* fifo_buf); +void mpl3115a2_convert_fifo_batch(uint8_t start, volatile uint8_t* buf, struct mpl3115a2_data_t* data); + +// Add NEW prototypes +void mpl3115a2_set_sealevel_pressure(float hPa); +float mpl3115a2_get_sealevel_pressure(void); + +#ifdef __cplusplus +} +#endif + +#endif // _MPL3115A2_H \ No newline at end of file diff --git a/i2c/ssd1306_i2c/ssd1306_i2c.c b/i2c/ssd1306_i2c/ssd1306_i2c.c index 6be5ea42c..f5cad4e37 100644 --- a/i2c/ssd1306_i2c/ssd1306_i2c.c +++ b/i2c/ssd1306_i2c/ssd1306_i2c.c @@ -188,7 +188,7 @@ void SSD1306_scroll(bool on) { 0x00, // dummy byte 0x00, // start page 0 0x00, // time interval - 0x03, // end page 3 SSD1306_NUM_PAGES ?? + SSD1306_NUM_PAGES - 1, // end page 0x00, // dummy byte 0xFF, // dummy byte SSD1306_SET_SCROLL | (on ? 0x01 : 0) // Start/stop scrolling diff --git a/pico_w/bt/CMakeLists.txt b/pico_w/bt/CMakeLists.txt index e14345398..3a0d5df64 100644 --- a/pico_w/bt/CMakeLists.txt +++ b/pico_w/bt/CMakeLists.txt @@ -176,10 +176,6 @@ if (TARGET FreeRTOS-Kernel) CYW43_TASK_STACK_SIZE=1024 NO_SYS=0 ) - target_include_directories(picow_bt_example_common_freertos INTERFACE - ${FREERTOS_KERNEL_PATH}/include - ${FREERTOS_KERNEL_PATH}/portable/ThirdParty/GCC/RP2040/include - ) # variant: no cyw43 lwip | freertos add_library(picow_bt_example_no_cyw43_lwip_freertos INTERFACE) diff --git a/pico_w/bt/standalone/CMakeLists.txt b/pico_w/bt/standalone/CMakeLists.txt index 28d20b687..ef03fe482 100644 --- a/pico_w/bt/standalone/CMakeLists.txt +++ b/pico_w/bt/standalone/CMakeLists.txt @@ -1,68 +1,2 @@ -# Standalone example that reads from the on board temperature sensor and sends notifications via BLE -# Flashes slowly each second to show it's running -add_executable(picow_ble_temp_sensor - server.c server_common.c - ) -target_link_libraries(picow_ble_temp_sensor - pico_stdlib - pico_btstack_ble - pico_btstack_cyw43 - pico_cyw43_arch_none - hardware_adc - ) -target_include_directories(picow_ble_temp_sensor PRIVATE - ${CMAKE_CURRENT_LIST_DIR} # For btstack config - ) -pico_btstack_make_gatt_header(picow_ble_temp_sensor PRIVATE "${CMAKE_CURRENT_LIST_DIR}/temp_sensor.gatt") - -pico_add_extra_outputs(picow_ble_temp_sensor) -example_auto_set_url(picow_ble_temp_sensor) - -# Standalone example that connects to picow_ble_temp_sensor and reads the temperature -# Flahes once quickly each second when it's running but not connected to another device -# Flashes twice quickly each second when connected to another device and reading it's temperature -add_executable(picow_ble_temp_reader - client.c - ) -target_link_libraries(picow_ble_temp_reader - pico_stdlib - pico_btstack_ble - pico_btstack_cyw43 - pico_cyw43_arch_none - hardware_adc - ) -target_include_directories(picow_ble_temp_reader PRIVATE - ${CMAKE_CURRENT_LIST_DIR} # For btstack config - ) -target_compile_definitions(picow_ble_temp_reader PRIVATE - RUNNING_AS_CLIENT=1 -) - -pico_add_extra_outputs(picow_ble_temp_reader) -example_auto_set_url(picow_ble_temp_reader) - -if (WIFI_SSID AND WIFI_PASSWORD) - # Another version of the sensor example, but this time also runs iperf over wifi - add_executable(picow_ble_temp_sensor_with_wifi - server_with_wifi.c server_common.c - ) - target_link_libraries(picow_ble_temp_sensor_with_wifi - pico_stdlib - pico_btstack_ble - pico_btstack_cyw43 - pico_cyw43_arch_lwip_threadsafe_background - pico_lwip_iperf - hardware_adc - ) - target_include_directories(picow_ble_temp_sensor_with_wifi PRIVATE - ${CMAKE_CURRENT_LIST_DIR} # For btstack config - ) - target_compile_definitions(picow_ble_temp_sensor_with_wifi PRIVATE - WIFI_SSID=\"${WIFI_SSID}\" - WIFI_PASSWORD=\"${WIFI_PASSWORD}\" - ) - pico_btstack_make_gatt_header(picow_ble_temp_sensor_with_wifi PRIVATE "${CMAKE_CURRENT_LIST_DIR}/temp_sensor.gatt") - - pico_add_extra_outputs(picow_ble_temp_sensor_with_wifi) - example_auto_set_url(picow_ble_temp_sensor_with_wifi) -endif() +add_subdirectory(client) +add_subdirectory(server) diff --git a/pico_w/bt/standalone/btstack_config.h b/pico_w/bt/standalone/btstack_config_common.h similarity index 95% rename from pico_w/bt/standalone/btstack_config.h rename to pico_w/bt/standalone/btstack_config_common.h index c392dffe9..336e547fe 100644 --- a/pico_w/bt/standalone/btstack_config.h +++ b/pico_w/bt/standalone/btstack_config_common.h @@ -1,5 +1,5 @@ -#ifndef _PICO_BTSTACK_BTSTACK_CONFIG_H -#define _PICO_BTSTACK_BTSTACK_CONFIG_H +#ifndef _PICO_BTSTACK_CONFIG_COMMON_H +#define _PICO_BTSTACK_CONFIG_COMMON_H #ifndef ENABLE_BLE #error Please link to pico_btstack_ble diff --git a/pico_w/bt/standalone/client/CMakeLists.txt b/pico_w/bt/standalone/client/CMakeLists.txt new file mode 100644 index 000000000..808d2fdf9 --- /dev/null +++ b/pico_w/bt/standalone/client/CMakeLists.txt @@ -0,0 +1,23 @@ +# Standalone example that connects to picow_ble_temp_sensor and reads the temperature +# Flahes once quickly each second when it's running but not connected to another device +# Flashes twice quickly each second when connected to another device and reading it's temperature +add_executable(picow_ble_temp_reader + client.c + ) +target_link_libraries(picow_ble_temp_reader + pico_stdlib + pico_btstack_ble + pico_btstack_cyw43 + pico_cyw43_arch_none + hardware_adc + ) +target_include_directories(picow_ble_temp_reader PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # For our common btstack config + ) +target_compile_definitions(picow_ble_temp_reader PRIVATE + RUNNING_AS_CLIENT=1 +) + +pico_add_extra_outputs(picow_ble_temp_reader) +example_auto_set_url(picow_ble_temp_reader) diff --git a/pico_w/bt/standalone/client/btstack_config.h b/pico_w/bt/standalone/client/btstack_config.h new file mode 100644 index 000000000..ce1919916 --- /dev/null +++ b/pico_w/bt/standalone/client/btstack_config.h @@ -0,0 +1,6 @@ +#ifndef _PICO_BTSTACK_CONFIG_H +#define _PICO_BTSTACK_CONFIG_H + +#include "btstack_config_common.h" + +#endif diff --git a/pico_w/bt/standalone/client.c b/pico_w/bt/standalone/client/client.c similarity index 100% rename from pico_w/bt/standalone/client.c rename to pico_w/bt/standalone/client/client.c diff --git a/pico_w/bt/standalone/server.c b/pico_w/bt/standalone/server.c deleted file mode 100644 index 7fa9db74e..000000000 --- a/pico_w/bt/standalone/server.c +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include -#include "btstack.h" -#include "pico/cyw43_arch.h" -#include "pico/btstack_cyw43.h" -#include "hardware/adc.h" -#include "pico/stdlib.h" - -#include "server_common.h" - -#define HEARTBEAT_PERIOD_MS 1000 - -static btstack_timer_source_t heartbeat; -static btstack_packet_callback_registration_t hci_event_callback_registration; - -static void heartbeat_handler(struct btstack_timer_source *ts) { - static uint32_t counter = 0; - counter++; - - // Update the temp every 10s - if (counter % 10 == 0) { - poll_temp(); - if (le_notification_enabled) { - att_server_request_can_send_now_event(con_handle); - } - } - - // Invert the led - static int led_on = true; - led_on = !led_on; - cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_on); - - // Restart timer - btstack_run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS); - btstack_run_loop_add_timer(ts); -} - -int main() { - stdio_init_all(); - - // initialize CYW43 driver architecture (will enable BT if/because CYW43_ENABLE_BLUETOOTH == 1) - if (cyw43_arch_init()) { - printf("failed to initialise cyw43_arch\n"); - return -1; - } - - // Initialise adc for the temp sensor - adc_init(); - adc_select_input(ADC_CHANNEL_TEMPSENSOR); - adc_set_temp_sensor_enabled(true); - - l2cap_init(); - sm_init(); - - att_server_init(profile_data, att_read_callback, att_write_callback); - - // inform about BTstack state - hci_event_callback_registration.callback = &packet_handler; - hci_add_event_handler(&hci_event_callback_registration); - - // register for ATT event - att_server_register_packet_handler(packet_handler); - - // set one-shot btstack timer - heartbeat.process = &heartbeat_handler; - btstack_run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS); - btstack_run_loop_add_timer(&heartbeat); - - // turn on bluetooth! - hci_power_control(HCI_POWER_ON); - - // btstack_run_loop_execute is only required when using the 'polling' method (e.g. using pico_cyw43_arch_poll library). - // This example uses the 'threadsafe background` method, where BT work is handled in a low priority IRQ, so it - // is fine to call bt_stack_run_loop_execute() but equally you can continue executing user code. - -#if 0 // btstack_run_loop_execute() is not required, so lets not use it - btstack_run_loop_execute(); -#else - // this core is free to do it's own stuff except when using 'polling' method (in which case you should use - // btstacK_run_loop_ methods to add work to the run loop. - - // this is a forever loop in place of where user code would go. - while(true) { - sleep_ms(1000); - } -#endif - return 0; -} diff --git a/pico_w/bt/standalone/server/CMakeLists.txt b/pico_w/bt/standalone/server/CMakeLists.txt new file mode 100644 index 000000000..b18ac013a --- /dev/null +++ b/pico_w/bt/standalone/server/CMakeLists.txt @@ -0,0 +1,20 @@ +# Standalone example that reads from the on board temperature sensor and sends notifications via BLE +# Flashes slowly each second to show it's running +add_executable(picow_ble_temp_sensor + server.c + ) +target_link_libraries(picow_ble_temp_sensor + pico_stdlib + pico_btstack_ble + pico_btstack_cyw43 + pico_cyw43_arch_none + hardware_adc + ) +target_include_directories(picow_ble_temp_sensor PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # For our common btstack config + ) +pico_btstack_make_gatt_header(picow_ble_temp_sensor PRIVATE "${CMAKE_CURRENT_LIST_DIR}/temp_sensor.gatt") + +pico_add_extra_outputs(picow_ble_temp_sensor) +example_auto_set_url(picow_ble_temp_sensor) diff --git a/pico_w/bt/standalone/server/btstack_config.h b/pico_w/bt/standalone/server/btstack_config.h new file mode 100644 index 000000000..ce1919916 --- /dev/null +++ b/pico_w/bt/standalone/server/btstack_config.h @@ -0,0 +1,6 @@ +#ifndef _PICO_BTSTACK_CONFIG_H +#define _PICO_BTSTACK_CONFIG_H + +#include "btstack_config_common.h" + +#endif diff --git a/pico_w/bt/standalone/server_common.c b/pico_w/bt/standalone/server/server.c similarity index 53% rename from pico_w/bt/standalone/server_common.c rename to pico_w/bt/standalone/server/server.c index fd429196c..f7466d60b 100644 --- a/pico_w/bt/standalone/server_common.c +++ b/pico_w/bt/standalone/server/server.c @@ -6,12 +6,17 @@ #include #include "btstack.h" +#include "pico/cyw43_arch.h" +#include "pico/btstack_cyw43.h" #include "hardware/adc.h" +#include "pico/stdlib.h" #include "temp_sensor.h" -#include "server_common.h" +#define HEARTBEAT_PERIOD_MS 1000 +#define ADC_CHANNEL_TEMPSENSOR 4 #define APP_AD_FLAGS 0x06 + static uint8_t adv_data[] = { // Flags general discoverable 0x02, BLUETOOTH_DATA_TYPE_FLAGS, APP_AD_FLAGS, @@ -24,8 +29,13 @@ static const uint8_t adv_data_len = sizeof(adv_data); int le_notification_enabled; hci_con_handle_t con_handle; uint16_t current_temp; +static btstack_timer_source_t heartbeat; +static btstack_packet_callback_registration_t hci_event_callback_registration; + +extern uint8_t const profile_data[]; +static void poll_temp(void); -void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { +static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { UNUSED(size); UNUSED(channel); bd_addr_t local_addr; @@ -63,7 +73,7 @@ void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint } } -uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size) { +static uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size) { UNUSED(connection_handle); if (att_handle == ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_TEMPERATURE_01_VALUE_HANDLE){ @@ -72,11 +82,11 @@ uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t att_hand return 0; } -int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) { +static int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) { UNUSED(transaction_mode); UNUSED(offset); UNUSED(buffer_size); - + if (att_handle != ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_TEMPERATURE_01_CLIENT_CONFIGURATION_HANDLE) return 0; le_notification_enabled = little_endian_read_16(buffer, 0) == GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION; con_handle = connection_handle; @@ -86,7 +96,7 @@ int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, return 0; } -void poll_temp(void) { +static void poll_temp(void) { adc_select_input(ADC_CHANNEL_TEMPSENSOR); uint32_t raw32 = adc_read(); const uint32_t bits = 12; @@ -97,10 +107,96 @@ void poll_temp(void) { // ref https://github.com/raspberrypi/pico-micropython-examples/blob/master/adc/temperature.py const float conversion_factor = 3.3 / (65535); float reading = raw16 * conversion_factor; - + // The temperature sensor measures the Vbe voltage of a biased bipolar diode, connected to the fifth ADC channel - // Typically, Vbe = 0.706V at 27 degrees C, with a slope of -1.721mV (0.001721) per degree. + // Typically, Vbe = 0.706V at 27 degrees C, with a slope of -1.721mV (0.001721) per degree. float deg_c = 27 - (reading - 0.706) / 0.001721; current_temp = deg_c * 100; printf("Write temp %.2f degc\n", deg_c); - } \ No newline at end of file + } + +static void heartbeat_handler(struct btstack_timer_source *ts) { + static uint32_t counter = 0; + counter++; + + // Update the temp every 10s + if (counter % 10 == 0) { + poll_temp(); + if (le_notification_enabled) { + att_server_request_can_send_now_event(con_handle); + } + } + + // Invert the led + static int led_on = true; + led_on = !led_on; + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_on); + + // Restart timer + btstack_run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS); + btstack_run_loop_add_timer(ts); +} + +static volatile bool key_pressed; +void key_pressed_func(void *param) { + int key = getchar_timeout_us(0); // get any pending key press but don't wait + if (key == 's' || key == 'S') { + key_pressed = true; + } +} + +int main() { + stdio_init_all(); + +restart: + // initialize CYW43 driver architecture (will enable BT if/because CYW43_ENABLE_BLUETOOTH == 1) + if (cyw43_arch_init()) { + printf("failed to initialise cyw43_arch\n"); + return -1; + } + + // Get notified if the user presses a key + printf("Press the \"S\" key to Stop bluetooth\n"); + stdio_set_chars_available_callback(key_pressed_func, NULL); + + // Initialise adc for the temp sensor + adc_init(); + adc_select_input(ADC_CHANNEL_TEMPSENSOR); + adc_set_temp_sensor_enabled(true); + + l2cap_init(); + sm_init(); + + att_server_init(profile_data, att_read_callback, att_write_callback); + + // inform about BTstack state + hci_event_callback_registration.callback = &packet_handler; + hci_add_event_handler(&hci_event_callback_registration); + + // register for ATT event + att_server_register_packet_handler(packet_handler); + + // set one-shot btstack timer + heartbeat.process = &heartbeat_handler; + btstack_run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS); + btstack_run_loop_add_timer(&heartbeat); + + // turn on bluetooth! + hci_power_control(HCI_POWER_ON); + + key_pressed = false; + while(!key_pressed) { + async_context_poll(cyw43_arch_async_context()); + async_context_wait_for_work_until(cyw43_arch_async_context(), at_the_end_of_time); + } + + cyw43_arch_deinit(); + + printf("Press the \"S\" key to Start bluetooth\n"); + key_pressed = false; + while(!key_pressed) { + sleep_ms(1000); + } + goto restart; + return 0; +} diff --git a/pico_w/bt/standalone/temp_sensor.gatt b/pico_w/bt/standalone/server/temp_sensor.gatt similarity index 100% rename from pico_w/bt/standalone/temp_sensor.gatt rename to pico_w/bt/standalone/server/temp_sensor.gatt diff --git a/pico_w/bt/standalone/server_common.h b/pico_w/bt/standalone/server_common.h deleted file mode 100644 index 80b9c59fb..000000000 --- a/pico_w/bt/standalone/server_common.h +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef SERVER_COMMON_H_ -#define SERVER_COMMON_H_ - -#define ADC_CHANNEL_TEMPSENSOR 4 - -extern int le_notification_enabled; -extern hci_con_handle_t con_handle; -extern uint16_t current_temp; -extern uint8_t const profile_data[]; - -void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); -uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size); -int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size); -void poll_temp(void); - -#endif diff --git a/pico_w/bt/standalone/server_with_wifi.c b/pico_w/bt/standalone/server_with_wifi.c deleted file mode 100644 index af17e6f05..000000000 --- a/pico_w/bt/standalone/server_with_wifi.c +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include -#include "btstack.h" -#include "pico/cyw43_arch.h" -#include "pico/stdlib.h" -#include "hardware/adc.h" - -#include "lwip/netif.h" -#include "lwip/ip4_addr.h" -#include "lwip/apps/lwiperf.h" - -#include "server_common.h" - -#define HEARTBEAT_PERIOD_MS 1000 - -static void heartbeat_handler(async_context_t *context, async_at_time_worker_t *worker); - -static async_at_time_worker_t heartbeat_worker = { .do_work = heartbeat_handler }; -static btstack_packet_callback_registration_t hci_event_callback_registration; - -static void heartbeat_handler(async_context_t *context, async_at_time_worker_t *worker) { - static uint32_t counter = 0; - counter++; - - // Update the temp every 10s - if (counter % 10 == 0) { - poll_temp(); - if (le_notification_enabled) { - att_server_request_can_send_now_event(con_handle); - } - } - - // Invert the led - static int led_on = true; - led_on = !led_on; - cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_on); - - // Restart timer - async_context_add_at_time_worker_in_ms(context, &heartbeat_worker, HEARTBEAT_PERIOD_MS); -} - -// Report IP results and exit -static void iperf_report(void *arg, enum lwiperf_report_type report_type, - const ip_addr_t *local_addr, u16_t local_port, const ip_addr_t *remote_addr, u16_t remote_port, - u32_t bytes_transferred, u32_t ms_duration, u32_t bandwidth_kbitpsec) { - static uint32_t total_iperf_megabytes = 0; - uint32_t mbytes = bytes_transferred / 1024 / 1024; - float mbits = bandwidth_kbitpsec / 1000.0; - - total_iperf_megabytes += mbytes; - - printf("Completed iperf transfer of %u MBytes @ %.1f Mbits/sec\n", mbytes, mbits); - printf("Total iperf megabytes since start %u Mbytes\n", total_iperf_megabytes); -} - -int main() { - stdio_init_all(); - - // initialize CYW43 architecture - // - will enable BT if CYW43_ENABLE_BLUETOOTH == 1 - // - will enable lwIP if CYW43_LWIP == 1 - if (cyw43_arch_init()) { - printf("failed to initialise cyw43_arch\n"); - return -1; - } - - // Initialise adc for the temp sensor - adc_init(); - adc_select_input(ADC_CHANNEL_TEMPSENSOR); - adc_set_temp_sensor_enabled(true); - - l2cap_init(); - sm_init(); - att_server_init(profile_data, att_read_callback, att_write_callback); - - // inform about BTstack state - hci_event_callback_registration.callback = &packet_handler; - hci_add_event_handler(&hci_event_callback_registration); - - // register for ATT event - att_server_register_packet_handler(packet_handler); - - // use an async worker for for the led - async_context_add_at_time_worker_in_ms(cyw43_arch_async_context(), &heartbeat_worker, HEARTBEAT_PERIOD_MS); - - // Connect to Wi-Fi - cyw43_arch_enable_sta_mode(); - printf("Connecting to Wi-Fi...\n"); - if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { - printf("failed to connect.\n"); - return 1; - } else { - printf("Connected.\n"); - } - - // setup iperf - cyw43_arch_lwip_begin(); - printf("\nReady, running iperf server at %s\n", ip4addr_ntoa(netif_ip4_addr(netif_list))); - lwiperf_start_tcp_server_default(&iperf_report, NULL); - cyw43_arch_lwip_end(); - - // turn on bluetooth! - hci_power_control(HCI_POWER_ON); - - // For threadsafe background we can just enter a loop - while(true) { - sleep_ms(1000); - } - - cyw43_arch_deinit(); - return 0; -} diff --git a/pico_w/wifi/CMakeLists.txt b/pico_w/wifi/CMakeLists.txt index 31797084b..b096d2583 100644 --- a/pico_w/wifi/CMakeLists.txt +++ b/pico_w/wifi/CMakeLists.txt @@ -20,9 +20,11 @@ else() add_subdirectory_exclude_platforms(http_client) add_subdirectory_exclude_platforms(mqtt) - if (NOT PICO_MBEDTLS_PATH) - message("Skipping tls examples as PICO_MBEDTLS_PATH is not defined") + if (NOT TARGET pico_mbedtls) + message("Skipping tls_client and ota_update examples as Mbed TLS is not available") else() add_subdirectory_exclude_platforms(tls_client) + # note ota_update doesn't use Mbed TLS, but it requires picotool with Mbed TLS, so the above check is a good proxy + add_subdirectory_exclude_platforms(ota_update rp2040) endif() endif() diff --git a/pico_w/wifi/http_client/picow_http_client.c b/pico_w/wifi/http_client/picow_http_client.c index 86eeee29f..d4b9bb57e 100644 --- a/pico_w/wifi/http_client/picow_http_client.c +++ b/pico_w/wifi/http_client/picow_http_client.c @@ -21,7 +21,7 @@ int main() { return 1; } cyw43_arch_enable_sta_mode(); - if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 10000)) { + if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { printf("failed to connect\n"); return 1; } diff --git a/pico_w/wifi/http_client/picow_http_verify.c b/pico_w/wifi/http_client/picow_http_verify.c index a87fc16a3..139b47dc0 100644 --- a/pico_w/wifi/http_client/picow_http_verify.c +++ b/pico_w/wifi/http_client/picow_http_verify.c @@ -66,7 +66,7 @@ int main() { return 1; } cyw43_arch_enable_sta_mode(); - if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 10000)) { + if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { printf("failed to connect\n"); return 1; } diff --git a/pico_w/wifi/httpd/CMakeLists.txt b/pico_w/wifi/httpd/CMakeLists.txt index f028ef6dd..4b28a2483 100644 --- a/pico_w/wifi/httpd/CMakeLists.txt +++ b/pico_w/wifi/httpd/CMakeLists.txt @@ -28,5 +28,5 @@ pico_set_lwip_httpd_content(pico_httpd_content INTERFACE ${CMAKE_CURRENT_LIST_DIR}/content/test.shtml ${CMAKE_CURRENT_LIST_DIR}/content/ledpass.shtml ${CMAKE_CURRENT_LIST_DIR}/content/ledfail.shtml - ${CMAKE_CURRENT_LIST_DIR}/content/img/rpi.png + ${CMAKE_CURRENT_LIST_DIR}/content/img/rpi.png.gz ) diff --git a/pico_w/wifi/httpd/content/img/rpi.png b/pico_w/wifi/httpd/content/img/rpi.png.gz similarity index 99% rename from pico_w/wifi/httpd/content/img/rpi.png rename to pico_w/wifi/httpd/content/img/rpi.png.gz index 753319c89..f6275e050 100755 Binary files a/pico_w/wifi/httpd/content/img/rpi.png and b/pico_w/wifi/httpd/content/img/rpi.png.gz differ diff --git a/pico_w/wifi/httpd/content/index.shtml b/pico_w/wifi/httpd/content/index.shtml index 7952f5925..b5ea397f4 100644 --- a/pico_w/wifi/httpd/content/index.shtml +++ b/pico_w/wifi/httpd/content/index.shtml @@ -4,7 +4,7 @@

Pico httpd example

-

+

Uptime is seconds

CGI handler test

diff --git a/pico_w/wifi/iperf/picow_iperf.c b/pico_w/wifi/iperf/picow_iperf.c index 927e5c7b2..f2ed4b34d 100644 --- a/pico_w/wifi/iperf/picow_iperf.c +++ b/pico_w/wifi/iperf/picow_iperf.c @@ -19,6 +19,17 @@ #error IPERF_SERVER_IP not defined #endif +#if USE_LED +// Invert led +static void led_worker_fn(async_context_t *context, async_at_time_worker_t *worker) { + // Invert the led + static int led_on = true; + led_on = !led_on; + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_on); + async_context_add_at_time_worker_in_ms(context, worker, 1000); +} +#endif + // Report IP results and exit static void iperf_report(void *arg, enum lwiperf_report_type report_type, const ip_addr_t *local_addr, u16_t local_port, const ip_addr_t *remote_addr, u16_t remote_port, @@ -36,12 +47,12 @@ static void iperf_report(void *arg, enum lwiperf_report_type report_type, #endif } +// Note: This is called from an interrupt handler void key_pressed_func(void *param) { int key = getchar_timeout_us(0); // get any pending key press but don't wait if (key == 'd' || key == 'D') { - cyw43_arch_lwip_begin(); - cyw43_arch_disable_sta_mode(); - cyw43_arch_lwip_end(); + bool *exit = (bool*)param; + *exit = true; } } @@ -53,9 +64,6 @@ int main() { return 1; } - // Get notified if the user presses a key - stdio_set_chars_available_callback(key_pressed_func, cyw43_arch_async_context()); - cyw43_arch_enable_sta_mode(); printf("Connecting to Wi-Fi... (press 'd' to disconnect)\n"); if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { @@ -77,23 +85,15 @@ int main() { #endif cyw43_arch_lwip_end(); - while(cyw43_wifi_link_status(&cyw43_state, CYW43_ITF_STA) != CYW43_LINK_DOWN) { + bool exit = false; + stdio_set_chars_available_callback(key_pressed_func, &exit); + #if USE_LED - static absolute_time_t led_time; - static int led_on = true; - - // Invert the led - if (absolute_time_diff_us(get_absolute_time(), led_time) < 0) { - led_on = !led_on; - cyw43_gpio_set(&cyw43_state, 0, led_on); - led_time = make_timeout_time_ms(1000); - - // Check we can read back the led value - bool actual_led_val = !led_on; - cyw43_gpio_get(&cyw43_state, 0, &actual_led_val); - assert(led_on == actual_led_val); - } + // start the led flashing + async_at_time_worker_t led_worker = { .do_work = led_worker_fn }; + async_context_add_at_time_worker_in_ms(cyw43_arch_async_context(), &led_worker, 0); #endif + while(!exit && cyw43_wifi_link_status(&cyw43_state, CYW43_ITF_STA) != CYW43_LINK_DOWN) { // the following #ifdef is only here so this same example can be used in multiple modes; // you do not need it in your code #if PICO_CYW43_ARCH_POLL @@ -102,7 +102,7 @@ int main() { cyw43_arch_poll(); // you can poll as often as you like, however if you have nothing else to do you can // choose to sleep until either a specified time, or cyw43_arch_poll() has work to do: - cyw43_arch_wait_for_work_until(led_time); + cyw43_arch_wait_for_work_until(at_the_end_of_time); #else // if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work // is done via interrupt in the background. This sleep is just an example of some (blocking) @@ -110,6 +110,7 @@ int main() { sleep_ms(1000); #endif } + cyw43_arch_disable_sta_mode(); cyw43_arch_deinit(); printf("Test complete\n"); diff --git a/pico_w/wifi/mbedtls_config_examples_common.h b/pico_w/wifi/mbedtls_config_examples_common.h index 5ffe3b6e6..fbfae97a3 100644 --- a/pico_w/wifi/mbedtls_config_examples_common.h +++ b/pico_w/wifi/mbedtls_config_examples_common.h @@ -11,6 +11,7 @@ #define MBEDTLS_ALLOW_PRIVATE_ACCESS #define MBEDTLS_HAVE_TIME +#define MBEDTLS_PLATFORM_MS_TIME_ALT #define MBEDTLS_CIPHER_MODE_CBC #define MBEDTLS_ECP_DP_SECP192R1_ENABLED diff --git a/pico_w/wifi/mqtt/CMakeLists.txt b/pico_w/wifi/mqtt/CMakeLists.txt index 23dfa250f..76dcdcca3 100644 --- a/pico_w/wifi/mqtt/CMakeLists.txt +++ b/pico_w/wifi/mqtt/CMakeLists.txt @@ -33,11 +33,10 @@ if (NOT MQTT_CERT_INC) set(MQTT_CERT_INC mqtt_client.inc) endif() -set(TARGET_NAME picow_mqtt_client) -add_executable(${TARGET_NAME} +add_executable(picow_mqtt_client mqtt_client.c ) -target_link_libraries(${TARGET_NAME} +target_link_libraries(picow_mqtt_client pico_stdlib hardware_adc pico_cyw43_arch_lwip_threadsafe_background @@ -45,31 +44,31 @@ target_link_libraries(${TARGET_NAME} pico_mbedtls pico_lwip_mbedtls ) -target_include_directories(${TARGET_NAME} PRIVATE +target_include_directories(picow_mqtt_client PRIVATE ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required ) -target_compile_definitions(${TARGET_NAME} PRIVATE +target_compile_definitions(picow_mqtt_client PRIVATE WIFI_SSID=\"${WIFI_SSID}\" WIFI_PASSWORD=\"${WIFI_PASSWORD}\" MQTT_SERVER=\"${MQTT_SERVER}\" ) if (EXISTS "${MQTT_CERT_PATH}/${MQTT_CERT_INC}") - target_compile_definitions(${TARGET_NAME} PRIVATE + target_compile_definitions(picow_mqtt_client PRIVATE MQTT_CERT_INC=\"${MQTT_CERT_INC}\" # contains the tls certificates for MQTT_SERVER needed by the client ALTCP_MBEDTLS_AUTHMODE=MBEDTLS_SSL_VERIFY_REQUIRED ) - target_include_directories(${TARGET_NAME} PRIVATE + target_include_directories(picow_mqtt_client PRIVATE ${MQTT_CERT_PATH} ) endif() if (MQTT_USERNAME AND MQTT_PASSWORD) - target_compile_definitions(${TARGET_NAME} PRIVATE + target_compile_definitions(picow_mqtt_client PRIVATE MQTT_USERNAME=\"${MQTT_USERNAME}\" MQTT_PASSWORD=\"${MQTT_PASSWORD}\" ) endif() -pico_add_extra_outputs(${TARGET_NAME}) +pico_add_extra_outputs(picow_mqtt_client) # Ignore warnings from lwip code set_source_files_properties( diff --git a/pico_w/wifi/ntp_client/picow_ntp_client.c b/pico_w/wifi/ntp_client/picow_ntp_client.c index 0fe3f0b13..2f1d3a1e3 100644 --- a/pico_w/wifi/ntp_client/picow_ntp_client.c +++ b/pico_w/wifi/ntp_client/picow_ntp_client.c @@ -16,18 +16,17 @@ typedef struct NTP_T_ { ip_addr_t ntp_server_address; - bool dns_request_sent; struct udp_pcb *ntp_pcb; - absolute_time_t ntp_test_time; - alarm_id_t ntp_resend_alarm; + async_at_time_worker_t request_worker; + async_at_time_worker_t resend_worker; } NTP_T; #define NTP_SERVER "pool.ntp.org" #define NTP_MSG_LEN 48 #define NTP_PORT 123 #define NTP_DELTA 2208988800 // seconds between 1 Jan 1900 and 1 Jan 1970 -#define NTP_TEST_TIME (30 * 1000) -#define NTP_RESEND_TIME (10 * 1000) +#define NTP_TEST_TIME_MS (30 * 1000) +#define NTP_RESEND_TIME_MS (10 * 1000) // Called with results of operation static void ntp_result(NTP_T* state, int status, time_t *result) { @@ -36,17 +35,11 @@ static void ntp_result(NTP_T* state, int status, time_t *result) { printf("got ntp response: %02d/%02d/%04d %02d:%02d:%02d\n", utc->tm_mday, utc->tm_mon + 1, utc->tm_year + 1900, utc->tm_hour, utc->tm_min, utc->tm_sec); } - - if (state->ntp_resend_alarm > 0) { - cancel_alarm(state->ntp_resend_alarm); - state->ntp_resend_alarm = 0; - } - state->ntp_test_time = make_timeout_time_ms(NTP_TEST_TIME); - state->dns_request_sent = false; + async_context_remove_at_time_worker(cyw43_arch_async_context(), &state->resend_worker); + hard_assert(async_context_add_at_time_worker_in_ms(cyw43_arch_async_context(), &state->request_worker, NTP_TEST_TIME_MS)); // repeat the request in future + printf("Next request in %ds\n", NTP_TEST_TIME_MS / 1000); } -static int64_t ntp_failed_handler(alarm_id_t id, void *user_data); - // Make an NTP request static void ntp_request(NTP_T *state) { // cyw43_arch_lwip_begin/end should be used around calls into lwIP to ensure correct locking. @@ -63,14 +56,6 @@ static void ntp_request(NTP_T *state) { cyw43_arch_lwip_end(); } -static int64_t ntp_failed_handler(alarm_id_t id, void *user_data) -{ - NTP_T* state = (NTP_T*)user_data; - printf("ntp request failed\n"); - ntp_result(state, -1, NULL); - return 0; -} - // Call back with a DNS result static void ntp_dns_found(const char *hostname, const ip_addr_t *ipaddr, void *arg) { NTP_T *state = (NTP_T*)arg; @@ -106,6 +91,26 @@ static void ntp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_ad pbuf_free(p); } +// Called to make a NTP request +static void request_worker_fn(__unused async_context_t *context, async_at_time_worker_t *worker) { + NTP_T* state = (NTP_T*)worker->user_data; + hard_assert(async_context_add_at_time_worker_in_ms(cyw43_arch_async_context(), &state->resend_worker, NTP_RESEND_TIME_MS)); // in case UDP request is lost + int err = dns_gethostbyname(NTP_SERVER, &state->ntp_server_address, ntp_dns_found, state); + if (err == ERR_OK) { + ntp_request(state); // Cached DNS result, make NTP request + } else if (err != ERR_INPROGRESS) { // ERR_INPROGRESS means expect a callback + printf("dns request failed\n"); + ntp_result(state, -1, NULL); + } +} + +// Called to resend an NTP request if it appears to get lost +static void resend_worker_fn(__unused async_context_t *context, async_at_time_worker_t *worker) { + NTP_T* state = (NTP_T*)worker->user_data; + printf("ntp request failed\n"); + ntp_result(state, -1, NULL); +} + // Perform initialisation static NTP_T* ntp_init(void) { NTP_T *state = (NTP_T*)calloc(1, sizeof(NTP_T)); @@ -120,6 +125,10 @@ static NTP_T* ntp_init(void) { return NULL; } udp_recv(state->ntp_pcb, ntp_recv, state); + state->request_worker.do_work = request_worker_fn; + state->request_worker.user_data = state; + state->resend_worker.do_work = resend_worker_fn; + state->resend_worker.user_data = state; return state; } @@ -128,26 +137,12 @@ void run_ntp_test(void) { NTP_T *state = ntp_init(); if (!state) return; + printf("Press 'q' to quit\n"); + hard_assert(async_context_add_at_time_worker_in_ms(cyw43_arch_async_context(), &state->request_worker, 0)); // make the first request while(true) { - if (absolute_time_diff_us(get_absolute_time(), state->ntp_test_time) < 0 && !state->dns_request_sent) { - // Set alarm in case udp requests are lost - state->ntp_resend_alarm = add_alarm_in_ms(NTP_RESEND_TIME, ntp_failed_handler, state, true); - - // cyw43_arch_lwip_begin/end should be used around calls into lwIP to ensure correct locking. - // You can omit them if you are in a callback from lwIP. Note that when using pico_cyw_arch_poll - // these calls are a no-op and can be omitted, but it is a good practice to use them in - // case you switch the cyw43_arch type later. - cyw43_arch_lwip_begin(); - int err = dns_gethostbyname(NTP_SERVER, &state->ntp_server_address, ntp_dns_found, state); - cyw43_arch_lwip_end(); - - state->dns_request_sent = true; - if (err == ERR_OK) { - ntp_request(state); // Cached result - } else if (err != ERR_INPROGRESS) { // ERR_INPROGRESS means expect a callback - printf("dns request failed\n"); - ntp_result(state, -1, NULL); - } + int key = getchar_timeout_us(0); + if (key == 'q' || key == 'Q') { + break; } #if PICO_CYW43_ARCH_POLL // if you are using pico_cyw43_arch_poll, then you must poll periodically from your @@ -155,7 +150,7 @@ void run_ntp_test(void) { cyw43_arch_poll(); // you can poll as often as you like, however if you have nothing else to do you can // choose to sleep until either a specified time, or cyw43_arch_poll() has work to do: - cyw43_arch_wait_for_work_until(state->dns_request_sent ? at_the_end_of_time : state->ntp_test_time); + cyw43_arch_wait_for_work_until(at_the_end_of_time); #else // if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work // is done via interrupt in the background. This sleep is just an example of some (blocking) @@ -176,7 +171,7 @@ int main() { cyw43_arch_enable_sta_mode(); - if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 10000)) { + if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { printf("failed to connect\n"); return 1; } diff --git a/pico_w/wifi/ota_update/CMakeLists.txt b/pico_w/wifi/ota_update/CMakeLists.txt new file mode 100644 index 000000000..c1db62720 --- /dev/null +++ b/pico_w/wifi/ota_update/CMakeLists.txt @@ -0,0 +1,33 @@ +add_executable(picow_ota_update + picow_ota_update.c + ) +target_compile_definitions(picow_ota_update PRIVATE + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + PICO_CRT0_IMAGE_TYPE_TBYB=1 + ) +target_include_directories(picow_ota_update PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts + ) +target_link_libraries(picow_ota_update + pico_cyw43_arch_lwip_threadsafe_background + pico_stdlib + pico_sha256 + boot_uf2_headers + ) + +pico_use_wifi_firmware_partition(picow_ota_update) + +pico_hash_binary(picow_ota_update) +pico_sign_binary(picow_ota_update ${CMAKE_CURRENT_LIST_DIR}/private.pem) + +# By default this example requires a partition table in flash, and will +# update the partition that is not currently in use. To use it without +# a partition table in flash, uncomment the following lines to make it +# a no_flash binary, so it can update the currently running program. + +# pico_set_binary_type(picow_ota_update no_flash) +# pico_package_uf2_output(picow_ota_update 0x10000000) + +pico_add_extra_outputs(picow_ota_update) diff --git a/pico_w/wifi/ota_update/README.md b/pico_w/wifi/ota_update/README.md new file mode 100644 index 000000000..f507382ec --- /dev/null +++ b/pico_w/wifi/ota_update/README.md @@ -0,0 +1,102 @@ +# Overview + +This example demonstrates how to implement an Over-The-Air (OTA) software update mechanism using facilities provided by the RP2350 bootrom. + +A python script runs on a host machine to _push_ a new (UF2 format) application image to a Pico 2 W running the `ota_update` example application. The incoming application image is received via the LwIP IP stack and programmed into Pico 2 W flash memory. + +Two flash partitions ("A"/"B") are used for application images so that the application can be running from one, while the new application image is being written to the other. The RP2350 bootrom provides support for determining the location in flash memory of the partition to use for the new version of the application. + +On successful completion of the flash programming, the Pico 2 W is rebooted and the updated applicating image is selected and executed by the RP2350 bootrom. This process can be repeated as required. + +## More detail + +The Pico 2 W listens on TCP port 4242. The host [python_ota_update.py](python_ota_update.py) script transmits the update image in a series of fixed sized chunks. Each chunk contains an integer number of UF2 image blocks. If the update image UF2 block count is not an exact sub-multiple of the number of chunks the script will pad the last chunk as required. + +On receipt of pushed chunks, the ota_update image will program the UF2 block stream into flash. In addition to programming, each chunk is 'hashed', using SHA256, and the calculated hash transmitted to the host as an acknowledgement of the received chunk. The host script compares the local and remotely computed hashes and if data corruption has occurred the update will halt. + +The flash must be appropriately partitioned for this example to work. Two partitions are required, one for the currently running software and the other to be updated with incoming data. + +Note: This example _also_ demonstrates how the CYW43 Wi-fi firmware can be stored in a separate flash partition(s). This means the Pico 2 W application can be updated separately which reduces the size of the update download, but also requires four partitions. + +For more information flash partitioning and boot image selection please see section 5 of [RP2350 datasheet](https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf) + +# How to + +## Flash partitioning + +Before the example can be run, the Flash on the Pico 2 W must be appropriately partitioned. + +The required partition data can be loaded by creating a UF2 from the supplied partition table JSON in this folder: + +``` +picotool partition create main.json pt.uf2 +``` +then dragging & dropping this UF2 onto the device, or loading it using `picotool` and rebooting: +``` +picotool load pt.uf2 +picotool reboot -u +``` + +> **NOTE** +> `reboot -u` reboots the device (as is required to install the partition table) then returns to BOOTSEL mode so we can send more commands to the device. + +With the partition table loaded, you then need to load the Wi-Fi firmware UF2 (`picow_ota_update_wifi_firmware.uf2`) and load and execute the main program (`picow_ota_update.uf2`) - either by dragging and dropping them in order, or using `picotool`: +``` +picotool load picow_ota_update_wifi_firmware.uf2 +picotool load -x picow_ota_update.uf2 +``` + +> **NOTE** +> The first `load` writes the Wifi Firmware into one of the partitions set aside for that. The second `load -x` loads the main application code into one of the main partitions, then resets the chip to start execution + +The device should now have the following firmware layout. + +| Partition | Purpose | +| ----------|-------- | +| 0 | Partition A for the application firmware | +| 1 | Partition B for the application firmware | +| 2 | Partition A for the wifi firmware | +| 3 | Partition B for the wifi firmware | + +## Operation + +This example will send debug output text on the default UART. On startup it displays the current boot partition (from where the firmware is running) and IP address of the Pico 2 W + +``` +Boot partition was 0 +Starting server at 192.168.0.103 on port 4242 +``` + +Once running, you can use [python_ota_update.py](python_ota_update.py) to upload new UF2s from the host to the Pico 2 W using its IP address. For example: +``` +python ./python_ota_update.py 192.168.0.103 picow_ota_update.uf2 +``` +This will update the Pico 2 W at `192.168.0.103` with the specified image. + +``` +Boot partition was 1 +``` + +The update is downloaded into the main partition that's not currently in use, before rebooting the Pico 2 W to run the new version of the software. This choice of software is made by the RP2350 bootrom based on the version number of the software + +## Try before you Buy + +The `picow_ota_update.uf2` application is marked with the "Try Before You Buy" (TBYB) attribute by specifying `PICO_CRT0_IMAGE_TYPE_TBYB=1` in its compile definitions. + +This means that if a newly downloaded image partition is not explicitly "bought" by calling the `rom_explicit_buy` function within 16.7 seconds, the device will reboot and revert to the previously used partition. This gives some protection against a bad OTA update being applied. See section 5.1.17 of [RP2350 datasheet](https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf) for more details about Try Before You Buy. + +### Wifi Firmware + +This example also demonstrates how TBYB can be applied to other partitions - in this case the Wifi firmware. In debug builds you should see debug output which shows the partition that's currently being used for the wifi firmware. + +The example uses the `pico_use_wifi_firmware_partition` keyword to make the Wifi firmware appear in its own partition rather than embedded in the application as is usually the case. + +``` +Chosen CYW43 firmware in partition 2 +``` + +Two UF2 files are generated for the wifi firmware, `picow_ota_update_wifi_firmware.uf2` and `picow_ota_update_wifi_firmware_tbyb.uf2`. These images are the same except only the latter image has the TBYB attribute set. + +You can update the current Wifi partition with these UF2 files using [python_ota_update.py](python_ota_update.py). + +If you use `picow_ota_update_wifi_firmware_tbyb.uf2` for the update then if something goes wrong before the partition is explicitly "bought" by calling the `rom_explicit_buy` function, then the device will switch back to the previous working Wifi firmware partition after a reboot. This does not happen if you use `picow_ota_update_wifi_firmware.uf2` for the update. In this way the TBYB attribute gives you some protection against an OTA update applying bad Wifi firmware. diff --git a/pico_w/wifi/ota_update/lwipopts.h b/pico_w/wifi/ota_update/lwipopts.h new file mode 100644 index 000000000..8571ed509 --- /dev/null +++ b/pico_w/wifi/ota_update/lwipopts.h @@ -0,0 +1,10 @@ +#ifndef _LWIPOPTS_H +#define _LWIPOPTS_H + +// Generally you would define your own explicit list of lwIP options +// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html) +// +// This example uses a common include to avoid repetition +#include "lwipopts_examples_common.h" + +#endif diff --git a/pico_w/wifi/ota_update/main.json b/pico_w/wifi/ota_update/main.json new file mode 100644 index 000000000..38f23e311 --- /dev/null +++ b/pico_w/wifi/ota_update/main.json @@ -0,0 +1,64 @@ +{ + "version": [1, 0], + "unpartitioned": { + "families": ["absolute"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + } + }, + "partitions": [ + { + "name": "Main A", + "id": 0, + "size": "1744K", + "families": ["rp2350-arm-s", "rp2350-riscv"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + } + }, + { + "name": "Main B", + "id": 0, + "size": "1744K", + "families": ["rp2350-arm-s", "rp2350-riscv"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + }, + "link": ["a", 0] + }, + { + "name": "Firmware A", + "id": "0x776966696669726d", + "start": "3500k", + "size": "256K", + "families": ["cyw43-firmware"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + }, + "ignored_during_riscv_boot": true, + "no_reboot_on_uf2_download": true + }, + { + "name": "Firmware B", + "id": 12345, + "size": "256K", + "families": ["cyw43-firmware"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + }, + "link": ["a", 2], + "ignored_during_riscv_boot": true, + "no_reboot_on_uf2_download": true + } + ] +} diff --git a/pico_w/wifi/ota_update/picow_ota_update.c b/pico_w/wifi/ota_update/picow_ota_update.c new file mode 100644 index 000000000..da14f5754 --- /dev/null +++ b/pico_w/wifi/ota_update/picow_ota_update.c @@ -0,0 +1,365 @@ +/** + * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include "pico/stdlib.h" +#include "pico/cyw43_arch.h" +#include "pico/sha256.h" +#include "pico/bootrom.h" +#include "boot/picobin.h" +#include "boot/picoboot.h" +#include "boot/uf2.h" + +#include "lwip/pbuf.h" +#include "lwip/tcp.h" + +#define TCP_PORT 4242 +// #define DEBUG_printf(...) printf(__VA_ARGS__) +#define DEBUG_printf(...) +#define BUF_SIZE 2048 +#define POLL_TIME_S 5 + +#define FLASH_SECTOR_ERASE_SIZE 4096u + +typedef struct TCP_UPDATE_SERVER_T_ { + struct tcp_pcb *server_pcb; + struct tcp_pcb *client_pcb; + bool complete; + __attribute__((aligned(4))) uint8_t buffer_sent[SHA256_RESULT_BYTES]; + __attribute__((aligned(4))) uint8_t buffer_recv[BUF_SIZE]; + int sent_len; + int recv_len; + int num_blocks; + int blocks_done; + uint32_t family_id; + uint32_t flash_update; + int32_t write_offset; + uint32_t write_size; + uint32_t highest_erased_sector; +} TCP_UPDATE_SERVER_T; + +typedef struct uf2_block uf2_block_t; + +static TCP_UPDATE_SERVER_T* tcp_update_server_init(void) { + TCP_UPDATE_SERVER_T *state = calloc(1, sizeof(TCP_UPDATE_SERVER_T)); + if (!state) { + DEBUG_printf("failed to allocate state\n"); + return NULL; + } + return state; +} + + +static __attribute__((aligned(4))) uint8_t workarea[4 * 1024]; + +static err_t tcp_update_server_close(void *arg) { + TCP_UPDATE_SERVER_T *state = (TCP_UPDATE_SERVER_T*)arg; + err_t err = ERR_OK; + if (state->client_pcb != NULL) { + tcp_arg(state->client_pcb, NULL); + tcp_poll(state->client_pcb, NULL, 0); + tcp_sent(state->client_pcb, NULL); + tcp_recv(state->client_pcb, NULL); + tcp_err(state->client_pcb, NULL); + err = tcp_close(state->client_pcb); + if (err != ERR_OK) { + DEBUG_printf("close failed %d, calling abort\n", err); + tcp_abort(state->client_pcb); + err = ERR_ABRT; + } + state->client_pcb = NULL; + } + if (state->server_pcb) { + tcp_arg(state->server_pcb, NULL); + tcp_close(state->server_pcb); + state->server_pcb = NULL; + } + return err; +} + +static err_t tcp_update_server_result(void *arg, int status) { + TCP_UPDATE_SERVER_T *state = (TCP_UPDATE_SERVER_T*)arg; + if (status == 0) { + DEBUG_printf("test success\n"); + } else { + DEBUG_printf("test failed %d\n", status); + } + state->complete = true; + return tcp_update_server_close(arg); +} + +static err_t tcp_update_server_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) { + TCP_UPDATE_SERVER_T *state = (TCP_UPDATE_SERVER_T*)arg; + DEBUG_printf("tcp_update_server_sent %u\n", len); + state->sent_len += len; + + if (state->sent_len >= SHA256_RESULT_BYTES) { + + // We should get the data back from the client + state->recv_len = 0; + DEBUG_printf("Waiting for buffer from client\n"); + } + + return ERR_OK; +} + +err_t tcp_update_server_send_data(void *arg, struct tcp_pcb *tpcb) +{ + TCP_UPDATE_SERVER_T *state = (TCP_UPDATE_SERVER_T*)arg; + + state->sent_len = 0; + DEBUG_printf("Writing %ld bytes to client\n", SHA256_RESULT_BYTES); + // this method is callback from lwIP, so cyw43_arch_lwip_begin is not required, however you + // can use this method to cause an assertion in debug mode, if this method is called when + // cyw43_arch_lwip_begin IS needed + cyw43_arch_lwip_check(); + err_t err = tcp_write(tpcb, state->buffer_sent, SHA256_RESULT_BYTES, TCP_WRITE_FLAG_COPY); + if (err != ERR_OK) { + DEBUG_printf("Failed to write data %d\n", err); + return tcp_update_server_result(arg, -1); + } + return ERR_OK; +} + +err_t tcp_update_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { + TCP_UPDATE_SERVER_T *state = (TCP_UPDATE_SERVER_T*)arg; + if (!p) { + return tcp_update_server_result(arg, -1); + } + // this method is callback from lwIP, so cyw43_arch_lwip_begin is not required, however you + // can use this method to cause an assertion in debug mode, if this method is called when + // cyw43_arch_lwip_begin IS needed + cyw43_arch_lwip_check(); + if (p->tot_len > 0) { + DEBUG_printf("tcp_update_server_recv %d/%d err %d\n", p->tot_len, state->recv_len, err); + + // Receive the buffer + const uint16_t buffer_left = BUF_SIZE - state->recv_len; + state->recv_len += pbuf_copy_partial(p, state->buffer_recv + state->recv_len, + p->tot_len > buffer_left ? buffer_left : p->tot_len, 0); + tcp_recved(tpcb, p->tot_len); + } + pbuf_free(p); + + // Have we have received the whole buffer + if (state->recv_len == BUF_SIZE) { + + for (int i=0; i < BUF_SIZE/sizeof(uf2_block_t); i++) { + // check it matches + uf2_block_t* block; + block = (uf2_block_t*)(state->buffer_recv + i * sizeof(uf2_block_t)); + + if (state->num_blocks == 0) { + state->num_blocks = block->num_blocks; + state->family_id = block->file_size; // or familyID; + + resident_partition_t uf2_target_partition; + rom_flash_flush_cache(); + rom_get_uf2_target_partition(workarea, sizeof(workarea), state->family_id, &uf2_target_partition); + printf("Code Target partition is %lx %lx\n", uf2_target_partition.permissions_and_location, uf2_target_partition.permissions_and_flags); + + uint16_t first_sector_number = (uf2_target_partition.permissions_and_location & PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_BITS) >> PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_LSB; + uint16_t last_sector_number = (uf2_target_partition.permissions_and_location & PICOBIN_PARTITION_LOCATION_LAST_SECTOR_BITS) >> PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB; + uint32_t code_start_addr = first_sector_number * 0x1000; + uint32_t code_end_addr = (last_sector_number + 1) * 0x1000; + uint32_t code_size = code_end_addr - code_start_addr; + printf("Start %lx, End %lx, Size %lx\n", code_start_addr, code_end_addr, code_size); + + state->flash_update = code_start_addr + XIP_BASE; + state->write_offset = code_start_addr + XIP_BASE - block->target_addr; + state->write_size = code_size; + DEBUG_printf("Write Offset %lx, Size %lx\n", state->write_offset, state->write_size); + } + + if (state->blocks_done != block->block_no) { + DEBUG_printf("block number mismatch - expected %d, got %d\n", state->blocks_done, block->block_no); + return tcp_update_server_result(arg, -1); + } + if (state->family_id != block->file_size) { + DEBUG_printf("family id mismatch\n"); + return tcp_update_server_result(arg, -1); + } + DEBUG_printf("tcp_update_server_recv buffer ok\n"); + + // Write to flash + struct cflash_flags flags; + int8_t ret; + (void)ret; + if (block->target_addr / FLASH_SECTOR_ERASE_SIZE > state->highest_erased_sector) { + flags.flags = + (CFLASH_OP_VALUE_ERASE << CFLASH_OP_LSB) | + (CFLASH_SECLEVEL_VALUE_SECURE << CFLASH_SECLEVEL_LSB) | + (CFLASH_ASPACE_VALUE_STORAGE << CFLASH_ASPACE_LSB); + ret = rom_flash_op(flags, + block->target_addr + state->write_offset, + FLASH_SECTOR_ERASE_SIZE, NULL); + state->highest_erased_sector = block->target_addr / FLASH_SECTOR_ERASE_SIZE; + DEBUG_printf("Checked Erase Returned %d, start %x, size %x, highest erased %x\n", ret, block->target_addr + state->write_offset, FLASH_SECTOR_ERASE_SIZE, state->highest_erased_sector); + } + flags.flags = + (CFLASH_OP_VALUE_PROGRAM << CFLASH_OP_LSB) | + (CFLASH_SECLEVEL_VALUE_SECURE << CFLASH_SECLEVEL_LSB) | + (CFLASH_ASPACE_VALUE_STORAGE << CFLASH_ASPACE_LSB); + ret = rom_flash_op(flags, + block->target_addr + state->write_offset, + 256, (void*)block->data); + DEBUG_printf("Checked Program Returned %d, start %x, size %x\n", ret, block->target_addr + state->write_offset, 256); + + // Download complete? + state->blocks_done++; + if (state->blocks_done >= state->num_blocks) { + tcp_update_server_result(arg, 0); + return ERR_OK; + } + } + + // Hash the received data + pico_sha256_state_t sha_state; + int rc = pico_sha256_start_blocking(&sha_state, SHA256_BIG_ENDIAN, true); // using some DMA system resources + hard_assert(rc == PICO_OK); + pico_sha256_update_blocking(&sha_state, (const uint8_t*)state->buffer_recv, sizeof(state->buffer_recv)); + + // Get the result of the sha256 calculation + sha256_result_t* result; + result = (sha256_result_t*)state->buffer_sent; + pico_sha256_finish(&sha_state, result); + + // Send another buffer + return tcp_update_server_send_data(arg, state->client_pcb); + } + return ERR_OK; +} + +static err_t tcp_update_server_poll(void *arg, struct tcp_pcb *tpcb) { + DEBUG_printf("tcp_update_server_poll_fn\n"); + return tcp_update_server_result(arg, -1); // no response is an error? +} + +static void tcp_update_server_err(void *arg, err_t err) { + if (err != ERR_ABRT) { + DEBUG_printf("tcp_client_err_fn %d\n", err); + tcp_update_server_result(arg, err); + } +} + +static err_t tcp_update_server_accept(void *arg, struct tcp_pcb *client_pcb, err_t err) { + TCP_UPDATE_SERVER_T *state = (TCP_UPDATE_SERVER_T*)arg; + if (err != ERR_OK || client_pcb == NULL) { + DEBUG_printf("Failure in accept\n"); + tcp_update_server_result(arg, err); + return ERR_VAL; + } + DEBUG_printf("Client connected\n"); + + state->client_pcb = client_pcb; + tcp_arg(client_pcb, state); + tcp_sent(client_pcb, tcp_update_server_sent); + tcp_recv(client_pcb, tcp_update_server_recv); + tcp_poll(client_pcb, tcp_update_server_poll, POLL_TIME_S * 2); + tcp_err(client_pcb, tcp_update_server_err); + + return ERR_OK; +} + +static bool tcp_update_server_open(void *arg) { + TCP_UPDATE_SERVER_T *state = (TCP_UPDATE_SERVER_T*)arg; + printf("Starting server at %s on port %u\n", ip4addr_ntoa(netif_ip4_addr(netif_list)), TCP_PORT); + + struct tcp_pcb *pcb = tcp_new_ip_type(IPADDR_TYPE_ANY); + if (!pcb) { + DEBUG_printf("failed to create pcb\n"); + return false; + } + + err_t err = tcp_bind(pcb, NULL, TCP_PORT); + if (err) { + DEBUG_printf("failed to bind to port %u\n", TCP_PORT); + return false; + } + + state->server_pcb = tcp_listen_with_backlog(pcb, 1); + if (!state->server_pcb) { + DEBUG_printf("failed to listen\n"); + if (pcb) { + tcp_close(pcb); + } + return false; + } + + tcp_arg(state->server_pcb, state); + tcp_accept(state->server_pcb, tcp_update_server_accept); + + return true; +} + +int main() { + stdio_init_all(); + +#ifdef __riscv + // Increased bootrom stack is required for some of the functions in this example + bootrom_stack_t stack = { + .base = malloc(0x400), + .size = 0x400 + }; + rom_set_bootrom_stack(&stack); +#endif + + if (cyw43_arch_init()) { + printf("failed to initialise\n"); + return 1; + } + + cyw43_arch_enable_sta_mode(); + + printf("Connecting to Wi-Fi...\n"); + if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { + printf("failed to connect.\n"); + return 1; + } else { + printf("Connected.\n"); + } + + boot_info_t boot_info = {}; + int ret = rom_get_boot_info(&boot_info); + printf("Boot partition was %d\n", boot_info.partition); + + if (rom_get_last_boot_type() == BOOT_TYPE_FLASH_UPDATE) { + printf("Someone updated into me\n"); + if (boot_info.reboot_params[0]) printf("Flash update base was %x\n", boot_info.reboot_params[0]); + if (boot_info.tbyb_and_update_info) printf("Update info %x\n", boot_info.tbyb_and_update_info); + ret = rom_explicit_buy(workarea, sizeof(workarea)); + if (ret) printf("Buy returned %d\n", ret); + ret = rom_get_boot_info(&boot_info); + if (boot_info.tbyb_and_update_info) printf("Update info now %x\n", boot_info.tbyb_and_update_info); + } + + + TCP_UPDATE_SERVER_T *state = tcp_update_server_init(); + if (!state) { + return -1; + } + if (!tcp_update_server_open(state)) { + tcp_update_server_result(state, -1); + return -1; + } + + bool led_state = false; + while(!state->complete) { + // Do your application code here + led_state = !led_state; + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_state); + sleep_ms(250); + } + + cyw43_arch_deinit(); + ret = rom_reboot(REBOOT2_FLAG_REBOOT_TYPE_FLASH_UPDATE, 1000, state->flash_update, 0); + printf("Done - rebooting for a flash update boot %d\n", ret); + free(state); + sleep_ms(2000); + return 0; +} diff --git a/pico_w/wifi/ota_update/private.pem b/pico_w/wifi/ota_update/private.pem new file mode 100644 index 000000000..bf777d897 --- /dev/null +++ b/pico_w/wifi/ota_update/private.pem @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BgUrgQQACg== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHQCAQEEIAXAdiilH8wT07TESUzWPt+BY9+NcchvYU3xbnpK+CBNoAcGBSuBBAAK +oUQDQgAEYYJtMQFGW4AB94tU3u/Qir5sRcYjBYMqCa+8gxsYd9OwMS3dqWKsnVBz +dyy7bFWdJzXDMb9o20xRRd57Q9xSYw== +-----END EC PRIVATE KEY----- diff --git a/pico_w/wifi/ota_update/python_ota_update.py b/pico_w/wifi/ota_update/python_ota_update.py new file mode 100644 index 000000000..b49453e85 --- /dev/null +++ b/pico_w/wifi/ota_update/python_ota_update.py @@ -0,0 +1,77 @@ +#!/usr/bin/python + +import socket +import sys +import hashlib + +# Check server ip address set +if len(sys.argv) < 2: + raise RuntimeError('pass IP address of the server') + +# Check file is set +if len(sys.argv) < 3: + raise RuntimeError('pass UF2 file for update') + +# Set the server address here like 1.2.3.4 +SERVER_ADDR = sys.argv[1] + +# These constants should match the server +BUF_SIZE = 2048 +UF2_BLOCK = 512 +SERVER_PORT = 4242 + +# Open socket to the server +sock = socket.socket() +addr = (SERVER_ADDR, SERVER_PORT) +sock.connect(addr) + +file = sys.argv[2] + +with open(file, 'rb') as f: + data = f.read() + +data = bytearray(data) + +# Skip abs block +data = data[UF2_BLOCK:] + +print("Len data", len(data), f"/{BUF_SIZE}", len(data) / BUF_SIZE) + +while (len(data) % BUF_SIZE != 0): + data.extend([0] * UF2_BLOCK) + +print("Len data now", len(data), f"/{BUF_SIZE}", len(data) / BUF_SIZE) + +# Repeat test for a number of iterations +for i in range(len(data) // BUF_SIZE): + print("I", i, "of", len(data) // BUF_SIZE, ' '*10, end='\r') + uf2_buf = data[i*BUF_SIZE:(i+1)*BUF_SIZE] + + # Send the data back to the server + write_len = sock.send(uf2_buf) + if write_len != BUF_SIZE: + raise RuntimeError('wrong amount of data written') + + if i < (len(data) // BUF_SIZE) - 1: + # Read 32 bytes from the server + total_size = 32 + read_buf = b'' + while total_size > 0: + buf = sock.recv(32) + # print('read %d bytes from server' % len(buf)) + total_size -= len(buf) + read_buf += buf + + # Check size of data received + if len(read_buf) != 32: + raise RuntimeError('wrong amount of data read %d', len(read_buf)) + + # Check the hash matches + h = hashlib.new('sha256') + h.update(uf2_buf) + if read_buf != h.digest(): + raise RuntimeError('buffer mismatch') + +# All done +sock.close() +print("\nupload completed") diff --git a/pico_w/wifi/tls_client/picow_tls_client.c b/pico_w/wifi/tls_client/picow_tls_client.c index 9a375b1d4..2623911e5 100644 --- a/pico_w/wifi/tls_client/picow_tls_client.c +++ b/pico_w/wifi/tls_client/picow_tls_client.c @@ -7,8 +7,8 @@ #include "pico/stdlib.h" #include "pico/cyw43_arch.h" -#define TLS_CLIENT_SERVER "worldtimeapi.org" -#define TLS_CLIENT_HTTP_REQUEST "GET /api/ip HTTP/1.1\r\n" \ +#define TLS_CLIENT_SERVER "fw-download-alias1.raspberrypi.com" +#define TLS_CLIENT_HTTP_REQUEST "GET /net_install/boot.sig HTTP/1.1\r\n" \ "Host: " TLS_CLIENT_SERVER "\r\n" \ "Connection: close\r\n" \ "\r\n" diff --git a/pico_w/wifi/wifi_scan/picow_wifi_scan.c b/pico_w/wifi/wifi_scan/picow_wifi_scan.c index 1e500f890..4ddd55129 100644 --- a/pico_w/wifi/wifi_scan/picow_wifi_scan.c +++ b/pico_w/wifi/wifi_scan/picow_wifi_scan.c @@ -19,6 +19,19 @@ static int scan_result(void *env, const cyw43_ev_scan_result_t *result) { return 0; } +// Start a wifi scan +static void scan_worker_fn(async_context_t *context, async_at_time_worker_t *worker) { + cyw43_wifi_scan_options_t scan_options = {0}; + int err = cyw43_wifi_scan(&cyw43_state, &scan_options, NULL, scan_result); + if (err == 0) { + bool *scan_started = (bool*)worker->user_data; + *scan_started = true; + printf("\nPerforming wifi scan\n"); + } else { + printf("Failed to start scan: %d\n", err); + } +} + #include "hardware/vreg.h" #include "hardware/clocks.h" @@ -30,26 +43,24 @@ int main() { return 1; } + printf("Press 'q' to quit\n"); cyw43_arch_enable_sta_mode(); - absolute_time_t scan_time = nil_time; - bool scan_in_progress = false; - while(true) { - if (absolute_time_diff_us(get_absolute_time(), scan_time) < 0) { - if (!scan_in_progress) { - cyw43_wifi_scan_options_t scan_options = {0}; - int err = cyw43_wifi_scan(&cyw43_state, &scan_options, NULL, scan_result); - if (err == 0) { - printf("\nPerforming wifi scan\n"); - scan_in_progress = true; - } else { - printf("Failed to start scan: %d\n", err); - scan_time = make_timeout_time_ms(10000); // wait 10s and scan again - } - } else if (!cyw43_wifi_scan_active(&cyw43_state)) { - scan_time = make_timeout_time_ms(10000); // wait 10s and scan again - scan_in_progress = false; - } + // Start a scan immediately + bool scan_started = false; + async_at_time_worker_t scan_worker = { .do_work = scan_worker_fn, .user_data = &scan_started }; + hard_assert(async_context_add_at_time_worker_in_ms(cyw43_arch_async_context(), &scan_worker, 0)); + + bool exit = false; + while(!exit) { + int key = getchar_timeout_us(0); + if (key == 'q' || key == 'Q') { + exit = true; + } + if (!cyw43_wifi_scan_active(&cyw43_state) && scan_started) { + // Start a scan in 10s + scan_started = false; + hard_assert(async_context_add_at_time_worker_in_ms(cyw43_arch_async_context(), &scan_worker, 10000)); } // the following #ifdef is only here so this same example can be used in multiple modes; // you do not need it in your code @@ -59,7 +70,7 @@ int main() { cyw43_arch_poll(); // you can poll as often as you like, however if you have nothing else to do you can // choose to sleep until either a specified time, or cyw43_arch_poll() has work to do: - cyw43_arch_wait_for_work_until(scan_time); + cyw43_arch_wait_for_work_until(at_the_end_of_time); #else // if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work // is done via interrupt in the background. This sleep is just an example of some (blocking) diff --git a/pio/CMakeLists.txt b/pio/CMakeLists.txt index 7b33a38a1..023bd4ea8 100644 --- a/pio/CMakeLists.txt +++ b/pio/CMakeLists.txt @@ -17,10 +17,10 @@ if (TARGET hardware_pio) add_subdirectory_exclude_platforms(spi) add_subdirectory_exclude_platforms(squarewave) add_subdirectory_exclude_platforms(st7789_lcd) + add_subdirectory_exclude_platforms(uart_dma) add_subdirectory_exclude_platforms(uart_rx) add_subdirectory_exclude_platforms(uart_tx) add_subdirectory_exclude_platforms(ws2812) - add_subdirectory_exclude_platforms(uart_pio_dma) else() message("Skipping PIO examples as hardware_pio is unavailable on this platform") endif() diff --git a/pio/i2c/pio_i2c.c b/pio/i2c/pio_i2c.c index a3a19f576..f8030eee1 100644 --- a/pio/i2c/pio_i2c.c +++ b/pio/i2c/pio_i2c.c @@ -67,9 +67,10 @@ uint8_t pio_i2c_get(PIO pio, uint sm) { } void pio_i2c_start(PIO pio, uint sm) { - pio_i2c_put_or_err(pio, sm, 1u << PIO_I2C_ICOUNT_LSB); // Escape code for 2 instruction sequence + pio_i2c_put_or_err(pio, sm, 2u << PIO_I2C_ICOUNT_LSB); // Escape code for 3 instruction sequence pio_i2c_put_or_err(pio, sm, set_scl_sda_program_instructions[I2C_SC1_SD0]); // We are already in idle state, just pull SDA low pio_i2c_put_or_err(pio, sm, set_scl_sda_program_instructions[I2C_SC0_SD0]); // Also pull clock low so we can present data + pio_i2c_put_or_err(pio, sm, pio_encode_mov(pio_isr, pio_null)); // Ensure ISR counter is clear following a write } void pio_i2c_stop(PIO pio, uint sm) { @@ -80,11 +81,12 @@ void pio_i2c_stop(PIO pio, uint sm) { }; void pio_i2c_repstart(PIO pio, uint sm) { - pio_i2c_put_or_err(pio, sm, 3u << PIO_I2C_ICOUNT_LSB); + pio_i2c_put_or_err(pio, sm, 4u << PIO_I2C_ICOUNT_LSB); pio_i2c_put_or_err(pio, sm, set_scl_sda_program_instructions[I2C_SC0_SD1]); pio_i2c_put_or_err(pio, sm, set_scl_sda_program_instructions[I2C_SC1_SD1]); pio_i2c_put_or_err(pio, sm, set_scl_sda_program_instructions[I2C_SC1_SD0]); pio_i2c_put_or_err(pio, sm, set_scl_sda_program_instructions[I2C_SC0_SD0]); + pio_i2c_put_or_err(pio, sm, pio_encode_mov(pio_isr, pio_null)); } static void pio_i2c_wait_idle(PIO pio, uint sm) { diff --git a/pio/ir_nec/ir_loopback/CMakeLists.txt b/pio/ir_nec/ir_loopback/CMakeLists.txt index 8c7d8f4b7..66d251061 100644 --- a/pio/ir_nec/ir_loopback/CMakeLists.txt +++ b/pio/ir_nec/ir_loopback/CMakeLists.txt @@ -1,4 +1,4 @@ -add_executable (pio_ir_loopback ir_loopback.c) +add_executable(pio_ir_loopback ir_loopback.c) # link the executable using the IR transmit and receive libraries # diff --git a/pio/ir_nec/nec_receive_library/CMakeLists.txt b/pio/ir_nec/nec_receive_library/CMakeLists.txt index 72d296d59..a3c30b42e 100644 --- a/pio/ir_nec/nec_receive_library/CMakeLists.txt +++ b/pio/ir_nec/nec_receive_library/CMakeLists.txt @@ -1,19 +1,20 @@ -# build a normal library -# -add_library(nec_receive_library nec_receive.c) +add_library(nec_receive_library INTERFACE) + +target_sources(nec_receive_library INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/nec_receive.c) # invoke pio_asm to assemble the state machine program # pico_generate_pio_header(nec_receive_library ${CMAKE_CURRENT_LIST_DIR}/nec_receive.pio) -target_link_libraries(nec_receive_library PRIVATE +target_link_libraries(nec_receive_library INTERFACE pico_stdlib hardware_pio ) # add the `binary` directory so that the generated headers are included in the project # -target_include_directories (nec_receive_library PUBLIC +target_include_directories (nec_receive_library INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) diff --git a/pio/ir_nec/nec_transmit_library/CMakeLists.txt b/pio/ir_nec/nec_transmit_library/CMakeLists.txt index dfb96bf28..9ae2bed27 100644 --- a/pio/ir_nec/nec_transmit_library/CMakeLists.txt +++ b/pio/ir_nec/nec_transmit_library/CMakeLists.txt @@ -1,20 +1,21 @@ -# build a normal library -# -add_library(nec_transmit_library nec_transmit.c) +add_library(nec_transmit_library INTERFACE) + +target_sources(nec_transmit_library INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/nec_transmit.c) # invoke pio_asm to assemble the PIO state machine programs # pico_generate_pio_header(nec_transmit_library ${CMAKE_CURRENT_LIST_DIR}/nec_carrier_burst.pio) pico_generate_pio_header(nec_transmit_library ${CMAKE_CURRENT_LIST_DIR}/nec_carrier_control.pio) -target_link_libraries(nec_transmit_library PRIVATE +target_link_libraries(nec_transmit_library INTERFACE pico_stdlib hardware_pio ) # add the `binary` directory so that the generated headers are included in the project # -target_include_directories (nec_transmit_library PUBLIC +target_include_directories (nec_transmit_library INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) diff --git a/pio/uart_dma/CMakeLists.txt b/pio/uart_dma/CMakeLists.txt new file mode 100644 index 000000000..d87a50928 --- /dev/null +++ b/pio/uart_dma/CMakeLists.txt @@ -0,0 +1,11 @@ +add_executable(pio_uart_dma) +pico_generate_pio_header(pio_uart_dma ${CMAKE_CURRENT_LIST_DIR}/../uart_rx/uart_rx.pio) +pico_generate_pio_header(pio_uart_dma ${CMAKE_CURRENT_LIST_DIR}/../uart_tx/uart_tx.pio) +target_sources(pio_uart_dma PRIVATE uart_dma.c) +target_link_libraries(pio_uart_dma PRIVATE + pico_stdlib + hardware_pio + hardware_dma + ) +pico_add_extra_outputs(pio_uart_dma) +example_auto_set_url(pio_uart_dma) diff --git a/pio/uart_pio_dma/uart_pio_dma.c b/pio/uart_dma/uart_dma.c similarity index 100% rename from pio/uart_pio_dma/uart_pio_dma.c rename to pio/uart_dma/uart_dma.c diff --git a/pio/uart_pio_dma/CMakeLists.txt b/pio/uart_pio_dma/CMakeLists.txt deleted file mode 100644 index 108477610..000000000 --- a/pio/uart_pio_dma/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -add_executable(uart_pio_dma) -pico_generate_pio_header(uart_pio_dma ${CMAKE_CURRENT_LIST_DIR}/../uart_rx/uart_rx.pio) -pico_generate_pio_header(uart_pio_dma ${CMAKE_CURRENT_LIST_DIR}/../uart_tx/uart_tx.pio) -target_sources(uart_pio_dma PRIVATE uart_pio_dma.c) -target_link_libraries(uart_pio_dma PRIVATE - pico_stdlib - hardware_pio - hardware_dma - ) -pico_add_extra_outputs(uart_pio_dma) -example_auto_set_url(uart_pio_dma) diff --git a/pio/ws2812/ws2812_parallel.c b/pio/ws2812/ws2812_parallel.c index 77cbfd824..5bf1eda50 100644 --- a/pio/ws2812/ws2812_parallel.c +++ b/pio/ws2812/ws2812_parallel.c @@ -29,16 +29,16 @@ static uint8_t *current_strip_out; static bool current_strip_4color; static inline void put_pixel(uint32_t pixel_grb) { - *current_strip_out++ = pixel_grb & 0xffu; - *current_strip_out++ = (pixel_grb >> 8u) & 0xffu; *current_strip_out++ = (pixel_grb >> 16u) & 0xffu; + *current_strip_out++ = (pixel_grb >> 8u) & 0xffu; + *current_strip_out++ = pixel_grb & 0xffu; if (current_strip_4color) { - *current_strip_out++ = 0; // todo adjust? + *current_strip_out++ = 0; // todo adjust? } } static inline uint32_t urgb_u32(uint8_t r, uint8_t g, uint8_t b) { - return + return ((uint32_t) (r) << 8) | ((uint32_t) (g) << 16) | (uint32_t) (b); diff --git a/sha/mbedtls_sha256/mbedtls_sha256.c b/sha/mbedtls_sha256/mbedtls_sha256.c index cceac8ab7..a2414a265 100644 --- a/sha/mbedtls_sha256/mbedtls_sha256.c +++ b/sha/mbedtls_sha256/mbedtls_sha256.c @@ -14,9 +14,16 @@ #include "pico/stdlib.h" #include "mbedtls/sha256.h" +#include "mbedtls/version.h" #define BUFFER_SIZE 10000 +#if MBEDTLS_VERSION_MAJOR < 3 +#define mbedtls_sha256_starts mbedtls_sha256_starts_ret +#define mbedtls_sha256_update mbedtls_sha256_update_ret +#define mbedtls_sha256_finish mbedtls_sha256_finish_ret +#endif + int main() { stdio_init_all(); // nist 3 @@ -32,14 +39,14 @@ int main() { mbedtls_sha256_context ctx; mbedtls_sha256_init(&ctx); uint64_t start = time_us_64(); - int rc = mbedtls_sha256_starts_ret(&ctx, 0); + int rc = mbedtls_sha256_starts(&ctx, 0); hard_assert(rc == 0); for(int i = 0; i < 1000000; i += BUFFER_SIZE) { - rc = mbedtls_sha256_update_ret(&ctx, buffer, BUFFER_SIZE); + rc = mbedtls_sha256_update(&ctx, buffer, BUFFER_SIZE); hard_assert(rc == 0); } unsigned char mbed_result[32]; - rc = mbedtls_sha256_finish_ret(&ctx, mbed_result); + rc = mbedtls_sha256_finish(&ctx, mbed_result); hard_assert(rc == 0); uint64_t mbed_time = (time_us_64() - start) / 1000; printf("Mbedtls time for sha256 of 1M bytes %"PRIu64"ms\n", mbed_time); diff --git a/status_led/CMakeLists.txt b/status_led/CMakeLists.txt new file mode 100644 index 000000000..e1cfd1e68 --- /dev/null +++ b/status_led/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(status_blink) +add_subdirectory(color_blink) diff --git a/status_led/color_blink/CMakeLists.txt b/status_led/color_blink/CMakeLists.txt new file mode 100644 index 000000000..80fa4639b --- /dev/null +++ b/status_led/color_blink/CMakeLists.txt @@ -0,0 +1,14 @@ +# Blink the colored "status" LED connected to the GPIO defined by PICO_DEFAULT_WS2812_PIN for your board +add_executable(color_blink + color_blink.c +) +# You can define PICO_DEFAULT_WS2812_PIN yourself to add a WS2812 led to a normal GPIO +#target_compile_definitions(color_blink PRIVATE +# PICO_DEFAULT_WS2812_PIN=16 +#) +target_link_libraries(color_blink + pico_stdlib + pico_status_led +) +pico_add_extra_outputs(color_blink) +example_auto_set_url(color_blink) diff --git a/status_led/color_blink/color_blink.c b/status_led/color_blink/color_blink.c new file mode 100644 index 000000000..dbdfe896d --- /dev/null +++ b/status_led/color_blink/color_blink.c @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2025 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico/stdlib.h" +#include "pico/status_led.h" + +#if !PICO_COLORED_STATUS_LED_AVAILABLE +#warning The color_blink example requires a board with a WS2812 LED +#endif + +#ifndef LED_DELAY_MS +#define LED_DELAY_MS 250 +#endif + +int main() { + bool rc = status_led_init(); + hard_assert(rc); + hard_assert(colored_status_led_supported()); // This assert fails if your board does not have WS2812 support + uint32_t count = 0; + while (true) { + // flash red then green then blue + uint32_t color = PICO_COLORED_STATUS_LED_COLOR_FROM_RGB(count % 3 == 0 ? 0xaa : 0, count % 3 == 1 ? 0xaa : 0, count % 3 == 2 ? 0xaa : 0); + colored_status_led_set_on_with_color(color); + count++; + sleep_ms(LED_DELAY_MS); + assert(colored_status_led_get_state()); + colored_status_led_set_state(false); + sleep_ms(LED_DELAY_MS); + assert(!colored_status_led_get_state()); + } + status_led_deinit(); +} diff --git a/status_led/status_blink/CMakeLists.txt b/status_led/status_blink/CMakeLists.txt new file mode 100644 index 000000000..531edad90 --- /dev/null +++ b/status_led/status_blink/CMakeLists.txt @@ -0,0 +1,16 @@ +# Blink the "status" LED either connected to the GPIO defined by PICO_DEFAULT_LED_PIN for your board +# or via the WiFi chip on boards like Pico 2 or Pico 2 W + +add_executable(status_blink + status_blink.c +) +# You can define PICO_DEFAULT_LED_PIN yourself to add a led to a different GPIO +#target_compile_definitions(status_blink PRIVATE +# PICO_DEFAULT_LED_PIN=15 +#) +target_link_libraries(status_blink + pico_stdlib + pico_status_led +) +pico_add_extra_outputs(status_blink) +example_auto_set_url(status_blink) diff --git a/status_led/status_blink/status_blink.c b/status_led/status_blink/status_blink.c new file mode 100644 index 000000000..e798f865d --- /dev/null +++ b/status_led/status_blink/status_blink.c @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2025 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico/stdlib.h" +#include "pico/status_led.h" + +#ifndef LED_DELAY_MS +#define LED_DELAY_MS 250 +#endif + +int main() { + bool rc = status_led_init(); + hard_assert(rc); + while (true) { + status_led_set_state(true); + sleep_ms(LED_DELAY_MS); + assert(status_led_get_state()); + status_led_set_state(false); + sleep_ms(LED_DELAY_MS); + assert(!status_led_get_state()); + } + status_led_deinit(); +} diff --git a/universal/wrapper/CMakeLists.txt b/universal/wrapper/CMakeLists.txt index cf6024c18..548f9310e 100644 --- a/universal/wrapper/CMakeLists.txt +++ b/universal/wrapper/CMakeLists.txt @@ -51,8 +51,9 @@ function(pico_add_extra_outputs TARGET) if (NOT (PICO_PLATFORM MATCHES rp2040)) get_target_property(BINARY_TYPE ${SOURCE_TARGET} PICO_TARGET_BINARY_TYPE) if (${BINARY_TYPE} STREQUAL "no_flash") - message("Packaging no_flash universal binary to SRAM, so it has a load_map") - pico_package_uf2_output(${SOURCE_TARGET} 0x20000000) + message("Ensuring no_flash universal binary has a load_map") + # This is required so it is copied to the correct location in SRAM + pico_ensure_load_map(${SOURCE_TARGET}) endif() endif() _pico_add_extra_outputs(${SOURCE_TARGET})