《UVM实战》学习笔记——第六章 sequence机制

文章目录

  • 前言
  • 一、sequence的启动与执行
    • 1、启动
    • 2、启动方式
    • 3、sequence分类
  • 二、sequence的仲裁机制
    • 1、sequence相关的宏
    • 2、sequencer的仲裁算法(6种)
    • 3、sequence独占sequencer
    • 4、sequence失效
  • 三、sequence的进阶使用
    • 1、transaction内容和匹配
    • 2、m_sequencer 、p_sequencer
    • 3、virtual sequence和virtual sequencer
    • 4、sequence中使用config_db
    • 5、response
    • 6、sequence library
    • 7、layering sequence(暂时不一定会用到,属于高级应用)
  • 题目


前言

今天天气:小雨


一、sequence的启动与执行

1、启动

将不同的sequence设置成sequencermain phasedefault sequence,当sequencer执行到此时,启动sequence。
会自动执行sequence的body任务,也会自动调用pre_bodypost_body任务。

2、启动方式

(1)直接启动

my_sequence my_seq;
my_seq = new("my_seq");   实例化sequence
my_seq.start(sequencer);  里面是sequencer的指针

(2)default sequence

uvm_config_db #(uvm_object_wrapper)::set(this,
										"env.i_agt.sqr.main_phase",
										"default sequence",
										my_sequence::type_id::get());

3、sequence分类

sequence要预留一些可供外部随机化的变量,一部分是外部通过层次化传递约束来控制item的随机变量,一部分用来对对象之间加以组织和时序控制。

也就是说,transaction里面有随机变量,包含这个transaction的sequence也有自己的随机变量

  • 扁平类/flat sequence:只包含最小的颗粒度,只有item实例

  • 层次类/hierarchical sequence:更高的sequence组织底层的sequence,让他们顺序或者并行执行,挂载到同一个sequencer上面
    《UVM实战》学习笔记——第六章 sequence机制_第1张图片

  • 虚拟类/virtual sequence:最终控制整个测试场景,协调顶层的测试场景。他不会固定挂载在某一个sequencer上面,而是将内部不同类型的sequence挂载到不同的目标sequencer上面

《UVM实战》学习笔记——第六章 sequence机制_第2张图片
《UVM实战》学习笔记——第六章 sequence机制_第3张图片

顶层环境可以随机化sequence,从而影响item的内容

《UVM实战》学习笔记——第六章 sequence机制_第4张图片

二、sequence的仲裁机制

同一个sequencer在同一时刻可以启动多个sequence
此时就涉及到了sequence的仲裁机制,到底哪个transaction会先产生

除了transaction有优先级之外,sequence也有优先级

对sequence设置优先级的本质就是设置transaction的优先级

fork
	my_seq0.start(env.i_agt.sqr, null, 100);
	my_seq1.start(env.i_agt.sqr, null, 200);
join  这个是优先级相同,所以交替产生
设置优先级后,先seq1全部产生完毕,再产生seq0
第二个参数是parent phase

1、sequence相关的宏

(1)四个系列:uvm_do 、uvm_do_on 、uvm_send、 uvm_rand_send

  • uvm_do:默认优先级-1
  • uvm_do_with:默认优先级-1
  • uvm_do_pri (my_trans, 100):第二个参数为优先级,优先级为大于等于-1的整数,数字越大,优先级越高
  • uvm_do_pri_with (my_trans, 100, {my_trans.pload.size<500})
  • uvm_do_on (tr, this.m_sequencer)显示指定使用哪个sequencer去发送sequence,第一个参数为sequence指针,第二个为sequencer的指针,默认是sequence启动的时候指定的sequencer,sequence将这个指针存在成员变量m_sequencer里面 (后面的virtual sequence会用到!很重要)
  • uvm_do_on_pri(tr, this, 100):第三个参数为优先级
  • uvm_do_on_with:第三个为约束
  • uvm_do_on_pri_with
  • uvm_create:实例化transaction(可以用new函数代替),然后自己再随机化transaction
  • uvm_send:将处理完后的transaction发送出去,两者成对使用
  • uvm_send_pri:设置优先级
  • uvm_rand_send:随机化transaction,前提是transaction已经实例化了
  • uvm_rand_send_pri
  • uvm_rand_send_with

uvm_send uvm_rand_send系列宏存在的意义:有的transaction占用内存比较大,为了使得前后两次发送的transaction共用一块内存,而去修改内容不相同,目的是节约内存

