ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

基于UVM的AHB Lite eFlash控制器验证平台设计与实践

基于UVM的AHB Lite eFlash控制器验证平台设计与实践 1. 项目缘起为什么需要一个专门的AHB Lite eFlash控制器验证平台在芯片设计领域尤其是涉及嵌入式存储eFlash的SoC项目中验证工作往往占据了整个开发周期的大头。我最近刚结束一个项目核心任务就是为一款集成了eFlash控制器的IP模块搭建一个完整的验证环境。这个控制器通过AHB Lite总线与系统其他部分通信负责执行对片上eFlash的擦除、编程和读取操作。听起来似乎是个标准的总线接口验证对吧但实际一上手问题就来了。最开始的方案是复用之前验证AHB SRAM控制器的环境简单改改地址映射和寄存器模型就打算开跑。结果第一个测试向量下去就卡住了一个简单的读操作在AHB总线上看时序和数据都对但读回来的数据却和预期不符。排查了半天才发现eFlash的访问延迟和SRAM完全不同它存在一个固定的读延迟Read Latency并且编程和擦除操作是异步的需要轮询状态寄存器或者等待中断。我们之前的环境完全没考虑这些特性导致激励和检查器Checker的时间窗完全错位。这次教训让我深刻意识到对于eFlash控制器这种带有复杂时序和状态机的模块一个通用、粗粒度的验证平台是远远不够的必须为其量身定制。这就是本次项目的核心驱动力构建一个基于UVM方法学、专门针对AHB Lite接口eFlash控制器的验证平台。目标不仅仅是跑通几个基础用例而是要能高效、可靠地覆盖控制器所有可能的工作状态、异常场景和边界条件确保这颗“心脏”在复杂的SoC系统中稳定跳动。这个平台需要理解eFlash的“脾气”如页编程、块擦除的时序能精准模拟AHB总线的交互并能自动检查从发起事务到最终数据一致性的完整链路。2. 平台顶层架构与组件拆解一个健壮的UVM验证平台其顶层架构决定了后续所有开发、调试和回归测试的效率。对于这个AHB Lite eFlash控制器验证平台我将其划分为四个核心层次自顶向下分别是测试场景层、环境层、接口层和被测设计DUT层。整个平台的架构是围绕“激励生成-驱动-监测-检查-报告”这一闭环设计的。平台顶层框图概念描述整个验证环境以一个uvm_env类为容器。在这个容器内最核心的组件是ahb_agent和eflash_scoreboard。ahb_agent是一个标准的UVM Agent内部实例化了ahb_sequencer、ahb_driver和ahb_monitor。ahb_driver负责将ahb_transaction序列项sequence item中的信息按照AHB Lite协议时序通过virtual interface驱动到DUT的引脚上。ahb_monitor则是一个被动的观察者它从同一个virtual interface上捕捉总线上发生的所有事务无论这个事务是由我们的测试发起的还是由其他潜在的主设备虽然本项目是单一主设备发起的并将其转换为ahb_transaction发送给后续的分析组件。eflash_scoreboard是整个平台的“大脑”和“裁判”。它通过UVM的analysis_port和analysis_export机制订阅了ahb_monitor和eflash_reference_model的输出。它的工作流程是每当ahb_monitor捕捉到一个写操作比如配置寄存器、发起编程命令它会将这个事务副本发送给eflash_reference_model。参考模型是一个用SystemVerilog或C编写的、高抽象层次的eFlash行为模型它模拟了eFlash阵列的真实行为包括数据存储、擦除状态、编程时间等。当ahb_monitor捕捉到一个读操作读数据或读状态时scoreboard会同时收到来自Monitor的真实DUT输出事务以及来自reference_model基于之前所有操作计算出的预期输出事务并进行比对。任何不匹配都会立即报告错误。此外平台中还有一个eflash_reg_adapter和eflash_reg_model。寄存器模型并不是必须的但它能极大提升验证效率。通过寄存器模型我们可以使用uvm_reg的read()/write()方法进行寄存器访问这些调用在后台会被自动转换成通过ahb_sequencer发送的AHB总线事务使得测试用例的编写更加抽象和便捷。注意在搭建初期很多人会纠结是否一开始就引入寄存器模型。我的经验是如果控制器的寄存器数量超过10个且交互复杂那么尽早引入是值得的。它虽然增加了一些前期建模工作量但能避免在测试用例中大量出现硬编码的uvm_do_with宏使测试逻辑更清晰后期维护成本更低。3. AHB Lite接口Agent的深度定制AHB Lite Agent是平台与DUT物理接口对话的“喉舌”。虽然市面上有开源的AHB/UVCUniversal Verification Component但直接用于eFlash控制器验证往往需要深度定制。这里的关键在于我们的Driver和Monitor必须理解eFlash操作在AHB总线上的“特殊表现”。首先是Driver的定制。一个标准的AHB Driver可能只处理单次传输SINGLE或未定义长度的突发INCR。但对于eFlash我们经常需要模拟CPU通过AHB Lite对控制器进行寄存器配置通常是32位字访问以及进行连续的数据读写例如写入一个页的数据。因此我们的ahb_transaction需要扩展除了包含HADDR、HWDATA、HRDATA、HWRITE等基本字段外还需要一个burst_type字段虽然AHB Lite只支持SINGLE和INCR但我们可以用INCR来模拟连续访问以及一个data_queue用于存放突发传输的数据数组。更关键的是时序模拟。eFlash控制器的响应可能不是即时的。例如当Driver发起一个“块擦除”命令的写操作后DUT可能需要几十微秒才能完成擦除在此期间如果Driver立即发起一个读状态寄存器的操作DUT可能返回“忙”。因此Driver需要具备简单的“协议感知”能力。我们在Driver的run_phase中实现了一个状态机当发送一个命令寄存器写操作后Driver可以自动插入若干个空闲周期基于配置或者更智能地在发送下一个事务前先模拟一个对状态寄存器的轮询读操作直到读到“就绪”标志。这可以通过在Sequence中编排事务来实现但将部分智能放在Driver里能让Sequence更专注于业务逻辑。其次是Monitor的定制。ahb_monitor的核心任务是准确捕捉总线事务。这里的一个常见陷阱是信号采样点的选择。AHB协议规定传输地址和控制在HCLK上升沿有效写数据也在同一个上升沿有效而读数据则在之后的上升沿由从设备提供。我们必须确保Monitor的采样时钟与Driver的驱动时钟同步并且处理好各种握手信号HREADY,HRESP。特别是对于错误响应HRESPERRORMonitor需要能正确捕获并封装到事务中传递给Scoreboard做异常情况检查。此外一个实用的技巧是让Monitor具备事务过滤功能。因为AHB总线可能挂载多个从设备我们的Monitor只应关心目标eFlash控制器地址空间内的事务。我们可以在Monitor中配置一个地址范围addr_min,addr_max只将落在此范围内的事务广播出去这样可以减少Scoreboard和Reference Model的处理负担避免无关信号干扰。4. eFlash行为参考模型与记分板的设计策略参考模型Reference Model和记分板Scoreboard是验证平台正确性的“黄金标准”。它们的设计直接决定了验证的深度和可靠性。eFlash参考模型的设计关键在于平衡精度与速度。一个过于复杂的、试图精确模拟每个晶体管行为的模型不仅开发困难运行也会极其缓慢。我们的目标是做一个“行为正确”的模型。这个模型内部维护着一个虚拟的存储阵列通常用logic [7:0] mem [int]这样的关联数组实现以及每个存储单元的状态如已擦除、已编程、无效。它需要实现几个核心方法write_register(addr, data): 处理对控制寄存器如命令寄存器、地址寄存器、数据寄存器的写入。当接收到特定的命令字如8’hA5代表页编程时模型会触发相应的内部状态机。read_register(addr): 返回模型内部寄存器值如状态寄存器忙/闲、操作成功/失败、ID寄存器等。apply_delay(): 模拟真实eFlash的操作延迟。例如当模型执行“页编程”命令时它会启动一个内部计数器或使用#delay在非时序区域在延迟期间状态寄存器应反映为“忙”。get_expected_data(addr): 当Scoreboard需要比对读数据时调用此函数根据模型内部存储阵列的状态返回预期数据。一个高级的增强功能是模拟eFlash的物理特性比如写保护如果对写保护的区块进行编程模型应记录操作失败并在状态寄存器中置位错误标志。擦除后全为1在模型中擦除操作应将对应存储区域的所有比特位置为1‘b1或8’hFF。编程只能将1变为0尝试将已编程为0的位再次编程为0是允许的但试图将其从0变为1在没有擦除的情况下应被视为错误或无效操作。模型可以不模拟这种位级变化但至少应在收到编程数据后与当前存储数据做逻辑“与”操作检查是否合法。记分板的设计则侧重于数据比对与覆盖率收集。我们的eflash_scoreboard继承自uvm_scoreboard内部有两个uvm_tlm_analysis_fifo分别用于缓存来自Monitor的DUT实际事务和来自参考模型的预期事务。在run_phase中它会不断地从两个FIFO中尝试获取事务。比对策略是对于写命令/数据事务Scoreboard将其转发给参考模型但不进行直接比对除非总线返回错误响应HRESP。对于读数据事务Scoreboard等待来自参考模型的预期读数据事务并与Monitor捕获的实际读数据事务进行比对。比对的字段包括HRDATA读数据和HRESP响应。对于读状态寄存器事务比对逻辑需要更灵活。因为DUT的实际操作时间可能与模型不完全一致在操作未完成时DUT返回“忙”而模型可能也已置“忙”这是匹配的。但当模型认为操作已完成时DUT可能还差几个周期才完成。因此对于状态寄存器的比对有时需要采用“模糊匹配”或引入一个小的时间窗口容忍度而不是严格的周期精确匹配。记分板另一个重要职责是收集功能覆盖率。我们可以在Scoreboard中定义覆盖组covergroup监控诸如“成功完成页编程操作”、“擦除被写保护区块触发错误”、“背靠背连续读操作”等场景是否被测试到。这些覆盖点与通过寄存器模型收集的代码覆盖率行覆盖、条件覆盖相辅相成共同衡量验证的完备性。5. 测试序列Sequence的构造与场景化测试序列Sequence是验证平台的“指挥棒”它定义了具体的测试场景。对于eFlash控制器我们的测试序列需要从简单到复杂从正常功能到异常情况层层递进。基础功能序列是首先要实现的。这包括寄存器读写验证序列随机或顺序地对所有可读写的寄存器进行写-读回比对确保地址映射和访问路径正确。ID/版本号读取序列验证只读寄存器。单字节/单字编程与读取序列验证最基本的编程和读取功能数据可以选用固定的8‘hAA、8’h55或随机数。单块擦除与验证序列擦除一个块然后读取该块内多个地址验证数据是否全为8‘hFF。构造这些序列时我习惯使用uvm_do宏族来发送事务但更推荐使用uvm_send或start_item()/finish_item()的方式因为它们对事务对象的控制更灵活。例如在构造一个页编程序列时我们可能需要先发送写命令寄存器事务再发送写地址寄存器事务然后通过一个循环发送多个写数据寄存器事务最后发送写启动编程命令寄存器事务。这个过程中我们可以将页地址和数据数组作为序列的成员变量使得序列可配置和可重用。高级场景与异常测试序列是验证的难点和重点。我们需要有意识地构造一些“刁钻”的场景中断测试序列使能编程完成中断在执行编程操作后等待中断发生并在中断服务程序中读取状态、清除中断标志。这需要序列能够与虚拟序列virtual sequence协调模拟处理器行为。超时测试序列向控制器发送一个非法或不被支持的命令检查控制器是否能在预设的超时时间内通过状态寄存器或HRESP报告错误。背靠背操作序列不等待上一个操作如擦除完成立即发起下一个操作如编程。检查控制器是否能正确处理这种冲突是排队、拒绝还是导致状态机混乱。电源状态模拟序列模拟在编程或擦除过程中发生复位通过HRESETn信号。复位解除后检查eFlash阵列的数据一致性以及控制器的状态是否恢复到默认值。地址边界测试序列针对页大小和块大小的边界进行编程和擦除例如对一个128字节的页尝试编程127、128、129字节的数据观察控制器行为。为了管理这些复杂的序列我通常会建立一个分层序列库。底层是原子操作序列如reg_write_seq,flash_read_seq中层是组合序列如flash_program_page_seq顶层是场景序列如interrupt_test_seq和虚拟序列base_vseq后者可以协调多个Agent虽然这里只有一个AHB Agent但为扩展考虑的序列同步执行。6. 调试与排查实战中遇到的典型问题与解决思路平台搭建和测试执行过程中遇到问题是家常便饭。记录下几个有代表性的调试案例或许能帮你绕过同样的坑。问题一记分板报告读数据不匹配但波形显示总线数据正确。这是最令人困惑的情况之一。波形上HRDATA明明是对的为什么Scoreboard说不对我们首先检查Scoreboard的比对逻辑。发现问题是事务对象transaction的拷贝时机。我们的ahb_monitor在HREADY为高、传输完成的那个时钟沿采样HRDATA并创建事务对象。然而参考模型在接到写命令事务时可能需要若干周期来计算预期数据。如果Scoreboard采用阻塞式的get方法从两个端口获取事务它可能在收到DUT读事务时参考模型的预期事务还没计算出来导致比对失败。解决方案在Scoreboard中使用uvm_tlm_analysis_fifo来缓冲事务。对于读操作Scoreboard先get实际事务然后以其地址为键在一个关联数组mailbox或队列中等待预期事务的到来。或者更简单的方法是让参考模型也以事务形式输出预期数据并同样送入一个FIFOScoreboard只需比较两个FIFO出来的事务这要求测试序列的激励顺序是确定性的或者参考模型的延迟是固定的、可预测的。问题二随机测试偶尔会挂起仿真超时。使用随机约束生成测试序列时有时仿真会卡住不前进也不出错。通过波形调试发现AHB总线的HREADY信号被DUT拉低了并且再也没有拉高。这通常是因为我们发送了一个DUT无法处理或未定义的命令或访问导致其内部状态机进入了一个死锁状态。解决方案在Driver或Sequence中增加“看门狗”机制。例如在Driver驱动一个事务后启动一个计数器如果连续N个周期HREADY都为低且没有错误响应HRESP则Driver可以主动终止本次传输例如在下个周期将HSEL拉低并报告一个警告然后继续后续事务。同时在Sequence的约束中要避免产生非法的命令码或访问未映射的地址空间。更根本的是需要与设计工程师确认状态机的完备性确保所有非法输入都有安全的恢复路径。问题三寄存器模型的前门访问frontdoor与后门访问backdoor混用导致数据不一致。为了加速测试我们有时会对eFlash存储阵列使用后门访问直接通过HDL路径强制或读取信号进行初始化或检查。但如果同时寄存器模型通过前门AHB总线访问控制器可能会产生冲突。例如后门直接修改了某个存储单元的数据但控制器内部的数据缓存如果有并未更新导致下一次前门读操作返回旧数据。解决方案明确划分前后门访问的职责。一个可行的规范是仅使用后门访问进行测试环境的初始化如在测试开始前将整个eFlash阵列预擦除为全FF所有的测试激励和结果检查都通过前门访问总线完成。这样最能模拟真实芯片的工作场景。如果必须混合使用则需要在关键操作点如后门写入后通过前门发起一个缓存无效化操作如果DUT支持或者简单地等待足够长的时间让缓存同步。7. 平台复用与项目集成考量一个好的验证平台不应是“一次性”的。在项目后期或衍生项目中我们可能需要将这个针对特定eFlash控制器的验证环境集成到更大的子系统或SoC级验证环境中去。垂直复用从模块级到系统级当这个eFlash控制器作为IP集成到更大的SoC中时我们的验证环境可以作为一个uvm_env的子环境被实例化到SoC的验证平台中。此时AHB Agent的virtual interface需要连接到SoC级AHB总线的相应信号上。我们需要将原本顶层的ahb_if接口替换为通过uvm_config_db从SoC测试平台顶层传递下来的、对应到该控制器总线接口的virtual interface。同时eFlash控制器的物理接口如与eFlash宏单元连接的信号可能需要被“黑盒化”处理或者连接到SoC平台级的eFlash行为模型。水平复用适配不同规格的eFlash控制器如果公司有另一款接口相同AHB Lite但容量、页大小、块大小不同的eFlash控制器我们可以通过配置化Configuration来复用大部分平台代码。具体做法是创建一个eflash_config类包含flash_size、page_size、block_size、t_prog编程时间、t_erase擦除时间等参数。在环境env的build_phase中创建并随机化或从外部文件加载这个配置对象并将其通过uvm_config_db设置到各个组件Reference Model, Scoreboard, Sequences中。参考模型、记分板的比对逻辑、序列中的地址/数据约束都基于这个配置对象来工作。这样通过更换配置文件就能快速将平台适配到新的控制器型号。回归测试与持续集成平台稳定后需要建立自动化的回归测试流程。我们将所有重要的测试用例sequences封装在一个测试列表testlist中使用脚本通常基于Makefile或Python调用仿真工具如VCS, Xcelium进行批量运行。关键是要收集每次回归的结果不仅是通过/失败还包括功能覆盖率和代码覆盖率报告。我习惯使用一个简单的数据库或日志文件来跟踪覆盖率增长趋势确保每次代码修改都不会使覆盖率下降。将这个过程集成到像Jenkins这样的CI/CD工具中可以实现每次代码提交后自动触发回归尽早发现集成错误。
返回列表