前一段时间,我写了一篇有关如何处理UVM agent组件中的reset的文章。有人问了一个很好的问题:如何处理计分板上的reset?在本文中,我将与您分享我在验证环境中处理reset的方式。
假设我们有一个带有APB接口的DUT,用于访问某些内部寄存器。
与在agent中处理reset一样,在完整的验证环境中,我尝试在每个组件中实现handle_reset()函数,并且应从env组件中的一个线程最后调用所有这些函数。
验证环境的简化版本可能如下所示:
步骤1:处理model中的reset
model组件是环境中最复杂的组件。它包含寄存器块以及用于建模DUT行为的任何其他元素和线程。所有这些元素都需要reset。
class cfs_dut_model extends uvm_component;
//function for handling reset inside the model
virtual function void handle_reset(string kind = "HARD");
//kill and restart any ongoing threads
reg_block.reset(kind);
//clear here any other elements part of the model class
endfunction
endclass
步骤#2:在计分板上处理reset
在记分板上处理reset应该首先杀死并重新启动任何正在进行的过程,然后重置model:
class cfs_dut_scoreboard extends uvm_component;
//function for handling reset inside the scoreboard
virtual function void handle_reset(string kind = "HARD");
//kill and restart any ongoing threads
model.reset(kind);
//clear here any other elements part of the scoreboard class
endfunction
endclass
步骤#3:处理覆盖率收集器内的reset
最有可能在覆盖率收集器内处理reset将意味着在reset时对DUT的内部状态进行一些采样,然后进行一些clear工作:
class cfs_dut_coverage extends uvm_component;
//function for handling reset inside the coverage
virtual function void handle_reset(string kind = "HARD");
//sample the state of the DUT at reset
//clear here any other elements part of the coverage class
endfunction
endclass
步骤#4:处理env中的reset
在环境中处理reset应该类似于在agent中处理reset的逻辑。
首先,我们需要有一个handle_reset()函数来进行实际的reset:
class cfs_dut_env extends uvm_component;
//function for handling reset inside the environment
virtual function void handle_reset(uvm_phase phase, string kind = "HARD");
coverage.handle_reset(kind);
scoreboard.handle_reset(kind);
//clear here any other elements part of the environment class
endfunction
endclass
接下来,我们需要一个实际上等待物理复位并调用handle_reset()函数的线程:
class cfs_dut_env extends uvm_component;
virtual task run_phase(uvm_phase phase);
forever begin
wait_reset_start();
handle_reset(phase, "HARD");
wait_reset_end();
end
endtask
endclass
在大多数情况下,此逻辑可以正常工作,但在极端情况下,此逻辑可能会崩溃。
在上述逻辑和本文的第1部分中,我们可以标识验证环境的多个并行的独立执行线程:
- 线程#1 – APB监视代理程序:用于收集APB事务的任务。该线程通常通过更新和检查寄存器值来影响寄存器块。
- 线程2 – APB代理:用于检测reset的任务。该线程将仅影响APB agent的内部逻辑,并且将要执行的操作之一是停止线程#1并重新启动它。有关详细信息,请参阅本文第1部分中的步骤1。
- 线程#3 –环境:用于检测reset的任务。该线程将影响DUT的模型(例如重置寄存器块),重置coverage类等。
如果复位是异步的,并且从不与时钟的上升沿同时激活,则一切正常,但是我们不能保证。
问题是,当所有线程都希望完全在同一仿真时间内采取某些措施时:
- 复位在时钟的上升沿变为有效
- 在APB上有寄存器访问
因为这些是并行线程,所以从验证工程师的角度来看,它们的执行顺序或多或少是随机的。
因此,例如,如果按以下顺序执行线程#3->#1->#2,则:
- 线程#3将清除模型,因此这意味着寄存器块将被复位
- 数据将被线程#1写入一些内部寄存器(如CTRL)中,因为它收集了APB访问
- 线程#2将复位APB agent并重新启动线程#1,但为时已晚,因为错误数据已写入CTRL寄存器模型中
有一个简单的解决方法:我们摆脱了用于处理agent程序(#2)中的复位的线程,而处理了线程#3中的agent程序复位。
首先,我们需要阻止agent自行处理reset:
class cfs_dut_env extends uvm_component;
virtual function void end_of_elaboration_phase(uvm_phase phase);
apb_agent.agent_config.set_should_handle_reset(0);
//do the same for any other agent part of the environment
endtask
endclass
接下来,我们应该在env中以正确的顺序处理reset代理的问题:
class cfs_dut_env extends uvm_component;
//function for handling reset inside the environment
virtual function void handle_reset(uvm_phase phase, string kind = "HARD");
apb_agent.handle_reset(phase, kind);
//do the same for any other agent part of the environment
coverage.handle_reset(kind);
scoreboard.handle_reset(kind);
//clear here any other elements part of the environment class
endfunction
endclass
这样,始终可以正确reset验证环境,因为:
- 如果首先发生在线程3,然后线程1,则线程3将重新启动线程1,因此没有APB事务到达模型
- 如果首先发生在线程1,然后是线程3,则线程1将数据推送到寄存器块模型中,但是线程3将立即将其清除。
可以将相同的逻辑应用于验证环境的任意数量的agent程序部分。
基本思想是:从一个线程处理整个环境的复位,以便您可以控制复位组件的顺序。
采用才reset处理方法的vip的例子:https://github.com/amiq-consulting/amiq_apb