(2)表格
《UVM实战》学习笔记——第六章 sequence机制_第5张图片
create + rand_send = do
create + rand_send_with = do_with

(2)宏实际内部操作
三个接口:pre_domid_dopost_do
一般是先实例化transaction,再调用start_itemfinishi_item两个任务
《UVM实战》学习笔记——第六章 sequence机制_第6张图片

(3)宏的参数
第一个参数可以是transaction的指针,也可以是sequence的指针
因此sequence是可以嵌套使用,将公用的函数任务等定义在基础的sequence里面,然后在此基础上进行派生新的sequence

virtual body();
	crc_seq cseq;   //定义的两个小的seq
	long_seq lseq;
	repeat(10) begin
		uvm_do(cseq);   //这里调用的是sequence的start任务
		uvm_do(lseq);
	end
endtask

transaction指针:调用start_item、finishi_item任务
sequence指针:调用start任务

2、sequencer的仲裁算法(6种)

  • SEQ_ARB_FIFO默认算法,按照先入先出的顺序,不考虑优先级
  • SEQ_ARB_WEIGHTED:按照优先级权重进行随机
  • SEQ_ARB_RANDOM:完全随机
  • SEQ_ARB_STRICT_FIFO:先按照优先级,再按照先入先出
  • SEQ_ARB_STRICT_RANDOM:先按照优先级,再随机,与到达时间无关
  • SEQ_ARB_USER:用户自定义

要按照优先级产生transaction的话,要进行设置(在顶层文件中设置)
env.i_agt.sqr.set_arbitration(SEQ_ARB_STRICT_FIFO);

《UVM实战》学习笔记——第六章 sequence机制_第7张图片

3、sequence独占sequencer

多个sequence在同一个sequencer上启动时,需要有先后顺序。
独占sequencer的所有权,使得某个sequence全部产生完毕后再交给其他sequence
(1)lock()和unlock()
sequence向sequencer发出请求,与其他sequence的请求都放在一个仲裁队伍里面,等前面的请求处理完毕,lock获得所有权,然后一直到unlock结束。
结束之后如果两者优先级相同,还是会交替发送transaction。
《UVM实战》学习笔记——第六章 sequence机制_第8张图片

(2)grab()和ungrab()
在sequencer下次授权期间可以无条件获得授权。唯一阻止它的只有预先获得授权的其他lock或者grab的sequence。暂时获得所有权,grab请求是放在队列的最前面优先级更高

(3)如果其他sequence lock住了sequencer的所有权,那grab还是会等待释放。

(4)举例
(child_seq也是#10ns,和lock——seq一样)

《UVM实战》学习笔记——第六章 sequence机制_第9张图片

《UVM实战》学习笔记——第六章 sequence机制_第10张图片

《UVM实战》学习笔记——第六章 sequence机制_第11张图片

上述seq发送是并行运行

(1)10ns:等待的有seq1、seq2、seq3、locks,按照优先级分别是seq1、seq2、seq3、locks或者seq1、seq2、locks、seq3,3和locks的顺序是随机的,因为他们优先级相同,lock住后就会一直享用授权。
(2)20ns:grab、seq1、seq2、seq3已经在排队了,但是此时被lock占用,无法去用他的权限
(3)40ns:lock全部发送完毕了,此时等待的有seq1、seq2、seq3、grab。此时grab就是最高特权,可以直接拿走授权
(4)70ns:grab全部执行完毕,seq1、seq2、seq3按照优先级执行。

《UVM实战》学习笔记——第六章 sequence机制_第12张图片

4、sequence失效

is_relevant()函数:返回值为1有效,0无效。强行使得sequence放弃sequencer的使用权
wait_for_relevant()任务:使sequence的无效条件消除。当sequencer发现启动的sequence都无效的时候,会调用该任务并等待其变成有效
两者要成对重载,如果都无效,且没有wait任务的话可能陷入死循环

三、sequence的进阶使用

1、transaction内容和匹配

(1)transaction和sequence里面的rand变量
如果在sequence里面定义rand变量去约束产生的transaction时,变量名字要与transaction里面的不同。
原因:相同的时候编译器首先会去找transaction里面的同名变量,然后设置,此时sequence对他的约束就失效了

(2)transaction的匹配
嵌套sequence:要求是同一种transaction,或者派生自这种transaction
如果想要sequencer发送两种不同的transaction,方法:在driversequencer设置transaction类型时写成uvm_sequence_item
(driver和sequencer是参数化类,在声明时要指定传输的transaction类型)
driver接收数据时,要进行强制转换,将req(uvm_sequence_item类型)转换为my_trans类型。要将父类句柄转换为子类句柄,才能使用成员变量。

为方便使用就定义了父类,具体使用是是不同的子类,因此要去使用这个子类的变量时就需要强制转换。

my_transaction m_trans;
seq_item_port.get_next_item(req);
if($cast(m_trans,req))
	drive_one_transaction(m_tansa);

2、m_sequencer 、p_sequencer

(1)m_sequencer:类型是uvm_sequencer_baseuvm_sequencer的基类),是sequence的成员变量,存储的是sequence启动时指定的sequencer指针

