use crate::{
commands::DmiOp,
error::{AbstractcsCmdErr, Error, Result},
operations::ProbeSession,
probe::WchLink,
regs::{self, Abstractcs, DMReg, Dmcontrol, Dmstatus},
};
use std::{thread, time::Duration};
pub const KEY1: u32 = 0x45670123;
pub const KEY2: u32 = 0xCDEF89AB;
pub trait DebugModuleInterface {
fn dmi_nop(&mut self) -> Result<()>;
fn dmi_read(&mut self, reg: u8) -> Result<u32>;
fn dmi_write(&mut self, reg: u8, value: u32) -> Result<()>;
fn read_dmi_reg<R>(&mut self) -> Result<R>
where
R: DMReg,
{
let val = self.dmi_read(R::ADDR)?;
Ok(R::from(val))
}
fn write_dmi_reg<R>(&mut self, reg: R) -> Result<()>
where
R: DMReg,
{
self.dmi_write(R::ADDR, reg.into())?;
Ok(())
}
}
impl DebugModuleInterface for WchLink {
fn dmi_nop(&mut self) -> Result<()> {
self.send_command(DmiOp::Nop)?;
Ok(())
}
fn dmi_read(&mut self, reg: u8) -> Result<u32> {
let mut n = 0;
loop {
let resp = self.send_command(DmiOp::read(reg))?;
if resp.op == 0x03 && resp.data == 0xffffffff && resp.addr == 0x7d {
return Err(Error::NotAttached);
}
if resp.is_success() {
return Ok(resp.data);
} else if n > 100 {
return Err(Error::Timeout);
} else if resp.is_busy() {
log::warn!("dmi_read: busy, retrying");
thread::sleep(Duration::from_millis(10));
n += 1;
} else {
return Err(Error::DmiFailed);
}
}
}
fn dmi_write(&mut self, reg: u8, value: u32) -> Result<()> {
self.send_command(DmiOp::write(reg, value))?;
Ok(())
}
}
impl ProbeSession {
fn clear_abstractcs_cmderr(&mut self) -> Result<()> {
let mut abstractcs = Abstractcs::from(0);
abstractcs.set_cmderr(0b111);
self.probe.write_dmi_reg(abstractcs)?;
Ok(())
}
fn clear_dmstatus_havereset(&mut self) -> Result<()> {
let mut dmcontrol = self.probe.read_dmi_reg::<Dmcontrol>()?;
dmcontrol.set_ackhavereset(true);
self.probe.write_dmi_reg(dmcontrol)?;
Ok(())
}
pub fn ensure_mcu_halt(&mut self) -> Result<()> {
let dmstatus = self.probe.read_dmi_reg::<Dmstatus>()?;
if dmstatus.allhalted() && dmstatus.anyhalted() {
log::trace!("Already halted, nop");
} else {
loop {
self.probe.dmi_write(0x10, 0x80000001)?;
let dmstatus = self.probe.read_dmi_reg::<Dmstatus>()?;
if dmstatus.anyhalted() && dmstatus.allhalted() {
break;
} else {
log::warn!("Not halt, try send");
thread::sleep(Duration::from_millis(10));
}
}
}
self.probe.dmi_write(0x10, 0x00000001)?;
Ok(())
}
pub fn ensure_mcu_resume(&mut self) -> Result<()> {
self.clear_dmstatus_havereset()?;
let dmstatus = self.probe.read_dmi_reg::<Dmstatus>()?;
if dmstatus.allrunning() && dmstatus.anyrunning() {
log::debug!("Already running, nop");
return Ok(());
}
self.probe.send_command(DmiOp::write(0x10, 0x80000001))?;
self.probe.send_command(DmiOp::write(0x10, 0x80000001))?;
self.probe.send_command(DmiOp::write(0x10, 0x00000001))?;
self.probe.send_command(DmiOp::write(0x10, 0x40000001))?;
let dmstatus = self.probe.read_dmi_reg::<Dmstatus>()?;
if dmstatus.allresumeack() && dmstatus.anyresumeack() {
log::debug!("Resumed");
Ok(())
} else {
log::warn!("Resume fails");
Ok(())
}
}
pub fn reset_debug_module(&mut self) -> Result<()> {
self.probe.dmi_write(0x10, 0x00000000)?;
self.probe.dmi_write(0x10, 0x00000001)?;
let dmcontrol = self.probe.read_dmi_reg::<Dmcontrol>()?;
if dmcontrol.dmactive() {
Ok(())
} else {
Err(Error::DmiFailed)
}
}
pub fn read_reg(&mut self, regno: u16) -> Result<u32> {
self.clear_abstractcs_cmderr()?;
let reg = regno as u32;
self.probe.dmi_write(0x04, 0x00000000)?; self.probe.dmi_write(0x17, 0x00220000 | (reg & 0xFFFF))?;
let abstractcs = self.probe.read_dmi_reg::<Abstractcs>()?;
if abstractcs.busy() {
return Err(Error::AbstractCommandError(AbstractcsCmdErr::Busy)); }
if abstractcs.cmderr() != 0 {
AbstractcsCmdErr::try_from_cmderr(abstractcs.cmderr() as _)?;
}
let resp = self.probe.dmi_read(0x04)?;
Ok(resp)
}
pub fn write_reg(&mut self, regno: u16, value: u32) -> Result<()> {
let reg = regno as u32;
self.probe.send_command(DmiOp::write(0x04, value))?;
self.probe
.send_command(DmiOp::write(0x17, 0x00230000 | (reg & 0xFFFF)))?;
let abstractcs = self.probe.read_dmi_reg::<Abstractcs>()?;
if abstractcs.busy() {
return Err(Error::AbstractCommandError(AbstractcsCmdErr::Busy)); }
if abstractcs.cmderr() != 0 {
AbstractcsCmdErr::try_from_cmderr(abstractcs.cmderr() as _)?;
}
Ok(())
}
pub fn read_mem32(&mut self, addr: u32) -> Result<u32> {
self.probe.dmi_write(0x20, 0x0002a303)?; self.probe.dmi_write(0x21, 0x00100073)?;
self.probe.dmi_write(0x04, addr)?; self.clear_abstractcs_cmderr()?;
self.probe.dmi_write(0x17, 0x00271005)?;
let abstractcs: Abstractcs = self.probe.read_dmi_reg()?;
if abstractcs.busy() {
return Err(Error::AbstractCommandError(AbstractcsCmdErr::Busy)); }
if abstractcs.cmderr() != 0 {
AbstractcsCmdErr::try_from_cmderr(abstractcs.cmderr() as _)?;
}
self.probe.dmi_write(0x17, 0x00221006)?; if abstractcs.busy() {
return Err(Error::AbstractCommandError(AbstractcsCmdErr::Busy)); }
if abstractcs.cmderr() != 0 {
AbstractcsCmdErr::try_from_cmderr(abstractcs.cmderr() as _)?;
}
let data0 = self.probe.dmi_read(0x04)?;
Ok(data0)
}
pub fn write_mem32(&mut self, addr: u32, data: u32) -> Result<()> {
self.probe.dmi_write(0x20, 0x0072a023)?; self.probe.dmi_write(0x21, 0x00100073)?;
self.probe.dmi_write(0x04, addr)?;
self.clear_abstractcs_cmderr()?;
self.probe.dmi_write(0x17, 0x00231005)?;
let abstractcs: Abstractcs = self.probe.read_dmi_reg()?;
log::trace!("{:?}", abstractcs);
if abstractcs.busy() {
return Err(Error::AbstractCommandError(AbstractcsCmdErr::Busy)); }
if abstractcs.cmderr() != 0 {
AbstractcsCmdErr::try_from_cmderr(abstractcs.cmderr() as _)?;
}
self.probe.dmi_write(0x04, data)?; self.clear_abstractcs_cmderr()?;
self.probe.dmi_write(0x17, 0x00271007)?;
let abstractcs: Abstractcs = self.probe.read_dmi_reg()?;
log::trace!("{:?}", abstractcs);
if abstractcs.busy() {
return Err(Error::AbstractCommandError(AbstractcsCmdErr::Busy)); }
if abstractcs.cmderr() != 0 {
AbstractcsCmdErr::try_from_cmderr(abstractcs.cmderr() as _)?;
}
Ok(())
}
pub fn write_mem8(&mut self, addr: u32, data: u8) -> Result<()> {
self.probe.dmi_write(0x20, 0x00728023)?; self.probe.dmi_write(0x21, 0x00100073)?;
self.probe.dmi_write(0x04, addr)?;
self.clear_abstractcs_cmderr()?;
self.probe.dmi_write(0x17, 0x00231005)?;
let abstractcs: Abstractcs = self.probe.read_dmi_reg()?;
log::trace!("{:?}", abstractcs);
if abstractcs.busy() {
return Err(Error::AbstractCommandError(AbstractcsCmdErr::Busy)); }
if abstractcs.cmderr() != 0 {
AbstractcsCmdErr::try_from_cmderr(abstractcs.cmderr() as _)?;
}
self.probe.dmi_write(0x04, data as u32)?; self.clear_abstractcs_cmderr()?;
self.probe.dmi_write(0x17, 0x00271007)?;
let abstractcs: Abstractcs = self.probe.read_dmi_reg()?;
log::trace!("{:?}", abstractcs);
if abstractcs.busy() {
return Err(Error::AbstractCommandError(AbstractcsCmdErr::Busy)); }
if abstractcs.cmderr() != 0 {
AbstractcsCmdErr::try_from_cmderr(abstractcs.cmderr() as _)?;
}
Ok(())
}
pub fn modify_mem32<F>(&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<F>(&mut self, addr: u32, until: F) -> Result<u32>
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_by_dmi(&mut self, addr: u32, len: u32) -> Result<Vec<u8>> {
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)
}
}
impl ProbeSession {
pub fn dump_core_csrs(&mut self) -> Result<()> {
let misa = self.read_reg(regs::MISA)?;
log::trace!("Read csr misa: {misa:08x}");
let misa = parse_misa(misa);
log::info!("RISC-V ISA(misa): {misa:?}");
let marchid = self.read_reg(regs::MARCHID)?;
log::trace!("Read csr marchid: {marchid:08x}");
let core_type = parse_marchid(marchid);
log::info!("RISC-V arch(marchid): {core_type:?}");
Ok(())
}
pub fn dump_regs(&mut self) -> Result<()> {
let dpc = self.read_reg(regs::DPC)?;
println!("dpc(pc): 0x{dpc:08x}");
let gprs = if self.chip_family.is_rv32ec() {
regs::GPRS_RVE
} else {
regs::GPRS_RVI
};
for (reg, name, regno) in gprs {
let val = self.read_reg(*regno)?;
println!("{reg:<4}{name:>5}: 0x{val:08x}");
}
for (reg, regno) in regs::CSRS {
let val = self.read_reg(*regno)?;
println!("{reg:<9}: 0x{val:08x}");
}
Ok(())
}
pub fn dump_pmp_csrs(&mut self) -> Result<()> {
for (name, addr) in regs::PMP_CSRS {
let val = self.read_reg(*addr)?;
log::debug!("{}: 0x{:08x}", name, val);
}
Ok(())
}
pub fn dump_dmi(&mut self) -> Result<()> {
log::warn!("The halt status may be incorrect because detaching might resume the MCU");
let dmstatus: regs::Dmstatus = self.probe.read_dmi_reg()?;
log::info!("{dmstatus:#x?}");
let dmcontrol: regs::Dmcontrol = self.probe.read_dmi_reg()?;
log::info!("{dmcontrol:#x?}");
let hartinfo: regs::Hartinfo = self.probe.read_dmi_reg()?;
log::info!("{hartinfo:#x?}");
let abstractcs: regs::Abstractcs = self.probe.read_dmi_reg()?;
log::info!("{abstractcs:#x?}");
let haltsum0 = self.probe.dmi_read(0x40)?;
log::info!("haltsum0: {:#x?}", haltsum0);
Ok(())
}
}
fn parse_marchid(marchid: u32) -> Option<String> {
if marchid == 0 {
None
} else {
Some(format!(
"{}{}{}-{}{}{}",
(((marchid >> 26) & 0x1F) + 64) as u8 as char,
(((marchid >> 21) & 0x1F) + 64) as u8 as char,
(((marchid >> 16) & 0x1F) + 64) as u8 as char,
(((marchid >> 10) & 0x1F) + 64) as u8 as char,
((((marchid >> 5) & 0x1F) as u8) + b'0') as char,
((marchid & 0x1F) + 64) as u8 as char,
))
}
}
fn parse_misa(misa: u32) -> Option<String> {
let mut s = String::new();
let mxl = (misa >> 30) & 0x3;
s.push_str(match mxl {
1 => "RV32",
2 => "RV64",
3 => "RV128",
_ => return None,
});
for i in 0..26 {
if (misa >> i) & 1 == 1 {
s.push((b'A' + i as u8) as char);
}
}
Some(s)
}