diff options
| author | Caleb Jamison <[email protected]> | 2024-08-29 12:28:23 -0400 |
|---|---|---|
| committer | Caleb Jamison <[email protected]> | 2024-08-29 21:08:25 -0400 |
| commit | 0434798439b9037a4fa5f30165879292e388f042 (patch) | |
| tree | e800bac5821057f524ac1d91175c9dd59ba4e0cf | |
| parent | 372270a9b962196ede9c60a705cc3138ba592fec (diff) | |
Import otp from rp-hal, helper fns for chipid and randid
Again, credit to @thejpster for doing the hard part and figuring out the
otp.
| -rw-r--r-- | embassy-rp/src/lib.rs | 2 | ||||
| -rw-r--r-- | embassy-rp/src/otp.rs | 108 | ||||
| -rw-r--r-- | examples/rp23/src/bin/otp.rs | 46 |
3 files changed, 156 insertions, 0 deletions
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index f8fcfe52b..c357c14c2 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs | |||
| @@ -32,6 +32,8 @@ pub mod gpio; | |||
| 32 | pub mod i2c; | 32 | pub mod i2c; |
| 33 | pub mod i2c_slave; | 33 | pub mod i2c_slave; |
| 34 | pub mod multicore; | 34 | pub mod multicore; |
| 35 | #[cfg(feature = "_rp235x")] | ||
| 36 | pub mod otp; | ||
| 35 | pub mod pwm; | 37 | pub mod pwm; |
| 36 | mod reset; | 38 | mod reset; |
| 37 | pub mod rom_data; | 39 | pub mod rom_data; |
diff --git a/embassy-rp/src/otp.rs b/embassy-rp/src/otp.rs new file mode 100644 index 000000000..cdaf5bded --- /dev/null +++ b/embassy-rp/src/otp.rs | |||
| @@ -0,0 +1,108 @@ | |||
| 1 | //! Interface to the RP2350's One Time Programmable Memory | ||
| 2 | |||
| 3 | // Credit: taken from `rp-hal` (also licensed Apache+MIT) | ||
| 4 | // https://github.com/rp-rs/rp-hal/blob/main/rp235x-hal/src/rom_data.rs | ||
| 5 | |||
| 6 | /// The ways in which we can fail to read OTP | ||
| 7 | #[derive(Debug, Clone)] | ||
| 8 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 9 | pub enum Error { | ||
| 10 | /// The user passed an invalid index to a function. | ||
| 11 | InvalidIndex, | ||
| 12 | /// The hardware refused to let us read this word, probably due to | ||
| 13 | /// read lock set earlier in the boot process. | ||
| 14 | InvalidPermissions, | ||
| 15 | } | ||
| 16 | |||
| 17 | /// OTP read address, using automatic Error Correction. | ||
| 18 | /// | ||
| 19 | /// A 32-bit read returns the ECC-corrected data for two neighbouring rows, or | ||
| 20 | /// all-ones on permission failure. Only the first 8 KiB is populated. | ||
| 21 | pub const OTP_DATA_BASE: *const u32 = 0x4013_0000 as *const u32; | ||
| 22 | |||
| 23 | /// OTP read address, without using any automatic Error Correction. | ||
| 24 | /// | ||
| 25 | /// A 32-bit read returns 24-bits of raw data from the OTP word. | ||
| 26 | pub const OTP_DATA_RAW_BASE: *const u32 = 0x4013_4000 as *const u32; | ||
| 27 | |||
| 28 | /// How many pages in OTP (post error-correction) | ||
| 29 | pub const NUM_PAGES: usize = 64; | ||
| 30 | |||
| 31 | /// How many rows in one page in OTP (post error-correction) | ||
| 32 | pub const NUM_ROWS_PER_PAGE: usize = 64; | ||
| 33 | |||
| 34 | /// How many rows in OTP (post error-correction) | ||
| 35 | pub const NUM_ROWS: usize = NUM_PAGES * NUM_ROWS_PER_PAGE; | ||
| 36 | |||
| 37 | /// Read one ECC protected word from the OTP | ||
| 38 | pub fn read_ecc_word(row: usize) -> Result<u16, Error> { | ||
| 39 | if row >= NUM_ROWS { | ||
| 40 | return Err(Error::InvalidIndex); | ||
| 41 | } | ||
| 42 | // First do a raw read to check permissions | ||
| 43 | let _ = read_raw_word(row)?; | ||
| 44 | // One 32-bit read gets us two rows | ||
| 45 | let offset = row >> 1; | ||
| 46 | // # Safety | ||
| 47 | // | ||
| 48 | // We checked this offset was in range already. | ||
| 49 | let value = unsafe { OTP_DATA_BASE.add(offset).read() }; | ||
| 50 | if (row & 1) == 0 { | ||
| 51 | Ok(value as u16) | ||
| 52 | } else { | ||
| 53 | Ok((value >> 16) as u16) | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | /// Read one raw word from the OTP | ||
| 58 | /// | ||
| 59 | /// You get the 24-bit raw value in the lower part of the 32-bit result. | ||
| 60 | pub fn read_raw_word(row: usize) -> Result<u32, Error> { | ||
| 61 | if row >= NUM_ROWS { | ||
| 62 | return Err(Error::InvalidIndex); | ||
| 63 | } | ||
| 64 | // One 32-bit read gets us one row | ||
| 65 | // # Safety | ||
| 66 | // | ||
| 67 | // We checked this offset was in range already. | ||
| 68 | let value = unsafe { OTP_DATA_RAW_BASE.add(row).read() }; | ||
| 69 | if value == 0xFFFF_FFFF { | ||
| 70 | Err(Error::InvalidPermissions) | ||
| 71 | } else { | ||
| 72 | Ok(value) | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | /// Get the random 64bit chipid from rows 0x0-0x3. | ||
| 77 | pub fn get_chipid() -> Result<u64, Error> { | ||
| 78 | let w0 = read_ecc_word(0x000)?.to_be_bytes(); | ||
| 79 | let w1 = read_ecc_word(0x001)?.to_be_bytes(); | ||
| 80 | let w2 = read_ecc_word(0x002)?.to_be_bytes(); | ||
| 81 | let w3 = read_ecc_word(0x003)?.to_be_bytes(); | ||
| 82 | |||
| 83 | Ok(u64::from_be_bytes([ | ||
| 84 | w3[0], w3[1], w2[0], w2[1], w1[0], w1[1], w0[0], w0[1], | ||
| 85 | ])) | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Get the 128bit private random number from rows 0x4-0xb. | ||
| 89 | /// | ||
| 90 | /// This ID is not exposed through the USB PICOBOOT GET_INFO command | ||
| 91 | /// or the ROM get_sys_info() API. However note that the USB PICOBOOT OTP | ||
| 92 | /// access point can read the entirety of page 0, so this value is not | ||
| 93 | /// meaningfully private unless the USB PICOBOOT interface is disabled via the | ||
| 94 | //// DISABLE_BOOTSEL_USB_PICOBOOT_IFC flag in BOOT_FLAGS0 | ||
| 95 | pub fn get_private_random_number() -> Result<u128, Error> { | ||
| 96 | let w0 = read_ecc_word(0x004)?.to_be_bytes(); | ||
| 97 | let w1 = read_ecc_word(0x005)?.to_be_bytes(); | ||
| 98 | let w2 = read_ecc_word(0x006)?.to_be_bytes(); | ||
| 99 | let w3 = read_ecc_word(0x007)?.to_be_bytes(); | ||
| 100 | let w4 = read_ecc_word(0x008)?.to_be_bytes(); | ||
| 101 | let w5 = read_ecc_word(0x009)?.to_be_bytes(); | ||
| 102 | let w6 = read_ecc_word(0x00a)?.to_be_bytes(); | ||
| 103 | let w7 = read_ecc_word(0x00b)?.to_be_bytes(); | ||
| 104 | |||
| 105 | Ok(u128::from_be_bytes([ | ||
| 106 | w7[0], w7[1], w6[0], w6[1], w5[0], w5[1], w4[0], w4[1], w3[0], w3[1], w2[0], w2[1], w1[0], w1[1], w0[0], w0[1], | ||
| 107 | ])) | ||
| 108 | } | ||
diff --git a/examples/rp23/src/bin/otp.rs b/examples/rp23/src/bin/otp.rs new file mode 100644 index 000000000..c4d79c6ea --- /dev/null +++ b/examples/rp23/src/bin/otp.rs | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | //! This example shows reading the OTP constants on the RP235x. | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | |||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_rp::block::ImageDef; | ||
| 9 | use embassy_rp::otp; | ||
| 10 | use embassy_time::Timer; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | #[link_section = ".start_block"] | ||
| 14 | #[used] | ||
| 15 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 16 | |||
| 17 | // Program metadata for `picotool info` | ||
| 18 | #[link_section = ".bi_entries"] | ||
| 19 | #[used] | ||
| 20 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 21 | embassy_rp::binary_info::rp_program_name!(c"OTP Read Example"), | ||
| 22 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 23 | embassy_rp::binary_info::rp_program_description!(c"OTP Read Example"), | ||
| 24 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 25 | ]; | ||
| 26 | |||
| 27 | #[embassy_executor::main] | ||
| 28 | async fn main(_spawner: Spawner) { | ||
| 29 | let _ = embassy_rp::init(Default::default()); | ||
| 30 | // | ||
| 31 | // add some delay to give an attached debug probe time to parse the | ||
| 32 | // defmt RTT header. Reading that header might touch flash memory, which | ||
| 33 | // interferes with flash write operations. | ||
| 34 | // https://github.com/knurling-rs/defmt/pull/683 | ||
| 35 | Timer::after_millis(10).await; | ||
| 36 | |||
| 37 | let unique_id = unwrap!(otp::get_unique_id()); | ||
| 38 | info!("Unique id:{:X}", unique_id); | ||
| 39 | |||
| 40 | let private_rand = unwrap!(otp::get_private_random_number()); | ||
| 41 | info!("Private Rand:{:X}", private_rand); | ||
| 42 | |||
| 43 | loop { | ||
| 44 | Timer::after_secs(1).await; | ||
| 45 | } | ||
| 46 | } | ||
