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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

### 验证体系

- [x] 宿主回归:`cargo test --lib` 默认 features 实测 **82 passed**(2026-09-19 实测,旧记 80 已修正);门禁全特征组合(见 `ci/gate.sh`,含 fs/net/usb/ble)实测 **159 passed**(2026-09-03,rustc 1.97;旧计数 148 已按实测修正,历史见 issue #11;计数随新增测试浮动,以 `ci/gate.sh` 实测为准)
- [x] 宿主回归:`cargo test --lib` 默认 features 实测 **89 passed**(2026-09-19 实测;82 + chip::delay 的 5 项纯算术 + chip::wch_pfic 的 2 项 PFIC 偏移/使能掩码守卫;此前的 88 里有一条用例因重复 `#[test]` 被注册两次,已去重);门禁全特征组合(见 `ci/gate.sh`,含 fs/net/usb/ble)实测 **166 passed**(2026-09-19 实测,rustc nightly-2025-08-08;旧计数 148→159 均按实测修正,历史见 issue #11;计数随新增测试浮动,以 `ci/gate.sh` 实测为准)
- [x] QEMU 执行级:`ci/gate.sh` 在 virt 机真跑内核——`qemu_pingpong` 200 轮乒乓 + `qemu_kernel_tests` 24 项全内核机制自测(抢占/时间片/阻塞类 IPC/定时器/时基/堆/总线/任务回收/可重入锁/优先级继承/完整 PI 多锁/PCP 天花板阻塞/PI 交叉持锁死锁确认/TLSF 碎片共限/TLSF 分配确定性/tickless 错峰唤醒/远期期限单次到点/UART RX 外部中断冻眠唤醒/早醒弹墙钟拍账/噪声风暴停留 idle 不漂移),全绿自退出;另有 tlsf 全局后端门禁(24/24 不变 = 分配器换引擎对内核透明)与 `qemu_smp` 9 项多核调度门禁
- [x] 产物级:`ci/gate.sh` 另校验 `ch583`(QingKe V4A,无 QEMU 机器可跑)的 ROM 启动头——flash `0x00..0x17` 的入口跳转 + 向量表第 5 字 boot option `0xF3F9BDA9` @`0x14`(官方 `startup_CH583.S`/`Link.ld` 的位置);链接期 `ASSERT` 钉地址、`ci/check_wch_boot.py` 钉产物字节,缺了它板上停在 ISP 而构建/门禁全绿
- [x] 无 A 扩展目标:`riscv32imc-unknown-none-elf`(CH572/青稞无 A 档位、esp32c3 真身)也能编译+链接同一示例——内核的原子读改写本就包在 `atomic-polyfill` 里(arc/semaphore/notify),缺的 `critical-section` RISC-V 实现已收归 `src/arch/riscv/critical.rs`(`mstatus.MIE` 存取恢复,单核语义);实测 **imc 产物 0 条 AMO/LR/SC**,有 A 的 imac 产物 12 条作阳性对照(即无 A 机器上不会踩非法指令),门禁含该构建步骤
Expand Down
10 changes: 6 additions & 4 deletions examples/multitask_ch572.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ extern crate alloc;
// (intsyscr 0x804 写 0 + 私有 CSR `0xbc0=0x25` 与 **`0xbc1=1`**);
// ②mtvec Direct 分发与 mcause=12(SysTick);③SysTick `CTLR=0x0F`
// (STRE|STCLK|STIE|STE,官方 `SysTick_Config` 同值;**CH572 的 CTLR 无 INIT 位**)
// + `SR.CNTIF` 写 0 清零;④运行期用 **`SR.SWIE`(bit31)** 请求切换
// (CH572 的 SWIE 在 SR 不在 CTLR;官方 SDK 的 SysTick 只定义不用,待上板确认,
// 备选是改走 `SWI_IRQn=14`);⑤flash 基址 0x00000000,启动头 boot option
// + `SR.CNTIF` 写 0 清零;④yield 走 **`SWI_IRQn=14`**(置 PFIC `IPSR` pending;
// 该 IRQ 已在 `IENR` 里与 SysTick 并列使能,官方三套移植同款;`SR.SWIE` 已不使用)
// 后是否即刻进 trap 并沿 mcause=14 完成切换;
// ⑤flash 基址 0x00000000,启动头 boot option
// `0xF3F9BDA9` @flash 0x14(官方 Link.ld 实测);⑥默认主频 100MHz(env 按官方
// `FREQ_SYS` 配,复位默认频率待实测);⑦`mcycle` 是否实现(决定 `delay_us`);
// `FREQ_SYS` 配,复位默认频率待实测);⑦`delay_us` 的实测精度(已改用 SysTick
// 计数,不再赌 `mcycle`;前置是 SysTick 已在跑——见 chip/ch572/mod.rs 注);
// ⑧SysTick 计数器是 **32 位**(与 ch583 的 64 位读法不同)。
//
// TODO(CH572 外设,待上板核对后补):GPIO/1×UART/1×SPI/1×TMR 的 BSP 层定义;
Expand Down
10 changes: 5 additions & 5 deletions examples/multitask_ch583.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ use alloc::vec::Vec;
// (intsyscr 0x804 写 0 + 私有 CSR 0xbc0=0x1f);②mtvec Direct 分发与
// mcause=12(SysTick);③SysTick CTLR=0x2F(INIT|STRE|STCLK|STIE|STE,
// **不含 SWIE**——与官方 SysTick_Config 同值) + SR.CNTIF 写 0 清零;
// ④运行期 CTLR.SWIE(bit31)请求切换的语义(**官方 CH583 的 FreeRTOS/
// RT-Thread/HarmonyOS 三套移植都不用它,一律走 SWI_IRQn=14 + SW_Handler;
// 本口沿用 ch32v103 模板的 SWIE,待上板确认,备选即改 IRQ 14**);
// ④yield 走 **`SWI_IRQn=14`**(置 PFIC `IPSR` pending;该 IRQ 已在 `IENR` 里与
// SysTick 并列使能,官方 FreeRTOS/RT-Thread/HarmonyOS 三套移植同款;
// `CTLR.SWIE` 已不使用)后,是否即刻进 trap 并沿 mcause=14 完成切换;
// ⑤flash 基址 0x00000000(尾部 64K 留给 BootLoader);⑥默认主频 60MHz
// (env 按此配);⑦`mcycle` 是否实现(决定 `delay_us`,未实现则 `delay_us`
// 会死循环——见 chip/ch583/mod.rs 注);⑧栈账:12 任务+timer+idle 实测
// (env 按此配);⑦`delay_us` 的实测精度(已改用 SysTick 计数,不再赌 `mcycle`;
// 前置是 SysTick 已在跑——见 chip/ch583/mod.rs 注);⑧栈账:12 任务+timer+idle 实测
// ≈20.9K 堆(见 main 注释,别再往上加任务)。
//
// TODO(CH583 外设,待上板核对后补):
Expand Down
90 changes: 54 additions & 36 deletions src/chip/ch572/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,21 @@
//! 实测产物 0 条 AMO/LR/SC(见 README「无 A 扩展目标」一条)。
//! - **SysTick 是 32 位**:`CNT`@+8、`CMP`@+16 各只有低字(无 CNTH/CMPHR),
//! 读法与 `ch583` 的 64 位高低重读不同。
//! - **软中断触发位在 `SR`(bit31)而非 `CTLR`**:`SysTick_SR_SWIE`,见 `irq()`;
//! `CTLR` 里**没有 INIT 位**(官方 `SysTick_Config` 写 `0x0F` = STRE|STCLK|STIE|STE)。
//! - **软中断触发位在 `SR`(bit31)而非 `CTLR`**(`SysTick_SR_SWIE`)——但本口
//! **不用它**:yield/抢占请求走官方三套 RTOS 移植的 `SWI_IRQn=14`(见 `irq()`),
//! `SWIE` 在初始化与运行期都不碰。`CTLR` 里也**没有 INIT 位**
//! (官方 `SysTick_Config` 写 `0x0F` = STRE|STCLK|STIE|STE)。
//! 该 IRQ 与 SysTick 一样要在 `IENR[0]` 里**使能**(= bit12|bit14,见
//! `setup_intrrupt`)——PFIC 的 pending 与 enable 分离,只置 pending 不使能,
//! 请求会挂在一个永不被取走的中断上(而构建/门禁仍全绿)。
//! - **私有 CSR 多一条**:官方 reset 序列为 `0xbc0=0x25` **且 `0xbc1=1`**
//! (ch583/ch32v* 只有 `0xbc0=0x1f`)。
//! - **12K SRAM / 240K CODE**:任务栈账比 ch583 紧得多(见示例注释)。
//!
//! 与 `ch583` 相同、已按官方源码确认的部分:
//! - PFIC/SysTick 基址同址(`0xE000E000`/`0xE000F000`),`IENR[0]@0x100` bit12
//! 使能核中断 12(= `PFIC_EnableIRQ(SysTick_IRQn)`,CH572 `SysTick_IRQn=12`);
//! - PFIC/SysTick 基址同址(`0xE000E000`/`0xE000F000`),`IENR[0]@0x100` 的
//! bit12|bit14 使能核中断 12 与 14(两个 `PFIC_EnableIRQ`,
//! `SysTick_IRQn=12` + `SWI_IRQn=14`——后者官方三套移植都有,漏了 yield 失效);
//! - `SR.CNTIF`(bit0)区分真 tick 与软中断请求,**写 0 清**(WCH 惯例,待上板复核);
//! - ROM 启动头:官方 `startup_CH572.S` 的 `.vector` 第 5 字同样是
//! `0xF3F9BDA9`,用官方 `Link.ld` 现场链接实测**同样落在 flash 0x14**
Expand All @@ -28,15 +34,15 @@
//! ②复位默认主频(env 按官方 `CH57x_common.h` 的 `FREQ_SYS=100MHz` 配置;
//! 本口无 BSP/时钟初始化,SAM 解锁与 PLL 未做,须实测校正);
//! ③`0x804=0` 后异常/中断入口是否确实不再硬件压栈(决定 36 字帧成立);
//! ④`SR.SWIE` 置 1 后是否即刻进 `SysTick` 入口、`SR.CNTIF` 是否仍为 0;
//! (官方 CH572 SDK 的 SysTick 只定义不用——若 SWIE 无效,备选是走
//! QingKe 专用 `SWI_IRQn=14` + `_int_dispatch` 里加一条分发)
//! ⑤`mcycle` 是否实现(决定 `delay_us`,未实现则读出恒 0 → 死循环);
//! ④置 `SWI_IRQn=14` pending 后是否即刻进 trap、并在 `_start_trap` 里按
//! mcause=14 走切换(官方三套移植的 yield 路径;`SWIE` 已不使用);
//! ⑤`delay_us` 的实测精度(现用 SysTick 计数,不再依赖 `mcycle`);
//! ⑥BootLoader 是否放行(启动头已复刻,仍需实板确认)。

mod port;

use super::{CPU_CLOCK_HZ, SYSTICK_CLOCK_HZ, TICK_CLOCK_HZ};
use super::wch_pfic;
use super::{SYSTICK_CLOCK_HZ, TICK_CLOCK_HZ};
use crate::port::Portable;
use crate::prelude::CriticalSection;
use crate::task::Task;
Expand All @@ -55,7 +61,8 @@ pub(crate) const PFIC_BASE: usize = 0xE000_E000;
pub(crate) fn setup_intrrupt() {
let stk = STK_BASE as *mut u32;
unsafe {
// 1. 停表(CTLR=0)、清 SR(CNTIF/SWIE 写 0 清)、CNT=0、CMP=TICKS-1
// 1. 停表(CTLR=0)、清 SR(CNTIF 写 0 清;SWIE 本口不用,一并清)、
// CNT=0、CMP=TICKS-1
// ——CH572 的 CNT/CMP 只有低字(32 位),没有 CNTH/CMPHR
stk.write_volatile(0);
stk.add(1).write_volatile(0); // SR
Expand All @@ -64,13 +71,17 @@ pub(crate) fn setup_intrrupt() {
stk.add(4).write_volatile(TICKS); // CMPL(32 位比较值)
// 2. CTLR = STRE(3)|STCLK(HCLK,2)|STIE(1)|STE(0) = 0x0F
// ——官方 `SysTick_Config` 同值;**CH572 的 CTLR 没有 INIT 位**,
// SWIE 也不在这里(在 SR.bit31,见 irq())
// (SWIE 在 SR.bit31,但本口不用它——yield 走 SWI_IRQn=14,见 irq())
stk.write_volatile(0x0000_000F);
}
// 3. PFIC:使能 SysTick(IENR[0] @0xE000E100,bit12 = 核中断号 12)
let ienr0 = (PFIC_BASE + 0x100) as *mut u32;
// 3. PFIC:使能 **SysTick(bit12) + 软中断(bit14)** —— pending 与 enable 是
// 两组分离的寄存器:`irq()` 只负责置 pending(`IPSR[0]`),中断要被取走还得
// 在 `IENR[0]` 里使能该 IRQ(官方 `PFIC_EnableIRQ(SWI_IRQn)` 就是这一步)。
// 漏掉 bit14 → `irq()` 的 pending 永远不被取走(yield/抢占只剩 tick 兜底),
// 而构建/门禁/host 测试全绿——2026-09-19 PR #19 独立审查实测。
let ienr0 = (PFIC_BASE + wch_pfic::IENR_OFFSET) as *mut u32;
unsafe {
ienr0.write_volatile(1 << 12);
ienr0.write_volatile(wch_pfic::ienr_enable_mask());
}
}

Expand All @@ -79,7 +90,7 @@ pub(crate) fn setup_intrrupt() {
pub(crate) fn reset_systick() {
let stk = STK_BASE as *mut u32;
unsafe {
stk.add(1).write_volatile(0); // SR @ +4(顺带清 SWIE)
stk.add(1).write_volatile(0); // SR @ +4
}
}

Expand Down Expand Up @@ -130,29 +141,29 @@ impl Portable for Ch572Porting {
unsafe { asm!(include_str!("restore_ctx.S"), options(noreturn, raw)) };
}

/// 软中断(调度请求):写 **`STK_SR.SWIE`(bit31)** 触发 SysTick 入口
/// ——这是 CH572 与 ch583 最大的机制差异(ch583 在 `CTLR`;CH572 的
/// `CTLR` 根本没有 SWIE 位)。ISR 读 `SR.CNTIF` 区分真 tick 与本请求。
/// 软中断(调度请求):置 PFIC 的 `SWI_IRQn=14` pending
/// (`IPSR[0] @0xE000E200` bit14)。
///
/// 读改写保留 SR 其它位:CNTIF(bit0)若已置位,写回 0/1 的效果取决于它的
/// 清法(写 0 清 或 W1C),两种情况下都不会误报真 tick——若写回的是 1 且
/// 它是"写 0 清",则 CNTIF 仍置位,ISR 会按真 tick 记一次账(与 ch583 口
/// 同款行为,上板核对点④)。
/// 为什么不用 CH572 自己的 `SR.SWIE`:官方 CH572 SDK 的 SysTick 只**定义**
/// 不用(全 SDK 零处调用),而官方三套 RTOS 移植的 yield 一律走
/// `PFIC_SetPendingIRQ(SWI_IRQn)` + `SW_Handler`(CH572 的 `SWI_IRQn=14`);
/// 走官方路径把"SWIE 到底灵不灵"这个只能上板排除的赌注消掉。
/// 该 IRQ 的使能位在 `setup_intrrupt()` 里与 SysTick 一并置上(`IENR[0]`
/// bit14)——置 pending 与使能缺一,本函数就是空操作。
#[inline]
fn irq() {
const SWIE: u32 = 1 << 31;
let sr = (STK_BASE + 4) as *mut u32;
let ipsr0 = (PFIC_BASE + wch_pfic::IPSR_OFFSET) as *mut u32;
unsafe {
sr.write_volatile(sr.read_volatile() | SWIE);
ipsr0.write_volatile(wch_pfic::irq_bit(wch_pfic::SWI_IRQ));
}
}
/// 关闭软中断(SWIE 是触发位,自清;防御性清一下)
/// 关闭软中断:清 `SWI_IRQn=14` 的 pending(`IPRR[0] @0xE000E280` bit14),
/// 防残留 pending 让 trap 出口立刻重入。
#[inline]
fn disable_irq() {
const SWIE: u32 = 1 << 31;
let sr = (STK_BASE + 4) as *mut u32;
let iprr0 = (PFIC_BASE + wch_pfic::IPRR_OFFSET) as *mut u32;
unsafe {
sr.write_volatile(sr.read_volatile() & !SWIE);
iprr0.write_volatile(wch_pfic::irq_bit(wch_pfic::SWI_IRQ));
}
}

Expand All @@ -163,16 +174,23 @@ impl Portable for Ch572Porting {
unsafe { stk.add(2).read_volatile() as u64 }
}

/// 硬件延时,单位 us。
/// 硬件延时,单位 us —— **用 SysTick 计数器**实现(不再赌 `mcycle`/Zicntr:
/// CH572 是否实现它没有一手证据,未实现时读出恒为常量 → 死循环;SysTick 是
/// 内核点拍赖以工作的外设,必然实现)。计数器 32 位、按 CMP 自动重装,
/// 等待按"到下次重装还剩多少"分块(跨重装差值见 `crate::chip::delay`,有 host 单测)。
///
/// ⚠️ 沿用 `ch32v103`/`ch583` 的 `mcycle` 实现——CH572 是否实现 `mcycle`
/// (Zicntr)待上板核对:若未实现则读出恒为常量,本函数会死循环。
/// 届时须改 SysTick/TMR 计时(CH57x 无 DWT)。主频按 env 的 `CPU_CLOCK_HZ`。
/// ⚠️ **前置条件:SysTick 已在跑**(即 `start_scheduler()` 之后)。计数器停着时
/// `elapsed()` 恒为 0,等待不会结束——`start_scheduler()` 之前需要延时用忙等。
#[inline]
fn delay_us(us: u64) {
let t0 = riscv::register::mcycle::read64();
let clock = (us * (CPU_CLOCK_HZ as u64)) / 1_000_000;
while riscv::register::mcycle::read64().wrapping_sub(t0) <= clock {}
let period = (SYSTICK_CLOCK_HZ / TICK_CLOCK_HZ) as u64; // = TICKS + 1
let mut remaining = us * (SYSTICK_CLOCK_HZ as u64) / 1_000_000;
while remaining > 0 {
let start = Self::systick();
let chunk = crate::chip::delay::next_chunk(remaining, start, period);
while crate::chip::delay::elapsed(start, Self::systick(), period) < chunk {}
remaining -= chunk;
}
}

/// 任务创建时为 CPU 准备任务现场(36 字帧,与 port.S 的保存宏互为镜像):
Expand Down
7 changes: 5 additions & 2 deletions src/chip/ch572/port.S
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
(0x804=0),mtvec=Direct 单入口分发。相对 ch583 的增量只有:
①`_setup_interrupts` 写 `0xbc0=0x25` **且多一条 `0xbc1=1`**(官方
`startup_CH572.S` reset 序列同款;CH583 是 `0xbc0=0x1f`、无 0xbc1);
②软中断触发位在 `SR.bit31` 而非 `CTLR.bit31`(见 mod.rs 的 irq());
③SysTick 计数器/比较值 32 位(见 mod.rs)。
②SysTick 计数器/比较值 32 位(见 mod.rs);
③yield 走 `SWI_IRQn=14`(与 ch583 一样,已在 `_int_dispatch` 加分发)。
启动头与代码基址(flash 0)与 CH583 一致:实测官方 `Link.ld` 下 boot option
同样落在 flash 0x14(见 memory.x 注)。

Expand Down Expand Up @@ -147,6 +147,9 @@ _int_dispatch:
and t0, t0, t1 // 取中断号(去 bit31)
li t1, 12 // SysTick
beq t0, t1, _isr_systick
li t1, 14 // SWI(软中断):官方三套 RTOS 移植的 yield 机制
// (irq() 置 IPSR bit14 pending);只请求切换,不做时间账
beq t0, t1, _switch_and_restore
// 未接入的外设中断/其它核心中断 → DefaultHandler(BSP 扩展点)
jal ra, DefaultHandler
j _switch_and_restore
Expand Down
6 changes: 3 additions & 3 deletions src/chip/ch572/port.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! CH572 中断胶水:SysTick 二合一处理(真 tick / SWIE 软中断)与切换入口。
//! 与 ch583/port.rs 同构——差别只在 **软中断触发位在 `SR` 而非 `CTLR`**
//! (见 `Ch572Porting::irq()`),dispatch 与切换路径完全一致。
//! CH572 中断胶水:真 tick 处理与切换入口。
//! 与 ch583/port.rs 完全同构——yield/抢占请求走 **`SWI_IRQn=14`**
//! (官方三套 RTOS 移植的做法,见 `Ch572Porting::irq()`),SysTick 入口只由真 tick 触发。

use super::Ch572Porting;
use crate::port::Portable;
Expand Down
Loading
Loading