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

2023-10-26


前言

今天天气:小雨


一、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());

方法二:
function void my_case0::build_phase(uvm_phase phase);
   	case0_sequence cseq;                                              
   	super.build_phase(phase);
   	cseq = new("cseq");                                               
   	uvm_config_db#(uvm_sequence_base)::set(this,                     
                                         "env.i_agt.sqr.main_phase", 
                                         "default_sequence",         
                                          cseq);                    
endfunction						

(3)uvm_do
通过宏来创建、随机、发送sequence

3、sequence分类

transaction和sequence都可以随机,在两处随机是一样的效果

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

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

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

  • 层次类/hierarchical sequence:更高的sequence组织底层的sequence,让他们顺序或者并行执行,挂载到同一个sequencer上面
    在这里插入图片描述

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

在这里插入图片描述
在这里插入图片描述

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

在这里插入图片描述

二、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)表格
在这里插入图片描述
create + rand_send = do
create + rand_send_with = do_with

(2)宏实际内部操作
三个接口:pre_domid_dopost_do
一般是先实例化transaction,再调用start_itemfinishi_item两个任务
在这里插入图片描述

(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);

在这里插入图片描述

3、sequence独占sequencer

多个sequence在同一个sequencer上启动时,需要有先后顺序。
独占sequencer的所有权,使得某个sequence全部产生完毕后再交给其他sequence
(1)lock()和unlock()
sequence向sequencer发出请求,与其他sequence的请求都放在一个仲裁队伍里面,等前面的请求处理完毕,lock获得所有权,然后一直到unlock结束。
结束之后如果两者优先级相同,还是会交替发送transaction。
在这里插入图片描述

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

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

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

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

上述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按照优先级执行。

在这里插入图片描述

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

适合场景:多驱动的情况下进行sequence的同步

假设有两个输入端口,需要同时向两个端口施加激励,可以设置两个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,里面的sequence会按照顺序执行,因而实现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的指针,本来是一个父类指针,转换为子类指针

在这里插入图片描述