但是case0_sequence在启动的时候是在my_sequencer上,所以此时m_sequencer的类型是my_sequencer类型

所以需要在my_sequence中强制转换m_sequencer变量为my_sequencer类型。才能去使用子类中的成员变量,也就是my_sequencer中 的变量。

其实是父类和子类的问题,在最基础的sequence中m_sequencer变量为uvm_sequencer_base的父类类型;当在后面总的sequence中使用了这些基础的sequence时,要想使用总的sequencer中的成员变量,就要把父类句柄转换为子类句柄。

uvm中定义了如下宏来解决这个问题

(2)p_sequencer
uvm_declare_p_sequencer(SEQUENCER):相当于my_sequncer p_seqencer; 声明了一个my_sequencer类型的成员变量p_seqencer(继承sequence时,也不需要在子类的sequence中再次写这个了,因为他是一个成员变量,继承之后也还在里面)。

`uvm_declare_p_sequencer(xxxx)   //xxxx为自己定义的sequencer名称

UVM在pre_body之前自动将m_sequencer变量转换为p_seqencer。
所以后面要用到sequencer的成员变量的时候,直接使用p_sequencer,就不用再去管m_sequencer了。

`uvm_do_with(m_trans,{m_trans.dmac == p_sequencer.dmac})

总的来说,这个宏声明一个子类句柄,并将父类句柄转换为子类句柄

3、virtual sequence和virtual sequencer

假设有两个输入端口,需要同时向两个端口施加激励,可以设置两个default sequence,但是假如两个激励要有先后顺序,就涉及到sequence的同步问题
(1)设置全局事件
->sendover @sendover
第二个sequence等待事件发生之后再进行输送
但是不推荐使用全局变量,因为其他也可以使用这个变量,容易产生错误

(2)virtual sequence实现sequence同步最好的方式
virtual含义:不产生transaction,只是控制其他transaction,统一调度
使用virtual sequence的时候一般都要使用宏 `uvm_declare_p_sequencer (SEQUENCER),通过它可以引用sequencer的成员变量

virtual sequence和virtual sequencer都不产生transaction,只是控制其他的sequence为相应的sequencer产生transaction。因此在定义时不需要指明要发送的transaction类型

下面代码是virtual sequence。

virtual sequence也可以在其中去启动其他virtual sequence,嵌套使用。

class case0_vseq extends uvm_sequence;
	`uvm_object_utils(case0_vseq)
	`uvm_declare_p_sequencer(my_vsqr)  //声明my_vsqr类型的变量p_seqencer
	virtual task body();  //sequence最重要的就是body任务
		my_transaction tr;
		drv0_seq seq0;   //定义的sequence0类型
		drv1_seq seq1;   //定义的sequence1类型
		`uvm_do_on(tr,p_sequencer.p_sqr0)  //指定发送transaction以及发送的sequence
		fork
			`uvm_do_on(tr,p_sequencer.p_sqr0)
			`uvm_do_on(tr,p_sequencer.p_sqr0)
		join
	endtask
endclass

drv0_seq 这个sequence的定义如下,是参数化的类

class drv0_seq extends uvm_sequence #(my_transaction);
	`uvm_object_utils(drv0_seq)
	virtual task body();
		repeat(10)
			`uvm_do(m_trans)
	endtask
endclass

以前有很多个sequence的话,需要很多个config_db去对应启动,现在只需要一个config_db就可以,减少写config_db时字符串路径出错的可能。

启动:直接在顶层文件中声明,因为virtual sequencerenv组件是同一等级的

