use probe_rs_target::{RawFlashAlgorithm, TransferEncoding};
use tracing::Level;
use zerocopy::IntoBytes;
use super::{FlashAlgorithm, FlashBuilder, FlashError, FlashPage, FlashProgress};
use crate::config::NvmRegion;
use crate::error::Error;
use crate::flashing::encoder::FlashEncoder;
use crate::flashing::{FlashLayout, FlashSector};
use crate::memory::MemoryInterface;
use crate::rtt::{Rtt, ScanRegion};
use crate::{Core, InstructionSet, core::CoreRegisters, session::Session};
use crate::{CoreStatus, Target};
use std::borrow::Cow;
use std::marker::PhantomData;
use std::{
fmt::Debug,
time::{Duration, Instant},
};
const INIT_TIMEOUT: Duration = Duration::from_secs(2);
pub(super) trait Operation {
const OPERATION: u32;
const NAME: &'static str;
}
pub(super) struct Erase;
impl Operation for Erase {
const OPERATION: u32 = 1;
const NAME: &'static str = "Erase";
}
pub(super) struct Program;
impl Operation for Program {
const OPERATION: u32 = 2;
const NAME: &'static str = "Program";
}
pub(super) struct Verify;
impl Operation for Verify {
const OPERATION: u32 = 3;
const NAME: &'static str = "Verify";
}
pub(super) enum FlashData {
Raw(FlashLayout),
Loaded {
encoder: FlashEncoder,
ignore_fills: bool,
},
}
impl FlashData {
pub fn layout(&self) -> &FlashLayout {
match self {
FlashData::Raw(layout) => layout,
FlashData::Loaded { encoder, .. } => encoder.flash_layout(),
}
}
pub fn layout_mut(&mut self) -> &mut FlashLayout {
if let FlashData::Loaded { encoder, .. } = self {
*self = FlashData::Raw(encoder.flash_layout().clone());
}
match self {
FlashData::Raw(layout) => layout,
FlashData::Loaded { .. } => unreachable!(),
}
}
pub fn encoder(&mut self, encoding: TransferEncoding, ignore_fills: bool) -> &FlashEncoder {
if let FlashData::Loaded {
encoder,
ignore_fills: was_ignore_fills,
} = self
&& *was_ignore_fills != ignore_fills
{
*self = FlashData::Raw(encoder.flash_layout().clone());
}
if let FlashData::Raw(layout) = self {
let layout = std::mem::take(layout);
let encoder = FlashEncoder::new(encoding, layout, ignore_fills);
*self = FlashData::Loaded {
encoder,
ignore_fills,
};
}
match self {
FlashData::Loaded { encoder, .. } => encoder,
FlashData::Raw(_) => unreachable!(),
}
}
}
pub(super) struct LoadedRegion {
pub region: NvmRegion,
pub data: FlashData,
}
impl LoadedRegion {
pub fn flash_layout(&self) -> &FlashLayout {
self.data.layout()
}
}
pub(super) struct Flasher {
pub(super) core_index: usize,
pub(super) flash_algorithm: FlashAlgorithm,
pub(super) loaded: bool,
pub(super) regions: Vec<LoadedRegion>,
pub(super) read_flasher_rtt: bool,
}
const STACK_FILL_BYTE: u8 = 0x56;
impl Flasher {
pub(super) fn new(
target: &Target,
core_index: usize,
raw_flash_algorithm: &RawFlashAlgorithm,
) -> Result<Self, FlashError> {
let flash_algorithm = FlashAlgorithm::assemble_from_raw_with_core(
raw_flash_algorithm,
&target.cores[core_index].name,
target,
)?;
Ok(Self {
core_index,
flash_algorithm,
loaded: false,
regions: Vec::new(),
read_flasher_rtt: false,
})
}
fn ensure_loaded(&mut self, session: &mut Session) -> Result<(), FlashError> {
if !self.loaded {
self.load(session)?;
self.loaded = true;
}
Ok(())
}
pub(super) fn flash_algorithm(&self) -> &FlashAlgorithm {
&self.flash_algorithm
}
pub(super) fn double_buffering_supported(&self) -> bool {
self.flash_algorithm.page_buffers.len() > 1
}
fn load(&mut self, session: &mut Session) -> Result<(), FlashError> {
tracing::debug!("Initializing the flash algorithm.");
let algo = &self.flash_algorithm;
let mut core = session.core(self.core_index).map_err(FlashError::Core)?;
tracing::debug!("Reset and halt core {}", self.core_index);
core.reset_and_halt(Duration::from_millis(500))
.map_err(FlashError::ResetAndHalt)?;
tracing::debug!("Downloading algorithm code to {:#010x}", algo.load_address);
core.write(algo.load_address, algo.instructions.as_bytes())
.map_err(FlashError::Core)?;
let mut data = vec![0; algo.instructions.len()];
core.read(algo.load_address, data.as_mut_bytes())
.map_err(FlashError::Core)?;
for (offset, (original, read_back)) in algo.instructions.iter().zip(data.iter()).enumerate()
{
if original == read_back {
continue;
}
tracing::error!(
"Failed to verify flash algorithm. Data mismatch at address {:#010x}",
algo.load_address + (4 * offset) as u64
);
tracing::error!("Original instruction: {:#010x}", original);
tracing::error!("Readback instruction: {:#010x}", read_back);
tracing::error!("Original: {:x?}", &algo.instructions);
tracing::error!("Readback: {:x?}", &data);
return Err(FlashError::FlashAlgorithmNotLoaded);
}
if algo.stack_overflow_check {
let stack_bottom = algo.stack_top - algo.stack_size;
if algo.stack_size & 3 == 0 {
let fill = vec![
u32::from_ne_bytes([
STACK_FILL_BYTE,
STACK_FILL_BYTE,
STACK_FILL_BYTE,
STACK_FILL_BYTE
]);
algo.stack_size as usize / 4
];
core.write_32(stack_bottom, &fill)
.map_err(FlashError::Core)?;
} else {
let fill = vec![STACK_FILL_BYTE; algo.stack_size as usize];
core.write_8(stack_bottom, &fill)
.map_err(FlashError::Core)?;
}
}
tracing::debug!("RAM contents match flashing algo blob.");
Ok(())
}
pub(super) fn init<'s, 'p, O: Operation>(
&'s mut self,
session: &'s mut Session,
progress: &'s mut FlashProgress<'p>,
clock: Option<u32>,
) -> Result<(ActiveFlasher<'s, 'p, O>, &'s mut [LoadedRegion]), FlashError> {
self.ensure_loaded(session)?;
let mut core = session.core(self.core_index).map_err(FlashError::Core)?;
let instruction_set = core.instruction_set().map_err(FlashError::Core)?;
tracing::debug!("Preparing Flasher for operation {}", O::NAME);
let mut flasher = ActiveFlasher::<O> {
core,
instruction_set,
rtt: None,
progress,
flash_algorithm: &self.flash_algorithm,
read_flasher_rtt: self.read_flasher_rtt,
_operation: PhantomData,
};
flasher.init(clock)?;
Ok((flasher, &mut self.regions))
}
pub(super) fn run_erase_all(
&mut self,
session: &mut Session,
progress: &mut FlashProgress<'_>,
) -> Result<(), FlashError> {
progress.started_erasing();
let result = if session.has_sequence_erase_all() {
session
.sequence_erase_all()
.map_err(|e| FlashError::ChipEraseFailed {
source: Box::new(e),
})?;
self.load(session)
} else {
self.run_erase(session, progress, |active, _| active.erase_all())
};
match result.is_ok() {
true => progress.finished_erasing(),
false => progress.failed_erasing(),
}
result
}
pub(super) fn run_blank_check<'p, T, F>(
&mut self,
session: &mut Session,
progress: &mut FlashProgress<'p>,
f: F,
) -> Result<T, FlashError>
where
F: FnOnce(&mut ActiveFlasher<'_, 'p, Erase>, &mut [LoadedRegion]) -> Result<T, FlashError>,
{
let (mut active, data) = self.init(session, progress, None)?;
let r = f(&mut active, data)?;
active.uninit()?;
Ok(r)
}
pub(super) fn run_erase<'p, T, F>(
&mut self,
session: &mut Session,
progress: &mut FlashProgress<'p>,
f: F,
) -> Result<T, FlashError>
where
F: FnOnce(&mut ActiveFlasher<'_, 'p, Erase>, &mut [LoadedRegion]) -> Result<T, FlashError>,
{
let (mut active, data) = self.init(session, progress, None)?;
let r = f(&mut active, data)?;
active.uninit()?;
Ok(r)
}
pub(super) fn run_program<'p, T, F>(
&mut self,
session: &mut Session,
progress: &mut FlashProgress<'p>,
f: F,
) -> Result<T, FlashError>
where
F: FnOnce(
&mut ActiveFlasher<'_, 'p, Program>,
&mut [LoadedRegion],
) -> Result<T, FlashError>,
{
let (mut active, data) = self.init(session, progress, None)?;
let r = f(&mut active, data)?;
active.uninit()?;
Ok(r)
}
pub(super) fn run_verify<'p, T, F>(
&mut self,
session: &mut Session,
progress: &mut FlashProgress<'p>,
f: F,
) -> Result<T, FlashError>
where
F: FnOnce(&mut ActiveFlasher<'_, 'p, Verify>, &mut [LoadedRegion]) -> Result<T, FlashError>,
{
let (mut active, data) = self.init(session, progress, None)?;
let r = f(&mut active, data)?;
active.uninit()?;
Ok(r)
}
pub(super) fn is_chip_erase_supported(&self, session: &Session) -> bool {
session.has_sequence_erase_all() || self.flash_algorithm().pc_erase_all.is_some()
}
pub(super) fn program(
&mut self,
session: &mut Session,
progress: &mut FlashProgress<'_>,
restore_unwritten_bytes: bool,
enable_double_buffering: bool,
skip_erasing: bool,
verify: bool,
) -> Result<(), FlashError> {
tracing::debug!("Starting program procedure.");
tracing::debug!("Double Buffering enabled: {:?}", enable_double_buffering);
tracing::debug!(
"Restoring unwritten bytes enabled: {:?}",
restore_unwritten_bytes
);
if restore_unwritten_bytes {
self.fill_unwritten(session, progress)?;
}
if !skip_erasing {
self.sector_erase(session, progress)?;
}
self.do_program(session, progress, enable_double_buffering)?;
if verify && !self.verify(session, progress, !restore_unwritten_bytes)? {
return Err(FlashError::Verify);
}
Ok(())
}
pub(super) fn fill_unwritten(
&mut self,
session: &mut Session,
progress: &mut FlashProgress<'_>,
) -> Result<(), FlashError> {
progress.started_filling();
fn fill_pages(
regions: &mut [LoadedRegion],
progress: &mut FlashProgress<'_>,
mut read: impl FnMut(u64, &mut [u8]) -> Result<(), FlashError>,
) -> Result<(), FlashError> {
for region in regions.iter_mut() {
let layout = region.data.layout_mut();
for fill in layout.fills.iter() {
let t = Instant::now();
let page = &mut layout.pages[fill.page_index()];
let page_offset = (fill.address() - page.address()) as usize;
let page_slice = &mut page.data_mut()[page_offset..][..fill.size() as usize];
read(fill.address(), page_slice)?;
progress.page_filled(fill.size(), t.elapsed());
}
}
Ok(())
}
let result = if self.flash_algorithm.pc_read.is_some() {
self.run_verify(session, &mut FlashProgress::empty(), |active, data| {
fill_pages(data, progress, |address, data| {
active.read_flash(address, data)
})
})
} else {
let mut core = session.core(0).map_err(FlashError::Core)?;
fill_pages(&mut self.regions, progress, |address, data| {
core.read(address, data).map_err(FlashError::Core)
})
};
match result.is_ok() {
true => progress.finished_filling(),
false => progress.failed_filling(),
}
result
}
pub(super) fn verify(
&mut self,
session: &mut Session,
progress: &mut FlashProgress<'_>,
ignore_filled: bool,
) -> Result<bool, FlashError> {
progress.started_verifying();
let result = self.do_verify(session, progress, ignore_filled);
match result.is_ok() {
true => progress.finished_verifying(),
false => progress.failed_verifying(),
}
result
}
fn do_verify(
&mut self,
session: &mut Session,
progress: &mut FlashProgress<'_>,
ignore_filled: bool,
) -> Result<bool, FlashError> {
let encoding = self.flash_algorithm.transfer_encoding;
if let Some(verify) = self.flash_algorithm.pc_verify {
self.run_verify(session, progress, |active, data| {
for region in data {
tracing::debug!("Verify using CMSIS function");
let flash_encoder = region.data.encoder(encoding, ignore_filled);
for page in flash_encoder.pages() {
let start = Instant::now();
let address = page.address();
let bytes = page.data();
tracing::debug!(
"Verifying page at address {:#010x} with size: {}",
address,
bytes.len()
);
let buffer_address = active.load_page_buffer(bytes, 0)?;
let result = active.call_function_and_wait(
&Registers {
pc: into_reg(verify)?,
r0: Some(into_reg(address)?),
r1: Some(into_reg(bytes.len() as u64)?),
r2: Some(into_reg(buffer_address)?),
r3: None,
},
false,
Duration::from_secs(30),
)?;
if result as u64 != address + bytes.len() as u64 {
tracing::debug!(
"Verification failed for page at address {:#010x}",
result
);
return Ok(false);
}
active
.progress
.page_verified(bytes.len() as u64, start.elapsed());
}
}
Ok(true)
})
} else {
tracing::debug!("Verify by reading back flash contents");
fn compare_flash(
regions: &[LoadedRegion],
progress: &mut FlashProgress<'_>,
ignore_filled: bool,
mut read: impl FnMut(u64, &mut [u8]) -> Result<(), FlashError>,
) -> Result<bool, FlashError> {
for region in regions {
let layout = region.data.layout();
for (idx, page) in layout.pages.iter().enumerate() {
let start = Instant::now();
let address = page.address();
let data = page.data();
let mut read_back = vec![0; data.len()];
read(address, &mut read_back)?;
if ignore_filled {
for fill in layout.fills() {
if fill.page_index() != idx {
continue;
}
let fill_offset = (fill.address() - address) as usize;
let fill_size = fill.size() as usize;
let default_bytes = &data[fill_offset..][..fill_size];
read_back[fill_offset..][..fill_size]
.copy_from_slice(default_bytes);
}
}
if data != read_back {
tracing::debug!(
"Verification failed for page at address {:#010x}",
address
);
return Ok(false);
}
progress.page_verified(data.len() as u64, start.elapsed());
}
}
Ok(true)
}
if self.flash_algorithm.pc_read.is_some() {
self.run_verify(session, &mut FlashProgress::empty(), |active, data| {
compare_flash(data, progress, ignore_filled, |address, data| {
active.read_flash(address, data)
})
})
} else {
let mut core = session.core(self.core_index).map_err(FlashError::Core)?;
compare_flash(&self.regions, progress, ignore_filled, |address, data| {
core.read(address, data).map_err(FlashError::Core)
})
}
}
}
fn sector_erase(
&mut self,
session: &mut Session,
progress: &mut FlashProgress<'_>,
) -> Result<(), FlashError> {
progress.started_erasing();
let encoding = self.flash_algorithm.transfer_encoding;
let result = self.run_erase(session, progress, |active, data| {
for region in data.iter_mut() {
for sector in region.data.encoder(encoding, false).sectors() {
active
.erase_sector(sector)
.map_err(|e| FlashError::EraseFailed {
sector_address: sector.address(),
source: Box::new(e),
})?;
}
}
Ok(())
});
match result.is_ok() {
true => progress.finished_erasing(),
false => progress.failed_erasing(),
}
result
}
fn do_program(
&mut self,
session: &mut Session,
progress: &mut FlashProgress<'_>,
enable_double_buffering: bool,
) -> Result<(), FlashError> {
progress.started_programming();
let program_result = if self.double_buffering_supported() && enable_double_buffering {
self.program_double_buffer(session, progress)
} else {
self.program_simple(session, progress)
};
match program_result.is_ok() {
true => progress.finished_programming(),
false => progress.failed_programming(),
}
program_result
}
fn program_simple(
&mut self,
session: &mut Session,
progress: &mut FlashProgress<'_>,
) -> Result<(), FlashError> {
let encoding = self.flash_algorithm.transfer_encoding;
self.run_program(session, progress, |active, data| {
for region in data.iter_mut() {
tracing::debug!(
" programming region: {:#010X?} ({} bytes)",
region.region.range,
region.region.range.end - region.region.range.start
);
let flash_encoder = region.data.encoder(encoding, false);
for page in flash_encoder.pages() {
active
.program_page(page)
.map_err(|error| FlashError::PageWrite {
page_address: page.address(),
source: Box::new(error),
})?;
}
}
Ok(())
})
}
fn program_double_buffer(
&mut self,
session: &mut Session,
progress: &mut FlashProgress<'_>,
) -> Result<(), FlashError> {
let encoding = self.flash_algorithm.transfer_encoding;
self.run_program(session, progress, |active, data| {
for region in data.iter_mut() {
tracing::debug!(
" programming region: {:#010X?} ({} bytes)",
region.region.range,
region.region.range.end - region.region.range.start
);
let flash_encoder = region.data.encoder(encoding, false);
let mut current_buf = 0;
let mut t = Instant::now();
let mut last_page_address = 0;
for page in flash_encoder.pages() {
let buffer_address = active.load_page_buffer(page.data(), current_buf)?;
active.wait_for_write_end(last_page_address)?;
last_page_address = page.address();
active
.progress
.page_programmed(page.size() as u64, t.elapsed());
t = Instant::now();
active.start_program_page_with_buffer(
buffer_address,
page.address(),
page.size() as u64,
)?;
if current_buf == 1 {
current_buf = 0;
} else {
current_buf = 1;
}
}
active.wait_for_write_end(last_page_address)?;
}
Ok(())
})
}
pub(crate) fn add_region(
&mut self,
region: NvmRegion,
builder: &FlashBuilder,
restore_unwritten_bytes: bool,
) -> Result<(), FlashError> {
let layout = builder.build_sectors_and_pages(
®ion,
&self.flash_algorithm,
restore_unwritten_bytes,
)?;
self.regions.push(LoadedRegion {
region,
data: FlashData::Raw(layout),
});
Ok(())
}
pub(crate) fn read_rtt_output(&mut self, read: bool) {
self.read_flasher_rtt = read;
}
}
struct Registers {
pc: u32,
r0: Option<u32>,
r1: Option<u32>,
r2: Option<u32>,
r3: Option<u32>,
}
impl Debug for Registers {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{:#010x} ({:?}, {:?}, {:?}, {:?})",
self.pc, self.r0, self.r1, self.r2, self.r3
)
}
}
fn into_reg(val: u64) -> Result<u32, FlashError> {
let reg_value: u32 = val
.try_into()
.map_err(|_| FlashError::RegisterValueNotSupported(val))?;
Ok(reg_value)
}
pub(super) struct ActiveFlasher<'op, 'p, O: Operation> {
pub(super) core: Core<'op>,
instruction_set: InstructionSet,
rtt: Option<Rtt>,
progress: &'op mut FlashProgress<'p>,
flash_algorithm: &'op FlashAlgorithm,
read_flasher_rtt: bool,
_operation: PhantomData<O>,
}
impl<O: Operation> ActiveFlasher<'_, '_, O> {
#[tracing::instrument(name = "Call to flash algorithm init", skip(self, clock))]
pub(super) fn init(&mut self, clock: Option<u32>) -> Result<(), FlashError> {
let algo = &self.flash_algorithm;
let Some(pc_init) = algo.pc_init else {
return Ok(());
};
let address = self.flash_algorithm.flash_properties.address_range.start;
let error_code = self
.call_function_and_wait(
&Registers {
pc: into_reg(pc_init)?,
r0: Some(into_reg(address)?),
r1: clock.or(Some(0)),
r2: Some(O::OPERATION),
r3: None,
},
true,
INIT_TIMEOUT,
)
.map_err(|error| FlashError::Init(Box::new(error)))?;
if error_code != 0 {
return Err(FlashError::RoutineCallFailed {
name: "init",
error_code,
});
}
Ok(())
}
pub(super) fn uninit(&mut self) -> Result<(), FlashError> {
tracing::debug!("Running uninit routine.");
let algo = &self.flash_algorithm;
let Some(pc_uninit) = algo.pc_uninit else {
return Ok(());
};
let error_code = self
.call_function_and_wait(
&Registers {
pc: into_reg(pc_uninit)?,
r0: Some(O::OPERATION),
r1: None,
r2: None,
r3: None,
},
false,
INIT_TIMEOUT,
)
.map_err(|error| FlashError::Uninit(Box::new(error)))?;
if error_code != 0 {
return Err(FlashError::RoutineCallFailed {
name: "uninit",
error_code,
});
}
Ok(())
}
pub(super) fn read_flash_size(&mut self) -> Result<u32, FlashError> {
tracing::debug!("Reading flash size.");
let algo = &self.flash_algorithm;
let Some(pc_flash_size) = algo.pc_flash_size else {
return Err(FlashError::FlashSizeFailed {
source: String::from("Flash algorithm does not implement the FlashSize function")
.into(),
});
};
let retval = self
.call_function_and_wait(
&Registers {
pc: into_reg(pc_flash_size)?,
r0: None,
r1: None,
r2: None,
r3: None,
},
false,
INIT_TIMEOUT,
)
.map_err(|error| FlashError::FlashSizeFailed {
source: Box::new(error),
})?;
if (retval as i32) < 0 {
return Err(FlashError::RoutineCallFailed {
name: "flash_size",
error_code: retval,
});
}
Ok(retval)
}
fn call_function_and_wait(
&mut self,
registers: &Registers,
init: bool,
duration: Duration,
) -> Result<u32, FlashError> {
self.call_function(registers, init)?;
let r = self.wait_for_completion(duration);
if r.is_err() {
tracing::debug!("Routine call failed: {:?}", r);
}
r
}
fn call_function(&mut self, registers: &Registers, init: bool) -> Result<(), FlashError> {
tracing::debug!("Calling routine {:?}, init={})", registers, init);
let algo = &self.flash_algorithm;
let regs: &'static CoreRegisters = self.core.registers();
let registers = [
(self.core.program_counter(), Some(registers.pc)),
(regs.argument_register(0), registers.r0),
(regs.argument_register(1), registers.r1),
(regs.argument_register(2), registers.r2),
(regs.argument_register(3), registers.r3),
(
regs.core_register(9),
if init {
Some(into_reg(algo.static_base)?)
} else {
None
},
),
(
self.core.stack_pointer(),
if init {
Some(into_reg(algo.stack_top)?)
} else {
None
},
),
(
self.core.return_address(),
if self.instruction_set == InstructionSet::Thumb2 {
Some(into_reg(algo.load_address + 1)?)
} else {
Some(into_reg(algo.load_address)?)
},
),
];
for (description, value) in registers {
if let Some(v) = value {
self.core.write_core_reg(description, v).map_err(|error| {
FlashError::Core(Error::WriteRegister {
register: description.to_string(),
source: Box::new(error),
})
})?;
if tracing::enabled!(Level::DEBUG) {
let value: u32 = self.core.read_core_reg(description).map_err(|error| {
FlashError::Core(Error::ReadRegister {
register: description.to_string(),
source: Box::new(error),
})
})?;
tracing::debug!(
"content of {} {:#x}: {:#010x} should be: {:#010x}",
description.name(),
description.id.0,
value,
v
);
}
}
}
self.core.run().map_err(FlashError::Run)?;
if let Some(rtt_address) = self.flash_algorithm.rtt_control_block
&& self.rtt.is_none()
&& self.read_flasher_rtt
{
match crate::rtt::try_attach_to_rtt(
&mut self.core,
Duration::from_secs(1),
&ScanRegion::Exact(rtt_address),
) {
Ok(rtt) => self.rtt = Some(rtt),
Err(crate::rtt::Error::NoControlBlockLocation) => {}
Err(error) => tracing::error!("RTT could not be initialized: {error}"),
}
}
Ok(())
}
#[tracing::instrument(skip(self))]
pub(super) fn wait_for_completion(&mut self, timeout: Duration) -> Result<u32, FlashError> {
tracing::debug!("Waiting for routine call completion.");
let regs = self.core.registers();
let start = Instant::now();
let mut last_read = Instant::now();
let poll_interval = Duration::from_millis(self.flash_algorithm.rtt_poll_interval);
loop {
match self
.core
.status()
.map_err(FlashError::UnableToReadCoreStatus)?
{
CoreStatus::Halted(_) => {
self.read_rtt()?;
break;
}
CoreStatus::LockedUp => {
return Err(FlashError::UnexpectedCoreStatus {
status: CoreStatus::LockedUp,
});
}
_ => {} }
let now = Instant::now();
if now - last_read >= poll_interval {
self.read_rtt()?;
last_read = now;
}
if now - start >= timeout {
self.read_rtt()?;
return Err(FlashError::Core(Error::Timeout));
}
}
self.check_for_stack_overflow()?;
let r = self
.core
.read_core_reg::<u32>(regs.result_register(0))
.map_err(|error| {
FlashError::Core(Error::ReadRegister {
register: regs.result_register(0).to_string(),
source: Box::new(error),
})
})?;
tracing::debug!("Routine returned {:x}.", r);
Ok(r)
}
fn read_rtt(&mut self) -> Result<(), FlashError> {
let Some(rtt) = &mut self.rtt else {
return Ok(());
};
for channel in rtt.up_channels().iter_mut() {
let mut buffer = vec![0; channel.buffer_size()];
match channel.read(&mut self.core, &mut buffer) {
Ok(read) if read > 0 => {
let message = String::from_utf8_lossy(&buffer[..read]).to_string();
let channel = channel.name().unwrap_or("unnamed");
tracing::debug!("RTT({channel}): {message}");
self.progress.message(message);
}
Ok(_) => (),
Err(error) => tracing::debug!("Reading RTT failed: {error}"),
}
}
Ok(())
}
fn check_for_stack_overflow(&mut self) -> Result<(), FlashError> {
let algo = &self.flash_algorithm;
if !algo.stack_overflow_check {
return Ok(());
}
let stack_bottom = algo.stack_top - algo.stack_size;
let read_back = self
.core
.read_word_8(stack_bottom)
.map_err(FlashError::Core)?;
if read_back != STACK_FILL_BYTE {
return Err(FlashError::StackOverflowDetected { operation: O::NAME });
}
Ok(())
}
pub(super) fn read_flash(&mut self, address: u64, data: &mut [u8]) -> Result<(), FlashError> {
if let Some(read_flash) = self.flash_algorithm.pc_read {
let page_size = self.flash_algorithm.flash_properties.page_size;
let buffer_address = self.flash_algorithm.page_buffers[0];
let mut read_address = address;
for slice in data.chunks_mut(page_size as usize) {
let result = self
.call_function_and_wait(
&Registers {
pc: into_reg(read_flash)?,
r0: Some(into_reg(read_address)?),
r1: Some(into_reg(slice.len() as u64)?),
r2: Some(into_reg(buffer_address)?),
r3: None,
},
false,
Duration::from_secs(30),
)
.map_err(|error| FlashError::FlashReadFailed {
source: Box::new(error),
})?;
if result != 0 {
return Err(FlashError::FlashReadFailed {
source: Box::new(FlashError::RoutineCallFailed {
name: "read_flash",
error_code: result,
}),
});
};
self.core
.read(buffer_address, slice)
.map_err(FlashError::Core)?;
read_address += slice.len() as u64;
}
Ok(())
} else {
self.core.read(address, data).map_err(FlashError::Core)
}
}
pub(super) fn load_page_buffer(
&mut self,
bytes: &[u8],
buffer_number: usize,
) -> Result<u64, FlashError> {
assert!(
buffer_number < self.flash_algorithm.page_buffers.len(),
"Trying to use non-existing buffer ({}/{}) for flashing. This is a bug. Please report it.",
buffer_number,
self.flash_algorithm.page_buffers.len()
);
let buffer_address = self.flash_algorithm.page_buffers[buffer_number];
self.load_data(buffer_address, bytes)?;
Ok(buffer_address)
}
fn load_data(&mut self, address: u64, bytes: &[u8]) -> Result<(), FlashError> {
tracing::debug!(
"Loading {} bytes of data into RAM at address {:#010x}\n",
bytes.len(),
address
);
let t1 = if tracing::enabled!(Level::INFO) {
Some(Instant::now())
} else {
None
};
let word_size = if self.core.is_64_bit() { 8 } else { 4 };
let bytes = if bytes.len().is_multiple_of(word_size) {
Cow::Borrowed(bytes)
} else {
let mut bytes = bytes.to_vec();
bytes.resize(
bytes.len().div_ceil(word_size) * word_size,
self.flash_algorithm.flash_properties.erased_byte_value,
);
Cow::Owned(bytes)
};
self.core.write(address, &bytes).map_err(FlashError::Core)?;
if let Some(t1) = t1 {
tracing::info!(
"Took {:?} to download {} byte page into ram",
t1.elapsed(),
bytes.len()
);
};
Ok(())
}
}
impl ActiveFlasher<'_, '_, Erase> {
pub(super) fn erase_all(&mut self) -> Result<(), FlashError> {
tracing::debug!("Erasing entire chip.");
let algo = &self.flash_algorithm;
let Some(pc_erase_all) = algo.pc_erase_all else {
return Err(FlashError::ChipEraseNotSupported);
};
let result = self
.call_function_and_wait(
&Registers {
pc: into_reg(pc_erase_all)?,
r0: None,
r1: None,
r2: None,
r3: None,
},
false,
Duration::from_secs(40),
)
.map_err(|error| FlashError::ChipEraseFailed {
source: Box::new(error),
})?;
if result != 0 {
Err(FlashError::ChipEraseFailed {
source: Box::new(FlashError::RoutineCallFailed {
name: "chip_erase",
error_code: result,
}),
})
} else {
Ok(())
}
}
pub(super) fn erase_sector(&mut self, sector: &FlashSector) -> Result<(), FlashError> {
let address = sector.address();
tracing::info!("Erasing sector at address {:#010x}", address);
let t1 = Instant::now();
let error_code = self.call_function_and_wait(
&Registers {
pc: into_reg(self.flash_algorithm.pc_erase_sector)?,
r0: Some(into_reg(address)?),
r1: None,
r2: None,
r3: None,
},
false,
Duration::from_millis(
self.flash_algorithm.flash_properties.erase_sector_timeout as u64,
),
)?;
tracing::info!(
"Done erasing sector. Result is {}. This took {:?}",
error_code,
t1.elapsed()
);
if error_code != 0 {
Err(FlashError::RoutineCallFailed {
name: "erase_sector",
error_code,
})
} else {
self.progress.sector_erased(sector.size(), t1.elapsed());
Ok(())
}
}
pub(super) fn blank_check(&mut self, sector: &FlashSector) -> Result<(), FlashError> {
let address = sector.address();
let size = sector.size();
tracing::info!(
"Checking for blanked flash between address {:#010x} and {:#010x}",
address,
address + size
);
let t1 = Instant::now();
if let Some(blank_check) = self.flash_algorithm.pc_blank_check {
let error_code = self.call_function_and_wait(
&Registers {
pc: into_reg(blank_check)?,
r0: Some(into_reg(address)?),
r1: Some(into_reg(size)?),
r2: Some(into_reg(
self.flash_algorithm
.flash_properties
.erased_byte_value
.into(),
)?),
r3: None,
},
false,
Duration::from_millis(
10_000,
),
)?;
tracing::info!(
"Done checking blank. Result is {}. This took {:?}",
error_code,
t1.elapsed()
);
if error_code != 0 {
Err(FlashError::RoutineCallFailed {
name: "blank_check",
error_code,
})
} else {
self.progress.sector_erased(sector.size(), t1.elapsed());
Ok(())
}
} else {
let mut data = vec![0; size as usize];
self.core
.read(address, &mut data)
.map_err(FlashError::Core)?;
if !data
.iter()
.all(|v| *v == self.flash_algorithm.flash_properties.erased_byte_value)
{
return Err(FlashError::ChipEraseFailed {
source: "Not all sectors were erased".into(),
});
}
Ok(())
}
}
}
impl ActiveFlasher<'_, '_, Program> {
pub(super) fn program_page(&mut self, page: &FlashPage) -> Result<(), FlashError> {
let t1 = Instant::now();
let address = page.address();
let bytes = page.data();
tracing::info!(
"Flashing page at address {:#08x} with size: {}",
address,
bytes.len()
);
let begin_data = self.load_page_buffer(bytes, 0)?;
self.start_program_page_with_buffer(begin_data, address, bytes.len() as u64)?;
self.wait_for_write_end(address)?;
tracing::info!("Flashing took: {:?}", t1.elapsed());
self.progress
.page_programmed(page.size() as u64, t1.elapsed());
Ok(())
}
pub(super) fn start_program_page_with_buffer(
&mut self,
buffer_address: u64,
page_address: u64,
data_size: u64,
) -> Result<(), FlashError> {
self.call_function(
&Registers {
pc: into_reg(self.flash_algorithm.pc_program_page)?,
r0: Some(into_reg(page_address)?),
r1: Some(into_reg(data_size)?),
r2: Some(into_reg(buffer_address)?),
r3: None,
},
false,
)
.map_err(|error| FlashError::PageWrite {
page_address,
source: Box::new(error),
})?;
Ok(())
}
fn wait_for_write_end(&mut self, last_page_address: u64) -> Result<(), FlashError> {
let timeout = Duration::from_millis(
self.flash_algorithm.flash_properties.program_page_timeout as u64,
);
self.wait_for_completion(timeout)
.and_then(|result| {
if result == 0 {
Ok(())
} else {
Err(FlashError::RoutineCallFailed {
name: "program_page",
error_code: result,
})
}
})
.map_err(|error| FlashError::PageWrite {
page_address: last_page_address,
source: Box::new(error),
})
}
}