uvm_sequence_library定义为是一堆sequence的集合,本质上其实就是uvm_sequence,只不过在普通的uvm_sequence的基础上封装了多种算法,支持对内部sequence的随机发送,或者有序发送等,在随机测试场景下会发挥大作用,从源码上看,uvm_sequnce_library实际上就是继承自uvm_sequence:
在uvm_sequence_library中会维护一个sequence的队列,每加入一个sequence,就会在队列中加入一个sequence。用户自定义的sequence_library可以继承自uvm_sequence_library:
1. class my_seq_library0 extends uvm_sequence_library#(uvm_sequence_item);
2. `uvm_object_utils(my_seq_library0)
3. `uvm_sequence_library_utils(my_seq_library0)
4. function void new(string name="my_seq_library0");
5. super.new(name);
6. init_sequence_library();
7. endfunction:new
8. endclass
在声明sequence_library的时候,需要注意三个点:
- seq_library要继承自uvm_sequence_library,且需要指定参数,一般为uvm_sequence_item,也可以是用户自定的transaction类型。
- seq_library除了要注册到uvm_object_utils之外,还需要通过`uvm_sequence_library_utils宏进行注册;
- 在seq_library的new函数中,需要调用init_sequence_library()函数初始化library中的sequence队列。
声明完一个sequence_library,如果不加入任何的sequence,那么这个sequence_library为空,这样的library不具备任何作用,需要在自定义的sequence中,将自定sequence加入到这个sequence_library中:
1. class my_sequence extends uvm_sequence#(uvm_sequence_item);
2. my_transaction my_tr;
3. `uvm_object_utils(my_sequence)
4. `uvm_add_to_seq_lib(my_sequence, my_seq_library0)
5. ......
6. virtual task body();
7. repeat(10) begin
8. `uvm_do(my_tr)
9. end
10. endtask:body
11. ......
12. endclass
在自定义的sequence中,使用uvm_add_to_seq_lib加入到sequence_library中,这个宏有两个参数,第一个是需要加入的sequence,第二个是sequence_library的名字。
同样的,对于同一个sequence,可以加入到不同的sequence_library中,上述代码可以写为:
1. class my_sequence extends uvm_sequence#(uvm_sequence_item);
2. my_transaction my_tr;
3. `uvm_object_utils(my_sequence)
4. `uvm_add_to_seq_lib(my_sequence, my_seq_library0)
5. `uvm_add_to_seq_lib(my_sequence, my_seq_library1)
6. `uvm_add_to_seq_lib(my_sequence, my_seq_library2)
7. ......
8. virtual task body();
9. repeat(10) begin
10. `uvm_do(my_tr)
11. end
12. endtask:body
13. ......
14. endclass
另外,对于同一个sequence_library,也可以加入不同的sequence,比如有另外一个my_sequence_new:
1. class my_sequence_new extends uvm_sequence#(uvm_sequence_item);
2. my_transaction my_tr;
3. `uvm_object_utils(my_sequence_new)
4. `uvm_add_to_seq_lib(my_sequence_new, my_seq_library0)
5. ......
6. virtual task body();
7. repeat(10) begin
8. `uvm_do(my_tr)
9. end
10. endtask:body
11. ......
12. endclass
sequence_library的启动实际上和sequence的启动类似,可以作为sequencer中main_phase的default_sequence启动,也可以调用sequence_library的start函数进行启动。
在tc中:
1. class my_tc extends base_test;
2. my_seq_library0 seq_lib;
3. `uvm_component_utils(my_tc)
4. ......
5. virtual function void build_phase(uvm_phase phase);
6. super.build_phase(phase);
7. seq_lib = my_seq_library0::type_id::create("seq_lib");
8. uvm_config_db#(uvm_sequence_base)::set(this, "env.i_agt.sqr.main_phase", "default_sequence", seq_lib);
9. endfunction:build_phase
10. ......
11. endclass
或者也可以写成:
1. class my_tc extends base_test;
2. my_seq_library0 seq_lib;
3. `uvm_component_utils(my_tc)
4. ......
5. virtual function void build_phase(uvm_phase phase);
6. super.build_phase(phase);
7. //seq_lib = my_seq_library0::type_id::create("seq_lib");
8. uvm_config_db#(uvm_object_wrap)::set(this, "env.i_agt.sqr.main_phase", "default_sequence", seq_lib::type_id::get());
9. endfunction:build_phase
10. ......
11. endclass
这里注意的是使用type_id::get()方法返回的是uvm_object_wrap类型的变量,和factory机制有关,因此uvm_config_db的参数必须是uvm_object_wrap,而使用seq_lib直接作为default_seq启动,则uvm_config_db的参数应该传递为uvm_sequence_base类型。
另外,还可以通过调用seq_lib的start函数启动,在tc的main_phase中:
1. class my_tc extends base_test;
2. my_seq_library0 seq_lib;
3. `uvm_component_utils(my_tc)
4. ......
5. virtual function void build_phase(uvm_phase phase);
6. super.build_phase(phase);
7. seq_lib = my_seq_library0::type_id::create("seq_lib");
8. //uvm_config_db#(uvm_object_wrap)::set(this, "env.i_agt.sqr.main_phase", "default_sequence", seq_lib::type_id::get());
9. endfunction:build_phase
10.
11. virtual function void main_phase(uvm_phase phase);
12. super.main_phase(phase);
13. phase.raise_objection(this);
14. seq_lib.start(env.i_agt.sqr, null, -1);
15. phase.drop_objection(this);
16. endfunction:main_phase
17. ......
18. endclass
本质上,sequence_library和普通的sequence没有差别,所以启动方式也和普通的sequence方法一样。从uvm_sequence_library的源码也可以看出来,其继承自uvm_sequence,可以理解为在常规的sequence基础上封装了一层,然后内部丰富了sequence的发送算法和随机数量。
2.1 选择算法控制
如果不加任何设置,调用start任务后,sequence_library将会随机发送加入到library的sequence,uvm提供了4种sequence_library的模式,在基类uvm_sequence_library中,通过select_mode控制:
select_mode的默认模式是:UVM_SEQ_LIB_RAND,也就是完全随机,那么其他几种模式的含义:
UVM_SEQ_LIB_RAND |
完全随机发送sequence |
UVM_SEQ_LIB_RANDC |
将加入到sequence_library中的数据随机排一个顺序,然后进行依次发送,能保证内部每个sequence都被发送一次 |
UVM_SEQ_LIB_ITEM |
不发送加入到sequence library中的sequence,发送自己产生的sequence,此时sequence library相当于一个普通的sequence |
UVM_SEQ_LIB_USER |
采用用户自定义的方式发送sequence,需要重载select_sequence方法实现 |
这里重点说明下UVM_SEQ_LIB_USER的用法,如果用户将sequence_library的模式设定为该模式,需要重载select_sequence方法,这个方法的原型为:
重载该函数,实现用户自定义的sequence的发送,在sequence_library中:
1. class my_seq_lib extends uvm_sequence_library#(uvm_sequence_item);
2. `uvm_object_utils(my_seq_lib)
3. `uvm_sequence_library_utils(my_seq_lib)
4. function void new(string name="my_seq_lib");
5. super.new(name);
6. init_sequence_library();
7. endfunction:new
8. virtual function int unsigned select_sequence(int unsigned max);
9. static int unsigned index[$];
10. static bit inited;
11. int value;
12. if (!inited) begin
13. for(int i=0; i<=max; i++) begin
14. if((sequence[i].get_type_name() == "seq0") ||
15. (sequence[i].get_type_name() == "seq1") ||
16. (sequence[i].get_type_name() == "seq3") ) begin
17. index.push_back(i);
18. end
19. inited = 1'b1;
20. end
21. value = $urandom_range(0, index.size()-1);
22. return index[value];
23. endfunction:select_sequence
24. endclass
重载函数的含义是:初始化一开始inited为0,如果在sequence_library内部的sequence队列中加入seq0、seq1和seq3,那么会将index队列增加seq的序号,然后将初始化inited赋值为1,也就是只进行一次index的入队,循环外使用一个随机选取index队列中的序号,将序号返回,返回后,sequence_library会按照返回值发送sequence。也就是说这个功能为:只在sequence序号为0,1,3的sequence中随机选取一个发送。
这里用到了sequence_library中维护的sequence队列sequence[$],其源码为:
即注册到factory机制的加入sequence_library中的所有sequence。
2.2 运行次数控制
实际上,在sequence_library中运行的sequence有次数说明,收到sequence_library中的变量max_random_count和min_random_count控制:
可以看到,这两个值默认是10,也就是说,如果不加以设置,以UVM_SEQ_LIB_RAND运行的sequence_library,会随机执行10次sequence的发送,如果是UVM_SEQ_LIB_ITEM,则会产生10个sequence_item发送。
sequence_library发送的机制其实是会选在min_random_count和max_random_count之间任意选择一个数来决定发送sequence的次数。可以通过设定这两个值改变sequence_library发送sequence的数量。可以在tc中通过uvm_configdb的方式改变值:
class my_tc extends base_test;
2. my_seq_library0 seq_lib;
3. `uvm_component_utils(my_tc)
4. ......
5. virtual function void build_phase(uvm_phase phase);
6. super.build_phase(phase);
7. seq_lib = my_seq_library0::type_id::create("seq_lib");
8. uvm_config_db#(uvm_seq_lib_mode)::set(this, "env.i_agt.sqr.main_phase", "default_sequence.select_mode", UVM_SEQ_LIB_RANDC);
9. uvm_config_db#(unsigned int)::set(this, "env.i_agt.sqr.main_phase", "default_sequence.min_sequence_count", 5);
10. uvm_config_db#(unsigned int)::set(this, "env.i_agt.sqr.main_phase", "default_sequence.max_sequence_count", 20);
11. uvm_config_db#(uvm_object_wrap)::set(this, "env.i_agt.sqr.main_phase", "default_sequence", seq_lib::type_id::get());
12. endfunction:build_phase
13.
14. endclass
上述代码执行,会将sequence_library的select_mode设置为UVM_SEQ_LIB_RANDC模式,然后设定最大seq_count为20,最小seq_count为5,也就是发送sequence的数量介于5到20之间的随机值。
3.1 通过sequence library cfg控制sequence library的参数
事实上,除了通过config_db的方式设定sequence_library的两个主要的参数之外,还可以通过sequence library cfg进行归一化设定,sequence library cfg在uvm中的源码为:
其内部包含的主要变量就是selection_mode,min_random_count和max_random_count,可以看到在new函数中,可以直接设定这三个参数,实现参数的传递,在tc中,实例化uvm_sequence_library_cfg,然后通过new函数实现参数配置,再通过config_db的方式传递cfg,也可以实现对sequence_library参数的控制。
1. class my_tc extends base_test;
2. my_seq_library0 seq_lib;
3. uvm_sequence_library_cfg seq_lib_cfg;
4. `uvm_component_utils(my_tc)
5. ......
6. virtual function void build_phase(uvm_phase phase);
7. super.build_phase(phase);
8. seq_lib = my_seq_library0::type_id::create("seq_lib");
9. seq_lib_cfg = new("seq_lib_cfg", UVM_SEQ_LIB_RANDC, 5, 20);
10. uvm_config_db#(uvm_object_wrap)::set(this, "env.i_agt.sqr.main_phase", "default_sequence", seq_lib::type_id::get());
11. uvm_config_db#(uvm_sequence_library_cfg)::set(this, "env.i_agt.sqr.main_phase", "default_sequence.config", seq_lib_cfg);
12. endfunction:build_phase
13.
14. endclass
3.2 实现控制sequence library参数的几种方法
配置sequence_library的三个参数:select_mode,min_sequence_count,max_sequence_count的方式,可以通过在tc中利用config_db的方式传递值,也可以通过uvm_sequence_library_cfg的方式,在new函数中直接传递值,然后利用uvm_config_db将cfg传递给default_sequence.config,实现这三个参数的控制。
实际上,因为这三个参数都是uvm_sequence_library基类的成员变量,tc中启动sequence_library之前,可以直接对这三个值进行赋值,也可以实现对参数的控制,例如如下tc代码:
1. class my_tc extends base_test;
2. my_seq_library0 seq_lib;
3. `uvm_component_utils(my_tc)
4. ......
5. virtual function void build_phase(uvm_phase phase);
6. super.build_phase(phase);
7. seq_lib = my_seq_library0::type_id::create("seq_lib");
8. seq_lib.select_mode=UVM_SEQ_LIB_ITEM;
9. seq_lib.min_sequence_count = 2;
10. seq_lib.max_sequence_count = 20;
11. uvm_config_db#(uvm_sequence_library_cfg)::set(this, "env.i_agt.sqr.main_phase", "default_sequence.config", seq_lib_cfg);
12. endfunction:build_phase
13. ......
14. endclass
或者在main_phase中调用start函数之前,对seq_lib进行赋值:
1. class my_tc extends base_test;
2. my_seq_library0 seq_lib;
3. `uvm_component_utils(my_tc)
4. ......
5. virtual function void build_phase(uvm_phase phase);
6. super.build_phase(phase);
7. seq_lib = my_seq_library0::type_id::create("seq_lib");
8. ......
9. //uvm_config_db#(uvm_sequence_library_cfg)::set(this, "env.i_agt.sqr.main_phase", "default_sequence.config", seq_lib_cfg);
10. endfunction:build_phase
11.
12. virtual task main_phase(uvm_phase phase);
13. super.main_phase(phase);
14. seq_lib.starting_phase = phase;
15. seq_lib.select_mode=UVM_SEQ_LIB_RANDC;
16. seq_lib.min_sequence_count = 2;
17. seq_lib.max_sequence_count = 20;
18. seq_lib.start(env.vseq.my_sqr, null, -1);
19. endtask:main_phase
20. ......
21. endclass
这里对sequence_library的参数控制方法进行总结:
- 首先可以通过uvm_config_db的方式直接传输这三个参数,参数传输的时候注意uvm_config_db参数化类的参数类型,以及“密钥”的类型(使用default_sequence.xxx)。
- 其次可以通过例化uvm_sequence_library_cfg的方式,在构造函数new中直接传递这三个变量。然后通过uvm_config_db的方式将cfg传输。
最后,可以通过层次化的方式直接赋值,即seq_lib.xxx直接赋值的方式,可以在default_sequence启动之间赋值,也可以在调用start函数之前赋值。