Skip to content

Commit 77ed92f

Browse files
authored
Merge pull request #35 from jessebraham/esp32c3
Add support for flashing the ESP32-C3
2 parents 16f1173 + 4044f53 commit 77ed92f

File tree

10 files changed

+334
-148
lines changed

10 files changed

+334
-148
lines changed

cargo-espflash/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ fn usage() -> Result<()> {
7676
[--ram] \
7777
[--release] \
7878
[--example EXAMPLE] \
79-
[--chip {{esp32,esp8266}}] \
79+
[--chip {{esp32,esp32c3,esp8266}}] \
8080
[--speed BAUD] \
8181
<serial>";
8282

File renamed without changes.
19.4 KB
Binary file not shown.

espflash/src/chip/esp32.rs

Lines changed: 16 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
use std::borrow::Cow;
2-
use std::io::Write;
3-
use std::iter::once;
4-
5-
use crate::chip::esp32::partition_table::PartitionTable;
6-
use crate::chip::{Chip, ChipType, EspCommonHeader, SegmentHeader, SpiRegisters, ESP_MAGIC};
7-
use crate::elf::{update_checksum, CodeSegment, FirmwareImage, RomSegment, ESP_CHECKSUM_MAGIC};
8-
use crate::flasher::FlashSize;
9-
use crate::Error;
10-
use bytemuck::{bytes_of, Pod, Zeroable};
1+
use bytemuck::bytes_of;
112
use sha2::{Digest, Sha256};
123

13-
mod partition_table;
4+
use crate::{
5+
chip::{
6+
encode_flash_size, get_segment_padding, save_flash_segment, save_segment, Chip, ChipType,
7+
EspCommonHeader, ExtendedHeader, SegmentHeader, SpiRegisters, ESP_MAGIC, SEG_HEADER_LEN,
8+
WP_PIN_DISABLED,
9+
},
10+
elf::{FirmwareImage, RomSegment, ESP_CHECKSUM_MAGIC},
11+
partition_table::PartitionTable,
12+
Error,
13+
};
1414

15-
pub struct Esp32;
15+
use std::{borrow::Cow, io::Write, iter::once};
1616

17-
const WP_PIN_DISABLED: u8 = 0xEE;
17+
pub struct Esp32;
1818

1919
const IROM_MAP_START: u32 = 0x400d0000;
2020
const IROM_MAP_END: u32 = 0x40400000;
@@ -26,22 +26,9 @@ const BOOT_ADDR: u32 = 0x1000;
2626
const PARTION_ADDR: u32 = 0x8000;
2727
const APP_ADDR: u32 = 0x10000;
2828

29-
#[derive(Copy, Clone, Zeroable, Pod)]
30-
#[repr(C)]
31-
struct ExtendedHeader {
32-
wp_pin: u8,
33-
clk_q_drv: u8,
34-
d_cs_drv: u8,
35-
gd_wp_drv: u8,
36-
chip_id: u16,
37-
min_rev: u8,
38-
padding: [u8; 8],
39-
append_digest: u8,
40-
}
41-
4229
impl ChipType for Esp32 {
43-
const DATE_REG1_VALUE: u32 = 0x15122500;
44-
const DATE_REG2_VALUE: u32 = 0;
30+
const CHIP_DETECT_MAGIC_VALUE: u32 = 0x00f01d83;
31+
4532
const SPI_REGISTERS: SpiRegisters = SpiRegisters {
4633
base: 0x3ff42000,
4734
usr_offset: 0x1c,
@@ -60,7 +47,7 @@ impl ChipType for Esp32 {
6047
fn get_flash_segments<'a>(
6148
image: &'a FirmwareImage,
6249
) -> Box<dyn Iterator<Item = Result<RomSegment<'a>, Error>> + 'a> {
63-
let bootloader = include_bytes!("../../bootloader/bootloader.bin");
50+
let bootloader = include_bytes!("../../bootloader/esp32-bootloader.bin");
6451

6552
let partition_table = PartitionTable::basic(0x10000, 0x3f0000).to_bytes();
6653

@@ -167,74 +154,6 @@ impl ChipType for Esp32 {
167154
}
168155
}
169156

170-
fn encode_flash_size(size: FlashSize) -> Result<u8, Error> {
171-
match size {
172-
FlashSize::Flash256Kb => Err(Error::UnsupportedFlash(size as u8)),
173-
FlashSize::Flash512Kb => Err(Error::UnsupportedFlash(size as u8)),
174-
FlashSize::Flash1Mb => Ok(0x00),
175-
FlashSize::Flash2Mb => Ok(0x10),
176-
FlashSize::Flash4Mb => Ok(0x20),
177-
FlashSize::Flash8Mb => Ok(0x30),
178-
FlashSize::Flash16Mb => Ok(0x40),
179-
FlashSize::FlashRetry => Err(Error::UnsupportedFlash(size as u8)),
180-
}
181-
}
182-
183-
const IROM_ALIGN: u32 = 65536;
184-
const SEG_HEADER_LEN: u32 = 8;
185-
186-
/// Actual alignment (in data bytes) required for a segment header: positioned
187-
/// so that after we write the next 8 byte header, file_offs % IROM_ALIGN ==
188-
/// segment.addr % IROM_ALIGN
189-
///
190-
/// (this is because the segment's vaddr may not be IROM_ALIGNed, more likely is
191-
/// aligned IROM_ALIGN+0x18 to account for the binary file header
192-
fn get_segment_padding(offset: usize, segment: &CodeSegment) -> u32 {
193-
let align_past = (segment.addr % IROM_ALIGN) - SEG_HEADER_LEN;
194-
let pad_len = (IROM_ALIGN - ((offset as u32) % IROM_ALIGN)) + align_past;
195-
if pad_len == 0 || pad_len == IROM_ALIGN {
196-
0
197-
} else if pad_len > SEG_HEADER_LEN {
198-
pad_len - SEG_HEADER_LEN
199-
} else {
200-
pad_len + IROM_ALIGN - SEG_HEADER_LEN
201-
}
202-
}
203-
204-
fn save_flash_segment(
205-
data: &mut Vec<u8>,
206-
segment: &CodeSegment,
207-
checksum: u8,
208-
) -> Result<u8, Error> {
209-
let end_pos = (data.len() + segment.data.len()) as u32 + SEG_HEADER_LEN;
210-
let segment_reminder = end_pos % IROM_ALIGN;
211-
212-
let checksum = save_segment(data, segment, checksum)?;
213-
214-
if segment_reminder < 0x24 {
215-
// Work around a bug in ESP-IDF 2nd stage bootloader, that it didn't map the
216-
// last MMU page, if an IROM/DROM segment was < 0x24 bytes over the page
217-
// boundary.
218-
data.write_all(&[0u8; 0x24][0..(0x24 - segment_reminder as usize)])?;
219-
}
220-
Ok(checksum)
221-
}
222-
223-
fn save_segment(data: &mut Vec<u8>, segment: &CodeSegment, checksum: u8) -> Result<u8, Error> {
224-
let padding = (4 - segment.data.len() % 4) % 4;
225-
226-
let header = SegmentHeader {
227-
addr: segment.addr,
228-
length: (segment.data.len() + padding) as u32,
229-
};
230-
data.write_all(bytes_of(&header))?;
231-
data.write_all(segment.data)?;
232-
let padding = &[0u8; 4][0..padding];
233-
data.write_all(padding)?;
234-
235-
Ok(update_checksum(segment.data, checksum))
236-
}
237-
238157
#[test]
239158
fn test_esp32_rom() {
240159
use std::fs::read;

espflash/src/chip/esp32c3.rs

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
use bytemuck::bytes_of;
2+
use sha2::{Digest, Sha256};
3+
4+
use crate::{
5+
chip::{
6+
encode_flash_size, get_segment_padding, save_flash_segment, save_segment, Chip, ChipType,
7+
EspCommonHeader, ExtendedHeader, SegmentHeader, SpiRegisters, ESP_MAGIC, SEG_HEADER_LEN,
8+
WP_PIN_DISABLED,
9+
},
10+
elf::{FirmwareImage, RomSegment, ESP_CHECKSUM_MAGIC},
11+
partition_table::PartitionTable,
12+
Error,
13+
};
14+
15+
use std::{borrow::Cow, io::Write, iter::once};
16+
17+
pub struct Esp32c3;
18+
19+
const IROM_MAP_START: u32 = 0x42000000;
20+
const IROM_MAP_END: u32 = 0x42800000;
21+
22+
const DROM_MAP_START: u32 = 0x3c000000;
23+
const DROM_MAP_END: u32 = 0x3c800000;
24+
25+
const BOOT_ADDR: u32 = 0x0;
26+
const PARTITION_ADDR: u32 = 0x8000;
27+
const APP_ADDR: u32 = 0x10000;
28+
29+
impl ChipType for Esp32c3 {
30+
const CHIP_DETECT_MAGIC_VALUE: u32 = 0x6921506f;
31+
const CHIP_DETECT_MAGIC_VALUE2: u32 = 0x1b31506f;
32+
33+
const SPI_REGISTERS: SpiRegisters = SpiRegisters {
34+
base: 0x60002000,
35+
usr_offset: 0x18,
36+
usr1_offset: 0x1C,
37+
usr2_offset: 0x20,
38+
w0_offset: 0x58,
39+
mosi_length_offset: Some(0x24),
40+
miso_length_offset: Some(0x28),
41+
};
42+
43+
fn addr_is_flash(addr: u32) -> bool {
44+
(IROM_MAP_START..IROM_MAP_END).contains(&addr)
45+
|| (DROM_MAP_START..DROM_MAP_END).contains(&addr)
46+
}
47+
48+
fn get_flash_segments<'a>(
49+
image: &'a FirmwareImage,
50+
) -> Box<dyn Iterator<Item = Result<RomSegment<'a>, Error>> + 'a> {
51+
let bootloader = include_bytes!("../../bootloader/esp32c3-bootloader.bin");
52+
53+
let partition_table = PartitionTable::basic(0x10000, 0x3f0000).to_bytes();
54+
55+
fn get_data<'a>(image: &'a FirmwareImage) -> Result<RomSegment<'a>, Error> {
56+
let mut data = Vec::new();
57+
58+
let header = EspCommonHeader {
59+
magic: ESP_MAGIC,
60+
segment_count: 0,
61+
flash_mode: image.flash_mode as u8,
62+
flash_config: encode_flash_size(image.flash_size)? + image.flash_frequency as u8,
63+
entry: image.entry,
64+
};
65+
data.write_all(bytes_of(&header))?;
66+
67+
let extended_header = ExtendedHeader {
68+
wp_pin: WP_PIN_DISABLED,
69+
clk_q_drv: 0,
70+
d_cs_drv: 0,
71+
gd_wp_drv: 0,
72+
chip_id: 5,
73+
min_rev: 0,
74+
padding: [0; 8],
75+
append_digest: 1,
76+
};
77+
data.write_all(bytes_of(&extended_header))?;
78+
79+
let mut checksum = ESP_CHECKSUM_MAGIC;
80+
81+
let _ = image.segments().collect::<Vec<_>>();
82+
83+
let mut flash_segments: Vec<_> = image.rom_segments(Chip::Esp32c3).collect();
84+
flash_segments.sort();
85+
let mut ram_segments: Vec<_> = image.ram_segments(Chip::Esp32c3).collect();
86+
ram_segments.sort();
87+
let mut ram_segments = ram_segments.into_iter();
88+
89+
let mut segment_count = 0;
90+
91+
for segment in flash_segments {
92+
loop {
93+
let pad_len = get_segment_padding(data.len(), &segment);
94+
if pad_len > 0 {
95+
if pad_len > SEG_HEADER_LEN {
96+
if let Some(ram_segment) = ram_segments.next() {
97+
checksum = save_segment(&mut data, &ram_segment, checksum)?;
98+
segment_count += 1;
99+
continue;
100+
}
101+
}
102+
let pad_header = SegmentHeader {
103+
addr: 0,
104+
length: pad_len as u32,
105+
};
106+
data.write_all(bytes_of(&pad_header))?;
107+
for _ in 0..pad_len {
108+
data.write_all(&[0])?;
109+
}
110+
segment_count += 1;
111+
} else {
112+
break;
113+
}
114+
}
115+
checksum = save_flash_segment(&mut data, &segment, checksum)?;
116+
segment_count += 1;
117+
}
118+
119+
for segment in ram_segments {
120+
checksum = save_segment(&mut data, &segment, checksum)?;
121+
segment_count += 1;
122+
}
123+
124+
let padding = 15 - (data.len() % 16);
125+
let padding = &[0u8; 16][0..padding as usize];
126+
data.write_all(padding)?;
127+
128+
data.write_all(&[checksum])?;
129+
130+
// since we added some dummy segments, we need to patch the segment count
131+
data[1] = segment_count as u8;
132+
133+
let mut hasher = Sha256::new();
134+
hasher.update(&data);
135+
let hash = hasher.finalize();
136+
data.write_all(&hash)?;
137+
138+
Ok(RomSegment {
139+
addr: APP_ADDR,
140+
data: Cow::Owned(data),
141+
})
142+
}
143+
144+
Box::new(
145+
once(Ok(RomSegment {
146+
addr: BOOT_ADDR,
147+
data: Cow::Borrowed(bootloader),
148+
}))
149+
.chain(once(Ok(RomSegment {
150+
addr: PARTITION_ADDR,
151+
data: Cow::Owned(partition_table),
152+
})))
153+
.chain(once(get_data(image))),
154+
)
155+
}
156+
}

espflash/src/chip/esp8266.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1-
use std::borrow::Cow;
2-
use std::io::Write;
3-
use std::iter::once;
4-
use std::mem::size_of;
1+
use bytemuck::bytes_of;
52

63
use super::{ChipType, EspCommonHeader, SegmentHeader, ESP_MAGIC};
7-
use crate::chip::{Chip, SpiRegisters};
8-
use crate::elf::{update_checksum, CodeSegment, FirmwareImage, RomSegment, ESP_CHECKSUM_MAGIC};
9-
use crate::flasher::FlashSize;
10-
use crate::Error;
11-
use bytemuck::bytes_of;
4+
use crate::{
5+
chip::{Chip, SpiRegisters},
6+
elf::{update_checksum, CodeSegment, FirmwareImage, RomSegment, ESP_CHECKSUM_MAGIC},
7+
flasher::FlashSize,
8+
Error,
9+
};
10+
11+
use std::{borrow::Cow, io::Write, iter::once, mem::size_of};
1212

1313
pub const IROM_MAP_START: u32 = 0x40200000;
1414
const IROM_MAP_END: u32 = 0x40300000;
1515

1616
pub struct Esp8266;
1717

1818
impl ChipType for Esp8266 {
19-
const DATE_REG1_VALUE: u32 = 0x00062000;
20-
const DATE_REG2_VALUE: u32 = 0;
19+
const CHIP_DETECT_MAGIC_VALUE: u32 = 0xfff0c101;
20+
2121
const SPI_REGISTERS: SpiRegisters = SpiRegisters {
2222
base: 0x60000200,
2323
usr_offset: 0x1c,

0 commit comments

Comments
 (0)