Skip to content

Commit e7361a7

Browse files
Merge pull request #39 from ninjasource/pico2-support
Add support for family ids other than RP2040 like RP2350
2 parents 7ab09ca + de5274b commit e7361a7

4 files changed

Lines changed: 196 additions & 48 deletions

File tree

src/address_range.rs

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,29 +32,84 @@ impl Default for AddressRange {
3232
}
3333

3434
pub const FLASH_SECTOR_ERASE_SIZE: u64 = 4096;
35-
pub const MAIN_RAM_START: u64 = 0x20000000;
36-
pub const MAIN_RAM_END: u64 = 0x20042000;
37-
pub const FLASH_START: u64 = 0x10000000;
38-
pub const FLASH_END: u64 = 0x15000000;
39-
pub const XIP_SRAM_START: u64 = 0x15000000;
40-
pub const XIP_SRAM_END: u64 = 0x15004000;
41-
pub const MAIN_RAM_BANKED_START: u64 = 0x21000000;
42-
pub const MAIN_RAM_BANKED_END: u64 = 0x21040000;
43-
pub const ROM_START: u64 = 0x00000000;
44-
pub const ROM_END: u64 = 0x00004000;
35+
pub const MAIN_RAM_START_RP2040: u64 = 0x20000000;
36+
pub const MAIN_RAM_END_RP2040: u64 = 0x20042000;
37+
pub const MAIN_RAM_START_RP2350: u64 = 0x20000000;
38+
pub const MAIN_RAM_END_RP2350: u64 = 0x20082000;
39+
pub const FLASH_START_RP2040: u64 = 0x10000000;
40+
pub const FLASH_END_RP2040: u64 = 0x15000000;
41+
// From RP2350 datasheet:
42+
// RP2040 required images to be stored at the beginning of flash (0x10000000). RP2350 supports storing executable images
43+
// in a partitions at arbitrary locations, to support more robust upgrade cycles via A/B versions, among other uses.
44+
// Therefore, the values below are possibly incorrect but FLASH_END_RP2040 appears to be incorrect too
45+
pub const FLASH_START_RP2350: u64 = 0x10000000;
46+
pub const FLASH_END_RP2350: u64 = 0x15000000;
47+
pub const XIP_SRAM_START_RP2040: u64 = 0x15000000;
48+
pub const XIP_SRAM_END_RP2040: u64 = 0x15004000;
49+
pub const XIP_SRAM_START_RP2350: u64 = 0x13ffc000;
50+
pub const XIP_SRAM_END_RP2350: u64 = 0x14000000;
51+
pub const MAIN_RAM_BANKED_START_RP2040: u64 = 0x21000000;
52+
pub const MAIN_RAM_BANKED_END_RP2040: u64 = 0x21040000;
53+
pub const ROM_START_RP2040: u64 = 0x00000000;
54+
pub const ROM_END_RP2040: u64 = 0x00004000;
55+
pub const ROM_START_RP2350: u64 = 0x00000000;
56+
pub const ROM_END_RP2350: u64 = 0x00008000;
4557

4658
pub const RP2040_ADDRESS_RANGES_FLASH: &[AddressRange] = &[
47-
AddressRange::new(FLASH_START, FLASH_END, AddressRangeType::Contents),
48-
AddressRange::new(MAIN_RAM_START, MAIN_RAM_END, AddressRangeType::NoContents),
4959
AddressRange::new(
50-
MAIN_RAM_BANKED_START,
51-
MAIN_RAM_BANKED_END,
60+
FLASH_START_RP2040,
61+
FLASH_END_RP2040,
62+
AddressRangeType::Contents,
63+
),
64+
AddressRange::new(
65+
MAIN_RAM_START_RP2040,
66+
MAIN_RAM_END_RP2040,
67+
AddressRangeType::NoContents,
68+
),
69+
AddressRange::new(
70+
MAIN_RAM_BANKED_START_RP2040,
71+
MAIN_RAM_BANKED_END_RP2040,
5272
AddressRangeType::NoContents,
5373
),
5474
];
5575

