diff --git a/CHANGELOG.md b/CHANGELOG.md index d4e220d..15bfa21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,10 +13,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Support WCH-Link firmware 2.9, some raw commands are changed - Support Flash protect and unprotect (#14) - Fix stuck for CH5xx devices, due to unsppported read ram rom split command +- Add `--chip` option to specify chip type +- Check probe type when doing mode-switch ### Fixed - Constraint regs for riscv32ec variant +- Wrong 0x0c command interpretation, this should be a set chip speed command + +### Changed + +- Refine error messages ## [0.0.4] - 2023-07-01 diff --git a/src/commands.rs b/src/commands.rs index 0acf322..cae87df 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -181,6 +181,7 @@ impl Command for SetFlashProtected { } /// Get Chip UID, the UID is also avaliable in the `wchisp` command. +// ??? 0x11, 0x01, _ (riscvchip) #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] pub enum QueryChipInfo { V1 = 0x09, @@ -261,6 +262,29 @@ impl Command for Reset { } } +/// (0x0c, [riscvchip, 1/2/3]) +pub struct SetTwoLineMode { + pub riscvchip: u8, + pub speed: u8, // 1, 2, 3 +} + +impl Command for SetTwoLineMode { + type Response = bool; + const COMMAND_ID: u8 = 0x0c; + fn payload(&self) -> Vec { + vec![self.riscvchip, self.speed] + } +} + +impl Response for bool { + fn from_payload(resp: &[u8]) -> Result { + if resp.len() != 1 { + return Err(Error::InvalidPayloadLength); + } + Ok(resp[0] == 0x01) // 1 means success + } +} + /// DMI operations pub enum DmiOp { Nop, diff --git a/src/commands/control.rs b/src/commands/control.rs index 15397ed..ea2d307 100644 --- a/src/commands/control.rs +++ b/src/commands/control.rs @@ -54,7 +54,7 @@ impl fmt::Display for ProbeInfo { } } -/// (0x0d, 0x02) +/// ?SetChipType (0x0d, 0x02) pub struct AttachChip; impl Command for AttachChip { type Response = AttachChipResponse; @@ -64,7 +64,7 @@ impl Command for AttachChip { } } -#[derive(Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct AttachChipResponse { pub chip_family: RiscvChip, pub riscvchip: u8, diff --git a/src/device.rs b/src/device.rs index 9a0a5b6..e464f13 100644 --- a/src/device.rs +++ b/src/device.rs @@ -60,7 +60,18 @@ impl WchLink { .unwrap_or(false) }) .nth(nth) - .map_or(Err(crate::error::Error::ProbeNotFound), Ok)?; + .map_or(Err(crate::error::Error::ProbeNotFound), Ok); + + // check if there's a device with the DAP VID/PID + let device = match device { + Ok(device) => device, + Err(e) => { + if let Ok(_) = open_usb_device(VENDOR_ID_DAP, PRODUCT_ID_DAP, nth) { + return Err(crate::error::Error::ProbeModeNotSupported); + } + return Err(e); + } + }; let mut device_handle = device.open()?; @@ -130,9 +141,15 @@ pub fn try_switch_from_rv_to_dap(nth: usize) -> Result<()> { }; let info = dev.probe_info()?; info!("probe info: {:?}", info); - - let _ = dev.send_command(RawCommand::<0xff>(vec![0x41])); - Ok(()) + if info.variant.can_switch_mode() { + let _ = dev.send_command(RawCommand::<0xff>(vec![0x41])); + Ok(()) + } else { + log::error!("Cannot switch mode for WCH-LinkRV: not supported"); + return Err(crate::Error::Custom( + "WCH-Link-CH549 does not support mode switch".into(), + )); + } } /// Switch from RV mode to DAP mode diff --git a/src/error.rs b/src/error.rs index 5117702..30c80f9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,7 @@ use thiserror::Error; +use crate::RiscvChip; + /// Alias for a `Result` with the error type `wlink::Error`. pub type Result = std::result::Result; @@ -9,14 +11,18 @@ pub enum Error { Custom(String), #[error("USB error: {0}")] Rusb(#[from] rusb::Error), - #[error("WCH-Link not found or not in RV mode, please check your connection or use mode-switch tool to switch to RV mode")] + #[error("WCH-Link not found, please check your connection")] ProbeNotFound, + #[error("WCH-Link is connected, but is not in RV mode")] + ProbeModeNotSupported, #[error("Unknown WCH-Link variant: {0}")] UnknownLinkVariant(u8), #[error("Unknown RISC-V Chip: 0x{0:02x}")] UnknownChip(u8), #[error("Probe is not attached to an MCU, or debug is not enabled. (hint: use wchisp to enable debug)")] NotAttached, + #[error("Chip mismatch: expected {0:?}, got {1:?}")] + ChipMismatch(RiscvChip, RiscvChip), #[error("WCH-Link underlying protocol error: {0:#04x} {1:#04x?}")] Protocol(u8, Vec), #[error("Invalid payload length")] diff --git a/src/lib.rs b/src/lib.rs index 446195f..86604d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -use std::fmt; +use std::{fmt, str::FromStr}; pub mod commands; pub mod device; @@ -39,6 +39,13 @@ impl WchLinkVariant { _ => Err(Error::UnknownLinkVariant(value)), } } + + pub fn can_switch_mode(&self) -> bool { + match self { + WchLinkVariant::Ch549 => false, + _ => true, + } + } } impl fmt::Display for WchLinkVariant { @@ -86,11 +93,14 @@ impl RiscvChip { RiscvChip::CH32V103 | RiscvChip::CH32V003 | RiscvChip::CH32V20X | RiscvChip::CH32V30X ) } - fn can_disable_debug(&self) -> bool { + + /// Very unsafe. This disables the debug interface of the chip. + /// Command sequence is 810e0101 + pub fn can_disable_debug(&self) -> bool { matches!(self, RiscvChip::CH57X | RiscvChip::CH56X | RiscvChip::CH58X) } - fn reset_command(&self) -> crate::commands::Reset { + pub fn reset_command(&self) -> crate::commands::Reset { match self { RiscvChip::CH57X | RiscvChip::CH58X => crate::commands::Reset::Normal2, _ => crate::commands::Reset::Normal, @@ -146,3 +156,21 @@ impl RiscvChip { } } } + +// for clap parser +impl FromStr for RiscvChip { + type Err = Error; + + fn from_str(s: &str) -> std::result::Result { + match &*s.to_ascii_uppercase() { + "CH32V103" => Ok(RiscvChip::CH32V103), + "CH32V20X" => Ok(RiscvChip::CH32V20X), + "CH32V30X" => Ok(RiscvChip::CH32V30X), + "CH32V003" => Ok(RiscvChip::CH32V003), + "CH56X" => Ok(RiscvChip::CH56X), + "CH57X" => Ok(RiscvChip::CH57X), + "CH58X" => Ok(RiscvChip::CH58X), + _ => Err(Error::UnknownChip(0)), + } + } +} diff --git a/src/main.rs b/src/main.rs index 60a3894..87c7963 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use std::{thread::sleep, time::Duration}; use anyhow::Result; -use wlink::{commands, device::WchLink, format::read_firmware_from_file, regs}; +use wlink::{commands, device::WchLink, format::read_firmware_from_file, regs, RiscvChip}; use clap::{Parser, Subcommand}; @@ -20,6 +20,10 @@ struct Cli { #[arg(long, global = true)] detach: bool, + /// Specify the chip type, e.g. CH32V30X + #[arg(long, global = true)] + chip: Option, + #[command(subcommand)] command: Option, } @@ -137,7 +141,7 @@ fn main() -> Result<()> { Some(command) => { let mut probe = WchLink::open_nth(device_index)?; probe.probe_info()?; - probe.attach_chip()?; + probe.attach_chip(cli.chip)?; match command { Dump { address, length } => { log::info!( diff --git a/src/operations.rs b/src/operations.rs index bcb3709..d393d2a 100644 --- a/src/operations.rs +++ b/src/operations.rs @@ -18,23 +18,36 @@ impl WchLink { Ok(info) } /// Attach chip and get chip info - pub fn attach_chip(&mut self) -> Result<()> { + pub fn attach_chip(&mut self, expected_chip: Option) -> Result<()> { if self.chip.is_some() { log::warn!("Chip already attached"); } - let mut chip_info = None; let probe_info = self.send_command(commands::control::GetProbeInfo)?; + + let mut chip_info = None; for _ in 0..3 { // self.send_command(commands::control::DetachChip)?; - // unknown command - // 0x07 seems to be the expected richvchip id - self.send_command(commands::RawCommand::<0x0c>(vec![0x07, 0x01]))?; + self.send_command(commands::SetTwoLineMode { + riscvchip: expected_chip.unwrap_or(RiscvChip::CH32V30X) as u8, + speed: 1, // 1 high, 2, medium, 3 low + })?; if let Ok(resp) = self.send_command(commands::control::AttachChip) { log::info!("Attached chip: {}", resp); chip_info = Some(resp); + + if let Some(expected_chip) = expected_chip { + if resp.chip_family != expected_chip { + log::error!( + "Attached chip type ({:?}) does not match expected chip type ({:?})", + resp.chip_family, + expected_chip + ); + return Err(Error::ChipMismatch(expected_chip, resp.chip_family)); + } + } break; } else { log::debug!("retrying...");