diff --git a/espflash/src/error.rs b/espflash/src/error.rs index a42cf869..dec227fa 100644 --- a/espflash/src/error.rs +++ b/espflash/src/error.rs @@ -131,6 +131,9 @@ https://github.com/espressif/esp32c3-direct-boot-example" chip: Chip, frequency: FlashFrequency, }, + #[error("The {chip} does not support {feature}")] + #[diagnostic(code(espflash::unsupported_feature))] + UnsupportedFeature { chip: Chip, feature: String }, } #[derive(Error, Debug, Diagnostic)] @@ -449,7 +452,7 @@ impl From for FlashDetectError { pub struct UnsupportedImageFormatError { format: ImageFormatKind, chip: Chip, - revision: Option, + revision: Option<(u32, u32)>, } impl Display for UnsupportedImageFormatError { @@ -459,9 +462,11 @@ impl Display for UnsupportedImageFormatError { "Image format {} is not supported by the {}", self.format, self.chip )?; - if let Some(revision) = self.revision { - write!(f, " revision {}", revision)?; + + if let Some((major, minor)) = self.revision { + write!(f, " revision v{major}.{minor}")?; } + Ok(()) } } @@ -491,7 +496,7 @@ impl Diagnostic for UnsupportedImageFormatError { } impl UnsupportedImageFormatError { - pub fn new(format: ImageFormatKind, chip: Chip, revision: Option) -> Self { + pub fn new(format: ImageFormatKind, chip: Chip, revision: Option<(u32, u32)>) -> Self { UnsupportedImageFormatError { format, chip, diff --git a/espflash/src/flasher/mod.rs b/espflash/src/flasher/mod.rs index 32082c87..0a53e096 100644 --- a/espflash/src/flasher/mod.rs +++ b/espflash/src/flasher/mod.rs @@ -608,22 +608,25 @@ impl Flasher { /// Read and print any information we can about the connected board pub fn board_info(&mut self) -> Result<(), Error> { let chip = self.chip(); - let target = chip.into_target(); - let maybe_revision = target.chip_revision(self.connection())?; + let features = target.chip_features(self.connection())?; let freq = target.crystal_freq(self.connection())?; let mac = target.mac_address(self.connection())?; - print!("Chip type: {}", chip); - match maybe_revision { - Some(revision) => println!(" (revision {})", revision), - None => println!(), + // The ESP8266 does not have readable major/minor revision numbers, so we have + // nothing to print if targeting it. + print!("Chip type: {chip}"); + if chip != Chip::Esp8266 { + let (major, minor) = target.chip_revision(self.connection())?; + println!(" (revision v{major}.{minor})"); + } else { + println!(""); } - println!("Crystal frequency: {}MHz", freq); + println!("Crystal frequency: {freq}MHz"); println!("Flash size: {}", self.flash_size); println!("Features: {}", features.join(", ")); - println!("MAC address: {}", mac); + println!("MAC address: {mac}"); Ok(()) } @@ -687,9 +690,11 @@ impl Flasher { bootloader, partition_table, image_format, - self.chip - .into_target() - .chip_revision(&mut self.connection)?, + Some( + self.chip + .into_target() + .chip_revision(&mut self.connection)?, + ), flash_mode, flash_size.or(Some(self.flash_size)), flash_freq, diff --git a/espflash/src/targets/esp32.rs b/espflash/src/targets/esp32.rs index e2aaabd7..24adc4e4 100644 --- a/espflash/src/targets/esp32.rs +++ b/espflash/src/targets/esp32.rs @@ -118,24 +118,25 @@ impl Target for Esp32 { Ok(features) } - fn chip_revision(&self, connection: &mut Connection) -> Result, Error> { - let word3 = self.read_efuse(connection, 3)?; - let word5 = self.read_efuse(connection, 5)?; + fn major_chip_version(&self, connection: &mut Connection) -> Result { + let apb_ctl_date = connection.read_reg(0x3FF6_607C)?; - let apb_ctrl_date = connection.read_reg(0x3ff6_607c)?; + let rev_bit0 = (self.read_efuse(connection, 3)? >> 15) & 0x1; + let rev_bit1 = (self.read_efuse(connection, 5)? >> 20) & 0x1; + let rev_bit2 = (apb_ctl_date >> 31) & 0x1; - let rev_bit0 = (word3 >> 15) & 0x1 != 0; - let rev_bit1 = (word5 >> 20) & 0x1 != 0; - let rev_bit2 = (apb_ctrl_date >> 31) & 0x1 != 0; + let combine_value = (rev_bit2 << 2) | (rev_bit1 << 1) | rev_bit0; - let revision = match (rev_bit0, rev_bit1, rev_bit2) { - (true, true, true) => 3, - (true, true, false) => 2, - (true, false, _) => 1, - (false, _, _) => 0, - }; + match combine_value { + 1 => Ok(1), + 3 => Ok(2), + 7 => Ok(3), + _ => Ok(0), + } + } - Ok(Some(revision)) + fn minor_chip_version(&self, connection: &mut Connection) -> Result { + Ok((self.read_efuse(connection, 5)? >> 24) & 0x3) } fn crystal_freq(&self, connection: &mut Connection) -> Result { @@ -152,7 +153,7 @@ impl Target for Esp32 { bootloader: Option>, partition_table: Option, image_format: Option, - _chip_revision: Option, + _chip_revision: Option<(u32, u32)>, flash_mode: Option, flash_size: Option, flash_freq: Option, diff --git a/espflash/src/targets/esp32c2.rs b/espflash/src/targets/esp32c2.rs index 1ebc3b35..76529439 100644 --- a/espflash/src/targets/esp32c2.rs +++ b/espflash/src/targets/esp32c2.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, ops::Range}; use esp_idf_part::PartitionTable; -use super::{Chip, Esp32Params, ReadEFuse, SpiRegisters, Target}; +use super::{bytes_to_mac_addr, Chip, Esp32Params, ReadEFuse, SpiRegisters, Target}; use crate::{ connection::Connection, elf::FirmwareImage, @@ -50,18 +50,15 @@ impl Target for Esp32c2 { } fn chip_features(&self, _connection: &mut Connection) -> Result, Error> { - Ok(vec!["WiFi"]) + Ok(vec!["WiFi", "BLE"]) } - fn chip_revision(&self, connection: &mut Connection) -> Result, Error> { - let block1_addr = self.efuse_reg() + 0x44; - let num_word = 3; - let pos = 18; - - let value = connection.read_reg(block1_addr + (num_word * 0x4))?; - let value = (value & (0x7 << pos)) >> pos; + fn major_chip_version(&self, connection: &mut Connection) -> Result { + Ok(self.read_efuse(connection, 17)? >> 20 & 0x3) + } - Ok(Some(value)) + fn minor_chip_version(&self, connection: &mut Connection) -> Result { + Ok(self.read_efuse(connection, 17)? >> 16 & 0xf) } fn crystal_freq(&self, _connection: &mut Connection) -> Result { @@ -88,7 +85,7 @@ impl Target for Esp32c2 { bootloader: Option>, partition_table: Option, image_format: Option, - _chip_revision: Option, + _chip_revision: Option<(u32, u32)>, flash_mode: Option, flash_size: Option, flash_freq: Option, @@ -110,6 +107,18 @@ impl Target for Esp32c2 { } } + /// What is the MAC address? + fn mac_address(&self, connection: &mut Connection) -> Result { + let word5 = self.read_efuse(connection, 16)?; + let word6 = self.read_efuse(connection, 17)?; + + let bytes = ((word6 as u64) << 32) | word5 as u64; + let bytes = bytes.to_be_bytes(); + let bytes = &bytes[2..]; + + Ok(bytes_to_mac_addr(bytes)) + } + fn spi_registers(&self) -> SpiRegisters { SpiRegisters { base: 0x6000_2000, diff --git a/espflash/src/targets/esp32c3.rs b/espflash/src/targets/esp32c3.rs index fa0ea1ec..d32fefd6 100644 --- a/espflash/src/targets/esp32c3.rs +++ b/espflash/src/targets/esp32c3.rs @@ -40,7 +40,7 @@ impl Esp32c3 { impl ReadEFuse for Esp32c3 { fn efuse_reg(&self) -> u32 { - 0x6000_8830 + 0x6000_8800 } } @@ -50,18 +50,18 @@ impl Target for Esp32c3 { } fn chip_features(&self, _connection: &mut Connection) -> Result, Error> { - Ok(vec!["WiFi"]) + Ok(vec!["WiFi", "BLE"]) } - fn chip_revision(&self, connection: &mut Connection) -> Result, Error> { - let block1_addr = self.efuse_reg() + 0x14; - let num_word = 3; - let pos = 18; + fn major_chip_version(&self, connection: &mut Connection) -> Result { + Ok(self.read_efuse(connection, 22)? >> 24 & 0x3) + } - let value = connection.read_reg(block1_addr + (num_word * 0x4))?; - let value = (value & (0x7 << pos)) >> pos; + fn minor_chip_version(&self, connection: &mut Connection) -> Result { + let hi = self.read_efuse(connection, 22)? >> 23 & 0x1; + let lo = self.read_efuse(connection, 20)? >> 18 & 0x7; - Ok(Some(value)) + Ok((hi << 3) + lo) } fn crystal_freq(&self, _connection: &mut Connection) -> Result { @@ -75,7 +75,7 @@ impl Target for Esp32c3 { bootloader: Option>, partition_table: Option, image_format: Option, - chip_revision: Option, + chip_revision: Option<(u32, u32)>, flash_mode: Option, flash_size: Option, flash_freq: Option, @@ -93,7 +93,7 @@ impl Target for Esp32c3 { flash_size, flash_freq, )?)), - (ImageFormatKind::DirectBoot, None | Some(3..)) => { + (ImageFormatKind::DirectBoot, None | Some((3.., _))) => { Ok(Box::new(DirectBootFormat::new(image, 0)?)) } _ => Err( diff --git a/espflash/src/targets/esp32s2.rs b/espflash/src/targets/esp32s2.rs index dc234523..33ee052c 100644 --- a/espflash/src/targets/esp32s2.rs +++ b/espflash/src/targets/esp32s2.rs @@ -40,21 +40,21 @@ impl Esp32s2 { } fn get_block2_version(&self, connection: &mut Connection) -> Result { - let blk2_word4 = self.read_efuse(connection, 15)?; + let blk2_word4 = self.read_efuse(connection, 27)?; let block2_version = (blk2_word4 >> 4) & 0x7; Ok(block2_version) } fn get_flash_version(&self, connection: &mut Connection) -> Result { - let blk1_word3 = self.read_efuse(connection, 8)?; + let blk1_word3 = self.read_efuse(connection, 20)?; let flash_version = (blk1_word3 >> 21) & 0xf; Ok(flash_version) } fn get_psram_version(&self, connection: &mut Connection) -> Result { - let blk1_word3 = self.read_efuse(connection, 8)?; + let blk1_word3 = self.read_efuse(connection, 20)?; let psram_version = (blk1_word3 >> 28) & 0xf; Ok(psram_version) @@ -67,7 +67,7 @@ impl Esp32s2 { impl ReadEFuse for Esp32s2 { fn efuse_reg(&self) -> u32 { - 0x3f41_a030 + 0x3f41_a000 } } @@ -106,6 +106,17 @@ impl Target for Esp32s2 { Ok(features) } + fn major_chip_version(&self, connection: &mut Connection) -> Result { + Ok(self.read_efuse(connection, 20)? >> 18 & 0x3) + } + + fn minor_chip_version(&self, connection: &mut Connection) -> Result { + let hi = self.read_efuse(connection, 20)? >> 20 & 0x1; + let lo = self.read_efuse(connection, 21)? >> 4 & 0x7; + + Ok((hi << 3) + lo) + } + fn crystal_freq(&self, _connection: &mut Connection) -> Result { // The ESP32-S2's XTAL has a fixed frequency of 40MHz. Ok(40) @@ -125,7 +136,7 @@ impl Target for Esp32s2 { bootloader: Option>, partition_table: Option, image_format: Option, - _chip_revision: Option, + _chip_revision: Option<(u32, u32)>, flash_mode: Option, flash_size: Option, flash_freq: Option, diff --git a/espflash/src/targets/esp32s3.rs b/espflash/src/targets/esp32s3.rs index ec098fc4..63bafe5d 100644 --- a/espflash/src/targets/esp32s3.rs +++ b/espflash/src/targets/esp32s3.rs @@ -2,7 +2,7 @@ use std::ops::Range; use esp_idf_part::PartitionTable; -use super::{bytes_to_mac_addr, Chip, Esp32Params, ReadEFuse, SpiRegisters, Target}; +use super::{Chip, Esp32Params, ReadEFuse, SpiRegisters, Target}; use crate::{ connection::Connection, elf::FirmwareImage, @@ -33,11 +33,19 @@ impl Esp32s3 { pub fn has_magic_value(value: u32) -> bool { CHIP_DETECT_MAGIC_VALUES.contains(&value) } + + fn blk_version_major(&self, connection: &mut Connection) -> Result { + Ok(self.read_efuse(connection, 96)? & 0x3) + } + + fn blk_version_minor(&self, connection: &mut Connection) -> Result { + Ok(self.read_efuse(connection, 20)? >> 24 & 0x7) + } } impl ReadEFuse for Esp32s3 { fn efuse_reg(&self) -> u32 { - 0x6000_7030 + 0x6000_7000 } } @@ -50,6 +58,29 @@ impl Target for Esp32s3 { Ok(vec!["WiFi", "BLE"]) } + fn major_chip_version(&self, connection: &mut Connection) -> Result { + let major = self.read_efuse(connection, 22)? >> 24 & 0x3; + + // Workaround: The major version field was allocated to other purposes when + // block version is v1.1. Luckily only chip v0.0 have this kind of block version + // and efuse usage. + if self.minor_chip_version(connection)? == 0 + && self.blk_version_major(connection)? == 1 + && self.blk_version_minor(connection)? == 1 + { + Ok(0) + } else { + Ok(major) + } + } + + fn minor_chip_version(&self, connection: &mut Connection) -> Result { + let hi = self.read_efuse(connection, 22)? >> 23 & 0x1; + let lo = self.read_efuse(connection, 20)? >> 18 & 0x7; + + Ok((hi << 3) + lo) + } + fn crystal_freq(&self, _connection: &mut Connection) -> Result { // The ESP32-S3's XTAL has a fixed frequency of 40MHz. Ok(40) @@ -61,7 +92,7 @@ impl Target for Esp32s3 { bootloader: Option>, partition_table: Option, image_format: Option, - _chip_revision: Option, + _chip_revision: Option<(u32, u32)>, flash_mode: Option, flash_size: Option, flash_freq: Option, @@ -83,17 +114,6 @@ impl Target for Esp32s3 { } } - fn mac_address(&self, connection: &mut Connection) -> Result { - let word5 = self.read_efuse(connection, 5)?; - let word6 = self.read_efuse(connection, 6)?; - - let bytes = ((word6 as u64) << 32) | word5 as u64; - let bytes = bytes.to_be_bytes(); - let bytes = &bytes[2..]; - - Ok(bytes_to_mac_addr(bytes)) - } - fn spi_registers(&self) -> SpiRegisters { SpiRegisters { base: 0x6000_2000, diff --git a/espflash/src/targets/esp8266.rs b/espflash/src/targets/esp8266.rs index 2cd5e598..2b132371 100644 --- a/espflash/src/targets/esp8266.rs +++ b/espflash/src/targets/esp8266.rs @@ -46,6 +46,20 @@ impl Target for Esp8266 { Ok(vec!["WiFi"]) } + fn major_chip_version(&self, _connection: &mut Connection) -> Result { + Err(Error::UnsupportedFeature { + chip: Chip::Esp8266, + feature: "reading the major chip version".into(), + }) + } + + fn minor_chip_version(&self, _connection: &mut Connection) -> Result { + Err(Error::UnsupportedFeature { + chip: Chip::Esp8266, + feature: "reading the minor chip version".into(), + }) + } + fn crystal_freq(&self, connection: &mut Connection) -> Result { let uart_div = connection.read_reg(UART_CLKDIV_REG)? & UART_CLKDIV_MASK; let est_xtal = (connection.get_baud()? * uart_div) / 1_000_000 / XTAL_CLK_DIVIDER; @@ -60,7 +74,7 @@ impl Target for Esp8266 { _bootloader: Option>, _partition_table: Option, image_format: Option, - _chip_revision: Option, + _chip_revision: Option<(u32, u32)>, flash_mode: Option, flash_size: Option, flash_freq: Option, diff --git a/espflash/src/targets/mod.rs b/espflash/src/targets/mod.rs index b53ded2e..d4253508 100644 --- a/espflash/src/targets/mod.rs +++ b/espflash/src/targets/mod.rs @@ -254,11 +254,18 @@ pub trait Target: ReadEFuse { /// Enumerate the chip's features, read from eFuse fn chip_features(&self, connection: &mut Connection) -> Result, Error>; - /// Deterimine the chip's revision number, if it has one - fn chip_revision(&self, _connection: &mut Connection) -> Result, Error> { - Ok(None) + /// Deterimine the chip's revision number + fn chip_revision(&self, connection: &mut Connection) -> Result<(u32, u32), Error> { + let major = self.major_chip_version(connection)?; + let minor = self.minor_chip_version(connection)?; + + Ok((major, minor)) } + fn major_chip_version(&self, connection: &mut Connection) -> Result; + + fn minor_chip_version(&self, connection: &mut Connection) -> Result; + /// What is the crystal frequency? fn crystal_freq(&self, connection: &mut Connection) -> Result; @@ -288,7 +295,7 @@ pub trait Target: ReadEFuse { bootloader: Option>, partition_table: Option, image_format: Option, - chip_revision: Option, + chip_revision: Option<(u32, u32)>, flash_mode: Option, flash_size: Option, flash_freq: Option, @@ -296,8 +303,8 @@ pub trait Target: ReadEFuse { /// What is the MAC address? fn mac_address(&self, connection: &mut Connection) -> Result { - let word5 = self.read_efuse(connection, 5)?; - let word6 = self.read_efuse(connection, 6)?; + let word5 = self.read_efuse(connection, 17)?; + let word6 = self.read_efuse(connection, 18)?; let bytes = ((word6 as u64) << 32) | word5 as u64; let bytes = bytes.to_be_bytes();