diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c78623..f33dd25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Regression in `flash` command - Use chip type from the protocol, close #25 - Support 2.10 (aka. v30) firmware, close #27 +- Fix `--no-detach` option is not working +- Use DMI to read memory, avoid using probe commands ### Changed diff --git a/src/commands.rs b/src/commands.rs index c26961b..d504a7a 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -286,19 +286,17 @@ impl Command for Reset { /// Speed settings #[derive(Debug, Copy, Clone, clap::ValueEnum, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[derive(Default)] pub enum Speed { /// 400kHz Low = 0x03, /// 4000kHz Medium = 0x02, /// 6000kHz + #[default] High = 0x01, } -impl Default for Speed { - fn default() -> Self { - Speed::High - } -} + /// Set CLK Speed, 0x0C #[derive(Debug)] diff --git a/src/dmi.rs b/src/dmi.rs index 70e8e92..edb0dba 100644 --- a/src/dmi.rs +++ b/src/dmi.rs @@ -2,7 +2,7 @@ use crate::{ commands::DmiOp, device::WchLink, error::{AbstractcsCmdErr, Error, Result}, - regs::{Abstractcs, DMReg, Dmstatus}, + regs::{Abstractcs, DMReg, Dmcontrol, Dmstatus}, }; use std::{thread, time::Duration}; @@ -116,6 +116,19 @@ impl<'a, D: DebugModuleInterface> Algorigthm<'a, D> { Ok(()) } + pub fn reset_debug_module(&mut self) -> Result<()> { + self.dmi.dmi_write(0x10, 0x00000000)?; + self.dmi.dmi_write(0x10, 0x00000001)?; + + let dmcontrol = self.dmi.read_dmi_reg::()?; + + if dmcontrol.dmactive() { + Ok(()) + } else { + Err(Error::DmiFailed) + } + } + pub fn read_mem32(&mut self, addr: u32) -> Result { self.dmi.dmi_write(0x20, 0x0002a303)?; // lw x6,0(x5) self.dmi.dmi_write(0x21, 0x00100073)?; // ebreak @@ -142,7 +155,6 @@ impl<'a, D: DebugModuleInterface> Algorigthm<'a, D> { } let data0 = self.dmi.dmi_read(0x04)?; - Ok(data0) } @@ -182,7 +194,39 @@ impl<'a, D: DebugModuleInterface> Algorigthm<'a, D> { Ok(()) } - pub fn unlock_flash(&mut self) {} + pub fn write_mem8(&mut self, addr: u32, data: u8) -> Result<()> { + self.dmi.dmi_write(0x20, 0x00728023)?; // sb x7,0(x5) + self.dmi.dmi_write(0x21, 0x00100073)?; // ebreak + + self.dmi.dmi_write(0x04, addr)?; // data0 <- address + + self.clear_abstractcs_cmderr()?; + self.dmi.dmi_write(0x17, 0x00231005)?; // x5 <- data0 + + let abstractcs: Abstractcs = self.dmi.read_dmi_reg()?; + log::trace!("{:?}", abstractcs); + if abstractcs.busy() { + return Err(Error::AbstractCommandError(AbstractcsCmdErr::Busy)); //resue busy + } + if abstractcs.cmderr() != 0 { + AbstractcsCmdErr::try_from_cmderr(abstractcs.cmderr() as _)?; + } + + self.dmi.dmi_write(0x04, data as u32)?; // data0 <- data + self.clear_abstractcs_cmderr()?; + + self.dmi.dmi_write(0x17, 0x00271007)?; // x7 <- data0 + + let abstractcs: Abstractcs = self.dmi.read_dmi_reg()?; + log::trace!("{:?}", abstractcs); + if abstractcs.busy() { + return Err(Error::AbstractCommandError(AbstractcsCmdErr::Busy)); //resue busy + } + if abstractcs.cmderr() != 0 { + AbstractcsCmdErr::try_from_cmderr(abstractcs.cmderr() as _)?; + } + Ok(()) + } /// Read register value /// CSR: 0x0000 - 0x0fff @@ -224,4 +268,292 @@ impl<'a, D: DebugModuleInterface> Algorigthm<'a, D> { Ok(()) } + + pub fn modify_mem32(&mut self, addr: u32, f: F) -> Result<()> + where + F: FnOnce(u32) -> u32, + { + let data = self.read_mem32(addr)?; + let data = f(data); + self.write_mem32(addr, data)?; + Ok(()) + } + + pub fn wait_mem32(&mut self, addr: u32, until: F) -> Result + where + F: Fn(u32) -> bool, + { + loop { + let data = self.read_mem32(addr)?; + if until(data) { + return Ok(data); + } + thread::sleep(Duration::from_millis(1)); + } + } + + pub fn read_memory(&mut self, addr: u32, len: u32) -> Result> { + if len % 4 != 0 { + return Err(Error::Custom("len must be 4 bytes aligned".to_string())); + } + + let mut ret = Vec::with_capacity(len as usize); + for i in 0..len / 4 { + let data = self.read_mem32(addr + i * 4)?; + ret.extend_from_slice(&data.to_le_bytes()); + } + Ok(ret) + } + + fn lock_flash(&mut self) -> Result<()> { + const FLASH_CTLR: u32 = 0x40022010; + + self.modify_mem32(FLASH_CTLR, |r| r | 0x00008080)?; + Ok(()) + } + + /// unlock FLASH LOCK and FLOCK + fn unlock_flash(&mut self) -> Result<()> { + const FLASH_CTLR: u32 = 0x40022010; + const FLASH_KEYR: u32 = 0x40022004; + const FLASH_MODEKEYR: u32 = 0x40022024; + const KEY1: u32 = 0x45670123; + const KEY2: u32 = 0xCDEF89AB; + + let flash_ctlr = self.read_mem32(FLASH_CTLR)?; + log::debug!("flash_ctlr: 0x{:08x}", flash_ctlr); + // Test LOCK, FLOCK bits + if flash_ctlr & 0x00008080 == 0 { + // already unlocked + return Ok(()); + } + // unlock LOCK + self.write_mem32(FLASH_KEYR, KEY1)?; + self.write_mem32(FLASH_KEYR, KEY2)?; + + // unlock FLOCK + self.write_mem32(FLASH_MODEKEYR, KEY1)?; + self.write_mem32(FLASH_MODEKEYR, KEY2)?; + + let flash_ctlr = self.read_mem32(FLASH_CTLR)?; + log::debug!("flash_ctlr: 0x{:08x}", flash_ctlr); + + Ok(()) + } + + /// Erase by 256 bytes page + /// address must be 256 bytes aligned + pub fn fast_erase(&mut self, address: u32) -> Result<()> { + // require unlock + self.unlock_flash()?; + + const FLASH_STATR: u32 = 0x4002200C; + const BUSY_MASK: u32 = 0x00000001; + const START_MASK: u32 = 1 << 6; + // const EOP_MASK: u32 = 1 << 5; + const WPROTECT_ERR_MASK: u32 = 1 << 4; + + const FLASH_ADDR: u32 = 0x40022014; + const FLASH_CTLR: u32 = 0x40022010; + + const PAGE_ERASE_MASK: u32 = 1 << 17; + + if address & 0xff != 0 { + return Err(Error::Custom( + "address must be 256 bytes aligned".to_string(), + )); + } + + let statr = self.read_mem32(FLASH_STATR)?; + // check if busy + if statr & BUSY_MASK != 0 { + return Err(Error::Custom("flash busy".to_string())); + } + + self.modify_mem32(FLASH_CTLR, |r| r | PAGE_ERASE_MASK)?; + + self.write_mem32(FLASH_ADDR, address)?; + + self.modify_mem32(FLASH_CTLR, |r| r | START_MASK)?; + + loop { + let statr = self.read_mem32(FLASH_STATR)?; + // check if busy + if statr & BUSY_MASK != 0 { + thread::sleep(Duration::from_millis(1)); + } else { + if statr & WPROTECT_ERR_MASK != 0 { + return Err(Error::Custom("flash write protect error".to_string())); + } + self.write_mem32(FLASH_STATR, statr)?; // write 1 to clear EOP + + break; + } + } + // read 1 word to verify + let word = self.read_mem32(address)?; + println!("=> {:08x}", word); + + // end erase, disable page erase + self.modify_mem32(FLASH_CTLR, |r| r & (!PAGE_ERASE_MASK))?; + + self.lock_flash()?; + + Ok(()) + } + + pub fn fast_erase_32k(&mut self, address: u32) -> Result<()> { + // require unlock + self.unlock_flash()?; + + const FLASH_STATR: u32 = 0x4002200C; + const BUSY_MASK: u32 = 0x00000001; + const START_MASK: u32 = 1 << 6; + const WPROTECT_ERR_MASK: u32 = 1 << 4; + + const FLASH_ADDR: u32 = 0x40022014; + const FLASH_CTLR: u32 = 0x40022010; + + const BLOCK_ERASE_32K_MASK: u32 = 1 << 18; + + if address & 0x7fff != 0 { + return Err(Error::Custom( + "address must be 32k bytes aligned".to_string(), + )); + } + + let statr = self.read_mem32(FLASH_STATR)?; + // check if busy + if statr & BUSY_MASK != 0 { + return Err(Error::Custom("flash busy".to_string())); + } + + self.modify_mem32(FLASH_CTLR, |r| r | BLOCK_ERASE_32K_MASK)?; + + self.write_mem32(FLASH_ADDR, address)?; + + self.modify_mem32(FLASH_CTLR, |r| r | START_MASK)?; + + loop { + let statr = self.read_mem32(FLASH_STATR)?; + // check if busy + if statr & BUSY_MASK != 0 { + thread::sleep(Duration::from_millis(1)); + } else { + if statr & WPROTECT_ERR_MASK != 0 { + return Err(Error::Custom("flash write protect error".to_string())); + } + self.write_mem32(FLASH_STATR, statr)?; // write 1 to clear EOP + + break; + } + } + // read 1 word to verify + let word = self.read_mem32(address)?; + println!("=> {:08x}", word); + + // end erase + // disable page erase + self.modify_mem32(FLASH_CTLR, |r| r & (!BLOCK_ERASE_32K_MASK))?; + + self.lock_flash()?; + + Ok(()) + } + + pub fn erase_all(&mut self) -> Result<()> { + const FLASH_STATR: u32 = 0x4002200C; + const BUSY_MASK: u32 = 0x00000001; + + const FLASH_CTLR: u32 = 0x40022010; + const MASS_ERASE_MASK: u32 = 1 << 2; // MER + const START_MASK: u32 = 1 << 6; + + self.unlock_flash()?; + + self.modify_mem32(FLASH_CTLR, |r| r | MASS_ERASE_MASK)?; + + self.modify_mem32(FLASH_CTLR, |r| r | START_MASK)?; + + let statr = self.wait_mem32(FLASH_STATR, |r| r & BUSY_MASK == 0)?; + self.write_mem32(FLASH_STATR, statr)?; // write 1 to clear EOP + + // clear MER + self.modify_mem32(FLASH_CTLR, |r| r & (!MASS_ERASE_MASK))?; + + Ok(()) + } + + /// Program bytes. + /// + /// # Arguments + /// + /// * `address` - The start address of the flash page to program. + /// * `data` - The data to be written to the page. + /// + /// The page must be erased first + pub fn program_page(&mut self, address: u32, data: &[u8]) -> Result<()> { + // require unlock + self.unlock_flash()?; + + const FLASH_STATR: u32 = 0x4002200C; + const BUSY_MASK: u32 = 0x00000001; + const WRITE_BUSY_MASK: u32 = 1 << 1; + const WPROTECT_ERR_MASK: u32 = 1 << 4; + + const FLASH_CTLR: u32 = 0x40022010; + const PAGE_START_MASK: u32 = 1 << 21; // start page program + const PAGE_PROG_MASK: u32 = 1 << 16; // + + if address & 0xff != 0 { + return Err(Error::Custom( + "address must be 256 bytes aligned".to_string(), + )); + } + + // check if busy + let statr = self.read_mem32(FLASH_STATR)?; + if statr & BUSY_MASK != 0 { + return Err(Error::Custom("flash busy".to_string())); + } + + //let ctlr = self.read_mem32(FLASH_CTLR)?; + //let ctlr = ctlr | PAGE_PROG_MASK; + //self.write_mem32(FLASH_CTLR, ctlr)?; + self.modify_mem32(FLASH_CTLR, |r| r | PAGE_PROG_MASK)?; + + for (i, word) in data.chunks(4).enumerate() { + let word = u32::from_le_bytes(word.try_into().unwrap()); + self.write_mem32(address + (i as u32 * 4), word)?; + + // write busy wait + self.wait_mem32(FLASH_STATR, |r| r & WRITE_BUSY_MASK == 0)?; + } + + // start fast page program + self.modify_mem32(FLASH_CTLR, |r| r | PAGE_START_MASK)?; + + // busy wait + let statr = self.wait_mem32(FLASH_STATR, |r| r & BUSY_MASK == 0)?; + + self.write_mem32(FLASH_STATR, statr)?; // write 1 to clear EOP + if statr & WPROTECT_ERR_MASK != 0 { + return Err(Error::Custom("flash write protect error".to_string())); + } + + // verify + // read 1 word to verify + //let word = self.read_mem32(address)?; + //println!("=> {:08x}", word); + + // end program, clear PAGE_PROG + //let ctlr = self.read_mem32(FLASH_CTLR)?; + //let ctlr = ctlr & (!PAGE_PROG_MASK); // disable page erase + //self.write_mem32(FLASH_CTLR, ctlr)?; + self.modify_mem32(FLASH_CTLR, |r| r & (!PAGE_PROG_MASK))?; + + self.lock_flash()?; + + Ok(()) + } } diff --git a/src/main.rs b/src/main.rs index d023e59..17ec873 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,8 +20,8 @@ struct Cli { verbose: u8, /// Detach chip after operation - #[arg(long, global = true, default_value = "true")] - detach: bool, + #[arg(long, global = true, default_value = "false")] + no_detach: bool, /// Specify the chip type, e.g. CH32V30X #[arg(long, global = true)] @@ -173,15 +173,37 @@ fn main() -> Result<()> { probe.attach_chip(cli.chip)?; match command { Dev {} => { + // probe.reset_debug_module()?; + + // probe.reset_debug_module()?; + // probe.erase_flash_by_power_off()?; // const FLASH_KEYR: u32 = 0x2000_0030; - //let mut algo = wlink::dmi::Algorigthm::new(&mut probe); - // algo.write_mem32(FLASH_KEYR, 0x45670123)?; + let mut algo = wlink::dmi::Algorigthm::new(&mut probe); + + algo.reset_debug_module()?; + // algo.unlock_flash()?; + //algo.fast_erase_32k(0x0800_0000)?; + //algo.dump_pmp()?; + // 0x40001045 + // algo.write_mem8(0x40001040, 0x57)?; + // algo.write_mem8(0x40001040, 0xA8)?; + + // algo.write_mem8(0x2000_0000, 0xca)?; + + //algo.program_page(0x0800_0100, &[0x00; 256])?; + //algo.erase_all()?; + + //algo.lock_flash()?; + + //algo.write_mem32(0x2000_0000, 0x45670123)?; //algo.ensure_mcu_halt()?; - //let address = 0x40001041; - //let v = algo.read_mem32(address)?; - //println!("0x{:08x}: 0x{:08x}", address, v); + for i in 0..10 { + let address = 0x40001040 + i * 4; + let v = algo.read_mem32(address)?; + println!("0x{:08x}: 0x{:08x}", address, v); + } // algo.dump_pmp()?; } @@ -191,7 +213,22 @@ fn main() -> Result<()> { address, address + length ); - probe.read_memory(address, length)?; + + // probe.read_memory(address, length)?; + let mut algo = wlink::dmi::Algorigthm::new(&mut probe); + let out = algo.read_memory(address, length)?; + println!( + "{}", + nu_pretty_hex::config_hex( + &out, + nu_pretty_hex::HexConfig { + title: true, + ascii: true, + address_offset: address as _, + ..Default::default() + }, + ) + ); } Regs {} => { log::info!("Dump GPRs"); @@ -200,6 +237,9 @@ fn main() -> Result<()> { Halt {} => { log::info!("Halt MCU"); probe.ensure_mcu_halt()?; + + let dmstatus: regs::Dmstatus = probe.read_dmi_reg()?; + log::info!("{dmstatus:#x?}"); } Resume {} => { log::info!("Resume MCU"); @@ -303,7 +343,7 @@ fn main() -> Result<()> { } _ => unreachable!("unimplemented command"), } - if cli.detach { + if !cli.no_detach { probe.detach_chip()?; } } @@ -313,12 +353,12 @@ fn main() -> Result<()> { } pub fn parse_number(s: &str) -> std::result::Result { - let s = s.replace("_", "").to_lowercase(); - if s.starts_with("0x") { - Ok(u32::from_str_radix(&s[2..], 16) + let s = s.replace('_', "").to_lowercase(); + if let Some(hex_str) = s.strip_prefix("0x") { + Ok(u32::from_str_radix(hex_str, 16) .unwrap_or_else(|_| panic!("error while parsering {s:?}"))) - } else if s.starts_with("0b") { - Ok(u32::from_str_radix(&s[2..], 2) + } else if let Some(bin_str) = s.strip_prefix("0b") { + Ok(u32::from_str_radix(bin_str, 2) .unwrap_or_else(|_| panic!("error while parsering {s:?}"))) } else { Ok(s.parse().expect("must be a number")) diff --git a/src/operations.rs b/src/operations.rs index 9ec8fba..44dc004 100644 --- a/src/operations.rs +++ b/src/operations.rs @@ -41,7 +41,7 @@ impl WchLink { let mut chip_info = None; for _ in 0..3 { self.send_command(commands::SetSpeed { - riscvchip: expected_chip.unwrap_or(RiscvChip::CH32V20X) as u8, + riscvchip: expected_chip.unwrap_or(RiscvChip::CH32V103) as u8, speed: self.speed, })?; @@ -249,9 +249,7 @@ impl WchLink { self.send_command(control::EraseCodeFlash::ByPowerOff(chip_family))?; Ok(()) } else { - Err(Error::Custom(format!( - "Probe or Chip doesn't support power off erase", - ))) + Err(Error::Custom("Probe or Chip doesn't support power off erase".to_string())) } } @@ -264,9 +262,7 @@ impl WchLink { self.send_command(control::EraseCodeFlash::ByPinRST(chip_family))?; Ok(()) } else { - Err(Error::Custom(format!( - "Probe or Chip doesn't support RST pin erase", - ))) + Err(Error::Custom("Probe or Chip doesn't support RST pin erase".to_string())) } } @@ -345,7 +341,7 @@ impl WchLink { self.send_command(Program::WriteFlash)?; for chunk in data.chunks(write_pack_size as usize) { self.device_handle - .write_data_endpoint(&chunk, data_packet_size)?; + .write_data_endpoint(chunk, data_packet_size)?; let rxbuf = self.device_handle.read_data_endpoint(4)?; // 41 01 01 04 if rxbuf[3] != 0x04 { @@ -608,32 +604,6 @@ impl WchLink { Ok(()) } - - // via: SingleLineDebugMReset - pub fn reset_debug_module(&mut self) -> Result<()> { - self.ensure_mcu_halt()?; - - // Write command - self.send_command(DmiOp::write(0x10, 0x00000003))?; - - let dmcontrol = self.read_dmi_reg::()?; - if !(dmcontrol.dmactive() && dmcontrol.ndmreset()) { - return Err(Error::Custom( - "Value not written, DM reset might be not supported".into(), - )); - } - - // Write the debug module reset command - self.dmi_write(0x10, 0x00000002)?; - - let dmcontrol = self.read_dmi_reg::()?; - if !dmcontrol.ndmreset() { - Ok(()) - } else { - log::warn!("Reset is not successful"); - Ok(()) - } - } } // marchid => dc68d882