use std::path::PathBuf;
use clap::{ArgAction, Args, Parser, Subcommand, ValueEnum};
use ch32rv_contract::policy::{
ConfirmRunMode, EraseMode, ImageFormat, MonitorSource, RecoverMethod, Region, ResetPolicy,
VerifyMode,
};
#[derive(Parser)]
#[command(
name = "ch32rv",
version,
about = "Flashing and debugging tool for WCH CH32 RISC-V microcontrollers",
propagate_version = true
)]
pub struct Cli {
#[command(subcommand)]
pub command: Command,
#[arg(long, global = true, env = "CH32RV_PROBE")]
pub probe: Option<String>,
#[arg(long, global = true, env = "CH32RV_CHIP")]
pub chip: Option<String>,
#[arg(long, global = true, default_value_t = 0)]
pub core: u32,
#[arg(long, global = true, default_value = "high")]
pub speed: String,
#[arg(long, global = true)]
pub connect_under_reset: bool,
#[arg(long, global = true)]
pub json: bool,
#[arg(long, global = true, value_enum)]
pub progress: Option<ProgressMode>,
#[arg(long, global = true, env = "CH32RV_NON_INTERACTIVE")]
pub non_interactive: bool,
#[arg(long, global = true)]
pub yes: bool,
#[arg(long, global = true, default_value_t = 10)]
pub lock_timeout: u64,
#[arg(long, global = true)]
pub timeout: Option<u64>,
#[arg(long, global = true)]
pub duration: Option<u64>,
#[arg(long, global = true, env = "CH32RV_DB")]
pub db: Option<PathBuf>,
#[arg(long, global = true)]
pub log_file: Option<PathBuf>,
#[arg(long, global = true)]
pub capture: Option<PathBuf>,
#[arg(long, global = true)]
pub dry_run: bool,
#[arg(short = 'v', long, global = true, action = ArgAction::Count)]
pub verbose: u8,
#[arg(short = 'q', long, global = true, action = ArgAction::Count)]
pub quiet: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum ProgressMode {
Bar,
Ndjson,
None,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum SwitchState {
On,
Off,
}
#[derive(Subcommand)]
pub enum Command {
Flash(FlashArgs),
Verify(VerifyArgs),
Read(ReadArgs),
Write(WriteArgs),
Erase(EraseArgs),
Reset(ResetArgs),
Run(RunArgs),
Recover(RecoverArgs),
#[command(subcommand)]
Probe(ProbeCmd),
#[command(subcommand)]
Target(TargetCmd),
#[command(subcommand)]
Dbg(DbgCmd),
Monitor(MonitorArgs),
Gdb(GdbArgs),
Dap(DapArgs),
Isp(IspArgs),
#[command(subcommand)]
Boot(BootCmd),
#[command(subcommand)]
Db(DbCmd),
Capabilities,
Doctor(DoctorArgs),
Version,
Complete(CompleteArgs),
#[command(subcommand)]
Arduino(ArduinoCmd),
}
#[derive(Args)]
pub struct FlashArgs {
pub file: PathBuf,
#[arg(long, value_enum, default_value = "auto")]
pub format: ImageFormat,
#[arg(long)]
pub at: Option<String>,
#[arg(long, value_enum, default_value = "code")]
pub region: Region,
#[arg(long, value_enum, default_value = "auto")]
pub erase: EraseMode,
#[arg(long, value_enum, default_value = "readback")]
pub verify: VerifyMode,
#[arg(long)]
pub preverify: bool,
#[arg(long, value_enum, default_value = "run")]
pub reset: ResetPolicy,
#[arg(long, value_enum, num_args = 0..=1, default_missing_value = "pc")]
pub confirm_run: Option<ConfirmRunMode>,
#[arg(long, value_enum)]
pub sdi: Option<SwitchState>,
#[arg(long, value_enum)]
pub monitor: Option<MonitorSource>,
#[arg(long)]
pub restore_unwritten: bool,
#[arg(long)]
pub repeat: bool,
}
#[derive(Args)]
pub struct VerifyArgs {
pub file: PathBuf,
#[arg(long, value_enum, default_value = "auto")]
pub format: ImageFormat,
#[arg(long)]
pub at: Option<String>,
#[arg(long, value_enum, default_value = "code")]
pub region: Region,
}
#[derive(Args)]
#[command(group(clap::ArgGroup::new("src").required(true).args(["range", "region"])))]
pub struct ReadArgs {
#[arg(long)]
pub range: Option<String>,
#[arg(long)]
pub region: Option<String>,
#[arg(short, long)]
pub out: Option<PathBuf>,
#[arg(long, value_enum, default_value = "bin")]
pub format: ReadFormat,
#[arg(long)]
pub blank_check: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum ReadFormat {
Bin,
HexDump,
Ihex,
}
#[derive(Args)]
pub struct WriteArgs {
pub source: String,
#[arg(long)]
pub at: String,
#[arg(long, value_enum, default_value = "none")]
pub erase: WriteErase,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum WriteErase {
Auto,
None,
}
#[derive(Args)]
#[command(group(clap::ArgGroup::new("scope").required(true).args(["all", "region", "range"])))]
pub struct EraseArgs {
#[arg(long)]
pub all: bool,
#[arg(long)]
pub region: Option<String>,
#[arg(long)]
pub range: Option<String>,
}
#[derive(Args)]
pub struct ResetArgs {
#[arg(long)]
pub halt: bool,
#[arg(long)]
pub dm: bool,
#[arg(long, value_enum, num_args = 0..=1, default_missing_value = "pc")]
pub confirm_run: Option<ConfirmRunMode>,
}
#[derive(Args)]
pub struct RunArgs {
pub elf: PathBuf,
#[arg(long)]
pub no_flash: bool,
#[arg(long, value_enum)]
pub source: Option<MonitorSource>,
#[arg(long, value_enum)]
pub exit_on: Option<ExitOn>,
}
#[derive(Clone, Copy, Debug, ValueEnum)]
pub enum ExitOn {
Semihosting,
Timeout,
}
#[derive(Args)]
pub struct RecoverArgs {
#[arg(long, value_enum)]
pub method: RecoverMethod,
}
#[derive(Subcommand)]
pub enum ProbeCmd {
List {
#[arg(long)]
watch: bool,
},
Info,
#[command(subcommand)]
Power(PowerCmd),
#[command(subcommand)]
Mode(ModeCmd),
#[command(subcommand)]
Firmware(FirmwareCmd),
#[command(hide = true)]
Vendor {
#[arg(required = true)]
hex: Vec<String>,
},
}
#[derive(Subcommand)]
pub enum PowerCmd {
#[command(name = "3v3")]
V3v3 {
#[arg(value_enum)]
state: SwitchState,
},
#[command(name = "5v")]
V5 {
#[arg(value_enum)]
state: SwitchState,
},
Cycle {
#[arg(long, default_value_t = 300)]
off_ms: u64,
},
}
#[derive(Subcommand)]
pub enum ModeCmd {
Get,
Set {
#[arg(value_enum)]
mode: ProbeModeSet,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum ProbeModeSet {
Riscv,
Dap,
}
#[derive(Subcommand)]
pub enum FirmwareCmd {
Info,
Check {
#[arg(long)]
min: Option<String>,
},
Update {
#[arg(long)]
image: PathBuf,
},
}
#[derive(Subcommand)]
pub enum TargetCmd {
Info,
#[command(subcommand, name = "option")]
Opt(OptionCmd),
Protect {
#[arg(value_enum)]
state: SwitchState,
},
}
#[derive(Subcommand)]
pub enum OptionCmd {
Get,
Set {
#[arg(required = true)]
kv: Vec<String>,
},
Reset,
WriteRaw {
hex: String,
},
}
#[derive(Subcommand)]
pub enum DbgCmd {
Halt {
#[arg(long)]
reset: bool,
},
Resume,
Step {
n: Option<u32>,
},
Regs,
#[command(subcommand)]
Reg(RegCmd),
#[command(subcommand)]
Dmi(DmiCmd),
}
#[derive(Subcommand)]
pub enum RegCmd {
Read {
name: String,
},
Write {
name: String,
value: String,
},
}
#[derive(Subcommand)]
pub enum DmiCmd {
Read { addr: String },
Write { addr: String, value: String },
}
#[derive(Args)]
pub struct MonitorArgs {
#[command(subcommand)]
pub cmd: Option<MonitorCmd>,
#[arg(long, value_enum, default_value = "uart")]
pub source: MonitorSource,
#[arg(long)]
pub port: Option<String>,
#[arg(long, default_value_t = 115200)]
pub baud: u32,
#[arg(long)]
pub timestamps: bool,
#[arg(long)]
pub log: Option<PathBuf>,
#[arg(long)]
pub raw: bool,
#[arg(long)]
pub no_reconnect: bool,
}
#[derive(Subcommand)]
pub enum MonitorCmd {
List,
Sdi {
#[arg(value_enum)]
state: SwitchState,
},
}
#[derive(Args)]
pub struct GdbArgs {
#[arg(long, default_value = "127.0.0.1:3333")]
pub listen: String,
#[arg(long)]
pub reset_halt: bool,
#[arg(long)]
pub no_flash: bool,
}
#[derive(Args)]
pub struct DapArgs {
#[arg(long)]
pub port: Option<u16>,
#[arg(long)]
pub stdio: bool,
}
#[derive(Args)]
pub struct IspArgs {
#[command(subcommand)]
pub cmd: IspCmd,
#[arg(long, global = true, value_enum, default_value = "usb")]
pub transport: IspTransport,
#[arg(long, global = true)]
pub port: Option<String>,
#[arg(long, global = true)]
pub baud: Option<u32>,
#[arg(long, global = true)]
pub device: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum IspTransport {
Usb,
Uart,
}
#[derive(Subcommand)]
pub enum IspCmd {
List,
Info,
Enter {
#[arg(long)]
via: Option<String>,
},
Flash {
file: PathBuf,
#[arg(long, value_enum, default_value = "auto")]
erase: IspErase,
#[arg(long, value_enum, default_value = "on")]
verify: IspVerify,
#[arg(long, value_enum, default_value = "run")]
reset: IspReset,
},
Verify {
file: PathBuf,
},
Erase,
#[command(subcommand)]
Eeprom(EepromCmd),
#[command(subcommand)]
Config(IspConfigCmd),
Reset,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum IspErase {
Auto,
None,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum IspVerify {
On,
None,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum IspReset {
Run,
None,
}
#[derive(Subcommand)]
pub enum EepromCmd {
Read { out: Option<PathBuf> },
Write { file: PathBuf },
Erase,
}
#[derive(Subcommand)]
pub enum IspConfigCmd {
Get,
Set {
#[arg(required = true)]
kv: Vec<String>,
},
Reset,
}
#[derive(Subcommand)]
pub enum BootCmd {
Enter {
#[arg(long)]
method: Option<String>,
#[arg(long)]
port: Option<String>,
},
#[command(subcommand)]
Dfu(DfuCmd),
#[command(subcommand)]
Uf2(Uf2Cmd),
#[command(subcommand)]
Uart(UartBootCmd),
#[command(subcommand)]
Hid(HidBootCmd),
}
#[derive(Subcommand)]
pub enum DfuCmd {
Flash {
file: PathBuf,
#[arg(long)]
alt: Option<u8>,
#[arg(long)]
address: Option<String>,
},
Info,
}
#[derive(Subcommand)]
pub enum Uf2Cmd {
Flash {
file: PathBuf,
#[arg(long)]
volume: Option<String>,
},
}
#[derive(Subcommand)]
pub enum UartBootCmd {
Flash {
file: PathBuf,
#[arg(long)]
node: Option<String>,
},
Info {
#[arg(long)]
node: Option<String>,
},
}
#[derive(Subcommand)]
pub enum HidBootCmd {
Flash { file: PathBuf },
}
#[derive(Subcommand)]
pub enum DbCmd {
List {
#[arg(long)]
family: Option<String>,
#[arg(long)]
verified_only: bool,
},
Info {
sku: String,
},
}
#[derive(Args)]
pub struct DoctorArgs {
#[arg(long)]
pub emit_udev: bool,
}
#[derive(Args)]
pub struct CompleteArgs {
#[arg(value_enum)]
pub shell: Shell,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum Shell {
Bash,
Zsh,
Fish,
Powershell,
}
#[derive(Subcommand)]
pub enum ArduinoCmd {
Discovery,
Monitor,
}