Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
114 changes: 114 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
name: CI

on:
push:
branches: [main]
pull_request:

env:
# Bump in lockstep with Cargo.toml when those interfaces change.
CH32_DATA_REPO: ch32-rs/ch32-data
CH32_DATA_REF: 62145f5eef6d68962b6b428ef3a901e5383480e1

jobs:
fmt-clippy:
name: fmt + clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
path: flash-algorithms
- uses: actions/checkout@v6
with:
repository: ${{ env.CH32_DATA_REPO }}
ref: ${{ env.CH32_DATA_REF }}
path: ch32-data
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
with:
workspaces: |
flash-algorithms
ch32-data
shared-key: fmt-clippy
- name: generate ch32-metapac
working-directory: ch32-data
run: ./d gen
- name: cargo fmt --check
working-directory: flash-algorithms
# Explicit `-p` list (not `--all`) so fmt skips path deps like `ch32-metapac`.
run: |
cargo fmt --check \
-p xtask \
-p flash-algo-common \
-p flash-algo-v0 \
-p flash-algo-v00x \
-p flash-algo-v1 \
-p flash-algo-v3 \
-p flash-algo-l1 \
-p flash-algo-x0 \
-p flash-algo-f1
- name: cargo clippy -p xtask
working-directory: flash-algorithms
run: cargo clippy -p xtask -- -D warnings