(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的某动态运行的phase的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
高抽象级和低抽象级之间没有继承关系,需要进行映射

typedef enum{CLKON,CLKOFF,RESET,WRREG,RDREG} phy_cmd_t;
typedef enum{FREG_LOW_TRANS,FREG_MED_TRANS,FREG_HIGH_TRANS}layer_cmd_t;
class bus_trans extends uvm_sequence_item;
	rand phy_cmd_t cmd;
	rand int addr;
	rand int data;
	constraint cstr{
		soft addr=='h0;
		soft data=='h0;
	};
	...
endclass

class packet_seq extends uvm_sequence;
	rand int len;
	rand int addr;
	rand int data[];
	rand phy_cmd_t cmd;
	constraint cstr{
		soft len inside {[30:50]};
		soft addr[31:26]=='hFF00;
		data.size()==len;
	};
	...
	task body();
		bus_trans req;
		foreach(data[i])
			`uvm_do_with{req,{cmd==local::cmd;
							addr==local::addr;
							data==local::data[i];}}
	endtask
endclass

class layer_trans extends uvm_sequence_item;
	rand layer_cmd_t layer_cmd;
	rand int pkt_len;
	rand int pkt_idle;
	constraint cstr{
	soft pkt_len inside{[10:20]};
	layer_cmd == FREQ_LOW_TRANS -> pkt_idle inside {[300:400]};
	layer_cmd == FREQ_MED_TRANS -> pkt_idle inside {[100:200]};
	layer_cmd == FREQ_HIGH_TRANS -> pkt_idle inside {[20:40]};
};
...
endclass

class adapter_sequence extends uvm_sequence;
	`uvm_object_utils(adapter_seq)
	`uvm_declare_p_sequencer(phy_master_sequencer)
	...
	task body();
		layer_trans trans;
		packet_seq pkt;
		forever begin
			p_sequencer.up_sqr.get_next_item(req);
			void'($cast(trans,req));
			repeat(trans.pkt_len)begin
				`uvm_do(pkt)
				delay(trans.pkt_idle);
			end
			p_sequencer.up_sqr.item_done();
		end
	endtask
	virtual task delay(int delay);
	...
	endtask
endclass

class top_seq extends uvm_sequence;
	...
	task body();
		layer_trans trans;
		`uvm_do_with(trans,{layer_cmd == FREQ_LOW_TRANS;})
		`uvm_do_with(trans,{layer_cmd == FREQ_HIGH_TRANS;})
	endtask
endclass

class layering_sequencer extends uvm_sequencer;
...
endclass

class phy_master_sequencer extends uvm_sequencer;
	layering_sequencer up_sqr;
	...
endclasss

class phy_master_driver extends uvm_driver;
	...
	task run_phase(uvm_phase phase);
		REQ tmp;
		bus_trans req;
		forever begin
			seq_item_port.get_next_item(tmp);
			void'($cast(req,tmp));
			`uvm_info("DRV",$sformatf("got a item \n %s",req.sprint()),UVM_LOW)
			seq_item_port.item_done();
		end
	endtask
endclass

class phy_master_agent extends uvm_agent;
	phy_master_driver drv;
	phy_master_sequencer sqr;
	...
	function void build_phase(uvm_phase phase);
		sqr=reg_master_sequencer::type_id::create("sqr",this);
		drv=reg_master_driver::type_id::create("drv",this);
	endfunction
	function void connect_phase(uvm_phase phase);
		drv.seq_item_port.connect(sqr.seq_item_export);
	endfunction
endclass

class test1 extends uvm_test;
	layering_sequencer layer_sqr;
	phy_master_agent phy_agt;
	...
	function void build_phae(uvm_phase phase);
		layer_sqr=layering_sequencer::type_id::create("layer_sqr",this);
		phy_agt=phy_master_agent::type_id::create("phy_agt",this);
	endfunction
	function void connect_phase(uvm_phase phase);
		phy_agt.sqr.up_sqr=layer_sqr;
	endfunction
	task run_phase(uvm_phase phase);
		top_seq seq;
		adapter_seq adapter;
		phase.raise_objection(phase);
		seq=new();
		adapter=new();
		fork
			adapter.start(phy_agt.sqr);
		join_none
		seq.start(layer_sqr);
		phase.drop_objection(phase);
	endtask
endclass

这一部分代码是低层次的sequence获取高层次的sequence,并进行转换发送出去

class adapter_sequence extends uvm_sequence;
	`uvm_object_utils(adapter_seq)
	`uvm_declare_p_sequencer(phy_master_sequencer)
	...
	task body();
		layer_trans trans;
		packet_seq pkt;
		forever begin
			p_sequencer.up_sqr.get_next_item(req);
			void'($cast(trans,req));
			repeat(trans.pkt_len)begin
				`uvm_do(pkt)
				delay(trans.pkt_idle);
			end
			p_sequencer.up_sqr.item_done();
		end
	endtask
	virtual task delay(int delay);
	...
	endtask
endclass

高抽象级的sequence把产生的item给下一级的sequence,如何连接?
低层次sequencer中包含了高层次的sequencer的句柄,在低层次的sequence中去get高层次的sequence
在这里插入图片描述

题目

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协调地运行。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 假如“唐僧团队”裁员,你会先裁掉哪一位

    相信很多人看过水煮三国 大话西游 文中去西天取经的4人被影射成一个团队 其中 唐僧是TeamLeader 性格坚韧 目的明确 讲原则 懦弱没主意 孙悟空是团队中那个创意员工 业绩突出却个性极强 屡屡得罪人 猪八戒就好比那为人圆滑 偏偏干活时
  • 【ESP系列】AT指令案例

    前言 ESP系列芯片具有高性价比的联网功能 广受大家的认可 然而 在开发过程中 有时候我们想要使用ESP系列芯片的联网功能 却又不想为此编写繁杂的联网逻辑 串口交互逻辑等等 此时 我们可以运用AT指令来实现简洁的联网控制 本文将介绍这种基于
  • linux命令之查看jvm内存使用情况

    linux命令之查看jvm内存使用情况 1 使用 ps ef grep java 查询java的进程ID 2 使用jstat命令查看堆内存的使用情况 1 垃圾回收统计 jstat gc 进程ID 参数解释 S0C 第一个幸存区的大小 S1C

随机推荐

  • 【vue-treeselect】数据量大的时候懒加载并且可以搜索,树懒加载+搜索

    这两天快被这个懒加载加搜索搞崩溃了 今天小有收获 后面优化了再更新 主要说一下一棵树如何懒加载和搜索 1 ref不解释了 和本次代码无关 2 normalizer格式化内容不重要 3 load options很关键 4 search cha
  • Qt5 C++源码中使用中文的简单步骤

    本文不讲任何道理 当你在Qt5的C 源文件内使用中文时 你只需按顺序简单照做即可 不止是中文 其实你完全可以在代码中使用日韩法俄语等等各国语言 0 通用 源文件保存为带BOM的UTF 8格式 如果你准备跨平台 保存为带BOM的UTF 8是必
  • 计算机迭代步数英语,迭代计算

    迭代法是数值计算中一类典型方法 应用于方程求根 方程组求解 矩阵求特征值等方面 其基本思想是逐次逼近 先取一个粗糙的近似值 然后用同一个递推公式 反复校正此初值 直至达到预定精度要求为止 1 迭代计算次数指允许公式反复计算的次数 在Exce
  • 毕业设计记录(二):基于VUE框架与ECharts和Axios技术结合的Web移动高校实验室管理系统设计与实现

    目录 点击即跳转 参考文献阅读笔记 空间信息与规划系实验室情况统计表 毕业设计进度 前端设计 登陆界面 未美术优化 参考文献 总 参考文献阅读笔记 2 甄翠明 李克 基于Web的高校计算机实验室预约管理系统的研究与设计 J 现代信息科技 2
  • 【速度↑20%模型尺寸↓36%】极简开源人脸检测算法升级

    经过一年的各种尝试 调试 测试以及无数次失败 我们的开源人脸检测算法再次升级 我们团队专注人脸检测优化十几年 一直持续优化 向着最简单的算法努力 新版本提升 计算量更小 速度提升约20 模型尺寸精简36 85K参数降低至54K 准确率有所提
  • so库的反编译,反汇编

    Linux APP SO的反汇编工具 ida Pro 可以反汇编app和SO库 有函数名 但是不能反编译到code这一级别 下载最强的反编译工具 ida Pro 6 4 Plus rar 还有这个反汇编工具 没用过 转自 http bbs
  • protobuf的序列化和反序列化的分析

    一 protobuf的optional 数据类型序列化分析 1 optional 的protobuf的文件 格式 syntax proto2 message test proto optional int32 proto1 1 option
  • thinkphp5.0.24反序列化漏洞分析

    thinkphp5 0 24反序列化漏洞分析 文章目录 thinkphp5 0 24反序列化漏洞分析 具体分析 反序列化起点 toArray getRelationData分析 modelRelation生成 进入 call前的两个if c
  • 初步学习Oracle之PL/SQL

    PL SQL简介 PL SQL Procedure Language SQL 程序语言是 Oracle 对 sql 语言的过程化扩展 指在 SQL 命令语言中增加了过程处理语句 如分支 循环等 使 SQL 语言具有过程处理能力 把SQL 语
  • 【满分】【华为OD机试真题2023 JS】最差产品奖

    华为OD机试真题 2023年度机试题库全覆盖 刷题指南点这里 最差产品奖 知识点滑窗 时间限制 1s 空间限制 256MB 限定语言 不限 题目描述 A公司准备对他下面的N个产品评选最差奖 评选的方式是首先对每个产品进行评分 然后根据评分区
  • 在Android Studio中使用vulkan

    首先要确定手机是否支持Vulkan 可以下载一个aida64 在设备中如果能找到vulkan设备 说明支持 否则不支持 严格按照官方介绍的步骤一步步执行 就能获得官方推荐的可执行的例子 有很多 可以都试一试 那怎么在自己的工程中使用vulk
  • Vue模版语法&2种数据绑定方式

    Vue模板语法有2大类 1 插值语法 功能 用于解析标签体内容 写法 xx 其中xx是js表达式 且可以直接读取到data中的所有属性 p value p 在双大括号中 除了可以简单的传值外 还可以使用表达式 每个绑定都只能包含单个表达式
  • 中文新闻文本标题分类(基于飞桨、Text CNN)

    目录 一 设计方案概述 二 具体实现 三 结果及分析 四 总结 一 设计方案概述 主要网络模型设计 设计所使用网络模型为TextCNN 由于其本身就适用于短中句子 在标题分类这一方面应该能发挥其优势 TextCNN是Yoon Kim在201
  • R语言查看每列的缺失值

    代码 library tidyverse library naniar data gt miss var summary 注 gt 为管道函数 不懂可以看下面的链接 管道函数 miss var summary在naniar包里面
  • softwares

    视频切帧 变换分辨率 VLC 文件对比 Beyond Compare 远程控制 向日葵 TeamViewer
  • MATLAB安装配置MinGW-w64 C++编译器

    文章目录 前言 一 Mingw安装 1 安装教程 2 验证 二 MATLAB安装配置MinGW 总结 pic center 前言 只是为方便学习 不做其他用途 一 Mingw安装 在网上找到的安装一直报错 The file has been
  • uniapp(小程序)加载更多(LoadMore)在列表上的应用和刷新逻辑完善

    活动列表应用loadMore应用以及刷新逻辑完善 获取列表的方法会有3种状态 第一种是onLoad时 首屏的1页5条 第二种是加载更多触发的n页5条 以及第三种 当我们离开页面去往其他页面再回到列表页进行刷新触发的1页n条 首先先说加载更多
  • 全国DNS服务器IP地址大全、公共DNS大全

    各省公共DNS服务器IP大全 名称 各省公共DNS服务器IP大全 114 DNS 114 114 114 114 114 114 115 115 阿里 AliDNS 223 5 5 5 223 6 6 6 百度 BaiduDNS 180 7
  • 突发:香港警方警告 JPEX疑涉诈骗

    来源 香港明报 据香港明报跟进报道 未获发牌的 绿石数字资产平台 JPEX 涉疑违规在港宣传及经营 其后有客户表示提币失败 警务处长萧泽颐今日表示 警方前日收到证监会转介案件 因或涉及行骗成份 现由商业及罪案调查科跟进 至昨日下午3时收到8
  • 《UVM实战》学习笔记——第六章 sequence机制

    文章目录 前言 一 sequence的启动与执行 1 启动 2 启动方式 3 sequence分类 二 sequence的仲裁机制 1 sequence相关的宏 2 sequencer的仲裁算法 6种 3 sequence独占sequenc