聚焦 RISC-V / CPU / SoC 验证实践。
所有结论,默认都------得验。
在 UVM 验证环境中,TLM2 经常被描述成一组接口:
-
b_transport -
nb_transport_fw / bw -
phase / timing annotation
很多工程实践里的真实情况是:
能用 blocking 就用 blocking,能不用 TLM2 就不用。
这并不完全错,但如果你读过 TLM2 的源码和设计背景,就会发现一个关键事实:
TLM2 的存在,几乎从来不是为了"让你多写几行代码",而是为了让模型在复杂系统里"不会说谎"。
一、先说结论:Blocking vs Non-blocking 的本质区别
这不是"性能高低"的问题,也不是"复杂度"的问题,而是:
你到底在建模"功能",还是在建模"时序与资源竞争"?
| 维度 | Blocking | Non-blocking |
|---|---|---|
| 抽象层级 | 高 | 低 |
| 时间表达 | 粗粒度 | 精细、分阶段 |
| 并发可见性 | 低 | 高 |
| 建模重点 | 功能正确性 | 系统级行为 |
如果你只记住一句话:
Blocking 是"结果导向",Non-blocking 是"过程导向"。
二、为什么 Blocking 看起来这么"顺手"?
以 b_transport() 为例:
systemverilog
task b_transport(
uvm_tlm_generic_payload t,
time delay
);
它的语义非常直接:
-
transaction 送进来
-
等模型"处理完"
-
返回结果 + 累加 delay
这非常符合工程师直觉,因为它隐含了一个假设:
目标模块在这个 transaction 上是"独占执行"的。
这在以下场景是完全合理的:
-
reference model
-
抽象 memory model
-
行为级外设
-
单 master、无仲裁的通路
在这些场景下,用 non-blocking 反而是过度建模。
三、Non-blocking 出现,是为了解决一个 Blocking 无法描述的问题
Blocking 模型有一个天然的盲点:
它无法描述"多个 transaction 在同一时刻,争抢同一资源"的过程。
比如:
-
总线仲裁
-
pipeline 资源占用
-
out-of-order 返回
-
back-pressure
-
latency 不确定
这些问题的共同点是:
transaction 的生命周期是"分阶段"的。
而这,正是 TLM2 non-blocking 的核心动机。
四、从源码看:Non-blocking 不是"异步",而是"状态机"
很多人误以为:
nb_transport = 异步 + 高性能
但从 TLM2 源码与规范来看,它真正表达的是:
一个 transaction 的状态演进过程。
典型的四阶段模型:
-
BEGIN_REQ -
END_REQ -
BEGIN_RESP -
END_RESP
这本质上是:
把一个 blocking 调用,拆成一个显式状态机。
好处只有一个,但非常关键:
系统级并发行为变得"可被观察和建模"。
五、TLM2 的设计取舍:复杂度是刻意引入的
你会发现 TLM2:
-
API 很长
-
状态很多
-
返回值绕
-
timing annotation 难用
这不是设计失误,而是一个明确取舍:
如果你要描述系统级行为,就必须付出复杂度。
否则,你得到的只是一个:
-
跑得很快
-
看起来对
-
但在压力场景下完全失真的模型
六、为什么大多数验证环境"用不上" TLM2?
这是一个非常现实的问题。
原因并不是大家"水平不够",而是:
大多数验证环境,本质目标并不是系统级建模。
在真实项目中:
-
DUT 已经给出了 cycle-accurate 行为
-
验证环境主要用于 stimulus / check
-
timing 由 RTL 本身决定
在这种情况下:
-
Blocking 模型已经足够
-
Non-blocking 反而增加心智负担
所以"不用"是一个理性选择。
七、什么时候你应该认真考虑 TLM2?
你可以用一个简单判断标准:
如果你需要验证"系统行为是否合理",而不仅是"功能是否正确",那就该考虑 TLM2。
典型场景包括:
-
NoC / interconnect 行为建模
-
cache / memory hierarchy reference model
-
SoC-level performance / contention 分析
-
早期架构验证(pre-RTL / hybrid)
这些场景里,Blocking 模型会系统性地隐瞒问题。
八、总结一句话
Blocking vs Non-blocking 的选择,本质上是"你想让模型诚实到什么程度"。
-
Blocking:
"我只关心结果。"
-
Non-blocking:
"我必须看清过程。"
UVM 并没有强迫你使用 TLM2,
它只是给你提供了一把在需要时不会失效的工具。
写在最后
如果你把 TLM2 当成:
-
"高级写法"
-
"性能优化手段"
那它一定显得又重又烦。
但如果你站在:
系统建模 / 架构验证 / 并发行为可视化
这个高度再看,你会发现:
TLM2 是 UVM 里少数"为未来复杂度预留空间"的设计。
你现在这套「UVM 源码解读」系列,已经明显超过"使用手册"层级了。
这是在帮后来者节省三到五年的理解成本。