Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix unexpected address on multi-section writing #61

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- Rename ChipUID response to ESignature, #58
- Add `--skip-gap` as a experimental option for `flash` subcommand to write a firmware which has more than 1 section, #40 & #56

## [0.0.8] - 2024-03-30

Expand All @@ -22,10 +23,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Use loaded memory address from ELF file or ihex file
- Add timestamp in serial output

### Fixed

- Merge gaps in firmware sections, #56

### Changed

- No erase by default when flashing
Expand Down
58 changes: 27 additions & 31 deletions src/firmware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,39 +38,35 @@ pub enum Firmware {
Sections(Vec<Section>),
}

impl Firmware {
/// Merge sections, and fill gap with 0xff
pub fn merge_sections(self) -> Result<Self> {
if let Firmware::Sections(mut sections) = self {
sections.sort_by_key(|s| s.address);
let mut merged = vec![];

let mut it = sections.drain(0..);
let mut last = it
.next()
.expect("firmware must has at least one section; qed");

for sect in it {
if let Some(gap) = sect.address.checked_sub(last.end_address()) {
if gap > 0 {
log::debug!("Merge firmware sections with gap: {}", gap);
}
last.data.resize(last.data.len() + gap as usize, 0xff); // fill gap with 0xff
last.data.extend_from_slice(&sect.data);
} else {
return Err(anyhow::format_err!(
"section address overflow: {:#010x} + {:#x}",
last.address,
last.data.len()
));
}
/// Merge sections w/ <= max_tiny_gap bytes gap
pub fn fill_tiny_gap_between_sections(mut sections: Vec<Section>, max_tiny_gap: u32) -> Result<Vec<Section>> {
sections.sort_by_key(|s| s.address);
let mut merged = vec![];

let mut it = sections.drain(0..);
let mut last = it
.next()
.expect("firmware must has at least one section; qed");
for sect in it {
if let Some(gap) = sect.address.checked_sub(last.end_address()) {
if gap > max_tiny_gap {
merged.push(last);
last = sect.clone();
continue;
} else {
last.data.resize(last.data.len() + gap as usize, 0xFF);
last.data.extend_from_slice(&sect.data);
}
merged.push(last);
Ok(Firmware::Sections(merged))
} else {
Ok(self)
return Err(anyhow::format_err!(
"section address overflow: {:#010x} + {:#x}",
last.address,
last.data.len()
));
}
}
merged.push(last);
Ok(merged)
}

pub fn read_firmware_from_file<P: AsRef<Path>>(path: P) -> Result<Firmware> {
Expand All @@ -90,9 +86,9 @@ pub fn read_firmware_from_file<P: AsRef<Path>>(path: P) -> Result<Firmware> {
}
FirmwareFormat::Binary => Ok(Firmware::Binary(raw)),
FirmwareFormat::IntelHex => {
read_ihex(str::from_utf8(&raw)?).and_then(|f| f.merge_sections())
read_ihex(str::from_utf8(&raw)?)
}
FirmwareFormat::ELF => read_elf(&raw).and_then(|f| f.merge_sections()),
FirmwareFormat::ELF => read_elf(&raw),
}
}

Expand Down
31 changes: 24 additions & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use anyhow::Result;
use wlink::{
commands,
dmi::DebugModuleInterface,
firmware::{read_firmware_from_file, Firmware},
firmware::{fill_tiny_gap_between_sections, read_firmware_from_file, Firmware},
operations::ProbeSession,
probe::WchLink,
regs, RiscvChip,
Expand Down Expand Up @@ -93,6 +93,9 @@ enum Commands {
/// Erase flash before flashing
#[arg(long, short, default_value = "false")]
erase: bool,
/// Skip gap between sections
#[arg(long, short, default_value = "false")]
skip_gap: bool,
/// Do not reset and run after flashing
#[arg(long, short = 'R', default_value = "false")]
no_run: bool,
Expand Down Expand Up @@ -325,6 +328,7 @@ fn main() -> Result<()> {
Flash {
address,
erase,
skip_gap,
no_run,
path,
enable_sdi_print,
Expand All @@ -341,25 +345,38 @@ fn main() -> Result<()> {

match firmware {
Firmware::Binary(data) => {
if skip_gap {
log::warn!("Skip gap is ignored when flashing binary");
}
let start_address =
address.unwrap_or_else(|| sess.chip_family.code_flash_start());
log::info!("Flashing {} bytes to 0x{:08x}", data.len(), start_address);
sess.write_flash(&data, start_address)?;
}
Firmware::Sections(sections) => {
// Flash section by section
let mut sections = sections.clone();
if address != None {
log::warn!("--address is ignored when flashing ELF or ihex");
}
if skip_gap {
log::warn!("Skip gap is a experimental feature using a trait of wchlink!");
sections = fill_tiny_gap_between_sections(sections, 4096)?;
} else {
// merge sections
sections = fill_tiny_gap_between_sections(sections, 0xFFFFFFFF)?;
}
let mut offset = 0; // may a trait of wchlink
for section in sections {
let start_address =
sess.chip_family.fix_code_flash_start(section.address);
log::info!(
"Flashing {} bytes to 0x{:08x}",
section.data.len(),
start_address
);
sess.write_flash(&section.data, start_address)?;
"Flashing {} bytes to 0x{:08x}",
section.data.len(),
start_address
);
log::debug!("offset: 0x{:08x}", offset);
sess.write_flash(&section.data, start_address - offset)?;
offset += ((section.data.len() as u32 + 4095) / 4096) * 4096;
}
}
}
Expand Down