5676
pub const RP2040_ADDRESS_RANGES_RAM: &[AddressRange] = &[
57-
AddressRange::new(MAIN_RAM_START, MAIN_RAM_END, AddressRangeType::Contents),
58-
AddressRange::new(XIP_SRAM_START, XIP_SRAM_END, AddressRangeType::Contents),
59-
AddressRange::new(ROM_START, ROM_END, AddressRangeType::Ignore), // for now we ignore the bootrom if present
77+
AddressRange::new(
78+
MAIN_RAM_START_RP2040,
79+
MAIN_RAM_END_RP2040,
80+
AddressRangeType::Contents,
81+
),
82+
AddressRange::new(
83+
XIP_SRAM_START_RP2040,
84+
XIP_SRAM_END_RP2040,
85+
AddressRangeType::Contents,
86+
),
87+
AddressRange::new(ROM_START_RP2040, ROM_END_RP2040, AddressRangeType::Ignore), // for now we ignore the bootrom if present
88+
];
89+
90+
pub const RP2350_ADDRESS_RANGES_FLASH: &[AddressRange] = &[
91+
AddressRange::new(
92+
FLASH_START_RP2350,
93+
FLASH_END_RP2350,
94+
AddressRangeType::Contents,
95+
),
96+
AddressRange::new(
97+
MAIN_RAM_START_RP2350,
98+
MAIN_RAM_END_RP2350,
99+
AddressRangeType::NoContents,
100+
),
101+
];
102+
103+
pub const RP2350_ADDRESS_RANGES_RAM: &[AddressRange] = &[
104+
AddressRange::new(
105+
MAIN_RAM_START_RP2350,
106+
MAIN_RAM_END_RP2350,
107+
AddressRangeType::Contents,
108+
),
109+
AddressRange::new(
110+
XIP_SRAM_START_RP2350,
111+
XIP_SRAM_END_RP2350,
112+
AddressRangeType::Contents,
113+
),
114+
AddressRange::new(ROM_START_RP2350, ROM_END_RP2350, AddressRangeType::Ignore), // for now we ignore the bootrom if present
60115
];

