Skip to content
Merged
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
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ SFTool是一个专为SiFli系列SoC(系统芯片)设计的开源工具,用

## 特性

- 支持SF32LB52芯片
- 支持SF32LB52、SF32LB56、SF32LB58芯片
- 支持多种存储类型:NOR闪存、NAND闪存和SD卡
- 可配置的串口参数
- 可靠的闪存写入功能,支持验证和压缩
Expand Down Expand Up @@ -45,19 +45,34 @@ cargo build --release

```bash
sftool [选项] 命令 [命令选项]
sftool [选项] config <FILE>
```

### 全局选项

- `-c, --chip <CHIP>`: 目标芯片类型 (目前支持SF32LB52)
- `-m, --memory <MEMORY>`: 存储类型 [nor, nand, sd] (默认: nor)
- `-m, --memory <MEMORY>`: 存储类型 [nor, nand, sd] (默认: nor,不区分大小写)
- `-p, --port <PORT>`: 串行端口设备路径
- `-b, --baud <BAUD>`: 闪存/读取时使用的串口波特率 (默认: 1000000)
- `--before <OPERATION>`: 连接芯片前的操作 [default_reset, no_reset, no_reset_no_sync] (默认: default_reset)
- `--after <OPERATION>`: 工具完成后的操作 [soft_reset, no_reset] (默认: soft_reset)
- `--connect-attempts <ATTEMPTS>`: 连接尝试次数,负数或0表示无限次 (默认: 7)
- `--connect-attempts <ATTEMPTS>`: 连接尝试次数,负数或0表示无限次 (默认: 3)
- `--compat` : 兼容模式,如果经常出现超时错误或下载后校验失败,则应打开此选项。

### JSON 参数文件(sftool_param.json)

可以用 JSON 描述一次命令并通过 `config` 子命令执行:

```bash
sftool config sftool_param.json

# CLI 参数可以覆盖或补充 JSON 中的字段
sftool -c SF32LB52 -p /dev/ttyUSB0 config sftool_param.json
```

JSON 文件可以不包含所有字段,CLI 参数与默认值会先合并;合并后仍缺少必须参数才会报错。
schema 位于仓库中的 `sftool_param_schema.json`。

### 写入闪存命令

```bash
Expand Down
22 changes: 19 additions & 3 deletions README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ SFTool is an open-source tool specifically designed for SiFli series SoCs (Syste

## Features

- Support for SF32LB52 chip
- Support for SF32LB52, SF32LB56, SF32LB58 chips
- Support for multiple storage types: NOR flash, NAND flash, and SD card
- Configurable serial port parameters
- Reliable flash writing functionality with verification and compression support
Expand Down Expand Up @@ -47,19 +47,35 @@ cargo build --release

```bash
sftool [OPTIONS] COMMAND [COMMAND OPTIONS]
sftool [OPTIONS] config <FILE>
```

### Global Options

- `-c, --chip <CHIP>`: Target chip type (currently supporting SF32LB52)
- `-m, --memory <MEMORY>`: Storage type [nor, nand, sd] (default: nor)
- `-m, --memory <MEMORY>`: Storage type [nor, nand, sd] (default: nor, case-insensitive)
- `-p, --port <PORT>`: Serial port device path
- `-b, --baud <BAUD>`: Baud rate used for flashing/reading (default: 1000000)
- `--before <OPERATION>`: Operation before connecting to the chip [default_reset, no_reset, no_reset_no_sync] (default: default_reset)
- `--after <OPERATION>`: Operation after the tool completes [soft_reset, no_reset] (default: soft_reset)
- `--connect-attempts <ATTEMPTS>`: Number of connection attempts, negative or 0 means infinite (default: 7)
- `--connect-attempts <ATTEMPTS>`: Number of connection attempts, negative or 0 means infinite (default: 3)
- `--compat` : Compatibility mode, should be turned on if timeout errors or verification failures occur frequently after downloading.

### JSON Config (sftool_param.json)

You can describe a command in a JSON file (for automation) and run it with the `config` subcommand:

```bash
sftool config sftool_param.json