algos:
name: algos/${{ matrix.crate }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
crate: [v0, v00x, v1, v3, l1, x0, f1]
steps:
- uses: actions/checkout@v6
with:
path: flash-algorithms
- uses: actions/checkout@v6
with:
repository: ${{ env.CH32_DATA_REPO }}
ref: ${{ env.CH32_DATA_REF }}
path: ch32-data
- uses: dtolnay/rust-toolchain@nightly
with:
components: rust-src
- uses: Swatinem/rust-cache@v2
with:
workspaces: |
flash-algorithms
ch32-data
key: ${{ matrix.crate }}
- name: generate ch32-metapac
working-directory: ch32-data
run: ./d gen
- name: cargo build --release
working-directory: flash-algorithms/algos/${{ matrix.crate }}
run: cargo build --release

render:
name: render YAMLs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
path: flash-algorithms
- uses: actions/checkout@v6
with:
repository: ${{ env.CH32_DATA_REPO }}
ref: ${{ env.CH32_DATA_REF }}
path: ch32-data
- uses: dtolnay/rust-toolchain@nightly
with:
components: rust-src
- uses: Swatinem/rust-cache@v2
with:
workspaces: |
flash-algorithms
ch32-data
shared-key: render
- name: generate ch32-metapac
working-directory: ch32-data
run: ./d gen
- name: cargo run -p xtask
working-directory: flash-algorithms
run: cargo run -p xtask
28 changes: 27 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,28 @@
# Generated by Cargo
# will have compiled files and executables
debug
target

# These are backup files generated by rustfmt
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

# Generated by cargo mutants
# Contains mutation testing data
**/mutants.out*/

# rustc will dump stack traces when hitting an internal compiler error to PWD
rustc-ice-*.txt

# RustRover
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# Project-specific
Cargo.lock
target/
generated/
35 changes: 35 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[workspace]
resolver = "2"
members = ["algos/*", "xtask"]
default-members = ["xtask"]

[workspace.package]
edition = "2024"
license = "MIT OR Apache-2.0"
repository = "https://github.com/ch32-rs/flash-algorithms"
authors = [
"Andelf <andelf@gmail.com>",
"Aaron Qian <aq1018@gmail.com>",
]

[workspace.dependencies]
# Local path until ch32-data#36 merges.
ch32-metapac = { path = "../ch32-data/build/ch32-metapac", default-features = false, features = ["metadata"] }
flash-algorithm = { version = "0.7", default-features = false, features = ["erase-chip", "panic-handler"] }
panic-halt = "1"

[profile.release]
codegen-units = 1
debug = 2
debug-assertions = false
incremental = false
lto = "fat"
opt-level = "s"
overflow-checks = false

[profile.release.build-override]
codegen-units = 8
debug = false
debug-assertions = false
opt-level = 0
overflow-checks = false
47 changes: 46 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,46 @@
# flash-algorithms
# ch32-rs flash-algorithms

[probe-rs](https://probe.rs) flash algorithms and target YAMLs for WCH's CH32
MCU families, built on top of [ch32-metapac](https://github.com/ch32-rs/ch32-metapac).

## Coverage

One algo crate per (flash IP version, CPU arch) pair. `f1` is a Cortex-M3
build of the same `flash_v1` logic used by `v1` on Qingke RISC-V — Cargo can't
unify two ch32-metapac chip features, so the lib is duplicated.

| Crate | Flash peripheral | Target triple | Chip families |
| ------------ | ---------------- | ---------------------------------- | -------------------------- |
| `algos/v0` | `flash_v0` | `riscv32ec-unknown-none-elf` | CH32V003, CH641 |
| `algos/v00x` | `flash_v00x` | `riscv32ec_zmmul-unknown-none-elf` | CH32V002/4/5/6/7, CH32M007 |
| `algos/v1` | `flash_v1` | `riscv32imac-unknown-none-elf` | CH32V103 |
| `algos/v3` | `flash_v3` | `riscv32imac-unknown-none-elf` | CH32V2xx, CH32V3xx |
| `algos/x0` | `flash_x0` | `riscv32imac-unknown-none-elf` | CH32X035/X033, CH643 |
| `algos/l1` | `flash_l1` | `riscv32imac-unknown-none-elf` | CH32L103 |
| `algos/f1` | `flash_v1` | `thumbv7m-none-eabi` | CH32F103 (Cortex-M3) |

Each crate builds three binaries — `usr`, `sys`, `ob` — one per writable
region. VND (vendor / ESIG) is read-only and not programmed.

## Building

Each algo crate's `.cargo/config.toml` pins its target triple, so cargo only
picks it up when run from inside the crate:

```
cd algos/v0
cargo build --release
```

## Generating target YAMLs

```
cargo run -p xtask
```

Walks `../ch32-data/build/data/chips/`, builds every algo crate, emits one
probe-rs YAML per (chip × memory_option) into `generated/` (gitignored).

## License

Dual-licensed under MIT or Apache-2.0 at your option.
14 changes: 14 additions & 0 deletions algos/common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "flash-algo-common"
version = "0.1.0"
edition.workspace = true
license.workspace = true
repository.workspace = true
authors.workspace = true
publish = false

[lib]
name = "flash_algo_common"

[dependencies]
ch32-metapac = { workspace = true }
69 changes: 69 additions & 0 deletions algos/common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#![no_std]

pub use ch32_metapac::FLASH;
pub use ch32_metapac::metadata::{METADATA, MemoryRegion, Mode};

pub const KEY1: u32 = 0x4567_0123;
pub const KEY2: u32 = 0xCDEF_89AB;

pub const PROGRAM_TIMEOUT_MS: u32 = 1000;
pub const ERASE_TIMEOUT_MS: u32 = 2000;

pub const ERR_NOT_SUPPORTED: core::num::NonZeroU32 = core::num::NonZeroU32::MIN;

pub const fn str_eq(a: &str, b: &str) -> bool {
let (a, b) = (a.as_bytes(), b.as_bytes());
if a.len() != b.len() {
return false;
}
let mut i = 0;
while i < a.len() {
if a[i] != b[i] {
return false;
}
i += 1;
}
true
}

pub const fn region(name: &str) -> &'static MemoryRegion {
let mem = METADATA.memory;
let mut i = 0;
while i < mem.len() {
if str_eq(mem[i].name, name) {
return &mem[i];
}
i += 1;
}
panic!("region not found in METADATA.memory")
}

pub const fn fast(r: &MemoryRegion) -> (u32, u32) {
let mut i = 0;
while i < r.modes.len() {
if let Mode::Fast {
page_size,
load_size,
} = r.modes[i]
{
return (page_size, load_size);
}
i += 1;
}
panic!("region has no Fast programming mode")
}

pub const fn standard(r: &MemoryRegion) -> (u32, u32) {
let mut i = 0;
while i < r.modes.len() {
if let Mode::Standard {
erase_size,
write_size,
} = r.modes[i]
{
return (erase_size, write_size);
}
i += 1;
}
panic!("region has no Standard programming mode")
}
11 changes: 11 additions & 0 deletions algos/f1/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[build]
target = "thumbv7m-none-eabi"

[unstable]
build-std = ["core", "compiler_builtins"]

[target.'cfg(all(target_arch = "arm", target_os = "none"))']
rustflags = [
"-C", "link-arg=-Tmemory.x",
"-C", "link-arg=--defsym=ALGO_PLACEMENT_START_ADDRESS=0x20000020",
]
37 changes: 37 additions & 0 deletions algos/f1/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[package]
name = "flash-algo-f1"
version = "0.1.0"
edition.workspace = true
license.workspace = true
repository.workspace = true
authors.workspace = true
publish = false

[lib]
name = "flash_algo_f1"

[[bin]]
name = "usr"
test = false
doctest = false
bench = false

[[bin]]
name = "sys"
test = false
doctest = false
bench = false

[[bin]]
name = "ob"
test = false
doctest = false
bench = false

# F103 reuses the v1 flash IP but runs on a Cortex-M3 core; metapac can't
# unify two chip features, so this crate duplicates v1's lib instead of
# depending on `flash-algo-v1`.
[dependencies]
ch32-metapac = { workspace = true, features = ["pac", "ch32f103c8t6"] }
flash-algorithm = { workspace = true }
flash-algo-common = { path = "../common" }
Loading