src/elf.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
use crate::address_range::{
2-
self, AddressRange, RP2040_ADDRESS_RANGES_FLASH, RP2040_ADDRESS_RANGES_RAM,
1+
use crate::{
2+
address_range::{
3+
self, AddressRange, RP2040_ADDRESS_RANGES_FLASH, RP2040_ADDRESS_RANGES_RAM,
4+
RP2350_ADDRESS_RANGES_FLASH, RP2350_ADDRESS_RANGES_RAM,
5+
},
6+
Family,
37
};
48
use assert_into::AssertInto;
59
use elf::{abi::PT_LOAD, endian::EndianParse, segment::ProgramHeader, ElfStream, ParseError};
@@ -17,9 +21,21 @@ pub const PAGE_SIZE: u64 = 1 << LOG2_PAGE_SIZE;
1721
pub type PageMap = BTreeMap<u64, Vec<PageFragment>>;
1822

1923
// "determine_binary_type"
20-
pub fn is_ram_binary<E: EndianParse, S: Read + Seek>(file: &ElfStream<E, S>) -> Option<bool> {
24+
pub fn is_ram_binary<E: EndianParse, S: Read + Seek>(
25+
file: &ElfStream<E, S>,
26+
family: Family,
27+
) -> Option<bool> {
2128
let entry = file.ehdr.e_entry;
2229

30+
let (address_ranges_ram, address_ranges_flash) = match family {
31+
Family::RP2040 => (RP2040_ADDRESS_RANGES_RAM, RP2040_ADDRESS_RANGES_FLASH),
32+
Family::RP2XXX_ABSOLUTE
33+
| Family::RP2XXX_DATA
34+
| Family::RP2350_ARM_S
35+
| Family::RP2350_RISCV
36+
| Family::RP2350_ARM_NS => (RP2350_ADDRESS_RANGES_RAM, RP2350_ADDRESS_RANGES_FLASH),
37+
};
38+
2339
for segment in file.segments() {
2440
if segment.p_type == PT_LOAD && segment.p_memsz > 0 {
2541
let mapped_size = segment.p_filesz.min(segment.p_memsz);
@@ -28,9 +44,9 @@ pub fn is_ram_binary<E: EndianParse, S: Read + Seek>(file: &ElfStream<E, S>) ->
2844
// so call THAT a flash binary
2945
if entry >= segment.p_vaddr && entry < segment.p_vaddr + mapped_size {
3046
let effective_entry = entry + segment.p_paddr - segment.p_vaddr;
31-
if RP2040_ADDRESS_RANGES_RAM.is_address_initialized(effective_entry) {
47+
if address_ranges_ram.is_address_initialized(effective_entry) {
3248
return Some(true);
33-
} else if RP2040_ADDRESS_RANGES_FLASH.is_address_initialized(effective_entry) {
49+
} else if address_ranges_flash.is_address_initialized(effective_entry) {
3450
return Some(false);
3551
}
3652
}

src/main.rs

Lines changed: 103 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
use crate::{
2-
address_range::{MAIN_RAM_END, XIP_SRAM_END, XIP_SRAM_START},
2+
address_range::{
3+
FLASH_SECTOR_ERASE_SIZE, MAIN_RAM_END_RP2040, MAIN_RAM_END_RP2350, MAIN_RAM_START_RP2040,
4+
MAIN_RAM_START_RP2350, RP2040_ADDRESS_RANGES_FLASH, RP2040_ADDRESS_RANGES_RAM,
5+
RP2350_ADDRESS_RANGES_FLASH, RP2350_ADDRESS_RANGES_RAM, XIP_SRAM_END_RP2040,
6+
XIP_SRAM_END_RP2350, XIP_SRAM_START_RP2040, XIP_SRAM_START_RP2350,
7+
},
38
elf::{is_ram_binary, AddressRangesFromElfError, PageMap},
49
reporter::ProgressBarReporter,
510
};
611
use ::elf::{endian::AnyEndian, ElfStream, ParseError};
7-
use address_range::{
8-
FLASH_SECTOR_ERASE_SIZE, MAIN_RAM_START, RP2040_ADDRESS_RANGES_FLASH, RP2040_ADDRESS_RANGES_RAM,
9-
};
1012
use assert_into::AssertInto;
11-
use clap::Parser;
13+
use clap::{Parser, ValueEnum};
1214
use elf::{realize_page, AddressRangesExt, PAGE_SIZE};
1315
use env_logger::Env;
1416
use log::{debug, info, Level, LevelFilter};
15-
use static_assertions::const_assert;
1617
use std::{
1718
collections::HashSet,
1819
error::Error,
@@ -23,8 +24,8 @@ use std::{
2324
use sysinfo::Disks;
2425
use thiserror::Error;
2526
use uf2::{
26-
Uf2BlockData, Uf2BlockFooter, Uf2BlockHeader, RP2040_FAMILY_ID, UF2_FLAG_FAMILY_ID_PRESENT,
27-
UF2_MAGIC_END, UF2_MAGIC_START0, UF2_MAGIC_START1,
27+
Uf2BlockData, Uf2BlockFooter, Uf2BlockHeader, UF2_FLAG_FAMILY_ID_PRESENT, UF2_MAGIC_END,
28+
UF2_MAGIC_START0, UF2_MAGIC_START1,
2829
};
2930
use zerocopy::IntoBytes;
3031

@@ -44,6 +45,10 @@ struct Opts {
4445
#[clap(short, long)]
4546
deploy: bool,
4647

48+
/// Select family short name for UF2
49+
#[clap(value_enum, short, long, default_value_t = Family::default())]
50+
family: Family,
51+
4752
/// Connect to serial after deploy
4853
#[cfg(feature = "serial")]
4954
#[clap(short, long)]
@@ -61,6 +66,36 @@ struct Opts {
6166
output: Option<String>,
6267
}
6368

69+
// See https://github.com/microsoft/uf2/blob/master/utils/uf2families.json for list
70+
#[derive(Debug, ValueEnum, Clone, Copy)]
71+
#[repr(u32)]
72+
#[allow(non_camel_case_types)]
73+
pub enum Family {
74+
/// Raspberry Pi RP2040
75+
RP2040 = 0xe48bff56,
76+
77+
/// Raspberry Pi Microcontrollers: Absolute (unpartitioned) download
78+
RP2XXX_ABSOLUTE = 0xe48bff57,
79+
80+
/// Raspberry Pi Microcontrollers: Data partition download
81+
RP2XXX_DATA = 0xe48bff58,
82+
83+
/// Raspberry Pi RP2350, Secure Arm image
84+
RP2350_ARM_S = 0xe48bff59,
85+
86+
/// Raspberry Pi RP2350, RISC-V image
87+
RP2350_RISCV = 0xe48bff5a,
88+
89+
/// Raspberry Pi RP2350, Non-secure Arm image
90+
RP2350_ARM_NS = 0xe48bff5b,
91+
}
92+
93+
impl Default for Family {
94+
fn default() -> Self {
95+
Self::RP2040
96+
}
97+
}
98+
6499
#[derive(Error, Debug)]
65100
pub enum Elf2Uf2Error {
66101
#[error("Failed to get address ranges from elf")]
@@ -81,19 +116,52 @@ pub enum Elf2Uf2Error {
81116
EntryPointNotMapped,
82117
}
83118

84-
fn build_page_map(elf: &ElfStream<AnyEndian, impl Read + Seek>) -> Result<PageMap, Elf2Uf2Error> {
85-
let ram_style = is_ram_binary(elf).ok_or(Elf2Uf2Error::EntryPointNotMapped)?;
119+
fn build_page_map(
120+
elf: &ElfStream<AnyEndian, impl Read + Seek>,
121+
family: Family,
122+
) -> Result<PageMap, Elf2Uf2Error> {
123+
let ram_style = is_ram_binary(elf, family).ok_or(Elf2Uf2Error::EntryPointNotMapped)?;
86124

87125
if ram_style {
88126
debug!("Detected RAM binary");
89127
} else {
90128
debug!("Detected FLASH binary");
91129
}
92130

131+
let (
132+
address_ranges_ram,
133+
address_ranges_flash,
134+
main_ram_start,
135+
main_ram_end,
136+
xip_sram_start,
137+
xip_sram_end,
138+
) = match family {
139+
Family::RP2040 => (
140+
RP2040_ADDRESS_RANGES_RAM,
141+
RP2040_ADDRESS_RANGES_FLASH,
142+
MAIN_RAM_START_RP2040,
143+
MAIN_RAM_END_RP2040,
144+
XIP_SRAM_START_RP2040,
145+
XIP_SRAM_END_RP2040,
146+
),
147+
Family::RP2XXX_ABSOLUTE
148+
| Family::RP2XXX_DATA
149+
| Family::RP2350_ARM_S
150+
| Family::RP2350_RISCV
151+
| Family::RP2350_ARM_NS => (
152+
RP2350_ADDRESS_RANGES_RAM,
153+
RP2350_ADDRESS_RANGES_FLASH,
154+
MAIN_RAM_START_RP2350,
155+
MAIN_RAM_END_RP2350,
156+
XIP_SRAM_START_RP2350,
157+
XIP_SRAM_END_RP2350,
158+
),
159+
};
160+
93161
let valid_ranges = if ram_style {
94-
RP2040_ADDRESS_RANGES_RAM
162+
address_ranges_ram
95163
} else {
96-
RP2040_ADDRESS_RANGES_FLASH
164+
address_ranges_flash
97165
};
98166

99167
let mut pages = valid_ranges
@@ -110,9 +178,9 @@ fn build_page_map(elf: &ElfStream<AnyEndian, impl Read + Seek>) -> Result<PageMa
110178

111179
#[allow(clippy::manual_range_contains)]
112180
pages.keys().copied().for_each(|addr| {
113-
if addr >= MAIN_RAM_START && addr <= MAIN_RAM_END {
181+
if addr >= main_ram_start && addr <= main_ram_end {
114182
expected_ep_main_ram = expected_ep_main_ram.min(addr) | 0x1;
115-
} else if addr >= XIP_SRAM_START && addr < XIP_SRAM_END {
183+
} else if addr >= xip_sram_start && addr < xip_sram_end {
116184
expected_ep_xip_sram = expected_ep_xip_sram.min(addr) | 0x1;
117185
}
118186
});
@@ -132,7 +200,7 @@ fn build_page_map(elf: &ElfStream<AnyEndian, impl Read + Seek>) -> Result<PageMa
132200
elf.ehdr.e_entry as u32,
133201
));
134202
}
135-
const_assert!(0 == (MAIN_RAM_START & (PAGE_SIZE - 1)));
203+
assert!(0 == (main_ram_start & (PAGE_SIZE - 1)));
136204

137205
// TODO: check vector table start up
138206
// currently don't require this as entry point is now at the start, we don't know where reset vector is
@@ -167,6 +235,7 @@ fn write_output(
167235
elf_file: &mut ElfStream<AnyEndian, impl Read + Seek>,
168236
pages: &PageMap,
169237
mut output: impl Write,
238+
family: Family,
170239
) -> Result<(), Elf2Uf2Error> {
171240
let mut block_header = Uf2BlockHeader {
172241
magic_start0: UF2_MAGIC_START0,
@@ -176,7 +245,7 @@ fn write_output(
176245
payload_size: PAGE_SIZE.assert_into(),
177246
block_no: 0,
178247
num_blocks: pages.len().assert_into(),
179-
file_size: RP2040_FAMILY_ID,
248+
file_size: family as u32,
180249
};
181250

182251
let mut block_data: Uf2BlockData = [0; 476];
@@ -220,10 +289,14 @@ fn open_elf<T: Read + Seek>(input: T) -> Result<ElfStream<AnyEndian, T>, Elf2Uf2
220289
}
221290

222291
#[cfg_attr(not(test), expect(unused))]
223-
fn elf2uf2(input: impl Read + Seek, output: impl Write) -> Result<(), Elf2Uf2Error> {
292+
fn elf2uf2(
293+
input: impl Read + Seek,
294+
output: impl Write,
295+
family: Family,
296+
) -> Result<(), Elf2Uf2Error> {
224297
let mut elf = open_elf(input)?;
225-
let pages = build_page_map(&elf)?;
226-
write_output(&mut elf, &pages, output)
298+
let pages = build_page_map(&elf, family)?;
299+
write_output(&mut elf, &pages, output, family)
227300
}
228301

229302
fn main() -> Result<(), Box<dyn Error>> {
@@ -280,19 +353,25 @@ fn main() -> Result<(), Box<dyn Error>> {
280353
(File::create(&output_path)?, output_path)
281354
};
282355

356+
let family = options.family;
357+
358+
if options.verbose {
359+
info!("Using UF2 Family {:?}", family);
360+
}
361+
283362
let writer = BufWriter::new(output);
284363
let mut elf = open_elf(input)?;
285364
let should_print_progress = log::max_level() >= LevelFilter::Info;
286-
let pages = build_page_map(&elf)?;
365+
let pages = build_page_map(&elf, family)?;
287366

288367
let result = if should_print_progress {
289368
let len = pages.len() as u64 * 512;
290369
let mut reporter = ProgressBarReporter::new(len, writer);
291-
let result = write_output(&mut elf, &pages, &mut reporter);
370+
let result = write_output(&mut elf, &pages, &mut reporter, family);
292371
reporter.finish();
293372
result
294373
} else {
295-
write_output(&mut elf, &pages, writer)
374+
write_output(&mut elf, &pages, writer, family)
296375
};
297376

298377
if let Err(err) = result {
@@ -399,7 +478,7 @@ mod tests {
399478
pub fn hello_usb() {
400479
let bytes_in = io::Cursor::new(&include_bytes!("../hello_usb.elf")[..]);
401480
let mut bytes_out = Vec::new();
402-
elf2uf2(bytes_in, &mut bytes_out).unwrap();
481+
elf2uf2(bytes_in, &mut bytes_out, Family::RP2040).unwrap();
403482

404483
assert_eq!(bytes_out, include_bytes!("../hello_usb.uf2"));
405484
}
@@ -408,7 +487,7 @@ mod tests {
408487
pub fn hello_serial() {
409488
let bytes_in = io::Cursor::new(&include_bytes!("../hello_serial.elf")[..]);
410489
let mut bytes_out = Vec::new();
411-
elf2uf2(bytes_in, &mut bytes_out).unwrap();
490+
elf2uf2(bytes_in, &mut bytes_out, Family::RP2040).unwrap();
412491

413492
assert_eq!(bytes_out, include_bytes!("../hello_serial.uf2"));
414493
}

0 commit comments

Comments
 (0)