学习提示:这四个描述层的顺序,也是编写 PSL 属性时的推荐思考顺序——先写布尔表达式,再想时序关系,然后封装为属性,最后放入验证模型。
### 3.3 原理机制:四层之间的组合关系与语法层次
下图展示了 PSL 四层结构的核心关系:自底向上,逐层组合。底层提供“零件”,上层负责“装配”,最终形成可供形式化验证工具执行的完整模型。
关键组合规则:
- Boolean 层是唯一直接接触设计信号的层,其他层都通过 Boolean 表达式获取数据。
- Temporal 层只能叠加在 Boolean 表达式之上,不能脱离布尔层独立使用。
- Property 层将一个或多个时序属性封装为带语义的断言(`assert`)、假设(`assume`)或覆盖点(`cover`)。
- Model 层是最高层,它可以包含多个 Property,并负责约束验证环境(时钟、复位、输入假设等)。
### 3.4 流程步骤:从简单布尔表达式构建完整属性
按照“从下往上、逐层组装”的思路,构造 PSL 属性可分五步:
1. **识别设计信号**
确定参与属性的接口信号、时钟、复位等基础元素。
2. **写 Boolean 表达式**
用 `&&`、`||`、`!`、`==`、`!=` 等运算符描述信号之间的组合关系。
3. **加时序算子**
在布尔表达式外层叠加 `always`、`next`、`until`、`eventually` 等时序关键词,表达事件的时间先后顺序。
4. **封装为 Property**
使用 `assert`、`assume` 或 `cover` 将时序逻辑转换为可验证的属性声明。
5. **放入 Model 并验证**
在 `model` 结构中组织属性与假设,交给仿真器或形式化工具执行验证。
工程建议:即使在写一个复杂属性时,也先在草稿纸上单独列出“布尔表达式”和“时序算子”两部分,确认二者分离后再合并,能显著减少语法错误。
### 3.5 案例说明:请求-授权握手协议
以经典请求-授权协议为例,假设信号有:`req`、`grant`、`clk`、`reset`。需要验证的核心属性是:**当请求有效且下一周期没有复位时,授权信号应在下一周期到来**。
**第 1 步:Boolean 表达式**
```psl
req == 1'b1
grant == 1'b1
reset == 1'b0
```
**第 2 步:加时序算子**
```psl
always (req == 1'b1 -> next (grant == 1'b1))
```
含义:任何时钟周期,若 `req` 为高,则下一周期 `grant` 必须为高。
**第 3 步:封装为 Property**
```psl
assert always (req == 1'b1 && reset == 1'b0 -> next (grant == 1'b1));
```
**第 4 步:放入 Model**
```psl
model handshake_protocol {
assume always (clk);
assume never (reset == 1'b1 && req == 1'b1);
assert handshake_property;
};
```
由此完成了一条从底层信号到顶层验证模型的完整属性构造链路。
常见误区:在 Boolean 层中直接使用 `always`、`next` 等时序算子,会导致语法错误。时序算子只能出现在 Temporal 层及以上的表达式中。
### 3.6 表格总结:四层描述层一览
| 描述层 | 核心元素 | 常用关键字 | 典型示例 | 主要职责 |
|--------|----------|------------|----------|----------|
| Boolean | 信号、常量、逻辑运算 | `&&` `\|\|` `!` `==` | `req && grant` | 构造底层布尔条件 |
| Temporal | 时序算子、序列 | `always` `next` `until` | `always (a -> next b)` | 表达时间先后关系 |
| Property | 属性声明、蕴含 | `assert` `assume` `cover` | `assert always (a -> next b);` | 声明可执行验证目标 |
| Model | 模型、约束、环境 | `model` `assume` | `model m { ... }` | 组织属性与验证环境 |
### 3.7 误区提醒
- **误区一:把 Boolean 层和 Temporal 层混为一谈**
布尔表达式是纯组合逻辑,不含时间概念;时序算子才描述事件发生的时间关系。两者混用时,属性语义会变得模糊,甚至无法通过语法检查。
- **误区二:跳过 Property 层直接建模**
有些初学者喜欢把所有逻辑直接写在 `model` 中,导致模型庞大且难以维护。正确做法是先用 `assert`/`assume` 封装独立属性,再在 `model` 中引用。
- **误区三:忽略时钟与复位约束**
在 Boolean 层能写出正确表达式,但缺少时钟和复位约束,验证结果往往不可靠。Model 层中的 `assume` 必须明确指定时钟和复位行为。
### 3.8 实践清单
- [ ] 从信号列表出发,先写下 2~3 个核心布尔表达式
- [ ] 检查布尔表达式中是否混入了时序关键字(`always`/`next` 等)
- [ ] 使用 `always`/`next`/`until` 将布尔表达式扩展为时序逻辑
- [ ] 用 `assert` 声明验证目标,用 `assume` 约束环境
- [ ] 将属性放入 `model`,并明确时钟、复位、输入假设
- [ ] 用仿真器或形式化工具跑一次,确认属性不是 “空真”(vacuously true)
- [ ] 为每条属性添加注释,说明验证意图,便于后期维护