ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Synopsys USB VIP配置与TLM连接实战:常见错误排查与避坑指南

Synopsys USB VIP配置与TLM连接实战:常见错误排查与避坑指南 1. 为什么USB VIP的配置与TLM连接值得单独拿出来讲做过USB验证的人大概都有这个体会USB协议本身不算最难的难的是把VIP跑起来、把TLM连接接对、把transaction顺顺利利地从sequence送到DUT再收回来。我见过太多项目环境搭了两周波形dump了一堆最后发现是analysis port没连上或者monitor的item卡在FIFO里出不来。这类问题不涉及什么高深算法但就是能耗掉你大把时间。Synopsys的USB VIPVerification IP在业界用得相当广配套的UVM TLM机制也是标准套路。但标准归标准实际配置时踩的坑一个都不少VIP版本和VCS版本对不上、uvm_analysis_imp的宏写错、connect_phase里连了但build_phase里没建、analysis_fifo和普通tlm_fifo用混了导致阻塞……这些问题在文档里往往一笔带过但在真实项目里个个都是拦路虎。这篇内容面向的是已经有一定UVM基础、正在或即将用Synopsys USB VIP搭验证环境的工程师。我会把配置流程、TLM连接的关键节点、以及我自己踩过的典型错误拆开来讲尽量做到你照着做就能跑通遇到报错能对上号。核心关键词就四个Synopsys、USB VIP、TLM、配置、错误排查全文围绕它们展开。需要说明的是下面涉及的很多操作细节比如具体的VIP参数名、端口命名不同版本会有差异我讲的是通用思路和常见实践你实际用的时候要对着自己那版的VIP User Guide核对。这一点先讲在前面免得你照搬出问题。2. USB VIP配置的整体思路与方案选型2.1 先搞清楚你要验证的是Host还是DeviceUSB VIP通常同时支持Host模式和Device模式但一个具体的验证环境里你一般只需要其中一种。这个选择决定了后面agent的搭建方式、sequence的写法、甚至TLM连接的拓扑。如果你验证的是USB Device DUT那VIP要配成Host模式由VIP发起枚举、发IN/OUT tokenDUT响应。反过来验证Host DUT时VIP配成Device模式被动等待Host的请求。这个方向搞反了环境跑起来会一直卡在等token的状态波形上什么都看不到新手很容易在这里懵。配置方向上Host模式和Device模式在VIP的cfg对象里通常有一个类似is_host或者mode的字段来控制。我的建议是在build_phase里第一时间把这个字段设好别等到connect_phase才想起来因为VIP内部很多组件的构建逻辑依赖这个值。2.2 VIP版本与仿真器版本的匹配问题这是最容易被忽略、但后果最严重的一类坑。Synopsys的VIP是编译好的加密库它对VCS的版本有明确要求。你拿一个2022版的VIP去配2020版的VCS大概率在elaboration阶段就报一堆找不到符号的错误。我的做法是拿到VIP的第一件事不是急着写代码而是去看VIP安装目录下的release note或者README确认它支持的VCS版本范围。然后在项目里固定这个组合写进团队的setup脚本里。别小看这一步我见过一个项目因为换了VCS版本没同步换VIP整个环境编译不过排查了一整天才定位到版本不匹配。提示VIP的版本信息一般在安装路径的doc或release_notes目录下文件名里通常带版本号和日期。养成先看release note的习惯能省掉大量无谓的调试。2.3 环境拓扑的规划在动手写代码之前先在纸上把拓扑画清楚。一个典型的USB验证环境包含VIP agentdriver、monitor、sequencer、你的scoreboard、reference model、以及DUT。TLM连接就是把这几块用port和export接起来。这里有个经验USB的monitor通常会往外发多种类型的transaction比如token packet、data packet、handshake packet有的VIP还会发完整的transfer级别item。你要提前想清楚scoreboard需要哪一种然后在monitor的analysis port上接对应的fifo。如果接错了层级scoreboard收到的item字段对不上比对逻辑就全乱了。我一般会在规划阶段列一张表把每个组件的输出端口、输入端口、以及它们之间怎么连写清楚后面写代码时直接照着填效率高很多也不容易漏连。3. 核心配置细节与TLM连接实操要点3.1 agent的构建与cfg配置USB VIP的agent构建核心是把config对象配好。以常见的做法为例你需要在build_phase里创建cfg设置好模式、速度LS/FS/HS/SS、以及各种超时参数。速度这个参数特别关键。USB 2.0有Low Speed、Full Speed、High SpeedUSB 3.x还有SuperSpeed。你DUT支持哪个速度VIP就得配成对应速度。配错了枚举阶段就会失败波形上表现为VIP发的reset或者chirp序列和DUT对不上。我遇到过把HS的DUT配成FS的情况结果DUT一直不回handshake查了半天才发现是速度配置的问题。超时参数也值得说一句。VIP默认的超时值有时候偏保守在慢速仿真或者DUT响应较慢的场景下会误报超时。你可以适当调大但别调得太大否则真出问题时定位不到。我的习惯是先按默认跑如果确实因为超时误报再针对性调整并记录下调整的原因。3.2 TLM port与export的连接顺序TLM连接有个基本原则connect的方向是从发起方指向接收方。具体到UVM里就是port.connect(export)或者port.connect(imp)。这个方向搞反了编译能过但运行时数据流是断的。USB VIP的monitor一般提供一个analysis_port你要把它连到scoreboard的analysis_imp或者先连到一个uvm_tlm_analysis_fifo再从fifo连到scoreboard。这两种方式都常见区别在于直接连analysis_impmonitor一发itemscoreboard的write函数立刻被调用是阻塞式的即时处理。经过analysis_fifoitem先存进fifoscoreboard在自己的线程里主动get解耦了生产和消费的节奏。选哪种取决于你的scoreboard处理速度。如果scoreboard处理一个item很快直接连没问题如果处理逻辑复杂、耗时用fifo更稳避免monitor被拖慢。3.3 analysis_fifo和普通tlm_fifo的区别这个坑很多人踩热词里有人问uvm_tlm_fifo和uvm_tlm_analysis_fifo的区别这俩确实容易混。核心区别在于特性uvm_tlm_fifouvm_tlm_analysis_fifo写入接口put/get阻塞write非阻塞analysis_imp典型用途组件间主动通信monitor到scoreboard的单向数据流是否阻塞生产者是put会阻塞否write立即返回端口类型put_export/get_exportanalysis_export关键点analysis_fifo的write函数是永不阻塞的它内部用无限深度的队列存数据。而普通tlm_fifo的put在fifo满时会阻塞。monitor往外发数据时你绝对不希望它被阻塞所以monitor的输出一定要接analysis_fifo或者analysis_imp不能接普通tlm_fifo的put口。我见过有人把monitor的analysis_port连到普通tlm_fifo的put_export上编译直接报类型不匹配因为analysis_port的接口是write不是put。这种错误编译期就能发现还算好的。更隐蔽的是连接对了但fifo深度设太小跑大批量数据时fifo满了虽然analysis_fifo不阻塞但如果中间经过了别的有界队列就可能丢数据。3.4 connect_phase里的连接代码怎么写connect_phase是TLM连接的主战场。我习惯把所有连接集中写在这里一目了然。一个典型的片段长这样function void my_env::connect_phase(uvm_phase phase); super.connect_phase(phase); // monitor到scoreboard usb_agent.monitor.ap.connect(scoreboard.usb_export); // sequencer到driverVIP内部一般已连好确认即可 // scoreboard到reference model scoreboard.ref_export.connect(ref_model.imp); endfunction这里要注意VIP内部的driver和sequencer连接通常VIP自己会在它的agent里连好你不需要重复连。如果你手贱又连了一遍可能报port already connected的错。所以连接前先确认哪些是VIP内部搞定的哪些需要你在env层连。注意connect_phase里不要做任何耗时操作也不要创建对象。这个phase的职责就是连线创建对象应该在build_phase完成。把创建逻辑放到connect_phase轻则报null pointer重则环境行为不可预测。4. 完整实操流程与关键环节实现4.1 从零搭建环境的步骤拆解我把整个流程拆成六步你可以照着走确认版本组合VIP版本、VCS版本、UVM版本三者匹配写进setup脚本。搭建顶层env创建env类实例化agent、scoreboard、ref model。配置VIP cfg在env的build_phase里创建并配置USB VIP的cfg设置Host/Device模式、速度、超时。传递cfg用uvm_config_db把cfg设到agent注意路径要写对。连接TLM在connect_phase里把monitor的analysis port连到scoreboard。写sequence并启动在test里创建sequence用start方法挂到sequencer上raise_objection控制仿真时长。每一步都有细节下面挑几个重点讲。4.2 cfg传递的路径陷阱uvm_config_db的路径匹配是新手最容易翻车的地方。假设你在env里这样设uvm_config_db#(usb_cfg)::set(this, usb_agent, cfg, cfg);那agent里就得这样取if (!uvm_config_db#(usb_cfg)::get(this, , cfg, cfg)) uvm_fatal(CFG, usb cfg not found)注意set的第二个参数是相对路径usb_agentget的第一个参数是this即agent自己第二个参数是空字符串。这个对应关系搞错了get就返回0然后你的uvm_fatal触发环境起不来。我的经验是路径尽量用相对路径别用绝对路径因为绝对路径里带层次名一旦你改了组件名字所有set都得跟着改。相对路径配合this指针组件改名也不影响。4.3 sequence的启动与objection机制USB的sequence启动常见做法是在test的run_phase里task my_test::run_phase(uvm_phase phase); my_usb_sequence seq; phase.raise_objection(this); seq my_usb_sequence::type_id::create(seq); seq.start(env.usb_agent.sequencer); phase.drop_objection(this); endtask这里有个坑raise_objection和drop_objection必须成对而且drop要在sequence完全跑完之后。如果sequence里用了fork或者启动了子sequence你要确保所有子sequence都结束了再drop否则仿真可能提前结束波形不完整。另一个坑是objection的object。raise_objection(this)里的this是test这没问题。但如果你在sequence里也raise了objection要确保对应的drop也在sequence里别跨组件raise和drop那样容易漏。4.4 参数计算超时值怎么定USB的超时不是随便设的。以USB 2.0为例协议规定了各种超时时间比如Host在发出token后等待响应的时间。VIP的默认超时通常基于这些协议值但仿真时间单位和真实时间单位有换算关系。假设你的仿真时间单位是1nsUSB FS的某个超时协议值是16个bit timeFS的bit time是83.3ns那超时就是16 × 83.3 ≈ 1333ns。VIP内部一般会帮你算好但如果你要手动覆盖得按这个逻辑来。我的建议是除非有明确理由否则用VIP默认值别自己乱改。改之前先算清楚改完记录在案。4.5 实操现场一次完整的枚举流程环境搭好后第一次跑通常先验证枚举。枚举是USB通信的基础枚举过了后面的数据传输才有意义。枚举的大致流程VIPHost模式发resetDUT响应VIP发SET_ADDRESSDUT设地址VIP发GET_DESCRIPTORDUT返回设备描述符依次获取配置描述符、字符串描述符最后SET_CONFIGURATION。每一步在波形上都能看到对应的token、data、handshake。我一般会在scoreboard里加一些打印把monitor收到的每个packet的PID、地址、端点号打出来对照协议流程看。如果卡在某一步比如GET_DESCRIPTOR之后DUT没回data那可能是DUT的描述符没准备好或者VIP的请求格式不对。这时候看波形比看log更直接。5. 常见错误排查与避坑经验实录5.1 编译期错误速查错误现象可能原因解决方向找不到VIP的package符号VIP库没编译或路径没加检查-y或incdir确认VIP编译进库port类型不匹配analysis_port连到了put_export改用analysis_fifo或analysis_imp重复定义VIP内部已连你又连了一遍确认VIP内部连接去掉重复代码版本相关符号缺失VIP与VCS版本不匹配核对release note换匹配版本编译期错误相对好查因为报错信息通常指向具体行号。关键是别慌从报错的第一条开始看后面的错误往往是第一条引起的连锁反应。5.2 运行期卡死排查运行期卡死是最头疼的因为没报错就是不动。常见原因有几个TLM没连上monitor发了item但scoreboard没收到或者sequence发了但driver没拿到。排查方法是加打印在monitor的write函数、scoreboard的write函数、driver的get_next_item前后都打log看数据流断在哪一环。objection没drop仿真一直跑但没有任何transaction。检查drop_objection是否被执行到有时候sequence里有个wait永远等不到条件就卡住了。时钟或复位没起来VIP依赖时钟和复位信号如果接口上的clock没接对VIP内部状态机不跳转。检查interface的连接和时钟频率。我的排查顺序是先看log有没有打印再看波形有没有信号翻转最后看TLM的fifo里有没有积压。这三步基本能定位大部分卡死问题。5.3 analysis port连接了但收不到数据这个现象很典型编译过了connect_phase也没报错但scoreboard的write函数就是不触发。原因通常是monitor的analysis port在VIP内部被连到了别的地方或者monitor根本没使能。有些VIP的monitor默认是关闭的需要在cfg里打开比如设一个monitor_enable之类的字段。你没打开monitor就不发item自然收不到。另一个可能是connect_phase的执行顺序。UVM的phase是自顶向下执行的env的connect_phase在agent的connect_phase之前。如果你在env的connect_phase里连agent内部的port而agent的port还没创建好连接就会失败。解决办法是把连接放到agent的connect_phase里或者确保agent在env之前完成构建。5.4 独家避坑技巧几个我从实际项目里总结出来的技巧文档里一般不写给每个TLM连接加断言或打印在connect_phase结束后打印一条connect done的log确认所有连接都执行到了。别小看这一行log环境复杂时能帮你快速确认连接阶段有没有漏。fifo深度设大一点analysis_fifo虽然不阻塞但如果你的scoreboard处理慢fifo会一直涨占内存。设一个合理的深度上限配合监控能提前发现性能问题。sequence里加超时保护别让sequence无限等待。用fork...join_any配合#timeout超时就报错退出比仿真卡死强。版本信息写进log在test的build_phase里打印VIP版本、VCS版本、UVM版本出问题时第一时间能确认环境。5.5 关于transaction打印的关闭热词里有人问怎么关闭VIP的transaction打印。Synopsys VIP通常有详细的transaction打印跑大批量数据时log会爆炸。关闭方法一般是在cfg里设置verbosity或者用VIP提供的开关字段。具体字段名各版本不同常见的是enable_print或者verbosity相关的配置。我的做法是调试阶段打开确认流程对了之后关掉只在关键节点保留打印。这样既能看到必要信息又不至于被log淹没。6. 一些补充的实操体会USB VIP的配置和TLM连接说到底是个熟练活。第一次搭可能磕磕绊绊搭过两三次之后套路就固定了确认版本、配cfg、连TLM、跑枚举、查波形。真正花时间的往往不是写代码而是排查那些看起来连了但实际没通的问题。我个人在实际操作中的体会是把connect_phase里的每一行连接都当成一个待验证的假设连完就确认一次别攒到最后一起调。环境小的时候无所谓环境一大十几个port连在一起出问题根本不知道是哪根线断了。另外VIP的User Guide一定要翻尤其是版本相关的章节很多坑文档里其实写了只是我们习惯性地跳过。最后分享一个小技巧如果你不确定某个TLM连接该怎么连先去看VIP自带的example。Synopsys的VIP安装包里通常有example目录里面的环境是能跑通的照着改比从零写快得多也不容易出错。
返回列表