deque 参考模型与 Scoreboard
返回 项目总览。
Python collections.deque 天然支持队尾追加和队首弹出,因此可以用极少代码表达 FIFO 的行为规格:写入 append(),读取 popleft()。
reference queue
self.expected = deque()Scoreboard 不读取 DUT 内部 mem/count/pointer。它只根据测试请求和当前期望 occupancy 决定哪些操作应该被接受,再与 DUT 端口结果比较。这比直接窥视内部状态更接近端到端验证。
为什么要在驱动前预测
fifo_step() 先保存旧队列长度和队首:
pre_len = len(scoreboard.expected)
read_expected = scoreboard.expected[0] if (rd_en and pre_len > 0) else None
write_accepted = wr_en and (pre_len < DEPTH or read_expected is not None)接受条件必须基于本拍时钟沿之前的状态。若先修改 reference queue 再判断,就可能错误解释 empty/full 边界。
read_expected 同时表示本拍存在有效读。当 FIFO full 且有有效读时,write_accepted 仍为真,与 RTL 的 !full || read_ok 一致。
检查与更新顺序
驱动一拍后:
- 若预期读有效,比较
dout与旧队首; - 从 reference queue 弹出旧队首;
- 若写被接受,把
din追加到队尾; - 用新队列长度检查 empty/full/almost-full。
if read_expected is not None:
assert monitor.dout() == read_expected
scoreboard.apply_read()
if write_accepted:
scoreboard.apply_write(din)这个顺序尤其适用于 full 时读写同地址:先验证读出旧元素,再把新元素加入逻辑队尾。
flags 如何检查
assert flags["empty"] == int(len(expected) == 0)
assert flags["full"] == int(len(expected) == DEPTH)
assert flags["almost_full"] == int(len(expected) >= THRESHOLD)每一个 fifo_step() 都检查 flags,而不是只在 fill/drain 结束时检查。这样 count 如果中途偏离,失败会靠近根因周期。
独立模型与同源错误
deque 模型比复制 RTL 指针算法更独立:它从抽象队列规格出发,不需要模拟物理地址回绕。不过 reference model 和 DUT 仍可能共享同一个错误假设,例如双方都把 empty 同拍读写定义成只写。
边界语义要先写成规格
Scoreboard 不是规格本身。empty/full 同拍行为、
dout保持方式和 almost-full 阈值必须先由需求决定,再分别实现到 RTL 与 model。
当前模型的参数化限制
Python 文件把 DATA_WIDTH=8、DEPTH=16、threshold 14 写成常量,Makefile 另有一份相同参数。两份配置如果漂移,scoreboard 会基于错误容量判分。
改进方法包括从环境变量统一传参、从 DUT 参数可见对象读取可验证配置,或由同一个 regression script 同时生成 simulator 参数和 Python 配置。
常见误区
- 满时所有写都拒绝:忽略同拍有效读释放的空间。
- 先 append 再取 read expected:会破坏 empty 同拍读写语义。
- 只检查数据不检查 flags:count 错误可能延迟很久才反映为数据错。
- reference model 复制 RTL 指针细节:两边更容易出现同源 bug。
自测
- reference queue 满时同时读写,更新前后长度分别是多少?
- 为什么
read_expected要在driver.cycle()前保存? - 若 Makefile 把 DEPTH 改为 32,但 Python 仍为 16,会出现什么现象?
答案要点:都等于 DEPTH;需要旧队首和旧 occupancy;model 会过早认为 full 并产生错误预测。
面试复述要点
Scoreboard 用 deque 表达抽象 FIFO 规格,每拍先根据旧 occupancy 预测 read/write acceptance,再驱动 DUT、比较旧队首、更新 reference queue,并检查所有 flags。这能覆盖满状态同拍替换等边界。
上一篇:Cocotb 验证架构。下一篇:定向测试与随机回归。