UVM是一种标准验证方法,已被收录为IEEE 1800.12标准。 UVM包括在设计测试平台和测试用例方面定义的方法,并且还附带有一个类库,可帮助轻松轻松地构建有效的受约束的随机测试平台。该方法的一些优点和重点包括:
显然,随着UVM方法在验证行业中的采用越来越广泛,UVM的优点会盖过缺点。
事务级别建模(TLM,Transaction level Modelling)是一种在较高抽象级别上对任何系统或设计进行建模的方法。 在TLM中,使用事务对不同模块之间的通信进行建模,从而抽象出所有底层实现细节。 这是验证方法中用于提高模块化和重用性的主要概念之一。 即使DUT的实际接口由信号级别的活动表示,但大多数验证任务(如生成激励,功能检查,收集覆盖率数据等)都可以在事务级别上更好地完成,只要使它们独立于实际信号级别的细节即可 。 这有助于在项目内部和项目之间重用和更好地维护这些组件。
在事务级建模中,不同的组件或模块使用事务对象进行通信。TLM port 定义了一组用于特定连接的方法(API),这些方法的实际实现称为TLM export。TLM port和export之间的连接建立了两个组件之间的通信机制。
上面是一个简单的示例,演示了生产者如何使用一个简单的TLM port与消费者通信。生产者可以创建一个事务并将其“put”到TLM port,而“put”方法(也称为TLM export)的实现将在读取生产者创建的事务的使用者中进行,从而建立一个通信通道。
如果生产组件和消费组件都需要独立操作,则使用TLM FIFO进行事务性通信。在这种情况下(如下所示),生产组件生成事务并将其“放入”FIFO,而消费组件每次从FIFO获取一个事务并对其进行处理。
get()操作将从TLM FIFO中返回一个事务(如果可用),并从FIFO中删除该事务。如果FIFO中没有可用的事务,它将阻塞并等待,直到FIFO至少有一个事务。peek()操作将从TLM FIFO中返回一个事务(如果可用),而不会实际从FIFO中删除该项目。它也是一个阻塞调用,如果FIFO没有可用的条目,它将等待。
get()是一个从TLM FIFO获取事务的阻塞调用。因为它是阻塞的,所以如果FIFO中没有事务,任务get()将等待。try_get()是一个非阻塞调用,即使FIFO中没有可用的事务,它也会立即返回。try_get()的返回值指示是否返回成功。下面是使用get()和try_get()的两个等效实现
阻塞方法,get
class consumer extends uvm_component;
uvm_get_port #(simple_trans) get_port;
task run;
for(int i=0; i<10; i++)
begin
t = get(); //blocks until a transaction is returned //Do something with it.
end
endtask
endclass
非阻塞方法,try_get
class consumer extends uvm_component;
uvm_get_port #(simple_trans) get_port;
task run;
for(int i=0; i<10; i++)
begin
//Try get is nonblocking. So keep attempting
//on every cycle until you get something
//when it returns true
while(!get_port.try_get(t))
begin
wait_cycle(1); //Task that waits one clock cycle
end //Do something with it
end
endtask
endclass
TLM port/FIFOs用于具有使用put/get方法建立的通信通道的两个组件之间的事务级通信。
analysis port/FIFOs是另一种事务性通信通道,用于组件将事务广播给多个组件。
TLM port/FIFO用于driver和sequencer之间的连接,而analysis port/FIFOs用于monitor广播事务,这些事务可由scoreboard或覆盖率收集组件接收。
sequence item是对在两个组件之间传输的信息进行建模的对象(有时它也可以称为事务,transaction)。例如:考虑从CPU到主内存的内存访问,在主内存中CPU可以执行内存读或内存写,每个事务都有一些信息,比如地址、数据和读/写类型。sequence可以被认为是一个已定义的sequence item模式,它可以被发送到驱动程序以注入到设计中。sequence item的模式是由如何按顺序实现body()方法定义的。例如:扩展上面的例子,我们可以定义一个从10个事务中读取增量内存地址的sequence。在这种情况下,body()方法将被实现来生成10次sequence item,并将它们发送到驱动程序,同时在下一个项之前递增或随机地址。
uvm_transaction 派生自uvm_object。uvm_sequence_item不仅是一个事务,还组织了一些信息在一起,增加了一些其他信息,如:sequence id,和transaction id等等。建议使用uvm_sequence_item实现基于sequence的激励。
UVM agent是一个组件,它将一组其他uvm_components集中在DUT的特定pin级接口上。大多数dut具有多个逻辑接口,并且使用一个agent来对所有组件进行分组:driver, sequencer, monitor, 和其他组件,这些组件在特定的接口上进行操作。使用这种层次结构组织组件有助于跨具有相同接口的不同验证环境和项目重用“agent”。
下图是一个典型的agent组织方式。
如前所述,agent是基于DUT的逻辑接口进行分组的组件集合。一个agent通常有一个driver和一个sequencer来驱动它所操作的接口上的被DUT的激励。它还具有一个monitor和一个分析组件(如记分牌或覆盖率收集器)来分析该接口上的活动。此外,它还可以拥有配置agent及其组件的配置对象。
get_name返回对象的名称,由new构造函数或set_name()方法中的名称参数提供。get_full_name返回对象的完整层次结构名称。对于uvm_components,在打印语句中使用时非常有用,因为它显示了组件的完整层次结构。对于没有层次结构的sequence或config对象,它将输出与get_name相同的值
ACTIVE agent是能够在其操作的pin级接口上生成激励的代理。这意味着,像driver和sequencer这样的组件将被连接,并且会有一个sequence在其上运行以生成激励。
PASSIVE agent是不生成任何激励,但能监视接口上发生的活动的代理。这意味着,在被动代理中不会创建driver和sequencer。
在需要生成激励的模块级验证环境中,agent通常被配置为ACTIVE 。当我们从模块级转移到芯片级验证环境时,可以配置相同的agent,在这种环境中不需要生成激励,但是我们仍然可以在调试或覆盖率方面使用相同的agent来监视信号。
UVM agent有一个类型为UVM_ACTIVE_PASSIVE_e的变量,该变量定义了agent是ACTIVE的(UVM_ACTIVE),它构造了sequencer和driver,还是PASSIVE的(UVM_PASSIVE),它既不构造driver,也不构造sequencer。这个参数被称为active,默认情况下它被设置为UVM_ACTIVE。
当在environment类中创建agent时,可以使用set_config_int()更改这一点。然后,agent的build阶段应该具有如下代码,以有选择地构造driver和sequencer。
function void build_phase(uvm_phase phase);
if(m_cfg.active == UVM_ACTIVE)
begin //create driver, sequencer
end
endfunction
driver是将transaction或sequence item转换为一组基于信号接口协议的pin级别切换的组件。
sequencer是一个组件,它将sequence item从sequencer路由到driver,并将响应从driver路由回sequence。还负责多个sequence(如果存在)之间的仲裁。
在像UVM这样的TLM方法中,这些组件是必需的,激励产生是根据事务进行抽象的,而sequencer和driver是路由它们并将它们转换为实际的信号行为的组件。
monitor是观察pin级活动并将其观察结果转换为事务或sequence_items的组件。它还通过analysis port将这些事务发送到分析组件。
scoreboard是一个分析组件,用来检查DUT是否正常工作。UVM scoreboard使用来自agent内部实现的monitor来分析事务。
run_test方法(一个静态方法)可以激活UVM测试平台。它通常在顶级测试模块的“initial begin…end”块中调用,它接受要运行的测试类的参数。然后它触发测试类的构造,build_phase()将在层次结构中执行并进一步构造Env/Agent/Driver/Sequencer 对象。
运行一个sequence需要三个步骤:
my_sequence_c seq;
seq = my_sequence_c:: type_id::create(“my_seq”)
UVM sequence-driver API主要在sequence和driver端使用阻塞方法,如下所述,用于将sequence item从sequencer传输到driver并从driver收集响应。
在sequence端,有两种方法:
在driver侧
下图说明了sequencer和driver之间的协议握手,这是在sequencer和driver之间传输请求和响应时最常用的握手。
还存在一些其他替代方法:在driver中使用get()方法,它相当于调用get_next_item()和item_done()。
有时,如果从driver到sequence的响应包含比请求类中封装的信息更多的信息,那么还需要一个单独的响应端口。
在这种情况下,sequencer将使用get_response()阻塞方法,当driver使用put()方法在该端口上发送单独的响应时,该方法将被解除阻塞。如下图所示。
pre_body()是sequence类中的一个方法,它在调用sequence的body()方法之前被调用。在调用body()方法之后,依次调用post_body()方法。并不总是调用pre_body()和post_body()方法。uvm_sequence::start()有一个可选参数,如果将其设置为0,将导致这些方法不被调用。下面是一个sequence中的start()方法的形式参数。
virtual task start (
uvm_sequencer_base sequencer, // Pointer to sequencer
uvm_sequence_base parent_sequencer = null, // parent sequencer
integer this_priority = 100, // Priority on the sequencer
bit call_pre_post = 1); // pre_body and post_body called
start方法是一个阻塞方法,阻塞进程直到sequence的body方法执行完毕。
Cracking Digital VLSI Verification Interview 第六章
原文:https://www.cnblogs.com/icparadigm/p/12774139.html