From cb9ad34b0b8b6e8556f084ebc2ea16f3eebbd600 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Thu, 4 Dec 2025 17:13:05 +0100 Subject: [PATCH] Add pico_unique_id library to host platform Fixes #2750 --- src/common/pico_base_headers/BUILD.bazel | 1 + src/host.cmake | 1 + src/host/pico_unique_id/BUILD.bazel | 13 ++ src/host/pico_unique_id/CMakeLists.txt | 8 ++ .../pico_unique_id/include/pico/unique_id.h | 127 ++++++++++++++++++ src/host/pico_unique_id/unique_id.c | 30 +++++ 6 files changed, 180 insertions(+) create mode 100644 src/host/pico_unique_id/BUILD.bazel create mode 100644 src/host/pico_unique_id/CMakeLists.txt create mode 100644 src/host/pico_unique_id/include/pico/unique_id.h create mode 100644 src/host/pico_unique_id/unique_id.c diff --git a/src/common/pico_base_headers/BUILD.bazel b/src/common/pico_base_headers/BUILD.bazel index c22f9ae8b..4ac55612c 100644 --- a/src/common/pico_base_headers/BUILD.bazel +++ b/src/common/pico_base_headers/BUILD.bazel @@ -95,6 +95,7 @@ cc_library( "//src/host/hardware_sync:__pkg__", "//src/host/hardware_timer:__pkg__", "//src/host/pico_platform:__pkg__", + "//src/host/pico_unique_id:__pkg__", "//src/rp2040/boot_stage2:__pkg__", "//src/rp2040/pico_platform:__pkg__", "//src/rp2350/boot_stage2:__pkg__", diff --git a/src/host.cmake b/src/host.cmake index 10cafc7f9..731469d32 100644 --- a/src/host.cmake +++ b/src/host.cmake @@ -38,6 +38,7 @@ include (${CMAKE_DIR}/no_hardware.cmake) pico_add_subdirectory(${HOST_DIR}/pico_stdio) pico_add_subdirectory(${HOST_DIR}/pico_stdlib) pico_add_subdirectory(${HOST_DIR}/pico_time_adapter) + pico_add_subdirectory(${HOST_DIR}/pico_unique_id) unset(CMAKE_DIR) unset(COMMON_DIR) diff --git a/src/host/pico_unique_id/BUILD.bazel b/src/host/pico_unique_id/BUILD.bazel new file mode 100644 index 000000000..111de598c --- /dev/null +++ b/src/host/pico_unique_id/BUILD.bazel @@ -0,0 +1,13 @@ +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "pico_unique_id", + srcs = ["unique_id.c"], + hdrs = ["include/pico/unique_id.h"], + includes = ["include"], + target_compatible_with = ["//bazel/constraint:host"], + deps = [ + "//src/common/pico_base_headers", + "//src/host/pico_platform", + ], +) diff --git a/src/host/pico_unique_id/CMakeLists.txt b/src/host/pico_unique_id/CMakeLists.txt new file mode 100644 index 000000000..79881c16f --- /dev/null +++ b/src/host/pico_unique_id/CMakeLists.txt @@ -0,0 +1,8 @@ +pico_add_library(pico_unique_id) + +target_sources(pico_unique_id INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/unique_id.c +) + +target_include_directories(pico_unique_id_headers SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) +target_link_libraries(pico_unique_id_headers INTERFACE pico_base_headers) diff --git a/src/host/pico_unique_id/include/pico/unique_id.h b/src/host/pico_unique_id/include/pico/unique_id.h new file mode 100644 index 000000000..c7d194e19 --- /dev/null +++ b/src/host/pico_unique_id/include/pico/unique_id.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2021 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _PICO_UNIQUE_ID_H +#define _PICO_UNIQUE_ID_H + +#include "pico.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file pico/unique_id.h + * \defgroup pico_unique_id pico_unique_id + * + * \brief Unique device ID access API + * + * \if rp2040_specific + * RP2040 does not have an on-board unique identifier (all instances of RP2040 + * silicon are identical and have no persistent state). However, RP2040 boots + * from serial NOR flash devices which have at least a 64-bit unique ID as a standard + * feature, and there is a 1:1 association between RP2040 and flash, so this + * is suitable for use as a unique identifier for an RP2040-based board. + * + * This library injects a call to the flash_get_unique_id function from the + * hardware_flash library, to run before main, and stores the result in a + * static location which can safely be accessed at any time via + * pico_get_unique_id(). + * + * This avoids some pitfalls of the hardware_flash API, which requires any + * flash-resident interrupt routines to be disabled when called into. + * \endif + * + * \if rp2350_specific + * On boards using RP2350, the unique identifier is read from OTP memory on boot. + * \endif + * + * \if host_specific + * Host builds will return a fixed identifier starting with 0xA0 and incrementing + * by one for a total of PICO_UNIQUE_BOARD_ID_SIZE_BYTES bytes. + * The sequence can be changed by defining a project-specific version of + * pico_get_unique_board_id. + * \endif + */ + +#define PICO_UNIQUE_BOARD_ID_SIZE_BYTES 8 + +/** + * \brief Unique board identifier + * \ingroup pico_unique_id + * + * This structure contains an array of PICO_UNIQUE_BOARD_ID_SIZE_BYTES identifier bytes suitable for + * holding the unique identifier for the device. + * + * \if rp2040_specific + * On an RP2040-based board, the unique identifier is retrieved from the external NOR flash device at boot, + * or for PICO_NO_FLASH builds the unique identifier is set to all 0xEE. + * \endif + * + * \if rp2350_specific + * On an RP2350-based board, the unique identifier is retrieved from OTP memory at boot. + * \endif + * + * \if host_specific + * The unique identifier is generated in pico_get_unique_board_id. + * \endif + */ +typedef struct { + uint8_t id[PICO_UNIQUE_BOARD_ID_SIZE_BYTES]; +} pico_unique_board_id_t; + +/*! \brief Get unique ID + * \ingroup pico_unique_id + * + * Get the unique 64-bit device identifier. + * + * \if rp2040_specific + * On an RP2040-based board, the unique identifier is retrieved from the external NOR flash device at boot, + * or for PICO_NO_FLASH builds the unique identifier is set to all 0xEE. + * \endif + * + * \if rp2350_specific + * On an RP2350-based board, the unique identifier is retrieved from OTP memory at boot. + * \endif + * + * \if host_specific + * Host builds set the unique identifier to a sequence starting with 0xA0 and incrementing by one for each byte. + * Replace this function with a project-specific version if a different identifier is required. + * \endif + * + * \param id_out a pointer to a pico_unique_board_id_t struct, to which the identifier will be written + */ +void pico_get_unique_board_id(pico_unique_board_id_t *id_out); + +/*! \brief Get unique ID in string format + * \ingroup pico_unique_id + * + * Get the unique 64-bit device identifier formatted as a 0-terminated ASCII hex string. + * + * \if rp2040_specific + * On an RP2040-based board, the unique identifier is retrieved from the external NOR flash device at boot, + * or for PICO_NO_FLASH builds the unique identifier is set to all 0xEE. + * \endif + * + * \if rp2350_specific + * On an RP2350-based board, the unique identifier is retrieved from OTP memory at boot. + * \endif + * + * \if host_specific + * Host builds return a sequence starting with 0xA0 and incrementing by one for each byte. + * Replace pico_get_unique_board_id with a project-specific version if a different identifer is required. + * \endif + * + * \param id_out a pointer to a char buffer of size len, to which the identifier will be written + * \param len the size of id_out. For full serial, len >= 2 * PICO_UNIQUE_BOARD_ID_SIZE_BYTES + 1 + */ +void pico_get_unique_board_id_string(char *id_out, uint len); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/host/pico_unique_id/unique_id.c b/src/host/pico_unique_id/unique_id.c new file mode 100644 index 000000000..57792619a --- /dev/null +++ b/src/host/pico_unique_id/unique_id.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico/unique_id.h" + +PICO_WEAK_FUNCTION_DEF(pico_get_unique_board_id) +void PICO_WEAK_FUNCTION_IMPL_NAME(pico_get_unique_board_id)(pico_unique_board_id_t *id_out) { + for (int i = 0; i < PICO_UNIQUE_BOARD_ID_SIZE_BYTES; i++) { + id_out->id[i] = 0xa0 + i; + } +} + +PICO_WEAK_FUNCTION_DEF(pico_get_unique_board_id_string) +void PICO_WEAK_FUNCTION_IMPL_NAME(pico_get_unique_board_id_string)(char *id_out, uint len) { + assert(len > 0); + size_t i; + + pico_unique_board_id_t id; + pico_get_unique_board_id(&id); + + // Generate hex one nibble at a time + for (i = 0; (i < len - 1) && (i < PICO_UNIQUE_BOARD_ID_SIZE_BYTES * 2); i++) { + int nibble = (id.id[i/2] >> (4 - 4 * (i&1))) & 0xf; + id_out[i] = (char)(nibble < 10 ? nibble + '0' : nibble + 'A' - 10); + } + id_out[i] = 0; +}