# CLI options can override or fill missing fields in the JSON
sftool -c SF32LB52 -p /dev/ttyUSB0 config sftool_param.json
```

The JSON file does not need to include every field; CLI options and defaults are merged first, and
validation fails only if required values are still missing. The schema is in
`sftool_param_schema.json` in the repository.

### Write Flash Command

```bash
Expand Down
20 changes: 18 additions & 2 deletions sftool/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,35 @@ cargo build --release

```bash
sftool [OPTIONS] COMMAND [COMMAND_OPTIONS]
sftool [OPTIONS] config <FILE>
```

### Global Options

- `-c, --chip <CHIP>`: Target chip type (currently supports SF32LB52, SF32LB56, SF32LB58)
- `-m, --memory <MEMORY>`: Memory type [nor, nand, sd] (default: nor)
- `-m, --memory <MEMORY>`: Memory type [nor, nand, sd] (default: nor, case-insensitive)
- `-p, --port <PORT>`: Serial port device path
- `-b, --baud <BAUD>`: Baud rate for flash/read operations (default: 1000000)
- `--before <OPERATION>`: Operation before connecting to the chip [default_reset, no_reset, no_reset_no_sync] (default: default_reset)
- `--after <OPERATION>`: Operation after tool completion [soft_reset, no_reset] (default: soft_reset)
- `--connect-attempts <ATTEMPTS>`: Number of connection attempts, negative or 0 for infinite (default: 7)
- `--connect-attempts <ATTEMPTS>`: Number of connection attempts, negative or 0 for infinite (default: 3)
- `--compat`: Compatibility mode, enable if you frequently encounter timeout errors or checksum failures

### JSON Config (sftool_param.json)

You can describe a command in a JSON file (for automation) and run it with the `config` subcommand:

```bash
sftool config sftool_param.json

# CLI options can override or fill missing fields in the JSON
sftool -c SF32LB52 -p /dev/ttyUSB0 config sftool_param.json
```

The JSON file does not need to include every field; CLI options and defaults are merged first, and
validation fails only if required values are still missing. The schema is in
`sftool_param_schema.json` in the repository.

### Write Flash Command

