Skip to content
Open
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
2 changes: 1 addition & 1 deletion xtask/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ publish = false
anyhow = "1"
goblin = "0.10"
indexmap = "2"
probe-rs-target = { git = "https://github.com/ch32-rs/probe-rs", rev = "8bfc5b87e356934ef0af6ad3ee8f3f6162089327" }
probe-rs-target = { git = "https://github.com/ch32-rs/probe-rs", rev = "b22cdd4adf8b75f97bd30ad2fb9e42fd726eaf7b" }
scroll = "0.13"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
Expand Down
27 changes: 27 additions & 0 deletions xtask/src/chip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ pub struct Chip {
#[derive(Debug, Clone, Deserialize)]
pub struct Package {
pub name: String,
/// chip_id reported by WCH-Link `AttachChip`; drives `chip_detection`.
/// `0` means unknown — emitted by ch32-data when no ID is on record.
#[serde(default)]
pub device_id: u32,
}

#[derive(Debug, Clone, Deserialize)]
Expand All @@ -29,6 +33,16 @@ pub struct MemoryRegion {
pub modes: Vec<Mode>,
#[serde(default)]
pub access: Option<Access>,
#[serde(default)]
pub structs: Vec<MemoryStruct>,
}

#[derive(Debug, Clone, Deserialize)]
pub struct MemoryStruct {
#[serde(default)]
pub kind: Option<String>,
#[serde(default)]
pub version: Option<String>,
}

#[derive(Debug, Clone, Deserialize)]
Expand Down Expand Up @@ -124,6 +138,19 @@ impl Chip {
self.cores.first().and_then(|c| c.arch.as_deref())
}

/// `_ram_code` suffix indicates `OB.USER` carries an SRAM/CODE split
/// field (bits 5-7).
pub fn ob_version(&self) -> Option<&str> {
for region in &self.memory {
for s in &region.structs {
if s.kind.as_deref() == Some("ob") {
return s.version.as_deref();
}
}
}
None
}

pub fn variants(&self) -> Vec<Variant> {
let Some(cfg) = &self.memory_ram_code_config else {
return vec![Variant {
Expand Down
97 changes: 96 additions & 1 deletion xtask/src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use probe_rs_target::{
ArmCoreAccessOptions, Chip as PrChip, ChipFamily, Core as PrCore, CoreAccessOptions, CoreType,
MemoryAccess, MemoryRegion as PrMemoryRegion, NvmRegion, RamRegion, RiscvCoreAccessOptions,
SectorDescription, TargetDescriptionSource,
chip_detection::{ChipDetectionMethod, ObCodeRamSplit, WchLinkDetection},
};
use std::collections::{BTreeMap, HashMap};
use std::ops::Range;
Expand Down Expand Up @@ -139,6 +140,7 @@ fn build_family(
let mut variants: Vec<PrChip> = Vec::new();
let mut variant_algo_uses: BTreeMap<String, Vec<Range<u64>>> = BTreeMap::new();
let mut algo_kind: BTreeMap<String, String> = BTreeMap::new();
let mut detection_entries: BTreeMap<u32, DetectionGroup> = BTreeMap::new();

for chip in chips {
let default_opt = chip
Expand All @@ -147,6 +149,8 @@ fn build_family(
.map(|c| c.default.clone());

for package in &chip.packages {
let split = build_ob_code_ram_split(chip, &package.name);

for variant in chip.variants() {
let is_default_variant = match (&variant.option, &default_opt) {
(Some(opt), Some(def)) => opt == def,
Expand All @@ -169,6 +173,22 @@ fn build_family(
&mut algo_kind,
);

// Non-default code/RAM splits share the same chip_id; the OB
// lookup picks the right variant post-attach via OB.USER bits
// 5-7. device_id=0 is ch32-data's "unknown" sentinel — drop
// those so multiple unknown-ID packages don't collide on key
// 0x0 in the variants IndexMap (last-write-wins would
// otherwise hide all but one).
if is_default_variant && package.device_id != 0 {
let id = package.device_id;
let mask = mask_for(id);
let group = detection_entries.entry(mask).or_default();
group.variants.insert(id & mask, target_name.clone());
if let Some(s) = &split {
group.ob_code_ram_splits.insert(id & mask, s.clone());
}
}

variants.push(PrChip {
name: target_name,
part: None,
Expand All @@ -186,7 +206,16 @@ fn build_family(
}
}

let chip_detection = Vec::new();
let chip_detection: Vec<ChipDetectionMethod> = detection_entries
.into_iter()
.map(|(mask, group)| {
ChipDetectionMethod::WchLink(WchLinkDetection {
mask,
variants: group.variants,
ob_code_ram_splits: group.ob_code_ram_splits,
})
})
.collect();

let mut flash_algorithms = Vec::new();
for (algo_name, ranges) in variant_algo_uses {
Expand Down Expand Up @@ -379,6 +408,72 @@ fn find_template_region(chips: &[&Chip], kind: &str) -> Result<MemoryRegion> {
Err(anyhow!("no region of kind {} in family", kind))
}

/// CH643 (0x643xxxxx) needs exact match per wlink's `chips.rs`; everyone
/// else clears the package nibble.
fn mask_for(chip_id: u32) -> u32 {
match chip_id & 0xFFF0_0000 {
0x6430_0000 => 0xFFFF_FFFF,
_ => 0xFFFF_FF0F,
}
}

#[derive(Default)]
struct DetectionGroup {
variants: indexmap::IndexMap<u32, String>,
ob_code_ram_splits: indexmap::IndexMap<u32, ObCodeRamSplit>,
}

const OB_USER_ADDRESS: u64 = 0x1FFFF802;
const RAM_CODE_MASK: u8 = 0xE0;

fn build_ob_code_ram_split(chip: &Chip, package_name: &str) -> Option<ObCodeRamSplit> {
let cfg = chip.memory_ram_code_config.as_ref()?;
let ob_version = chip.ob_version()?;
let encoding = ram_code_encoding(ob_version)?;

let default_opt = &cfg.default;
let mut variants: indexmap::IndexMap<u8, String> = indexmap::IndexMap::new();
for raw in 0u8..8 {
let option_name = encoding(raw)?;
let suffix = if option_name == default_opt {
String::new()
} else {
format!("_{}", option_name)
};
let variant_name = format!("{}{}", package_name, suffix);
variants.insert(raw << 5, variant_name);
}

Some(ObCodeRamSplit {
address: OB_USER_ADDRESS,
mask: RAM_CODE_MASK,
variants,
})
}

/// Encoding follows `ch32-data/data/nv/ob_v{2,3}_ram_code.yaml`.
fn ram_code_encoding(ob_version: &str) -> Option<fn(u8) -> Option<&'static str>> {
match ob_version {
"v2_ram_code" => Some(|raw| match raw {
0b000 | 0b001 => Some("c128_r64"),
0b010 | 0b011 => Some("c144_r48"),
0b100 | 0b101 => Some("c160_r32"),
// 0b11x reserved on v2; fall back to factory default.
0b110 | 0b111 => Some("c128_r64"),
_ => None,
}),
"v3_ram_code" => Some(|raw| match raw {
0b000 | 0b001 => Some("c192_r128"),
0b010 | 0b011 => Some("c224_r96"),
0b100 | 0b101 => Some("c256_r64"),
0b110 => Some("c128_r192"),
0b111 => Some("c288_r32"),
_ => None,
}),
_ => None,
}
}

/// Collapse contiguous `USR_1`+`USR_2` runs into a single `USR` — the V2/V3
/// split is an OB-configurable line, not a HW boundary.
fn merge_regions(memory: &[MemoryRegion]) -> Vec<MemoryRegion> {
Expand Down