F Prime 速率组调度(Rate Group Scheduling)完全指南:从时钟分频到相位调度
【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址: https://gitcode.com/GitHub_Trending/fpr/fprime
导读
本文基于 F´(F Prime)飞行软件框架的系统功能参考文档,系统讲解其周期性执行机制——速率组调度(Rate Group Scheduling)。这是 F´ 中遥测采集、传感器读取、控制回路等所有周期性任务的驱动骨架:一个系统时钟源产生主 tick,经过分频后驱动多个速率组(Rate Group),再由速率组按序调用其成员组件。读完本文,你将掌握 RateGroupDriver 的分频原理与配置方法、Active/Passive Rate Group 的执行模型与超时检测(Overrun/Slip)机制、ActivePhaser 的相位调度模型,以及速率组的遥测与统计清除命令的实战用法。
概述:F´ 的周期执行骨架
速率组调度提供了以定义速率周期性执行组件的能力。其基本思想是:
- 一个系统时钟源生成主 tick(primary tick);
- 该主 tick 被分频,驱动多个以不同频率运行的速率组;
- 每个速率组按定义好的顺序调用一组组件。
这是 F´ 中循环处理的基础执行模式——遥测收集、传感器读取、控制回路以及其他周期性任务都通过速率组驱动。
该能力由一串协作组件构成:
- 周期源(cycle source):生成主 tick(如
LinuxTimer、PosixTime、ChronoTime等,详见 Time Services); - 速率组驱动器(Rate Group Driver):将主 tick 分频为多个子速率;
- 速率组组件(Rate Group):将周期调用分发给其成员组件。
时钟分频:RateGroupDriver
速率组驱动器接收一个主系统 tick,并将其分频产生不同速率的输出。其典型用法是:一个 1 Hz 的 tick 经分频后产生 1 Hz、0.5 Hz、0.25 Hz 的输出。分频比例(divider)在构造/配置时指定。RateGroupDriver 是被动组件,运行在调用者的线程上下文中。
配置接口与约束
驱动器通过configure()传入一组分频器(DividerSet):
RateGroupDriverImpl::configure(const DividerSet& dividerSet);从 源码实现 可以看到配置过程中的关键约束与内部处理:
- 表大小校验:
static_assert保证分频表大小与NUM_CYCLEOUT_OUTPUT_PORTS一致; - offset 合法性:任何端口的 offset 必须小于其 divisor(
offset == 0或offset < divisor),否则FW_ASSERT失败——因为 offset 大于等于 divisor 的端口永远不会被调用; - rollover 防溢出:
m_rollover为所有非零 divisor 的乘积,并断言乘积不会溢出FwSizeType;每次 tick 后m_ticks = (m_ticks + 1) % m_rollover,避免整数回绕导致跳周期; - 内容拷贝:
DividerSet的内容在调用时被复制,因此传入的数组可以是临时变量; - 必须在构造之后、任何端口调用之前调用
configure(),否则CycleIn_handler中的FW_ASSERT(this->m_configured)会失败。
分频逻辑
CycleIn_handler(源码)对每个配置了非零 divisor 且已连接的输出端口,判断(m_ticks % divisor) == offset是否成立,成立则调用该输出端口。文档中的分频示例:
| SchedIn 速率 | divider[0] | SchedOut[0] | divider[1] | SchedOut[1] | divider[2] | SchedOut[2] |
|---|---|---|---|---|---|---|
| 1 Hz | 1 | 1 Hz | 2 | 0.5 Hz | 4 | 0.25 Hz |
即:divisor 为 4 的端口每第 4 次CycleIn调用被触发一次。每个输出端口通常连接到某个速率组组件的输入。
设计约束
RateGroupDriver 被设计为可在 ISR 上下文运行(其需求 RGD-002 通过 Inspection 验证),因此实现避免:浮点计算、获取互斥锁、调用未知副作用的库、过长的实现路径。
速率组执行:三种变体
速率组按序调用其成员组件,并传递一个指示调用顺序的上下文值(context)。F´ 提供三种变体:
| 变体 | 线程模型 | 输入端口类型 | 适用场景 |
|---|---|---|---|
| Active Rate Group | 独立线程 | 异步(入队唤醒线程) | 需要将速率组执行与时钟源解耦的重负载处理 |
| Passive Rate Group | 调用者线程 | 同步 | 无需独立线程开销的轻量处理 |
| Active Phaser | 独立线程 | 异步 | 需要在周期内精确分配各组件执行时刻的相位调度 |
Active 与 Passive 速率组都会跟踪每个周期完成所需时间,并以遥测形式上报。
Active Rate Group:独立线程 + 超时检测
Svc::ActiveRateGroup是一个主动组件,驱动连接到Svc::Sched输出端口的一组组件。其输入Svc::Cycle端口是异步的:调用该端口会向队列投递一条消息,唤醒组件任务;任务随后按序调用所有成员输出端口。这使速率组执行与时钟源解耦。
上下文配置:一组上下文值以数组形式传入configure():
void configure(U32 contexts[], FwIndexType numContexts);每个 context 值可供组件区分速率组内的多次调用(组件头文件 提供了基于ContextArray的新签名,旧签名已标记DEPRECATED)。
超时/滑移(Slip)检测机制:组件在 CycleIn 被调用时置位标志,在速率组执行开始时清除该标志;若在周期结束时检测到标志再次被置位,则声明一次 cycle slip,发出RateGroupCycleSlip警告事件(WARNING_HI,源码),并递增RgCycleSlips遥测计数(fpp 定义)。这表示新周期在前一周期完成前就已开始——系统未满足时序要求,需要关注(速率过快或成员组件耗时过长)。其单元测试(ActiveRateGroupTester.cpp)验证了滑移事件与计数遥测的行为,包括节流计数。
Passive Rate Group:同步执行 + 每端口时序遥测
Svc::PassiveRateGroup是被动组件,在调用者线程上下文中执行(通常由 RateGroupDriver 直接同步调用)。输入Svc::Cycle端口是同步的,成员组件直接在调用速率组的那一方上下文中被调用。
configure()接受两个参数(fpp 定义):
- 一个 U32 上下文数组(每个输出端口一个值),传给每个速率组成员;
- 可选的
Os::RawTimeSource,指定用于周期执行时间测量的计时器源(默认为Os::RAWTIME_DEFAULT)。
计时源限制(重要)
关键限制:configure()中的RawTimeSource参数只影响每个周期的结束时间戳。周期的开始时间戳来自周期驱动器(如RateGroupDriver、LinuxTimer),它们以RAWTIME_DEFAULT构造Os::RawTime。当两者源不同时,Os::RawTime会拒绝该时间间隔(POSIX 实现返回INVALID_PARAMS),周期时间被上报为零。该特性仅在以下情况正确:
- 使用默认值
RAWTIME_DEFAULT;或 - 周期驱动器使用相同的非默认源。
要部署级地更换时钟,应覆盖config/Os/RawTimeSource.hpp并设置RAWTIME_DEFAULT,而不是在此处传入非默认源。参见 Configuring F´ 中的 OSAL 配置。
默认配置 中的枚举展示了可选源:
RAWTIME_DEFAULT = CLOCK_REALTIME—— 默认构造 RawTime 对象使用的时钟(POSIX 上即传给clock_gettime()的clockid_t);RAWTIME_REALTIME = CLOCK_REALTIME—— 墙钟时间,系统时间调整时可能跳变或渐变;RAWTIME_MONOTONIC = CLOCK_MONOTONIC—— 单调时钟,不受系统时间调整影响;RAWTIME_BOOTTIME = CLOCK_BOOTTIME—— 挂起期间也继续前进的单调时钟(仅 Linux)。
命令与原子统计
PassiveRateGroup支持CLEAR_STATISTICS同步命令(实现):将所有端口时长高水位(high water marks)和最大周期时间清零。周期计数不会被清除,因为它表示运行总数。所有统计量由无锁原子操作保护(m_maxTime与各端口 HWM),从而ISR 安全且消除了互斥锁开销。
遥测通道
| 通道 | 类型 | 描述 | 发送语义 |
|---|---|---|---|
MaxCycleTime | U32 | 速率组周期最大执行时间(微秒)。由CLEAR_STATISTICS命令清除 | 变化时更新(仅最大值增加时) |
CycleTime | U32 | 最近一次周期的执行时间(微秒) | 每周期发送 |
CycleCount | U32 | 已执行周期总数。不被CLEAR_STATISTICS清除 | 每周期发送 |
PortCycleTime | U32[PassiveRateGroupOutputPorts] | 最近一次周期中各成员端口的执行时间(微秒) | 每周期发送(仅当PassiveRateGroupCfg::PortCycleTime启用) |
PortCycleTimeHWM | U32[PassiveRateGroupOutputPorts] | 各端口执行时间的高水位(微秒)。由CLEAR_STATISTICS清除 | 变化时更新(任一高水位增加时),降低带宽占用 |
PortCycleTime每周期发送,提供每个周期执行的完整时序快照;PortCycleTimeHWM采用"变化时更新"语义,仅在至少一个端口的高水位上升时发送,在降低遥测带宽的同时仍能捕获时序异常。每端口高水位与周期统计的启用由配置项PassiveRateGroupCfg::PortCycleTime控制(见 实现 与 测试)。
使用场景
CLEAR_STATISTICS用于:系统变更后重新建立时序基线(re-baselining)、或在特定运行阶段隔离时序行为。
Active Phaser:周期内相位调度
Svc::ActivePhaser提供更精细的调度模型:每个周期被划分为可配置数量的 tick,各个端口调用被映射到周期内特定的 tick 窗口。这允许精确控制每个组件在周期内的调用时刻,实现相位调度——不同组件在周期内不同时间点运行,以摊开处理负载。
调度示例(来自 SDD):一个相位周期由 10 个 tick 组成。端口调用 A(长度 2)从 tick 0 开始,端口调用 B(长度 3)从 tick 2 开始,端口调用 C(长度 1)在 tick 6 和 tick 8 各启动一次。调度每 10 个 tick 重复。
- 端口调用按其注册顺序被调用;
- 若端口调用指定了长度,组件会在运行时检查该调用是否超过其指定长度(overrun);
- 发生超时时发出
MissedDeadline警告事件(需求 AP-003,见 SDD)。
Dropped Cycles(丢周期)语义:CycleIn与内部Tick端口声明为drop——队列满时到达的周期被丢弃。这是预期行为,且不上报任何事件或遥测通道:与ActiveRateGroup(会发出RateGroupCycleSlip并计数RgCycleSlips)不同,ActivePhaser对丢弃周期保持静默。被丢弃的周期会使相位基准永久滞后一个周期,且MissedDeadline不会观测到它(因为它只度量已执行周期内的超时)。操作员不应将组件没有事件视为没有丢周期。
配置要点汇总
| 配置项 | 配置位置/方式 | 说明 |
|---|---|---|
| 速率组成员输出端口数量 | 编译期配置(ActiveRateGroupOutputPorts/PassiveRateGroupOutputPorts等) | 输出端口数量在编译时确定 |
| RateGroupDriver 分频比例 | configure(const DividerSet&) | 构造后、端口调用前调用;offset 必须小于 divisor |
| 成员组件的上下文值 | configure(contexts, numContexts) | 基于端口号索引的配置表 |
| ActivePhaser tick 数与每端口时间窗口 | setup 时配置 | 决定周期内各调用的相位分配 |
| PassiveRateGroup 计时源 | configure(..., Os::RawTimeSource) | 默认RAWTIME_DEFAULT;仅影响周期结束时间戳 |
| 全局时钟源 | 覆盖config/Os/RawTimeSource.hpp中RAWTIME_DEFAULT | 部署级切换时钟(如改为CLOCK_MONOTONIC) |
深入阅读
- 周期源(cycle source)驱动速率组调度的机制参见 Time Services;
- 各组件完整设计文档:
- RateGroupDriver SDD —— 分频需求 RGD-001/RGD-002、端口表与 ISR 合规设计;
- ActiveRateGroup SDD —— 需求 ARG-001 至 ARG-004、滑移检测设计;
- PassiveRateGroup SDD —— 需求 FPRIME-PRG-001 至 005、
CLEAR_STATISTICS与遥测通道; - ActivePhaser SDD —— 需求 AP-001 至 003、丢周期语义;
- 速率组端口类型定义见 Svc::Sched 与 Svc::Cycle 对应的 fpp 接口;
- 参考实现:Ref 部署中的速率组拓扑连接示例见 Ref 拓扑 目录。
【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址: https://gitcode.com/GitHub_Trending/fpr/fprime
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考