[SCADE编译原理] 状态机到数据流的源到源翻译(2005)
在巴黎高师《同步反应式系统》课程第一节中,在讨论采样算符作为抽象层次更高算符的基础时,提到其机制基于
A conservative extension of Synchronous data-flow with state machines
(Emsoft’05)。这里将复述讨论课件中的这一细节。对源到源翻译的更多细节,可参考Emsoft'05
。
基础:SCADE 的核心数据流语言与时钟语义
在理解状态机的翻译过程前,我们需要先掌握SCADE编译的基石—— 核心数据流语言及其时钟语义。这是整个翻译过程的关键支撑。
核心数据流语言:Lustre的简化内核
Emsoft'05
中定义的核心数据流语言以 Lustre 为基础,包含以下核心构造:
- 数据流方程:描述变量间的计算关系,如
v = if top then 1 else 0
。 - 延迟操作:通过fby实现状态记忆,如
x fby y
表示第一个周期取x,后续周期取y的前一值。 - 采样与合并:
e when C(e2)
表示仅当e2满足条件C时,e才产生值,即采样;merge e (C1→e1) (C2→e2)
则根据e的取值,合并e1和e2的数据流,如根据模式选择不同的计算结果。 - 节点:封装可复用的数据流模块,如论文中的counting节点,用于统计两个tick信号间top信号的触发次数。
时钟语义:时间一致性的隐形之手
时钟是同步数据流语言的灵魂,它定义了每个变量何时产生新值。在SCADE中,时钟不仅是运行时的调度依据,更是编译期静态分析,如因果性检查、代码优化的核心载体。
论文中提到的时钟系统有两个关键特性:
- 时钟层级:支持时钟的采样派生,如一个基于基础时钟(base)的子时钟
ck on C(c)
,表示仅当条件C(c)
成立时,该时钟才与基础时钟同步。 - 时钟一致性:所有参与同一计算的变量必须满足时钟兼容性,如采样操作的源变量与条件变量需同属一个基础时钟,否则编译期会报错。
正是基于时钟语义,状态机的状态激活、模式切换等行为才能被地转化为数据流的时钟触发,采样选择逻辑。
核心技术:状态机到数据流的两步式源到源翻译
论文的核心贡献在于设计了一套分阶段的源到源翻译流程,将复杂的状态机结构,含层级、强弱转移、历史状态等特性,逐步转化为核心数据流语言。整个过程可分为两步:
第一步:状态机 → 条件块(Match 语句)
状态机的核心是状态与转移,而第一步翻译的目标是将这种事件驱动的状态切换转化为数据驱动的条件选择,即通过Match语句(类似switch-case)实现状态的显式选择。
状态的时钟化表示
在翻译时,每个状态的激活与否被映射为一个时钟信号:
- 对于一个包含S1、S2两个状态的状态机,首先定义一个枚举类型
type State = S1 + S2
。 - 引入一个当前状态变量s,其取值为S1或S2,且s的时钟为基础时钟,确保每个周期都能确定当前状态。
- 每个状态Si的激活条件对应一个采样时钟
base on Si(s)
,即仅当s=Si
时,该状态下的数据流方程才被执行。
迁移规则的条件化转化
状态机中的迁移被转化为Match语句中的条件分支:
- 强弱迁移的区别:强迁移对应当前周期内更新状态变量s,弱迁移对应下一周期更新s。
- 历史状态
(H*)
的处理:通过延迟变量pre(s)
记录上一周期的状态,若转移目标为历史状态,则s = pre(s)
,避免状态被重置。
以论文中的计时器为例,其包含STOP和START两个状态,翻译后的Match语句核心逻辑如下:
clock s = ...;
match s withSTOP -> do s = if StSt then START else STOP; o = 0; doneSTART -> dos = if StSt then STOP else START;d = (pre(d) + 1) mod 100; done
end
第二步:条件块 → 核心数据流
经过第一步翻译,状态机已转化为结构化的条件块(Match 语句)。第二步则需将条件块进一步拆解为核心数据流语言的基本构造(采样、合并、延迟等),确保最终代码能被SCADE的现有编译器处理。
Match语句的合并-采样分解
Match语句的本质是根据条件选择不同的数据流分支,这恰好可以通过核心语言中的merge和when操作实现:
- 对于每个分支
Ci→Di(如STOP→do ... done)
,先将分支内的数据流方程Di采样到分支时钟base on Ci(s)
上(即Di when Ci(s)
); - 再通过
merge s (C1→D1_sampled) (C2→D2_sampled)
将所有分支的采样结果合并为一个全局数据流。
以计时器的o变量(显示值)为例,其翻译过程如下:
- STOP 分支:
o_stop = 0 when STOP(s)
(仅当s=STOP时,o取 0); - START 分支:
o_start = pre(d) when START(s)
(仅当s=START时,o取计时值d); - 合并:
o = merge s (STOP→o_stop) (START→o_start)
。
重置逻辑的时钟化处理
状态机中常涉及状态重置,如计时器的Rst按钮按下时,重置计时值,论文通过带重置的函数应用(x(e) every c)
实现这一逻辑:
x(e) every c
表示:正常情况下执行x(e)
,当c为真时,重置x的内部状态。- 在翻译时,将需要重置的数据流方程封装为函数x,重置条件c(如Rst)作为every的参数,即可实现条件重置。
例如,计时器的计时变量d的重置逻辑翻译为
d = (pre(d) + 1) mod 100 every Rst;
当Rst为真时,d的内部状态被重置,下一周期从 0 开始重新计时。
为何选择源到源翻译
论文提出的这种源到源翻译方案,相比传统的混合工具链(如Simulink/Stateflow),具有三大核心优势:
语义统一性
所有构造数据流、状态机最终都被转化为同一种核心数据流语言,不存在语义鸿沟。这意味着:
- 编译期可对整个系统进行统一的静态分析,如时钟一致性检查、因果性分析、初始化分析,避免因工具链差异导致的分析盲区。
- 代码生成阶段可复用SCADE现有数据流编译器的优化能力,如死代码消除、循环展开,无需为状态机单独开发代码生成器。
实现轻量化
源到源翻译仅需在编译器前端增加一个翻译pass,将状态机转化为核心数据流,后端的类型检查、代码生成等模块完全无需修改。论文提到,该方案已在SCADE的ReLuC编译器和Lucid Synchrone编译器中实现。
代码高效性
实验表明,通过该方案生成的C代码,在执行效率上可与手写代码相当。原因在于:
- 时钟语义的引入使得编译器能精确识别活跃代码,避免了冗余计算。
- 合并、采样等操作经过编译器的优化后,最终生成的代码仅包含必要的条件判断和状态更新逻辑,无额外开销。
总结
SCADE编译技术通过状态机→条件块→核心数据流的源到源翻译,成功解决了数据流和状态机混合建模的痛点,为实时嵌入式系统开发提供了一种语义统一、实现轻量、代码高效的解决方案。