```bash
Expand Down
22 changes: 2 additions & 20 deletions sftool/sftool_param_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
},
"memory": {
"type": "string",
"enum": [ "nor", "nand", "sd" ],
"pattern": "^(?:[nN][oO][rR]|[nN][aA][nN][dD]|[sS][dD])$",
"default": "nor",
"description": "Memory type"
"description": "Memory type (case-insensitive)"
},
"port": {
"type": "string",
Expand Down Expand Up @@ -78,24 +78,6 @@
{ "required": [ "erase_region" ] },
{ "required": [ "stub" ] }
],
"allOf": [
{
"if": { "required": [ "write_flash" ] },
"then": { "required": [ "chip", "port" ] }
},
{
"if": { "required": [ "read_flash" ] },
"then": { "required": [ "chip", "port" ] }
},
{
"if": { "required": [ "erase_flash" ] },
"then": { "required": [ "chip", "port" ] }
},
{
"if": { "required": [ "erase_region" ] },
"then": { "required": [ "chip", "port" ] }
}
],
"additionalProperties": false,
"definitions": {
"hexString": {
Expand Down
44 changes: 32 additions & 12 deletions sftool/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,12 @@ pub enum Memory {
#[derive(Parser, Debug)]
#[command(author, version, about = "sftool CLI", long_about = None)]
pub struct Cli {
/// JSON configuration file path
#[arg(long = "config", short = 'f')]
pub config: Option<String>,

/// Target chip type
#[arg(short = 'c', long = "chip", value_enum)]
pub chip: Option<ChipType>,

/// Memory type (default: nor)
#[arg(short = 'm', long = "memory", value_enum)]
#[arg(short = 'm', long = "memory", value_enum, ignore_case = true)]
pub memory: Option<Memory>,

/// Serial port device
Expand Down Expand Up @@ -85,6 +81,10 @@ pub struct Cli {

#[derive(Subcommand, Debug, Clone)]
pub enum Commands {
/// Execute a command from a JSON configuration file
#[command(name = "config")]
Config(ConfigCommand),

/// Write a binary blob to flash
#[command(name = "write_flash")]
WriteFlash(WriteFlash),
Expand All @@ -106,6 +106,14 @@ pub enum Commands {
Stub(StubCommand),
}

#[derive(Parser, Debug, Clone)]
#[command(about = "Execute a command from a JSON configuration file")]
pub struct ConfigCommand {
/// JSON configuration file path
#[arg(required = true, value_name = "FILE")]
pub path: String,
}

#[derive(Parser, Debug, Clone)]
#[command(about = "Write a binary blob to flash")]
pub struct WriteFlash {
Expand Down Expand Up @@ -213,6 +221,17 @@ fn memory_to_string(memory: &Memory) -> String {
}
}

fn normalize_memory(memory: &str) -> Result<String> {
let normalized = memory.to_ascii_lowercase();
match normalized.as_str() {
"nor" | "nand" | "sd" => Ok(normalized),
_ => bail!(
"Invalid memory type '{}'. Must be one of: nor, nand, sd",
memory
),
}
}

/// Merge CLI arguments with configuration file, CLI args take precedence
pub fn merge_config(args: &Cli, config: Option<SfToolConfig>) -> Result<MergedConfig> {
// 使用配置文件或默认配置
Expand All @@ -225,11 +244,12 @@ pub fn merge_config(args: &Cli, config: Option<SfToolConfig>) -> Result<MergedCo
.map_err(|e| anyhow!("Invalid chip type in config: {}", e))?,
};

let memory = args
let memory_raw = args
.memory
.as_ref()
.map(memory_to_string)
.unwrap_or_else(|| base_config.memory.clone());
let memory = normalize_memory(&memory_raw)?;

let port = args
.port
Expand Down Expand Up @@ -285,11 +305,11 @@ pub enum CommandSource {
}

pub fn get_command_source(args: &Cli, config: Option<SfToolConfig>) -> Result<CommandSource> {
match (&args.command, &config) {
(Some(cmd), _) => Ok(CommandSource::Cli(cmd.clone())),
(None, Some(cfg)) => Ok(CommandSource::Config(cfg.clone())),
(None, None) => {
bail!("No command specified. Use a subcommand or provide a config file with a command.")
}
match &args.command {
Some(Commands::Config(_)) => config
.map(CommandSource::Config)
.ok_or_else(|| anyhow!("Config command requires a configuration file")),
Some(cmd) => Ok(CommandSource::Cli(cmd.clone())),
None => bail!("No command specified. Use a subcommand or `config <file>`."),
}
}
15 changes: 0 additions & 15 deletions sftool/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,21 +288,6 @@ impl SfToolConfig {
return Ok(());
}

// 验证芯片类型
self.parse_chip_type()?;

// 验证操作类型
self.parse_before()?;
self.parse_after()?;

// 验证内存类型
if !["nor", "nand", "sd"].contains(&self.memory.as_str()) {
return Err(format!(
"Invalid memory type '{}'. Must be one of: nor, nand, sd",
self.memory
));
}

// 验证文件路径格式中的十六进制字符串
if let Some(ref write_flash) = self.write_flash {
for file in &write_flash.files {
Expand Down
31 changes: 16 additions & 15 deletions sftool/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,21 @@ fn main() -> Result<()> {
tracing_subscriber::fmt().with_env_filter(env_filter).init();
let args = Cli::parse();

// Load config file if specified
let config = if let Some(ref config_path) = args.config {
let cfg = SfToolConfig::from_file(config_path)
.map_err(|e| anyhow!("Failed to load config file '{}': {}", config_path, e))?;
cfg.validate().map_err(|e| {
anyhow!(
"Configuration validation failed for '{}': {}",
config_path,
e
)
})?;
Some(cfg)
} else {
None
// Load config file when using the config subcommand
let config = match &args.command {
Some(Commands::Config(params)) => {
let cfg = SfToolConfig::from_file(&params.path)
.map_err(|e| anyhow!("Failed to load config file '{}': {}", params.path, e))?;
cfg.validate().map_err(|e| {
anyhow!(
"Configuration validation failed for '{}': {}",
params.path,
e
)
})?;
Some(cfg)
}
_ => None,
};

// Determine which command to execute
Expand Down Expand Up @@ -135,7 +136,7 @@ fn main() -> Result<()> {

match command_source {
CommandSource::Cli(command) => match command {
Commands::Stub(_) => {
Commands::Stub(_) | Commands::Config(_) => {
// handled earlier
}
Commands::WriteFlash(params) => {
Expand Down
Loading