function void my_case0::build_phase(uvm_phase phase);
	uvm_config_db #(uvm_object_wrapper)::set(this,
										"my_vsqr.main_phase",
										"default sequence",
										case0_vseq::type_id::get());
endfunction

有了virtual sequence就需要virtual sequencer。

(3)virtual sequencer包含指向其他真实sequencer的指针

//virtual sequencer组件
class my_vsqr extends uvm_sequencer;
	my_sequencer p_sqr0;
	my_sequencer p_sqr1;  //里面有两个sequencer的指针,类型是my_sequencer
endclass
//base_test组件
class base_test extends uvm_test;
	my_env env0;
	my_env env1;
	my_vsqr v_sqr;    //virtual sequencer和env组件是同一级别
	
	//build_phase()函数中实例化各个组件
	function void connect_phase(uvm_phase phase);
		v_sqr.p_sqr0 = env0.i_agt.sqr;  //把相应的sequencer赋值给vsqr里面的sequencer指针
		v_sqr.p_sqr1 = env1.i_agt.sqr;
	endfunction
endclass

使用宏:uvm_do_on(seq0, p_sequencer.sqr0)
p_sequencer相当于指向virtual sequencer的指针,本来是一个父类指针,转换为子类指针

《UVM实战》学习笔记——第六章 sequence机制_第13张图片

(4)启动方式
除了用封装好的宏之外,还可以手工启动sequence
string变量无法通过rand产生,所以此时必须是手工启动
这样做的好处是可以传递一些变量进去

seq0 = new("seq0");
seq0.filename = "data.txt";  //传递变量
seq0.start(p_sequencer.sqr0);

(5)在virtual sequence中控制objection

前情回顾:

以前的做法:sequence的启动是在env、sequencer的main phase中手工启动,同时objection一般伴随着sequence。也就是在component组件中

task my_env::main_phase(uvm_phase phase);
	phase.raise_objection(this);
	seq = my_sequence::type_id::create("seq");
	seq.start(i_agt.sqr);  //sequencer指针,指明sequence产生的transaction要发给谁
	phase.drop_objection(this);
endtask

starting_phase:uvm_sequence基类中的变量,类型是uvm_phase,sequencer在启动sequence时,自动进行如下操作。
(把sequencer的phase赋值给了sequence的staring_phase )

task my_sequencer::main_phase(uvm_phase phase);
	seq.staring_phase = phase;
	seq.start(this);
endtask

结合上述两点:
在sequence中使用starting_phase进行提起和撤销objection,控制验证平台的关闭
(sequence是弹夹,和objection在一起的话是非常合理的,因为弹夹没有transaction之后,仿真也就是objection就可以撤销了,这两者写在一起是很合理的)
(因为sequence是object类型,里面没有component组件的phase和main phase任务等,它只有body一个任务)

class my_sequence extends uvm_sequence #(my_transaction);
	my_transaction m_trans;
	virtual task body();
		if(starting_phase!=null)
			starting_phase.raise_objection(this);
			
		repeat(10)
			`uvm_do(m_trans)
		#1000;
		
		if(starting_phase!=null)
			starting_phase.drop_objection(this);
		endtask
endclass		

总结:上面介绍了sequence和objection在一起的由来和用法。

回到现在:

除了在main phase中手工启动sequence时,会自动给starting phase赋值外,
只有把sequence设置为某个sequencer的default phase时,starting phase才不为null
作为宏的参数时,为null。在此sequence中使用starting_phase.raise_objection(this)是没有用处的。

现在有三种sequence,普通的sequence、中间的virtual sequence、顶层的virtual sequence。因为只在最顶层的virtual sequencer中启动了sequence,所以最顶层的virtual sequence的starting phase不是null(也就是上面那句粉红色的话),其他的子类sequence的都为null。

因此,应该在最顶层的virtual sequence控制objection,和transaction的调度原理一样。在最顶层去控制,出现问题时只需要查找最顶层。

多个sequence同时启动,两种方式

  • fork join_nonewait_fork搭配使用(不能只使用fork join_none,否则还没等待完成就endtask了)
  • fork join

4、sequence中使用config_db

config_db机制之前主要用于component组件中传递参数

sequence是uvm object类,在set时最关键就是路径问题,它不是component,所以不存在UVM树形结构中,没办法得到路径。

在sequence的body任务中使用get_full_name()函数时得到如下路径:该sequence的sequencer的路径+sequence实例化的名字

uvm_test_tpo.env.i_agt.sqr.case0_sequence

4.1 component向sequence传递参数

下面两个代码是从case0(uvm_test)顶层向case0_sequence传递参数

(1)设置到sqr的路径,然后加上通配符,因为sequence在例化时名字是不固定的或者未知的

function void my_case0::build_phase(uvm_phase phase)
	`uvm_config_db #(int) :: set(this, "env.i_agt.sqr.*","count", 9)
