针对动态连接场景的验证环境搭建思路
面向动态连接场景的验证环境架构设计思路
(UVM/SV 实践总结)
片上跨模块通信链路(如 DMI/NoC/内部总线)常见一个典型特征:用户侧固定,通道结构随设计演进而频繁变化。例如新增通道、变更用户到通道的映射、调整仲裁入口等。
在项目尾期,这种变化最容易引发验证环境大规模重构,带来高维护成本与回归风险。
核心思路:让“用户”成为唯一不变的设计锚点,通过“路由层”和“代理层(proxy)”来吸收 DUT 的动态变化。
本文给出一套可扩展、工程可落地的环境架构,适用于 DMI、Cache Interface、NoC Port、AXI-Lite Hub 等多接口、多通道场景。
1. 问题本质
大多数设计把“用户请求”转发到不同的内部通道。
设计变动往往发生在:
- 通道数量变化(新增/删除)
- 用户与通道的绑定规则调整
- 通道宽度或握手协议变化
- 仲裁结构调整
实际不变的是:
- 用户数量固定
- 用户发的行为(load/store/rd/wr/rmw)稳定
因此,验证环境的关键不是通道,而是围绕用户构建稳定层,通道变化由中间层吸收。
2. 架构分层:让变化隔离在“路由层”
推荐将环境拆成三层:
用户层(稳定)
路由层(吸收变化)
代理层(对应接口)
2.1 用户层(固定)
- 30 个用户 = 30 套 user_agent
- 每个 user_agent 包含:seq → sqr → driver → monitor
- 不直接绑定 DUT 任一通道
- 只产出“统一格式事务”
- 用户层永远不需要为设计变化而改动
统一事务结构示例:
class dmi_txn extends uvm_sequence_item;rand bit [63:0] addr;rand int uid;rand int chan; // 通道类型,由配置决定rand bit need_ifa;rand bit is_read;rand byte data[];
endclass
用户层的核心任务:
产生行为,不关心流向。
3. 路由层(扩展点)
这是整个动态连接场景的关键组件。
user_driver → router → proxy_rdproxy_wrproxy_rw1proxy_new_x …
职责:
- 根据 txn 的
chan字段选择出口 - 动态映射用户到不同通道
- 被测模块增加通道时,只需在 router 中加分支
- 不改变 user_agent/sqr/seq
示例:
case (t.chan)RD : rd_proxy.send(t);WR : wr_proxy.send(t);RW1 : rw1_proxy.send(t);NEW_X : new_x_proxy.send(t);
endcase
路由层解决的就是动态连接问题本身。
4. 代理层(对应 DUT 各类接口)
每个 DUT 接口配一个代理(proxy),负责:
- 把抽象事务映射到具体握手
- 维持 beat、valid/ready 时序
- 把新的 DUT 接口差异封装在内部
新增接口时,只需新增:
proxy_new_x
代理层保证:
接口变化不会影响路由层与用户层。
5. 参考模型与 scoreboard
参考模型只关心:
- 拟进行的读写
- 仲裁逻辑
- 地址转换逻辑
- 数据一致性
不关心通道数量、接口名称。
因此通道扩展不会引入额外的 scoreboard 复杂度。
scoreboard 只需要订阅两个方向:
用户侧 rsp
内存侧 rsp
6. 动态连接场景的典型需求与处理办法
6.1 新增通道
解决:
- 增加 enum
- 路由层多一个 case
- 增加一个 proxy
其他层不动。
6.2 用户迁移到新通道
解决:
- 修改用户 profile 配置
- 路由自动选择新出口
不改 user_agent,不改 driver,不改 seq。
6.3 通道握手变更
解决:
- 调整对应 proxy
- 路由层与用户层不影响。
6.4 仲裁结构变化
解决:
- 修改参考模型 arb_model
- 不影响任何代理与 sqr。
7. 小结
针对动态连接场景,不需要推倒重建验证环境,只要保持:
- 用户固定
- 路由中间层承担变化
- 代理层屏蔽接口细节
在此架构下,DUT 的通道扩展不会引发大量重构,用例与用户层稳定,环境长期可维护。
