diff --git a/src/address_range.rs b/src/address_range.rs index 719a5bc..cd51c02 100644 --- a/src/address_range.rs +++ b/src/address_range.rs @@ -29,32 +29,4 @@ impl Default for AddressRange { from: 0, } } -} - -pub const FLASH_SECTOR_ERASE_SIZE: u32 = 4096; -pub const MAIN_RAM_START: u32 = 0x20000000; -pub const MAIN_RAM_END: u32 = 0x20042000; -pub const FLASH_START: u32 = 0x10000000; -pub const FLASH_END: u32 = 0x15000000; -pub const XIP_SRAM_START: u32 = 0x15000000; -pub const XIP_SRAM_END: u32 = 0x15004000; -pub const MAIN_RAM_BANKED_START: u32 = 0x21000000; -pub const MAIN_RAM_BANKED_END: u32 = 0x21040000; -pub const ROM_START: u32 = 0x00000000; -pub const ROM_END: u32 = 0x00004000; - -pub const RP2040_ADDRESS_RANGES_FLASH: &[AddressRange] = &[ - AddressRange::new(FLASH_START, FLASH_END, AddressRangeType::Contents), - AddressRange::new(MAIN_RAM_START, MAIN_RAM_END, AddressRangeType::NoContents), - AddressRange::new( - MAIN_RAM_BANKED_START, - MAIN_RAM_BANKED_END, - AddressRangeType::NoContents, - ), -]; - -pub const RP2040_ADDRESS_RANGES_RAM: &[AddressRange] = &[ - AddressRange::new(MAIN_RAM_START, MAIN_RAM_END, AddressRangeType::Contents), - AddressRange::new(XIP_SRAM_START, XIP_SRAM_END, AddressRangeType::Contents), - AddressRange::new(ROM_START, ROM_END, AddressRangeType::Ignore), // for now we ignore the bootrom if present -]; +} \ No newline at end of file diff --git a/src/boards/circuit_playground_bluefruit.rs b/src/boards/circuit_playground_bluefruit.rs new file mode 100644 index 0000000..231da83 --- /dev/null +++ b/src/boards/circuit_playground_bluefruit.rs @@ -0,0 +1,70 @@ +// Based off info from +// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf52840%2Fmemory.html +// and https://github.com/adafruit/Adafruit_nRF52_Bootloader/blob/master/src/boards/circuitplayground_nrf52840/pinconfig.c + +use crate::address_range::{AddressRange, AddressRangeType}; + +use super::BoardConfig; + +const FLASH_SECTOR_ERASE_SIZE: u32 = 4096; +const MAIN_RAM_START: u32 = 0x00800000; +const MAIN_RAM_END: u32 = 0x10000000; +const FLASH_START: u32 = 0x00100000; +const FLASH_END: u32 = 0x00800000; +const XIP_SRAM_START: u32 = 0x12000000; +const XIP_SRAM_END: u32 = 0x19FFFFFF; +const MAIN_RAM_BANKED_START: u32 = 0x60000000; +const MAIN_RAM_BANKED_END: u32 = 0xA0000000; +const BOOTLOADER_FLASH_START: u32 = 0x00000000; +const BOOTLOADER_FLASH_END: u32 = 0x00100000; + +pub struct CircuitPlaygroundBluefruit {} + +impl BoardConfig for CircuitPlaygroundBluefruit { + fn flash_sector_erase_size(&self) -> u32 { + FLASH_SECTOR_ERASE_SIZE + } + + fn main_ram_start(&self) -> u32 { + MAIN_RAM_START + } + + fn main_ram_end(&self) -> u32 { + MAIN_RAM_END + } + + fn xip_ram_start(&self) -> u32 { + XIP_SRAM_START + } + + fn xip_ram_end(&self) -> u32 { + XIP_SRAM_END + } + + fn family_id(&self) -> u32 { + 0xada52840 + } + + fn address_ranges_flash(&self) -> Vec { + vec![ + AddressRange::new(FLASH_START, FLASH_END, AddressRangeType::Contents), + AddressRange::new(MAIN_RAM_START, MAIN_RAM_END, AddressRangeType::NoContents), + AddressRange::new( + MAIN_RAM_BANKED_START, + MAIN_RAM_BANKED_END, + AddressRangeType::NoContents, + ), + ] + } + + fn address_range_ram(&self) -> Vec { + vec![ + AddressRange::new(MAIN_RAM_START, MAIN_RAM_END, AddressRangeType::Contents), + AddressRange::new(XIP_SRAM_START, XIP_SRAM_END, AddressRangeType::Contents), + AddressRange::new( + BOOTLOADER_FLASH_START, + BOOTLOADER_FLASH_END, AddressRangeType::Ignore + ) + ] + } +} \ No newline at end of file diff --git a/src/boards/mod.rs b/src/boards/mod.rs new file mode 100644 index 0000000..b388559 --- /dev/null +++ b/src/boards/mod.rs @@ -0,0 +1,16 @@ +use crate::address_range::AddressRange; + +pub trait BoardConfig { + fn flash_sector_erase_size(&self) -> u32; + fn main_ram_start(&self) -> u32; + fn main_ram_end(&self) -> u32; + fn xip_ram_start(&self) -> u32; + fn xip_ram_end(&self) -> u32; + fn family_id(&self) -> u32; + + fn address_ranges_flash(&self) -> Vec; + fn address_range_ram(&self) -> Vec; +} + +pub mod rp2040; +pub mod circuit_playground_bluefruit; \ No newline at end of file diff --git a/src/boards/rp2040.rs b/src/boards/rp2040.rs new file mode 100644 index 0000000..1c3517c --- /dev/null +++ b/src/boards/rp2040.rs @@ -0,0 +1,63 @@ +use crate::address_range::{AddressRange, AddressRangeType}; + +use super::BoardConfig; + +const FLASH_SECTOR_ERASE_SIZE: u32 = 4096; +const MAIN_RAM_START: u32 = 0x20000000; +const MAIN_RAM_END: u32 = 0x20042000; +const FLASH_START: u32 = 0x10000000; +const FLASH_END: u32 = 0x15000000; +const XIP_SRAM_START: u32 = 0x15000000; +const XIP_SRAM_END: u32 = 0x15004000; +const MAIN_RAM_BANKED_START: u32 = 0x21000000; +const MAIN_RAM_BANKED_END: u32 = 0x21040000; +const ROM_START: u32 = 0x00000000; +const ROM_END: u32 = 0x00004000; + +pub struct RP2040 {} + +impl BoardConfig for RP2040 { + fn flash_sector_erase_size(&self) -> u32 { + FLASH_SECTOR_ERASE_SIZE + } + + fn main_ram_start(&self) -> u32 { + MAIN_RAM_START + } + + fn main_ram_end(&self) -> u32 { + MAIN_RAM_END + } + + fn xip_ram_start(&self) -> u32 { + XIP_SRAM_START + } + + fn xip_ram_end(&self) -> u32 { + XIP_SRAM_END + } + + fn family_id(&self) -> u32 { + 0xe48bff56 + } + + fn address_ranges_flash(&self) -> Vec { + vec![ + AddressRange::new(FLASH_START, FLASH_END, AddressRangeType::Contents), + AddressRange::new(MAIN_RAM_START, MAIN_RAM_END, AddressRangeType::NoContents), + AddressRange::new( + MAIN_RAM_BANKED_START, + MAIN_RAM_BANKED_END, + AddressRangeType::NoContents, + ) + ] + } + + fn address_range_ram(&self) -> Vec { + vec![ + AddressRange::new(MAIN_RAM_START, MAIN_RAM_END, AddressRangeType::Contents), + AddressRange::new(XIP_SRAM_START, XIP_SRAM_END, AddressRangeType::Contents), + AddressRange::new(ROM_START, ROM_END, AddressRangeType::Ignore), // for now we ignore the bootrom if present + ] + } +} \ No newline at end of file diff --git a/src/elf.rs b/src/elf.rs index 33d07ef..8bc5381 100644 --- a/src/elf.rs +++ b/src/elf.rs @@ -1,6 +1,5 @@ use crate::{ - address_range::{self, AddressRange, RP2040_ADDRESS_RANGES_FLASH, RP2040_ADDRESS_RANGES_RAM}, - Opts, + address_range::{self, AddressRange}, boards::BoardConfig, get_board_config, Opts }; use assert_into::AssertInto; use std::{ @@ -107,9 +106,10 @@ impl Elf32Header { // so call THAT a flash binary if self.entry >= entry.vaddr && self.entry < entry.vaddr + mapped_size { let effective_entry = self.entry + entry.paddr - entry.vaddr; - if RP2040_ADDRESS_RANGES_RAM.is_address_initialized(effective_entry) { + let board_config: Box = get_board_config(); + if board_config.address_range_ram().iter().is_address_initialized(effective_entry) { return Some(true); - } else if RP2040_ADDRESS_RANGES_FLASH + } else if board_config.address_ranges_flash().iter() .is_address_initialized(effective_entry) { return Some(false); diff --git a/src/main.rs b/src/main.rs index f6ab182..47c5813 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,32 +1,47 @@ -use address_range::{ - FLASH_SECTOR_ERASE_SIZE, MAIN_RAM_START, RP2040_ADDRESS_RANGES_FLASH, RP2040_ADDRESS_RANGES_RAM, -}; -use assert_into::AssertInto; -use clap::Parser; -use elf::{realize_page, AddressRangesExt, Elf32Header, PAGE_SIZE}; +use clap::{Parser, ValueEnum}; +use elf::{AddressRangesExt, realize_page, Elf32Header, PAGE_SIZE}; use once_cell::sync::OnceCell; use pbr::{ProgressBar, Units}; -use static_assertions::const_assert; +use assert_into::AssertInto; use std::{ collections::HashSet, error::Error, fs::{self, File}, - io::{BufReader, Read, Seek, Write, BufWriter}, + io::{BufReader, BufWriter, Read, Seek, Write}, path::{Path, PathBuf}, }; use sysinfo::{DiskExt, SystemExt}; use uf2::{ - Uf2BlockData, Uf2BlockFooter, Uf2BlockHeader, RP2040_FAMILY_ID, UF2_FLAG_FAMILY_ID_PRESENT, + Uf2BlockData, Uf2BlockFooter, Uf2BlockHeader, UF2_FLAG_FAMILY_ID_PRESENT, UF2_MAGIC_END, UF2_MAGIC_START0, UF2_MAGIC_START1, }; use zerocopy::AsBytes; -use crate::address_range::{MAIN_RAM_END, XIP_SRAM_END, XIP_SRAM_START}; +use crate::boards::BoardConfig; +mod boards; mod address_range; mod elf; mod uf2; +#[derive(Clone, ValueEnum, Debug)] +enum Boards { + RP2040, + CircuitPlaygroundBluefruit +} + +impl std::fmt::Display for Boards{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl Default for Boards { + fn default() -> Self { + Boards::RP2040 + } +} + #[derive(Parser, Debug, Default)] #[clap(author = "Jonathan Nilsson")] struct Opts { @@ -48,6 +63,9 @@ struct Opts { /// Output file output: Option, + + #[clap(short, long, value_enum, default_value_t=Boards::RP2040)] + board: Boards } impl Opts { @@ -68,6 +86,7 @@ static OPTS: OnceCell = OnceCell::new(); fn elf2uf2(mut input: impl Read + Seek, mut output: impl Write) -> Result<(), Box> { let eh = Elf32Header::from_read(&mut input)?; + let board_config = get_board_config(); let entries = eh.read_elf32_ph_entries(&mut input)?; @@ -84,12 +103,12 @@ fn elf2uf2(mut input: impl Read + Seek, mut output: impl Write) -> Result<(), Bo } let valid_ranges = if ram_style { - RP2040_ADDRESS_RANGES_RAM + board_config.address_range_ram() } else { - RP2040_ADDRESS_RANGES_FLASH + board_config.address_ranges_flash() }; - let mut pages = valid_ranges.check_elf32_ph_entries(&entries)?; + let mut pages = valid_ranges.iter().check_elf32_ph_entries(&entries)?; if pages.is_empty() { return Err("The input file has no memory pages".into()); @@ -101,9 +120,9 @@ fn elf2uf2(mut input: impl Read + Seek, mut output: impl Write) -> Result<(), Bo #[allow(clippy::manual_range_contains)] pages.keys().copied().for_each(|addr| { - if addr >= MAIN_RAM_START && addr <= MAIN_RAM_END { + if addr >= board_config.main_ram_start() && addr <= board_config.main_ram_end() { expected_ep_main_ram = expected_ep_main_ram.min(addr) | 0x1; - } else if addr >= XIP_SRAM_START && addr < XIP_SRAM_END { + } else if addr >= board_config.xip_ram_start() && addr < board_config.xip_ram_end() { expected_ep_xip_sram = expected_ep_xip_sram.min(addr) | 0x1; } }); @@ -124,7 +143,7 @@ fn elf2uf2(mut input: impl Read + Seek, mut output: impl Write) -> Result<(), Bo ) .into()); } - const_assert!(0 == (MAIN_RAM_START & (PAGE_SIZE - 1))); + assert!(0 == (board_config.main_ram_start() & (PAGE_SIZE - 1))); // TODO: check vector table start up // currently don't require this as entry point is now at the start, we don't know where reset vector is @@ -136,14 +155,14 @@ fn elf2uf2(mut input: impl Read + Seek, mut output: impl Write) -> Result<(), Bo let touched_sectors: HashSet = pages .keys() - .map(|addr| addr / FLASH_SECTOR_ERASE_SIZE) + .map(|addr| addr / board_config.flash_sector_erase_size()) .collect(); let last_page_addr = *pages.last_key_value().unwrap().0; for sector in touched_sectors { - let mut page = sector * FLASH_SECTOR_ERASE_SIZE; + let mut page = sector * board_config.flash_sector_erase_size(); - while page < (sector + 1) * FLASH_SECTOR_ERASE_SIZE { + while page < (sector + 1) * board_config.flash_sector_erase_size() { if page < last_page_addr && !pages.contains_key(&page) { pages.insert(page, Vec::new()); } @@ -160,7 +179,7 @@ fn elf2uf2(mut input: impl Read + Seek, mut output: impl Write) -> Result<(), Bo payload_size: PAGE_SIZE, block_no: 0, num_blocks: pages.len().assert_into(), - file_size: RP2040_FAMILY_ID, + file_size: board_config.family_id(), }; let mut block_data: Uf2BlockData = [0; 476]; @@ -170,7 +189,8 @@ fn elf2uf2(mut input: impl Read + Seek, mut output: impl Write) -> Result<(), Bo }; if Opts::global().deploy { - println!("Transfering program to pico"); + let board_name = &Opts::global().board; + println!("Transfering program to {board_name}"); } let mut pb = if !Opts::global().verbose && Opts::global().deploy { @@ -224,6 +244,18 @@ fn elf2uf2(mut input: impl Read + Seek, mut output: impl Write) -> Result<(), Bo Ok(()) } +fn get_board_config() -> Box { + let board_config: Box = match &Opts::global().board { + Boards::RP2040 => { + Box::new(boards::rp2040::RP2040{}) + } + Boards::CircuitPlaygroundBluefruit => { + Box::new(boards::circuit_playground_bluefruit::CircuitPlaygroundBluefruit{}) + } + }; + board_config +} + fn main() -> Result<(), Box> { OPTS.set(Opts::parse()).unwrap(); @@ -237,11 +269,12 @@ fn main() -> Result<(), Box> { let sys = sysinfo::System::new_all(); let mut pico_drive = None; + let board_name = &Opts::global().board; for disk in sys.disks() { let mount = disk.mount_point(); - if mount.join("INFO_UF2.TXT").is_file() { - println!("Found pico uf2 disk {}", &mount.to_string_lossy()); + if mount.join("INFO_UF2.TXT").is_file() { + println!("Found {board_name} uf2 disk {}", &mount.to_string_lossy()); pico_drive = Some(mount.to_owned()); break; } @@ -276,10 +309,11 @@ fn main() -> Result<(), Box> { let mut counter = 0; + let board_name = &Opts::global().board; let serial_port_info = 'find_loop: loop { for port in serialport::available_ports()? { if !serial_ports_before.contains(&port) { - println!("Found pico serial on {}", &port.port_name); + println!("Found {board_name} serial on {}", &port.port_name); break 'find_loop Some(port); } } diff --git a/src/uf2.rs b/src/uf2.rs index d208e1a..0aa3e6b 100644 --- a/src/uf2.rs +++ b/src/uf2.rs @@ -13,8 +13,6 @@ pub const UF2_FLAG_FILE_CONTAINER: u32 = 0x00001000; pub const UF2_FLAG_FAMILY_ID_PRESENT: u32 = 0x00002000; pub const UF2_FLAG_MD5_PRESENT: u32 = 0x00004000; -pub const RP2040_FAMILY_ID: u32 = 0xe48bff56; - #[repr(packed)] #[derive(AsBytes, FromBytes, FromZeroes)] pub struct Uf2BlockHeader {