endfunction

(2)get数据的时候,由于不是component,第一个参数所以是null或者uvm_root::get(),第二个参数可以得到完整的sequencer+sequence实例化名字的路径

这里为什么用pre_body()函数去获取参数?

class case0_sequence extends ucm_sequence #(my_transaction);
	virtual task pre_body(); 
		if(`uvm_config_db #(int) :: get(null, get_full_name() ,"count", count))
			`uvm_info("seq0",$sformatf("get count value %0d",count),UVM_MEDIUM);
		else
			`uvm_error("seq0","cannot get count value");
	endtask
endclass

4.2 sequence传递参数给component

class case_vseq extends uvm_sequence;
	virtual task body();
		uvm_config_db #(int)::set(null, "uvm_test_top.env0.scb", "count", 0);
	endtask
endclass		

component如何获取这个参数:
以前:参数先设置好,然后在component的build phase中去get
但是sequence产生发送的时间是不固定的,因此就没有set好值,UVM定义了wait_modified任务。
前三个参数和uvm_config_db 是一样的,当检测到传递参数的值更新时,就返回,否则会一直等待

补充知识: void'()——告诉仿真器虽然这个函数有返回值,但是不输出返回值,这样可以使得log信息中没有一些警告。就是让仿真器不报这个警告

task my_scoreboard::main_phase(uvm_phase phase);
	fork
		while(1)begin
			uvm_config_db #(int)::wait_modified(this, "", "count");
			void'(uvm_config_db #(int)::get(this, "", "count", count));
		end
	join
endtask

此外,在sequence中也可以使用wait_modified这个任务

4.3 sequence传递参数给sequence
需要注意的是如果该sequence是在virtual sequencer中启动的话,那get_full_name()得到的结果是virtual sequencer,而不是env0.i_agt_sqr

5、response

response机制原理:driver把response交给sequencer,sequencer的内部有一个队列,有新的response就推入队列
新建一个transaction,然后返回给sequence

发送完数据后,允许driver返回一个response给sequencer,sequencer中有一个队列,默认大小为8。如果sequence没有及时取走的话,就会溢出。
set_id_info():假如多个sequence在一个sequencer上启动,需要知道id信息,才可以把response返回给对应的sequence

//sequence
get_response(rsp); sequence中使用这个任务

//driver
seq_item_port.get_next_item(req);
rsp = new("rsp");  driver中先例化
rsp.set_id_info(req);  拷贝id等信息到rsp中
seq_item_port.put_response(rsp);   发送rsp
seq_item_port.item_done();  发送done信号

seq_item_port.item_done(rsp);  上述两行可以合二为一,一般使用这种

5.1 response的数量问题
一个transaction可以有多个response,driver/sequence可以返回/接收多个response。

6、sequence library

一系列sequence的组合,根据特定的算法随机选择注册在其中的一些sequence,在body中去执行这些sequence

它本身要使用uvm_sequence_library_utils注册
其他sequence要使用uvm_add_to_seq_lib注册到这个sequence library中
最后将该library设置成default sequence

7、layering sequence(暂时不一定会用到,属于高级应用)

(1)定义:通过层次化的sequence可以分别构建transaction layertransport layerphysical layer等从高抽象级到低抽象级的transaction转化
(2)包含三个因素:高抽象级item、低抽象级item、中间做转换的sequence
高抽象级和低抽象级之间没有继承关系,需要进行映射
《UVM实战》学习笔记——第六章 sequence机制_第14张图片

《UVM实战》学习笔记——第六章 sequence机制_第15张图片

《UVM实战》学习笔记——第六章 sequence机制_第16张图片

《UVM实战》学习笔记——第六章 sequence机制_第17张图片
高抽象级的sequence把产生的item给下一级的sequence,如何连接?
把高级sequence的sequencer中定义一个端口,把端口和下一级agent的端口连接在一起
《UVM实战》学习笔记——第六章 sequence机制_第18张图片

题目

1、sequence和sequence item有什么区别?
sequence item是一个对象,其建模了两个验证组件之间传输的信息(有时也可以称为事务(transaction))。例如:读操作和写操作中的地址和数据信息。

sequence是由driver驱动的sequence item序列模式,由其body()任务实现。例如:连续读取10次事务。

2、uvm_transaction和uvm_sequence_item有什么区别?
uvm_transaction是从uvm_object派生的用于对事务进行建模的基类。

sequence item是在uvm_transaction的基础上还添加了一些其他信息的类,例如:sequence id。建议使用uvm_sequence_item实现基于sequence的激励。

3、什么是driver和sequencer,为什么需要它们?
driver是根据接口协议将事务转换为一组信号级切换的组件。
sequencer是一个将事务(sequence items)从sequence发送到driver,并将driver的响应反馈给sequence的组件。

sequencer也会对同时尝试访问Driver以激励设计接口的多个sequences进行仲裁。sequence和sequencer在事务级抽象,Driver在信号级对设计接口进行驱动,即单一设计模式。

4、哪个方法可以激活UVM验证平台,如何调用它?
run_test()方法(静态方法)用来激活UVM验证平台。通常在顶层的“ initial begin…end”块中调用,并且它使用一个参数指定要运行的test case。首先,产生这个test case的实例,然后run_test()方法在build_phase()中执行test class的构造函数,并进一步构造层次化的Env / Agent / Driver / Sequencer对象。

5、运行sequence需要什么步骤?
(1)创建一个序列。使用工厂创建方法创建一个序列:
my_sequence_c seq;
seq = my_sequence_c ::type_id :: create(“ my_seq”)
(2)配置或随机化序列,seq.randomize()
(3)开始一个sequence。使用sequence.start()方法启动序列。start方法需要输入一个指向sequencer的参数。
关于sequence的启动,UVM进一步做了封装,使用uvm_do

6、解释sequencer和driver之间的握手协议?
将sequence item从sequence传输到driver并收集来自driver的响应。
在sequence端:
start_item():请求sequencer访问driver
finish_item():使driver接收sequence item,这是阻塞调用,在driver调用item_done()方法后才返回。

在driver端:
get_next_item(req):这是driver中的一个阻塞方法,直到接收到sequence item,driver可以将其转换为引脚级协议信号。
item_done(req):向sequencer发出信号,表明它可以接受新的sequence请求,使得sequence解除对finish_item()方法的阻塞。

7、sequence中的pre_body()和post_body()函数是什么?它们总是被调用么?
pre_body()是sequence类中的方法,该方法在body()方法之前调用,然后调用post_body()方法。
uvm_sequence:: start()有一个可选参数,如果将其设置为0,将不调用这些方法。
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

8、Sequencer有哪些不同的仲裁机制可用于?
Sequencer有一个set_arbitration()的方法,调用该方法可以选择使用哪种算法进行仲裁。
SEQ_ARB_FIFO(Default if none specified)
SEQ_ARB_WEIGHTED
SEQ_ARB_RANDOM
SEQ_ARB_STRICT_FIFO
SEQ_ARB_STRICT_RANDOM
SEQ_ARB_USER

9、在sequencer上启动sequence时,如何指定sequence的优先级?
通过将优先级相对值参数传递给sequence的start()方法来指定优先级,第三个参数指定优先级。
seq_1.start(m_sequencer, this, 500); //Highest priority
seq_2.start(m_sequencer, this, 300); //Next Highest priority
seq_3.start(m_sequencer, this, 100); //Lowest priority among threesequences

10、sequence如何才能独占访问sequencer?
lock()和unlock((): sequence可以调用sequencer的lock方法,以通过sequencer的仲裁机制获得对driver的独占访问权限。如果还有其他标记为更高优先级的sequences,则此sequences需要等待。授予lock权限后,其他sequences将无法访问driver,直到该sequences在sequencer上调用unlock()方法。lock方法是阻塞调用,直到获得lock权限才返回。

grab()和ungrab():grab()方法类似于lock()方法,用于申请独占访问。grab()和lock()之间的区别在于,调用grab()时将立即生效,不考虑其他sequences的优先级,除非已经存在sequences调用了lock()或grab()。

11、流水线和非流水线sequence-driver模式有什么区别?
根据设计接口如何激励,可以分为两种sequence-driver模式:

非流水线模式:如果driver一次仅对驱动一个事务,则称为非流水线模型。在这种情况下,sequence可以将一个事务发送给driver,并且driver可能需要几个周期(基于接口协议)才能完成驱动该事务。只有在该事务完成驱动之后,driver才会接受sequencer的新事务。

流水线模式: 如果driver一次驱动多个事务,则称为流水线模式。在这种情况下,sequence可以继续向driver持续发送新事务,而无需等待driver完成之前事务的驱动。在这种情况下,对于从该sequence发送的每个事务,driver中都会有一个单独的进程来驱动该事务,而不用等到它完成上一次事务的驱动之后再接受新事务。如果我们不需要等待设计的响应,则此模式很有用。

12、我们如何确保在driver驱动多个sequences 时,则driver的响应会发送给正确的sequences ?
如果从driver返回了几个sequences之一的响应,则sequencer利用sequence中的sequence ID字段将响应返回给正确的sequence。driver中的响应处理代码应调用set_id_info(),以确保任何响应都具有与其原始请求相同的sequence ID。

13、什么是m_sequencer句柄?
启动sequence时,它始终与启动sequencer相关联。m_sequencer句柄包含该sequencer的引用。使用此句柄,sequence可以访问UVM组件层次结构中的信息。

14、什么是p_sequencer句柄,与m_sequencer相比有什么不同?
与sequencer,driver或monitor等UVM组件在整个仿真周期内都存在不同,UVM sequence是生命周期有限的对象。因此,如果在sequence从测试平台层次结构(组件层次结构)访问任何成员或句柄,则需要运行该sequence的sequencer的句柄。

m_sequencer是uvm_sequencer_base类型的句柄,默认情况下在uvm_sequence中可用。但是要访问在真实的sequencer,我们需要对m_sequencer进行转换(typecast),通常称为p_sequencer。下面是一个简单的示例,其中sequence要访问clock monitor组件的句柄。

15、生成sequence时,early randomization和late randomization有什么区别?
early randomization中,先调用randomize()方法对sequence对象进行随机化,然后再使用start_item()来请求对sequencer的访问。

late randomization中,先调用start_item(),直到从sequencer授予仲裁,然后在将事务发送到sequencer/driver之前调用randomize。

16、什么是subsequence?
从sequence的body()任务中,如果调用了另一个sequence的start(),则通常将其称为subsequence。

17、get_next_item()和try_next_item()有什么区别
get_next_item()是一个阻塞调用,直到存在可供驱动的sequence item为止,并返回指向sequence item的指针。
try_next_item()是非阻塞调用,如果没有可供驱动的sequence item,则返回空指针。

18、UVM driver类中的get_next_item()和get()方法之间有什么区别?
get_next_item()是一个阻塞调用,用于从sequencer FIFO获取sequence item。driver驱动完sequence item后需要先使用item_done()完成握手,然后再使用get_next_item()请求新的sequence item。

get()也是一个阻塞调用,同样用于从sequencer FIFO获取sequence item。但是在使用get()时,由于get()方法隐式完成了握手,因此无需显式调用item_done()。

19、driver中带和不带有参数的item_done()调用有什么区别?
item_done()方法是driver中的一种非阻塞方法,用于在get_next_item()或try_next_item()成功之后与sequencer完成握手。

如果不需要发回响应,则调用不带参数的item_done()。
如果需要发回响应,则将指向sequence_item的指针作为item_done()参数。

20、如何停止在sequencer上运行的所有sequences?
sequencer具有stop_sequences()方法,可用于停止所有sequences。但是此方法不检查driver当前是否正在驱动任何sequence_items,如果driver调用item_done()或put(),则可能会出现Fatal Error,因为sequences指针可能无效。

21、调用sequence.print()方法时,应该调用sequence中的哪个方法?
convert2string():建议实现此函数,该函数返回对象的字符串形式(其数据成员的值),这对于将调试信息打印到仿真器界面或日志文件很有用。

22、什么是virtual sequence,在哪里使用?有什么好处?
virtual sequence是控制多个sequencers中激励生成的sequence。由于sequences sequencers和drivers都只针对单个接口,几乎所有测试平台都需要virtual sequence来协调不同接口之间的激励。
Virtual sequences在子系统或系统级测试平台上也很有用,可以使模块级的sequence协调地运行。

你可能感兴趣的:(《UVM实战》学习笔记,学习)