From b7542061f79a20c1a3b5e83127c8ae767f13e088 Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Thu, 30 Sep 2021 16:31:21 -0700 Subject: [PATCH 1/4] Extract a module inside chip for code specific to the ESP32 and variants --- espflash/src/chip/{ => esp32}/esp32.rs | 9 +++-- espflash/src/chip/{ => esp32}/esp32c3.rs | 10 +++--- espflash/src/chip/{ => esp32}/esp32s2.rs | 10 +++--- espflash/src/chip/esp32/mod.rs | 36 ++++++++++++++++++++ espflash/src/chip/esp8266.rs | 17 ++++++---- espflash/src/chip/mod.rs | 42 ++++-------------------- 6 files changed, 66 insertions(+), 58 deletions(-) rename espflash/src/chip/{ => esp32}/esp32.rs (93%) rename espflash/src/chip/{ => esp32}/esp32c3.rs (91%) rename espflash/src/chip/{ => esp32}/esp32s2.rs (91%) create mode 100644 espflash/src/chip/esp32/mod.rs diff --git a/espflash/src/chip/esp32.rs b/espflash/src/chip/esp32/esp32.rs similarity index 93% rename from espflash/src/chip/esp32.rs rename to espflash/src/chip/esp32/esp32.rs index 7e6dd7f5..977b4b51 100644 --- a/espflash/src/chip/esp32.rs +++ b/espflash/src/chip/esp32/esp32.rs @@ -1,14 +1,13 @@ -use crate::chip::Esp32Params; +use std::ops::Range; -use crate::image_format::{Esp32BootloaderFormat, ImageFormat, ImageFormatId}; +use super::Esp32Params; use crate::{ chip::{Chip, ChipType, SpiRegisters}, elf::FirmwareImage, + image_format::{Esp32BootloaderFormat, ImageFormat, ImageFormatId}, Error, PartitionTable, }; -use std::ops::Range; - pub struct Esp32; const IROM_MAP_START: u32 = 0x400d0000; @@ -27,7 +26,7 @@ pub const PARAMS: Esp32Params = Esp32Params { app_addr: 0x10000, app_size: 0x3f0000, chip_id: 0, - default_bootloader: include_bytes!("../../bootloader/esp32-bootloader.bin"), + default_bootloader: include_bytes!("../../../bootloader/esp32-bootloader.bin"), }; impl ChipType for Esp32 { diff --git a/espflash/src/chip/esp32c3.rs b/espflash/src/chip/esp32/esp32c3.rs similarity index 91% rename from espflash/src/chip/esp32c3.rs rename to espflash/src/chip/esp32/esp32c3.rs index e0c37dd2..9788379e 100644 --- a/espflash/src/chip/esp32c3.rs +++ b/espflash/src/chip/esp32/esp32c3.rs @@ -1,13 +1,13 @@ -use crate::chip::Esp32Params; -use crate::image_format::{Esp32BootloaderFormat, ImageFormat, ImageFormatId}; +use std::ops::Range; + +use super::Esp32Params; use crate::{ chip::{ChipType, SpiRegisters}, elf::FirmwareImage, + image_format::{Esp32BootloaderFormat, ImageFormat, ImageFormatId}, Chip, Error, PartitionTable, }; -use std::ops::Range; - pub struct Esp32c3; const IROM_MAP_START: u32 = 0x42000000; @@ -26,7 +26,7 @@ pub const PARAMS: Esp32Params = Esp32Params { app_addr: 0x10000, app_size: 0x3f0000, chip_id: 5, - default_bootloader: include_bytes!("../../bootloader/esp32c3-bootloader.bin"), + default_bootloader: include_bytes!("../../../bootloader/esp32c3-bootloader.bin"), }; impl ChipType for Esp32c3 { diff --git a/espflash/src/chip/esp32s2.rs b/espflash/src/chip/esp32/esp32s2.rs similarity index 91% rename from espflash/src/chip/esp32s2.rs rename to espflash/src/chip/esp32/esp32s2.rs index a32c847b..7c078348 100644 --- a/espflash/src/chip/esp32s2.rs +++ b/espflash/src/chip/esp32/esp32s2.rs @@ -1,13 +1,13 @@ -use crate::chip::Esp32Params; -use crate::image_format::{Esp32BootloaderFormat, ImageFormat, ImageFormatId}; +use std::ops::Range; + +use super::Esp32Params; use crate::{ chip::{ChipType, SpiRegisters}, elf::FirmwareImage, + image_format::{Esp32BootloaderFormat, ImageFormat, ImageFormatId}, Chip, Error, PartitionTable, }; -use std::ops::Range; - pub struct Esp32s2; const IROM_MAP_START: u32 = 0x40080000; @@ -26,7 +26,7 @@ pub const PARAMS: Esp32Params = Esp32Params { app_addr: 0x10000, app_size: 0x100000, chip_id: 2, - default_bootloader: include_bytes!("../../bootloader/esp32s2-bootloader.bin"), + default_bootloader: include_bytes!("../../../bootloader/esp32s2-bootloader.bin"), }; impl ChipType for Esp32s2 { diff --git a/espflash/src/chip/esp32/mod.rs b/espflash/src/chip/esp32/mod.rs new file mode 100644 index 00000000..200b3233 --- /dev/null +++ b/espflash/src/chip/esp32/mod.rs @@ -0,0 +1,36 @@ +use crate::PartitionTable; + +mod esp32; +mod esp32c3; +mod esp32s2; + +pub use esp32::Esp32; +pub use esp32c3::Esp32c3; +pub use esp32s2::Esp32s2; + +#[derive(Clone, Copy, Debug)] +pub struct Esp32Params { + pub boot_addr: u32, + pub partition_addr: u32, + pub nvs_addr: u32, + pub nvs_size: u32, + pub phy_init_data_addr: u32, + pub phy_init_data_size: u32, + pub app_addr: u32, + pub app_size: u32, + pub chip_id: u16, + pub default_bootloader: &'static [u8], +} + +impl Esp32Params { + pub fn default_partition_table(&self) -> PartitionTable { + PartitionTable::basic( + self.nvs_addr, + self.nvs_size, + self.phy_init_data_addr, + self.phy_init_data_size, + self.app_addr, + self.app_size, + ) + } +} diff --git a/espflash/src/chip/esp8266.rs b/espflash/src/chip/esp8266.rs index 2c58596a..787573f2 100644 --- a/espflash/src/chip/esp8266.rs +++ b/espflash/src/chip/esp8266.rs @@ -1,12 +1,15 @@ -use super::ChipType; -use crate::{chip::SpiRegisters, elf::FirmwareImage, Chip, Error, PartitionTable}; - -use crate::error::UnsupportedImageFormatError; -use crate::image_format::{Esp8266Format, ImageFormat, ImageFormatId}; - use std::ops::Range; -pub const IROM_MAP_START: u32 = 0x40200000; +use super::ChipType; +use crate::{ + chip::SpiRegisters, + elf::FirmwareImage, + error::UnsupportedImageFormatError, + image_format::{Esp8266Format, ImageFormat, ImageFormatId}, + Chip, Error, PartitionTable, +}; + +const IROM_MAP_START: u32 = 0x40200000; const IROM_MAP_END: u32 = 0x40300000; pub struct Esp8266; diff --git a/espflash/src/chip/mod.rs b/espflash/src/chip/mod.rs index e86f74fa..1d210130 100644 --- a/espflash/src/chip/mod.rs +++ b/espflash/src/chip/mod.rs @@ -1,3 +1,5 @@ +use std::ops::Range; + use strum_macros::Display; use crate::{ @@ -5,21 +7,16 @@ use crate::{ error::ChipDetectError, flash_target::{Esp32Target, Esp8266Target, FlashTarget, RamTarget}, flasher::SpiAttachParams, + image_format::{ImageFormat, ImageFormatId}, Error, PartitionTable, }; -use crate::image_format::{ImageFormat, ImageFormatId}; -pub use esp32::Esp32; -pub use esp32c3::Esp32c3; -pub use esp32s2::Esp32s2; -pub use esp8266::Esp8266; -use std::ops::Range; - mod esp32; -mod esp32c3; -mod esp32s2; mod esp8266; +pub use esp32::{Esp32, Esp32Params, Esp32c3, Esp32s2}; +pub use esp8266::Esp8266; + pub trait ChipType { const CHIP_DETECT_MAGIC_VALUE: u32; const CHIP_DETECT_MAGIC_VALUE2: u32 = 0x0; // give default value, as most chips don't only have one @@ -198,30 +195,3 @@ impl Chip { } } } - -#[derive(Clone, Copy, Debug)] -pub struct Esp32Params { - pub boot_addr: u32, - pub partition_addr: u32, - pub nvs_addr: u32, - pub nvs_size: u32, - pub phy_init_data_addr: u32, - pub phy_init_data_size: u32, - pub app_addr: u32, - pub app_size: u32, - pub chip_id: u16, - pub default_bootloader: &'static [u8], -} - -impl Esp32Params { - pub fn default_partition_table(&self) -> PartitionTable { - PartitionTable::basic( - self.nvs_addr, - self.nvs_size, - self.phy_init_data_addr, - self.phy_init_data_size, - self.app_addr, - self.app_size, - ) - } -} From 7025dd400f03de00b4b04aaf4354393372e9d38a Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Thu, 30 Sep 2021 19:16:48 -0700 Subject: [PATCH 2/4] Create and implement a trait for reading eFuse values --- espflash/src/chip/esp32/esp32.rs | 6 ++- espflash/src/chip/esp32/esp32c3.rs | 6 ++- espflash/src/chip/esp32/esp32s2.rs | 6 ++- espflash/src/chip/esp8266.rs | 9 +++- espflash/src/chip/mod.rs | 20 +++++++ espflash/src/connection.rs | 48 +++++++++++++---- espflash/src/flasher.rs | 87 ++++++++++++------------------ 7 files changed, 116 insertions(+), 66 deletions(-) diff --git a/espflash/src/chip/esp32/esp32.rs b/espflash/src/chip/esp32/esp32.rs index 977b4b51..44f45ff5 100644 --- a/espflash/src/chip/esp32/esp32.rs +++ b/espflash/src/chip/esp32/esp32.rs @@ -2,7 +2,7 @@ use std::ops::Range; use super::Esp32Params; use crate::{ - chip::{Chip, ChipType, SpiRegisters}, + chip::{Chip, ChipType, ReadEFuse, SpiRegisters}, elf::FirmwareImage, image_format::{Esp32BootloaderFormat, ImageFormat, ImageFormatId}, Error, PartitionTable, @@ -76,6 +76,10 @@ impl ChipType for Esp32 { } } +impl ReadEFuse for Esp32 { + const EFUSE_REG_BASE: u32 = 0x3ff5a000; +} + #[test] fn test_esp32_rom() { use std::fs::read; diff --git a/espflash/src/chip/esp32/esp32c3.rs b/espflash/src/chip/esp32/esp32c3.rs index 9788379e..39dcba2f 100644 --- a/espflash/src/chip/esp32/esp32c3.rs +++ b/espflash/src/chip/esp32/esp32c3.rs @@ -2,7 +2,7 @@ use std::ops::Range; use super::Esp32Params; use crate::{ - chip::{ChipType, SpiRegisters}, + chip::{ChipType, ReadEFuse, SpiRegisters}, elf::FirmwareImage, image_format::{Esp32BootloaderFormat, ImageFormat, ImageFormatId}, Chip, Error, PartitionTable, @@ -77,3 +77,7 @@ impl ChipType for Esp32c3 { target.starts_with("riscv32imc-") } } + +impl ReadEFuse for Esp32c3 { + const EFUSE_REG_BASE: u32 = 0x60008830; +} diff --git a/espflash/src/chip/esp32/esp32s2.rs b/espflash/src/chip/esp32/esp32s2.rs index 7c078348..2daeef69 100644 --- a/espflash/src/chip/esp32/esp32s2.rs +++ b/espflash/src/chip/esp32/esp32s2.rs @@ -2,7 +2,7 @@ use std::ops::Range; use super::Esp32Params; use crate::{ - chip::{ChipType, SpiRegisters}, + chip::{ChipType, ReadEFuse, SpiRegisters}, elf::FirmwareImage, image_format::{Esp32BootloaderFormat, ImageFormat, ImageFormatId}, Chip, Error, PartitionTable, @@ -75,3 +75,7 @@ impl ChipType for Esp32s2 { target.starts_with("xtensa-esp32s2-") } } + +impl ReadEFuse for Esp32s2 { + const EFUSE_REG_BASE: u32 = 0x3F41A030; +} diff --git a/espflash/src/chip/esp8266.rs b/espflash/src/chip/esp8266.rs index 787573f2..343ce633 100644 --- a/espflash/src/chip/esp8266.rs +++ b/espflash/src/chip/esp8266.rs @@ -2,7 +2,7 @@ use std::ops::Range; use super::ChipType; use crate::{ - chip::SpiRegisters, + chip::{ReadEFuse, SpiRegisters}, elf::FirmwareImage, error::UnsupportedImageFormatError, image_format::{Esp8266Format, ImageFormat, ImageFormatId}, @@ -51,11 +51,16 @@ impl ChipType for Esp8266 { } } +impl ReadEFuse for Esp8266 { + const EFUSE_REG_BASE: u32 = 0x3ff00050; +} + #[test] fn test_esp8266_rom() { - use pretty_assertions::assert_eq; use std::fs::read; + use pretty_assertions::assert_eq; + let input_bytes = read("./tests/data/esp8266").unwrap(); let expected_bin = read("./tests/data/esp8266.bin").unwrap(); diff --git a/espflash/src/chip/mod.rs b/espflash/src/chip/mod.rs index 1d210130..72ce651d 100644 --- a/espflash/src/chip/mod.rs +++ b/espflash/src/chip/mod.rs @@ -3,6 +3,7 @@ use std::ops::Range; use strum_macros::Display; use crate::{ + connection::Connection, elf::FirmwareImage, error::ChipDetectError, flash_target::{Esp32Target, Esp8266Target, FlashTarget, RamTarget}, @@ -40,6 +41,16 @@ pub trait ChipType { fn supports_target(target: &str) -> bool; } +pub trait ReadEFuse { + const EFUSE_REG_BASE: u32; + + /// Given an active connection, read the nth word of the eFuse region. + fn read_efuse(&self, connection: &mut Connection, n: u32) -> Result { + let reg = Self::EFUSE_REG_BASE + (n * 0x4); + connection.read_reg(reg) + } +} + pub struct SpiRegisters { base: u32, usr_offset: u32, @@ -194,4 +205,13 @@ impl Chip { Chip::Esp8266 => Esp8266::SUPPORTED_TARGETS, } } + + pub fn read_efuse(&self, connection: &mut Connection, n: u32) -> Result { + match self { + Chip::Esp32 => Esp32.read_efuse(connection, n), + Chip::Esp32c3 => Esp32c3.read_efuse(connection, n), + Chip::Esp32s2 => Esp32s2.read_efuse(connection, n), + Chip::Esp8266 => Esp8266.read_efuse(connection, n), + } + } } diff --git a/espflash/src/connection.rs b/espflash/src/connection.rs index 9053447e..184a7f9e 100644 --- a/espflash/src/connection.rs +++ b/espflash/src/connection.rs @@ -1,15 +1,16 @@ -use std::io::Write; -use std::thread::sleep; -use std::time::Duration; - -use crate::encoder::SlipEncoder; -use crate::error::{ConnectionError, Error, ResultExt, RomError, RomErrorKind}; -use crate::flasher::Command; -use binread::io::Cursor; -use binread::{BinRead, BinReaderExt}; +use std::{io::Write, thread::sleep, time::Duration}; + +use binread::{io::Cursor, BinRead, BinReaderExt}; +use bytemuck::{bytes_of, Pod, Zeroable}; use serial::{BaudRate, SerialPort, SerialPortSettings, SystemPort}; use slip_codec::Decoder; +use crate::{ + encoder::SlipEncoder, + error::{ConnectionError, Error, ResultExt, RomError, RomErrorKind}, + flasher::Command, +}; + pub struct Connection { serial: SystemPort, decoder: Decoder, @@ -25,6 +26,15 @@ pub struct CommandResponse { pub error: u8, } +#[derive(Zeroable, Pod, Copy, Clone, Debug)] +#[repr(C)] +struct WriteRegParams { + addr: u32, + value: u32, + mask: u32, + delay_us: u32, +} + impl Connection { pub fn new(serial: SystemPort) -> Self { Connection { @@ -143,6 +153,26 @@ impl Connection { Err(Error::Connection(ConnectionError::ConnectionFailed)) } + pub fn read_reg(&mut self, reg: u32) -> Result { + self.with_timeout(Command::ReadReg.timeout(), |connection| { + connection.command(Command::ReadReg, ®.to_le_bytes()[..], 0) + }) + } + + pub fn write_reg(&mut self, addr: u32, value: u32, mask: Option) -> Result<(), Error> { + let params = WriteRegParams { + addr, + value, + mask: mask.unwrap_or(0xFFFFFFFF), + delay_us: 0, + }; + self.with_timeout(Command::WriteReg.timeout(), |connection| { + connection.command(Command::WriteReg, bytes_of(¶ms), 0) + })?; + + Ok(()) + } + fn read(&mut self) -> Result, Error> { let mut output = Vec::with_capacity(1024); self.decoder.decode(&mut self.serial, &mut output)?; diff --git a/espflash/src/flasher.rs b/espflash/src/flasher.rs index 7cf7d9c0..01de4536 100644 --- a/espflash/src/flasher.rs +++ b/espflash/src/flasher.rs @@ -1,16 +1,17 @@ +use std::{borrow::Cow, thread::sleep}; + use bytemuck::{__core::time::Duration, bytes_of, Pod, Zeroable}; use serial::{BaudRate, SystemPort}; use strum_macros::Display; -use std::thread::sleep; - -use crate::elf::RomSegment; -use crate::error::{ConnectionError, ElfError, FlashDetectError, ResultExt, RomError}; use crate::{ - chip::Chip, connection::Connection, elf::FirmwareImage, encoder::SlipEncoder, - error::RomErrorKind, Error, PartitionTable, + chip::Chip, + connection::Connection, + elf::{FirmwareImage, RomSegment}, + encoder::SlipEncoder, + error::{ConnectionError, ElfError, FlashDetectError, ResultExt, RomError, RomErrorKind}, + Error, PartitionTable, }; -use std::borrow::Cow; pub(crate) type Encoder<'a> = SlipEncoder<'a, SystemPort>; @@ -187,15 +188,6 @@ struct BeginParams { encrypted: u32, } -#[derive(Zeroable, Pod, Copy, Clone, Debug)] -#[repr(C)] -struct WriteRegParams { - addr: u32, - value: u32, - mask: u32, - delay_us: u32, -} - #[derive(Zeroable, Pod, Copy, Clone)] #[repr(C)] struct EntryParams { @@ -255,7 +247,7 @@ impl Flasher { } fn chip_detect(&mut self) -> Result<(), Error> { - let magic = self.read_reg(CHIP_DETECT_MAGIC_REG_ADDR)?; + let magic = self.connection.read_reg(CHIP_DETECT_MAGIC_REG_ADDR)?; let chip = Chip::from_magic(magic)?; self.chip = chip; @@ -375,8 +367,8 @@ impl Flasher { let spi_registers = self.chip.spi_registers(); - let old_spi_usr = self.read_reg(spi_registers.usr())?; - let old_spi_usr2 = self.read_reg(spi_registers.usr2())?; + let old_spi_usr = self.connection.read_reg(spi_registers.usr())?; + let old_spi_usr2 = self.connection.read_reg(spi_registers.usr2())?; let mut flags = 1 << 31; if !data.is_empty() { @@ -386,17 +378,21 @@ impl Flasher { flags |= 1 << 28; } - self.write_reg(spi_registers.usr(), flags, None)?; - self.write_reg(spi_registers.usr2(), 7 << 28 | command as u32, None)?; + self.connection + .write_reg(spi_registers.usr(), flags, None)?; + self.connection + .write_reg(spi_registers.usr2(), 7 << 28 | command as u32, None)?; if let (Some(mosi_data_length), Some(miso_data_length)) = (spi_registers.mosi_length(), spi_registers.miso_length()) { if !data.is_empty() { - self.write_reg(mosi_data_length, data.len() as u32 * 8 - 1, None)?; + self.connection + .write_reg(mosi_data_length, data.len() as u32 * 8 - 1, None)?; } if read_bits > 0 { - self.write_reg(miso_data_length, read_bits - 1, None)?; + self.connection + .write_reg(miso_data_length, read_bits - 1, None)?; } } else { let mosi_mask = if data.is_empty() { @@ -405,26 +401,32 @@ impl Flasher { data.len() as u32 * 8 - 1 }; let miso_mask = if read_bits == 0 { 0 } else { read_bits - 1 }; - self.write_reg(spi_registers.usr1(), miso_mask << 8 | mosi_mask << 17, None)?; + self.connection.write_reg( + spi_registers.usr1(), + miso_mask << 8 | mosi_mask << 17, + None, + )?; } if data.is_empty() { - self.write_reg(spi_registers.w0(), 0, None)?; + self.connection.write_reg(spi_registers.w0(), 0, None)?; } else { for (i, bytes) in data.chunks(4).enumerate() { let mut data_bytes = [0; 4]; data_bytes[0..bytes.len()].copy_from_slice(bytes); let data = u32::from_le_bytes(data_bytes); - self.write_reg(spi_registers.w0() + i as u32, data, None)?; + self.connection + .write_reg(spi_registers.w0() + i as u32, data, None)?; } } - self.write_reg(spi_registers.cmd(), 1 << 18, None)?; + self.connection + .write_reg(spi_registers.cmd(), 1 << 18, None)?; let mut i = 0; loop { sleep(Duration::from_millis(1)); - if self.read_reg(spi_registers.usr())? & (1 << 18) == 0 { + if self.connection.read_reg(spi_registers.usr())? & (1 << 18) == 0 { break; } i += 1; @@ -433,32 +435,13 @@ impl Flasher { } } - let result = self.read_reg(spi_registers.w0())?; - self.write_reg(spi_registers.usr(), old_spi_usr, None)?; - self.write_reg(spi_registers.usr2(), old_spi_usr2, None)?; - - Ok(result) - } - - fn read_reg(&mut self, reg: u32) -> Result { + let result = self.connection.read_reg(spi_registers.w0())?; self.connection - .with_timeout(Command::ReadReg.timeout(), |connection| { - connection.command(Command::ReadReg, ®.to_le_bytes()[..], 0) - }) - } - - fn write_reg(&mut self, addr: u32, value: u32, mask: Option) -> Result<(), Error> { - let params = WriteRegParams { - addr, - value, - mask: mask.unwrap_or(0xFFFFFFFF), - delay_us: 0, - }; + .write_reg(spi_registers.usr(), old_spi_usr, None)?; self.connection - .with_timeout(Command::WriteReg.timeout(), |connection| { - connection.command(Command::WriteReg, bytes_of(¶ms), 0) - })?; - Ok(()) + .write_reg(spi_registers.usr2(), old_spi_usr2, None)?; + + Ok(result) } /// The chip type that the flasher is connected to From e67cd28102c28168baf26ed1ddff5885dae79f26 Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Thu, 30 Sep 2021 22:12:39 -0700 Subject: [PATCH 3/4] Add methods for reading the crystal frequency and revision for applicable chips --- cargo-espflash/src/main.rs | 40 +++++++++++++++++++++--------- espflash/src/chip/esp32/esp32.rs | 25 +++++++++++++++++++ espflash/src/chip/esp32/esp32c3.rs | 21 ++++++++++++++++ espflash/src/chip/esp32/esp32s2.rs | 8 ++++++ espflash/src/chip/esp32/mod.rs | 1 + espflash/src/chip/esp8266.rs | 3 +++ espflash/src/chip/mod.rs | 34 +++++++++++++++++++++---- espflash/src/connection.rs | 18 ++++++++++---- espflash/src/flasher.rs | 5 ++++ 9 files changed, 133 insertions(+), 22 deletions(-) diff --git a/cargo-espflash/src/main.rs b/cargo-espflash/src/main.rs index 9fc6c1a0..3da70c84 100644 --- a/cargo-espflash/src/main.rs +++ b/cargo-espflash/src/main.rs @@ -1,5 +1,10 @@ -use crate::cargo_config::parse_cargo_config; -use crate::error::UnsupportedTargetError; +use std::{ + fs, + path::PathBuf, + process::{exit, Command, ExitStatus, Stdio}, + string::ToString, +}; + use cargo_metadata::Message; use clap::{App, Arg, SubCommand}; use error::Error; @@ -8,12 +13,8 @@ use miette::{IntoDiagnostic, Result, WrapErr}; use monitor::monitor; use package_metadata::CargoEspFlashMeta; use serial::{BaudRate, FlowControl, SerialPort}; -use std::{ - fs, - path::PathBuf, - process::{exit, Command, ExitStatus, Stdio}, - string::ToString, -}; + +use crate::{cargo_config::parse_cargo_config, error::UnsupportedTargetError}; mod cargo_config; mod error; @@ -23,6 +24,7 @@ mod package_metadata; fn main() -> Result<()> { miette::set_panic_hook(); + let mut app = App::new(env!("CARGO_PKG_NAME")) .bin_name("cargo") .subcommand( @@ -143,7 +145,7 @@ fn main() -> Result<()> { // provided, display the board info and terminate the application. let mut flasher = Flasher::connect(serial, speed)?; if matches.is_present("board_info") { - board_info(&flasher); + board_info(&mut flasher)?; return Ok(()); } @@ -200,9 +202,23 @@ fn main() -> Result<()> { Ok(()) } -fn board_info(flasher: &Flasher) { - println!("Chip type: {}", flasher.chip()); - println!("Flash size: {}", flasher.flash_size()); +fn board_info(flasher: &mut Flasher) -> Result<()> { + let chip = flasher.chip(); + let revision = chip.chip_revision(flasher.connection())?; + let freq = chip.crystal_freq(flasher.connection())?; + + // Print the detected chip type, and if available the silicon revision. + print!("Chip type: {}", chip); + if let Some(revision) = revision { + println!(" (revision {})", revision); + } else { + println!(); + } + + println!("Crystal frequency: {}MHz", freq); + println!("Flash size: {}", flasher.flash_size()); + + Ok(()) } fn build( diff --git a/espflash/src/chip/esp32/esp32.rs b/espflash/src/chip/esp32/esp32.rs index 44f45ff5..558cd2b4 100644 --- a/espflash/src/chip/esp32/esp32.rs +++ b/espflash/src/chip/esp32/esp32.rs @@ -3,6 +3,7 @@ use std::ops::Range; use super::Esp32Params; use crate::{ chip::{Chip, ChipType, ReadEFuse, SpiRegisters}, + connection::Connection, elf::FirmwareImage, image_format::{Esp32BootloaderFormat, ImageFormat, ImageFormatId}, Error, PartitionTable, @@ -32,6 +33,8 @@ pub const PARAMS: Esp32Params = Esp32Params { impl ChipType for Esp32 { const CHIP_DETECT_MAGIC_VALUE: u32 = 0x00f01d83; + const UART_CLKDIV_REG: u32 = 0x3ff40014; + const SPI_REGISTERS: SpiRegisters = SpiRegisters { base: 0x3ff42000, usr_offset: 0x1c, @@ -80,6 +83,28 @@ impl ReadEFuse for Esp32 { const EFUSE_REG_BASE: u32 = 0x3ff5a000; } +impl Esp32 { + pub fn chip_revision(&self, connection: &mut Connection) -> Result { + let word3 = self.read_efuse(connection, 3)?; + let word5 = self.read_efuse(connection, 5)?; + + let apb_ctrl_date = connection.read_reg(0x3FF6607C)?; + + let rev_bit0 = (word3 >> 15) & 0x1 != 0; + let rev_bit1 = (word5 >> 20) & 0x1 != 0; + let rev_bit2 = (apb_ctrl_date >> 31) & 0x1 != 0; + + let revision = match (rev_bit0, rev_bit1, rev_bit2) { + (true, true, true) => 3, + (true, true, false) => 2, + (true, false, _) => 1, + (false, _, _) => 0, + }; + + Ok(revision) + } +} + #[test] fn test_esp32_rom() { use std::fs::read; diff --git a/espflash/src/chip/esp32/esp32c3.rs b/espflash/src/chip/esp32/esp32c3.rs index 39dcba2f..8d53ef6b 100644 --- a/espflash/src/chip/esp32/esp32c3.rs +++ b/espflash/src/chip/esp32/esp32c3.rs @@ -3,6 +3,7 @@ use std::ops::Range; use super::Esp32Params; use crate::{ chip::{ChipType, ReadEFuse, SpiRegisters}, + connection::Connection, elf::FirmwareImage, image_format::{Esp32BootloaderFormat, ImageFormat, ImageFormatId}, Chip, Error, PartitionTable, @@ -33,6 +34,8 @@ impl ChipType for Esp32c3 { const CHIP_DETECT_MAGIC_VALUE: u32 = 0x6921506f; const CHIP_DETECT_MAGIC_VALUE2: u32 = 0x1b31506f; + const UART_CLKDIV_REG: u32 = 0x3ff40014; + const SPI_REGISTERS: SpiRegisters = SpiRegisters { base: 0x60002000, usr_offset: 0x18, @@ -53,6 +56,11 @@ impl ChipType for Esp32c3 { const SUPPORTED_TARGETS: &'static [&'static str] = &["riscv32imc-uknown-none-elf", "riscv32imc-esp-espidf"]; + fn crystal_freq(&self, _connection: &mut Connection) -> Result { + // The ESP32-C3's XTAL has a fixed frequency of 40MHz. + Ok(40) + } + fn get_flash_segments<'a>( image: &'a FirmwareImage, bootloader: Option>, @@ -81,3 +89,16 @@ impl ChipType for Esp32c3 { impl ReadEFuse for Esp32c3 { const EFUSE_REG_BASE: u32 = 0x60008830; } + +impl Esp32c3 { + pub fn chip_revision(&self, connection: &mut Connection) -> Result { + let block1_addr = Self::EFUSE_REG_BASE + 0x14; + let num_word = 3; + let pos = 18; + + let value = connection.read_reg(block1_addr + (num_word * 0x4))?; + let value = (value & (0x7 << pos)) >> pos; + + Ok(value) + } +} diff --git a/espflash/src/chip/esp32/esp32s2.rs b/espflash/src/chip/esp32/esp32s2.rs index 2daeef69..1ceb53f2 100644 --- a/espflash/src/chip/esp32/esp32s2.rs +++ b/espflash/src/chip/esp32/esp32s2.rs @@ -3,6 +3,7 @@ use std::ops::Range; use super::Esp32Params; use crate::{ chip::{ChipType, ReadEFuse, SpiRegisters}, + connection::Connection, elf::FirmwareImage, image_format::{Esp32BootloaderFormat, ImageFormat, ImageFormatId}, Chip, Error, PartitionTable, @@ -32,6 +33,8 @@ pub const PARAMS: Esp32Params = Esp32Params { impl ChipType for Esp32s2 { const CHIP_DETECT_MAGIC_VALUE: u32 = 0x000007c6; + const UART_CLKDIV_REG: u32 = 0x3f400014; + const SPI_REGISTERS: SpiRegisters = SpiRegisters { base: 0x3f402000, usr_offset: 0x18, @@ -51,6 +54,11 @@ impl ChipType for Esp32s2 { const SUPPORTED_TARGETS: &'static [&'static str] = &["xtensa-esp32s2-none-elf", "xtensa-esp32s2-espidf"]; + fn crystal_freq(&self, _connection: &mut Connection) -> Result { + // The ESP32-S2's XTAL has a fixed frequency of 40MHz. + Ok(40) + } + fn get_flash_segments<'a>( image: &'a FirmwareImage, bootloader: Option>, diff --git a/espflash/src/chip/esp32/mod.rs b/espflash/src/chip/esp32/mod.rs index 200b3233..ff876e1f 100644 --- a/espflash/src/chip/esp32/mod.rs +++ b/espflash/src/chip/esp32/mod.rs @@ -1,5 +1,6 @@ use crate::PartitionTable; +#[allow(clippy::module_inception)] mod esp32; mod esp32c3; mod esp32s2; diff --git a/espflash/src/chip/esp8266.rs b/espflash/src/chip/esp8266.rs index 343ce633..8660c501 100644 --- a/espflash/src/chip/esp8266.rs +++ b/espflash/src/chip/esp8266.rs @@ -17,6 +17,9 @@ pub struct Esp8266; impl ChipType for Esp8266 { const CHIP_DETECT_MAGIC_VALUE: u32 = 0xfff0c101; + const UART_CLKDIV_REG: u32 = 0x60000014; + const XTAL_CLK_DIVIDER: u32 = 2; + const SPI_REGISTERS: SpiRegisters = SpiRegisters { base: 0x60000200, usr_offset: 0x1c, diff --git a/espflash/src/chip/mod.rs b/espflash/src/chip/mod.rs index 72ce651d..1dff0cff 100644 --- a/espflash/src/chip/mod.rs +++ b/espflash/src/chip/mod.rs @@ -22,6 +22,10 @@ pub trait ChipType { const CHIP_DETECT_MAGIC_VALUE: u32; const CHIP_DETECT_MAGIC_VALUE2: u32 = 0x0; // give default value, as most chips don't only have one + const UART_CLKDIV_REG: u32; + const UART_CLKDIV_MASK: u32 = 0xFFFFF; + const XTAL_CLK_DIVIDER: u32 = 1; + const SPI_REGISTERS: SpiRegisters; const FLASH_RANGES: &'static [Range]; @@ -30,6 +34,16 @@ pub trait ChipType { const SUPPORTED_TARGETS: &'static [&'static str]; + /// Determine the frequency of the crytal on the connected chip. + fn crystal_freq(&self, connection: &mut Connection) -> Result { + let uart_div = connection.read_reg(Self::UART_CLKDIV_REG)? & Self::UART_CLKDIV_MASK; + let est_xtal = + (connection.get_baud().speed() as u32 * uart_div) / 1_000_000 / Self::XTAL_CLK_DIVIDER; + let norm_xtal = if est_xtal > 33 { 40 } else { 26 }; + + Ok(norm_xtal) + } + /// Get the firmware segments for writing an image to flash fn get_flash_segments<'a>( image: &'a FirmwareImage, @@ -206,12 +220,22 @@ impl Chip { } } - pub fn read_efuse(&self, connection: &mut Connection, n: u32) -> Result { + pub fn crystal_freq(&self, connection: &mut Connection) -> Result { match self { - Chip::Esp32 => Esp32.read_efuse(connection, n), - Chip::Esp32c3 => Esp32c3.read_efuse(connection, n), - Chip::Esp32s2 => Esp32s2.read_efuse(connection, n), - Chip::Esp8266 => Esp8266.read_efuse(connection, n), + Chip::Esp32 => Esp32.crystal_freq(connection), + Chip::Esp32c3 => Esp32c3.crystal_freq(connection), + Chip::Esp32s2 => Esp32s2.crystal_freq(connection), + Chip::Esp8266 => Esp8266.crystal_freq(connection), } } + + pub fn chip_revision(&self, connection: &mut Connection) -> Result, Error> { + let rev = match self { + Chip::Esp32 => Some(Esp32.chip_revision(connection)?), + Chip::Esp32c3 => Some(Esp32c3.chip_revision(connection)?), + _ => None, + }; + + Ok(rev) + } } diff --git a/espflash/src/connection.rs b/espflash/src/connection.rs index 184a7f9e..b57fdd61 100644 --- a/espflash/src/connection.rs +++ b/espflash/src/connection.rs @@ -11,11 +11,6 @@ use crate::{ flasher::Command, }; -pub struct Connection { - serial: SystemPort, - decoder: Decoder, -} - #[derive(Debug, Copy, Clone, BinRead)] pub struct CommandResponse { pub resp: u8, @@ -26,6 +21,12 @@ pub struct CommandResponse { pub error: u8, } +pub struct Connection { + serial: SystemPort, + speed: BaudRate, + decoder: Decoder, +} + #[derive(Zeroable, Pod, Copy, Clone, Debug)] #[repr(C)] struct WriteRegParams { @@ -39,6 +40,7 @@ impl Connection { pub fn new(serial: SystemPort) -> Self { Connection { serial, + speed: BaudRate::Baud115200, decoder: Decoder::new(), } } @@ -78,11 +80,17 @@ impl Connection { } pub fn set_baud(&mut self, speed: BaudRate) -> Result<(), Error> { + self.speed = speed; self.serial .reconfigure(&|setup: &mut dyn SerialPortSettings| setup.set_baud_rate(speed))?; + Ok(()) } + pub fn get_baud(&self) -> BaudRate { + self.speed + } + pub fn with_timeout Result>( &mut self, timeout: Duration, diff --git a/espflash/src/flasher.rs b/espflash/src/flasher.rs index 01de4536..63023d50 100644 --- a/espflash/src/flasher.rs +++ b/espflash/src/flasher.rs @@ -444,6 +444,11 @@ impl Flasher { Ok(result) } + /// The active serial connection being used by the flasher + pub fn connection(&mut self) -> &mut Connection { + &mut self.connection + } + /// The chip type that the flasher is connected to pub fn chip(&self) -> Chip { self.chip From 4286006e39273328557bff2c91193e2b4b8fa7cc Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Fri, 1 Oct 2021 08:52:14 -0700 Subject: [PATCH 4/4] Move board_info into Flasher where it's usable by both applications --- cargo-espflash/src/main.rs | 28 ++++++---------------------- espflash/src/flasher.rs | 18 ++++++++++++++++++ espflash/src/main.rs | 3 +-- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/cargo-espflash/src/main.rs b/cargo-espflash/src/main.rs index 3da70c84..333e03de 100644 --- a/cargo-espflash/src/main.rs +++ b/cargo-espflash/src/main.rs @@ -141,11 +141,13 @@ fn main() -> Result<()> { None }; - // Connect the Flasher to the target device. If the '--board-info' flag has been - // provided, display the board info and terminate the application. + // Connect the Flasher to the target device and print the board information + // upon connection. If the '--board-info' flag has been provided, we have + // nothing left to do so exit early. let mut flasher = Flasher::connect(serial, speed)?; + flasher.board_info()?; + if matches.is_present("board_info") { - board_info(&mut flasher)?; return Ok(()); } @@ -193,6 +195,7 @@ fn main() -> Result<()> { } else { flasher.load_elf_to_flash(&elf_data, bootloader, partition_table)?; } + println!("\nFlashing has completed!"); if matches.is_present("monitor") { monitor(flasher.into_serial()).into_diagnostic()?; @@ -202,25 +205,6 @@ fn main() -> Result<()> { Ok(()) } -fn board_info(flasher: &mut Flasher) -> Result<()> { - let chip = flasher.chip(); - let revision = chip.chip_revision(flasher.connection())?; - let freq = chip.crystal_freq(flasher.connection())?; - - // Print the detected chip type, and if available the silicon revision. - print!("Chip type: {}", chip); - if let Some(revision) = revision { - println!(" (revision {})", revision); - } else { - println!(); - } - - println!("Crystal frequency: {}MHz", freq); - println!("Flash size: {}", flasher.flash_size()); - - Ok(()) -} - fn build( release: bool, example: Option<&str>, diff --git a/espflash/src/flasher.rs b/espflash/src/flasher.rs index 63023d50..b09a2d10 100644 --- a/espflash/src/flasher.rs +++ b/espflash/src/flasher.rs @@ -459,6 +459,24 @@ impl Flasher { self.flash_size } + /// Read and print any information we can about the connected board + pub fn board_info(&mut self) -> Result<(), Error> { + let chip = self.chip(); + let maybe_revision = chip.chip_revision(self.connection())?; + let freq = chip.crystal_freq(self.connection())?; + let size = self.flash_size(); + + print!("Chip type: {}", chip); + match maybe_revision { + Some(revision) => println!(" (revision {})", revision), + None => println!(), + } + println!("Crystal frequency: {}MHz", freq); + println!("Flash size: {}", size); + + Ok(()) + } + /// Load an elf image to ram and execute it /// /// Note that this will not touch the flash on the device diff --git a/espflash/src/main.rs b/espflash/src/main.rs index a607f4f3..5738d25a 100644 --- a/espflash/src/main.rs +++ b/espflash/src/main.rs @@ -56,8 +56,7 @@ fn main() -> Result<()> { let mut flasher = Flasher::connect(serial, None)?; if board_info { - println!("Chip type: {}", flasher.chip()); - println!("Flash size: {}", flasher.flash_size()); + flasher.board_info()?; return Ok(()); }