UVM——sequence(二)

6. virtual sequence

​ 如果被嵌套的sequence需要在不同的sequencer中启动,也就是需要sequence之间同步,那么此时的嵌套sequence就是virtual sequence。它不产生transaction,只是控制其它sequence产生transaction发送给不同的sequencer,这就是为什么是virtual。

​ virtual sequence可以在启动的时候不需要sequencer,把它设成null,这也是它被称为virtual的另一个原因。

6.1 sequence通过find函数拿到启动sequencer

​ virtual sequence中的sequence需要拿到启动的sequencer,这可以通过uvm_top的find函数find函数通过传入component实例的名字可以返回这个实例的句柄,find函数返回uvm_component类型,需要转化哼真正的sequencer类型,这样就拿到了每个要启动的sequencer。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class runall_sequence extends uvm_sequence #(uvm_sequence_item);// virtual sequence
`uvm_object_utils(runall_sequence);

protected reset_sequence reset;
protected maxmult_sequence maxmult;
protected random_sequence random;
protected sequencer sequencer_h;
protected uvm_component uvm_component_h;//find函数返回uvm_component类型

function new(string name = "runall_sequence");
super.new(name);

uvm_component_h = uvm_top.find("*.env_h.sequencer_h");// 拿到启动的sequencer
// sequencer_h是env中的sequencer

if (uvm_component_h == null)
`uvm_fatal("RUNALL SEQUENCE", "Failed to get the sequencer")

if (!$cast(sequencer_h, uvm_component_h))//将sequence_h转化成sequencer类型
`uvm_fatal("RUNALL SEQUENCE", "Failed to cast from uvm_component_h.")

reset = reset_sequence::type_id::create("reset");
maxmult = maxmult_sequence::type_id::create("maxmult");
random = random_sequence::type_id::create("random");
endfunction : new

task body();
reset.start(sequencer_h);//在sequencer上启动sequence
maxmult.start(sequencer_h);
random.start(sequencer_h);
endtask : body

endclass : runall_sequence

virtual sequence的启动,因为上面通过find,每个sequence都拿到了sequencer,所以virtual sequence不需要指定sequencer。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class full_test extends tinyalu_base_test;
`uvm_component_utils(full_test);

runall_sequence runall_seq;//定义virtual sequence

task run_phase(uvm_phase phase);
runall_seq = new("runall_seq");
phase.raise_objection(this);
runall_seq.start(null); //启动virtual sequence,不需要指定sequencer
phase.drop_objection(this);
endtask : run_phase

function new (string name, uvm_component parent);
super.new(name,parent);
endfunction : new

endclass

6.2 通过virtual sequencer指定每个sequence的sequencer

除了上面的find函数,还可以通过virtual sequencer指定每个sequence的sequencer。

virtual sequencer中保存多个sequencer的句柄,指向真正要发送transaction的sequencer。

virtual sequencer是对不同sequencer的封装,本身不发送transaction,主要让程序看起来舒服,容易理解——virtual sequence在virtual sequencer上启动,virtual sequence中的sequence在virtual sequencer中的sequencer启动。

Virtual sequencer 有三个属性:

(1)Virtual sequencer控制其他的sequencer

(2)Virtual sequencer并不和任何driver相连

(3)Virtual sequencer本身并不处理item

1
2
3
4
5
6
7
8
9
10
11
12
//定义virtualsequencer
class my_vsqr extends uvm_sequencer;//不需要指定要发送的transaction类型

my_sequencer p_sqr0;
my_sequencer p_sqr1;

function new(string name, uvm_component parent);
super.new(name, parent);
endfunction

`uvm_component_utils(my_vsqr)
endclass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//定义virtual sequence
class case0_vseq extends uvm_sequence;//不需要指定要发送的transaction类型
`uvm_object_utils(case0_vseq)
`uvm_declare_p_sequencer(my_vsqr)
...
virtual task body();
my_transaction tr;
read_file_seq seq0;
drv1_seq seq1;
if(starting_phase != null)
starting_phase.raise_objection(this);
`uvm_do_on_with(tr, p_sequencer.p_sqr0, {tr.pload.size == 1500;})
`uvm_info("vseq", "send one longest packet on p_sequencer.p_sqr0", UVM_MEDIUM)
seq0 = new("seq0");
seq0.file_name = "data.txt";
seq1 = new("seq1");
fork
seq0.start(p_sequencer.p_sqr0);//在sqr0上启动seq0
seq1.start(p_sequencer.p_sqr1);
join
#100;
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask
endclass
1
2
3
4
5
6
7
8
9
// 在virtual sequencer上启动virtual sequence
function void my_case0::build_phase(uvm_phase phase);
super.build_phase(phase);

uvm_config_db#(uvm_object_wrapper)::set(this,
"v_sqr.main_phase",
"default_sequence",
case0_vseq::type_id::get());
endfunction

6.3 只在virtual sequence中控制objection

上一节中只在virtual sequence中控制objection。

virtual sequence不仅调度transaction,也对objection进行调度,避免层层查找在哪里调度了objection。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//非virtual sequence不控制objection
class drv1_seq extends uvm_sequence #(my_transaction);
my_transaction m_trans;
`uvm_object_utils(drv1_seq)

function new(string name= "drv1_seq");
super.new(name);
endfunction

virtual task body();//不控制objection
repeat (10) begin
`uvm_do(m_trans)
`uvm_info("drv1_seq", "send one transaction", UVM_MEDIUM)
end
endtask
endclass

除了手工启动(start函数)时需要给starting_phase赋值外,只有将sequence作为sequencer的某动态运行的phase的default_sequence时,starting_phase才不为null。如果将某sequence作为uvm_do宏的参数,那么此sequence的starting_phase为null。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 手工给starting_phase赋值
task my_case0::main_phase(uvm_phase phase);
sequence0 seq0;
sequence1 seq1;

seq0 = new("seq0");
seq0.starting_phase = phase;//将phase传给seq0的starting_phase
seq1 = new("seq1");
seq1.starting_phase = phase;//手动启动sequence时需要给starting_phase赋值
env.i_agt.sqr.set_arbitration(SEQ_ARB_STRICT_FIFO);
fork
seq0.start(env.i_agt.sqr, null, 100); //指定优先级
seq1.start(env.i_agt.sqr, null, 200);
join
endtask

6.4 在sequence慎用fork join_none

1
2
3
4
5
6
7
8
9
10
class vseq extends uvm_sequence;
my_sequence seq[4];
virtual task body();
for(int i=0;i<4;i++)
fork
automatic int j=i;
`uvm_do_on(seq[j],p_sequencer.p_sqr[j]);
join_none
endtask
endclass

上面例子中在fork join_none中并行调用`uvm_do,将for展开其实如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class vseq extends uvm_sequence;
my_sequence seq[4];
virtual task body();
fork
uvm_do_on(seq[0],p_sequencer.p_sqr[0]);
join_none
fork
uvm_do_on(seq[1],p_sequencer.p_sqr[1]);
join_none
fork
uvm_do_on(seq[2],p_sequencer.p_sqr[2]);
join_none
fork
uvm_do_on(seq[3],p_sequencer.p_sqr[3]);
join_none
endtask
endclass

当着四个进行都调用了,body任务就执行完了,sequence就执行完了,系统会杀死这个sequence,清空之前占据的内存,也会杀死启动的进程,那么这几个进程还没执行完就被杀死,所以sequence根本没有执行。

解决方法:wait fork

1
2
3
4
5
6
7
8
9
10
11
class vseq extends uvm_sequence;
my_sequence seq[4];
virtual task body();
for(int i=0;i<4;i++)
fork
automatic int j=i;
`uvm_do_on(seq[j],p_sequencer.p_sqr[j]);
join_none
wait fork; //等待当前层次上的fork执行完。
endtask
endclass

相当于:

1
2
3
4
5
6
7
8
9
10
11
class vseq extends uvm_sequence;
my_sequence seq[4];
virtual task body();
fork
uvm_do_on(seq[0],p_sequencer.p_sqr[0]);
uvm_do_on(seq[1],p_sequencer.p_sqr[1]);
uvm_do_on(seq[2],p_sequencer.p_sqr[2]);
uvm_do_on(seq[3],p_sequencer.p_sqr[3]);
join //执行完 fork join才会往下执行
endtask
endclass

7. 在sequence中使用config_db

7.1 在sequence中获得参数

UVM中使用get_full_name()可以获得对象的层次路径。比如在一个sequence中调用这个函数,返回:

uvm_test_top.env.i_agt.sqr.seq0

启动这个sequence的sequencer路径和这个sequence实例化时的名字

1
2
3
4
5
6
7
8
9
10
11
// case中设置sequence中的参数
function void my_case0::build_phase(uvm_phase phase);
super.build_phase(phase);
// 设置sequence中的参数,用通配符
uvm_config_db#(int)::set(this, "env.i_agt.sqr.*", "count", 9);

uvm_config_db#(uvm_object_wrapper)::set(this,
"env.i_agt.sqr.main_phase",
"default_sequence",
case0_sequence::type_id::get());
endfunction
1
2
3
4
5
6
7
// 在sequence的pre_body中取得参数
virtual task case0_sequence::pre_body();
if(uvm_config_db#(int)::get(null, get_full_name(), "count", count))
`uvm_info("seq0", $sformatf("get count value %0d via config_db", count), UVM_MEDIUM)
else
`uvm_error("seq0", "can't get count value!")
endtask

7.2 在sequence中设置参数

在sequence可以向component设置参数;也可以向sequence中设置参数。

sequence是动态运行的,它什么时候要设置参数不固定,component需要知道什么时候sequence设置了参数,这通过wait_modified任务实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//设置参数
virtual task case0_vseq::body();
my_transaction tr;
drv0_seq seq0;
drv1_seq seq1;
if(starting_phase != null)
starting_phase.raise_objection(this);
fork
`uvm_do_on(seq0, p_sequencer.p_sqr0);
`uvm_do_on(seq1, p_sequencer.p_sqr1);
begin
#10000;
uvm_config_db#(bit)::set(uvm_root::get(), "uvm_test_top.env0.scb", "cmp_en", 0);//向scb中设置参数
#10000;
uvm_config_db#(bit)::set(uvm_root::get(), "uvm_test_top.env0.scb", "cmp_en", 1);
end
join
#100;
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// scb中取得参数
task my_scoreboard::main_phase(uvm_phase phase);
my_transaction get_expect, get_actual, tmp_tran;
bit result;
bit cmp_en = 1'b1;

super.main_phase(phase);
fork
while(1) begin
// 等待cmp_en改变,否则阻塞
uvm_config_db#(bit)::wait_modified(this, "", "cmp_en");
void'(uvm_config_db#(bit)::get(this, "", "cmp_en", cmp_en));
`uvm_info("my_scoreboard", $sformatf("cmp_en value modified, the new value is %0d", cmp_en), UVM_LOW)
end
...
join
endtask

sequence宏的wait_modified类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 virtual task drv0_seq::body();
bit send_en = 1;
fork
while(1) begin
uvm_config_db#(bit)::wait_modified(null, get_full_name(), "send_en");
void'(uvm_config_db#(bit)::get(null, get_full_name, "send_en", send_en));
`uvm_info("drv0_seq", $sformatf("send_en value modified, the new value is %0d", send_en), UVM_LOW)
end
join_none
repeat (10) begin
`uvm_do(m_trans)
end
endtask
//在virtual sequence中启动这个sequence

8. sequence的响应

UVM中的sequence机制提供了sequence->sequencer->driver的数据传输机制,有时sequence也需要知道driver的响应,进而sequence调整产生的transaction。

8.1 put_response和get_response

rsp是UVM中定义的变量。

在sequence中get response:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 在sequence获得response
virtual task body();
if(starting_phase != null)
starting_phase.raise_objection(this);
repeat (10) begin
`uvm_do(m_trans)//发送transaction
get_response(rsp);//发送完tr后获得响应
`uvm_info("seq", "get one response", UVM_MEDIUM)
rsp.print();//对相应进行处理
end
#100;
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask

在driver中put response:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 在driver中返回响应
task my_driver::main_phase(uvm_phase phase);
vif.data <= 8'b0;
vif.valid <= 1'b0;
while(!vif.rst_n)
@(posedge vif.clk);
while(1) begin
seq_item_port.get_next_item(req);
drive_one_pkt(req);
rsp = new("rsp");//驱动完tr后,put response
rsp.set_id_info(req);//设置id,将req与rsp对应
seq_item_port.put_response(rsp);//返回response
seq_item_port.item_done();//最后调用item_done告诉sqr我发送完一个tr了,你可以发送下一个tr给我了
end
endtask

设置了id才知道哪个响应传给哪个sequence。

还可以直接在item_done中返回响应:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 在driver中返回响应
task my_driver::main_phase(uvm_phase phase);
vif.data <= 8'b0;
vif.valid <= 1'b0;
while(!vif.rst_n)
@(posedge vif.clk);
while(1) begin
seq_item_port.get_next_item(req);
drive_one_pkt(req);
rsp = new("rsp");
rsp.set_id_info(req);//设置id,将req与rsp对应
seq_item_port.item_done(rsp);
end
endtask

调用get_response(rsp)的时候,如果response_queue中没有响应,会阻塞在哪里,不能继续发送transaction。

8.2 response handler和另类response

上面提到的get_response阻塞问题,主要是因为发送transaction和get_response在同一个进程中,

1
2
`uvm_do(m_trans)//发送transaction
get_response(rsp);//获得响应

如果分开就可以解决阻塞问题,需要使用response_handler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class case0_sequence extends uvm_sequence #(my_transaction);
my_transaction m_trans;
...

virtual task pre_body();
use_response_handler(1);//打开response_handler
endtask
//重载response_handler函数
virtual function void response_handler(uvm_sequence_item response);
if(!$cast(rsp, response))//类型转换成my_transaction类型
`uvm_error("seq", "can't cast")
else begin
`uvm_info("seq", "get one response", UVM_MEDIUM)
rsp.print(); //对响应进行处理
end
endfunction

virtual task body();
if(starting_phase != null)
starting_phase.raise_objection(this);
repeat (10) begin
`uvm_do(m_trans)//body中只发送transaction
end
#100;
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask

`uvm_object_utils(case0_sequence)
endclass

8.3 另类的响应

uvm_do执行完后,它的第一个参数不是空指针,而是指向刚刚发送到driver的transaction;可以在driver将需要返回的信息传给这个transaction中,在sequence中调用完`uvm_do之后获取transaction中的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
//driver中返回信息到transaction
task my_driver::main_phase(uvm_phase phase);
vif.data <= 8'b0;
vif.valid <= 1'b0;
while(!vif.rst_n)
@(posedge vif.clk);
while(1) begin
seq_item_port.get_next_item(req);
drive_one_pkt(req);
req.frm_drv = "this is information from driver";//将信息存到穿过来的transaction中。
seq_item_port.item_done();
end
endtask
1
2
3
4
5
6
7
8
9
10
11
12
//在sequence获得driver返回的信息。
virtual task case0_sequence::body();
if(starting_phase != null)
starting_phase.raise_objection(this);
repeat (10) begin
`uvm_do(m_trans)
`uvm_info("seq", $sformatf("get information from driver: %0s", m_trans.frm_drv), UVM_MEDIUM)//获得driver返回的信息
end
#100;
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask

9. sequence library

根据特定算法随机选择注册在其中的sequence,并在body中执行这些sequence。

10. 补充一种启动sequence的方法

在sequence外,通过sequence句柄调用sequence中发送transaction的任务。

原理是:之前把发送transaction的语句写在了body中,sequence启动后,自动调用body,所以我们也可以把发送transaction语句写在其他任务中,让body为空。我们在sequence启动后,手动调用这些任务发送transaction。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class sequence1 extends uvm_sequence#(transaction);

`uvm_object_utils(sequence1);
function new(string name="sequence1");
super.new(name);
endfunction
task write();
transaction tr;
`uvm_info(get_type_name(),"write",UVM_LOW)
`uvm_do(tr);
endtask
task read();
transaction tr;
`uvm_info(get_type_name(),"read",UVM_LOW)
`uvm_do(tr);
endtask
task pre_body();
if(starting_phase != null)
starting_phase.raise_objection(this);
`uvm_info(get_type_name(),"pre_body",UVM_LOW)
endtask
task body();
`uvm_info(get_type_name(),"body",UVM_LOW)
wait(0);

endtask
task post_body();
`uvm_info(get_type_name(),"post_body",UVM_LOW)
if(starting_phase!=null)
starting_phase.drop_objection(this);
endtask
endclass
//在case中启动启动
class case0 extends base_test;
`uvm_component_utils(case0);

function new(string name="case0",uvm_component parent=null);
super.new(name,parent);
endfunction

task run_phase(uvm_phase phase);
sequence1 seq1;
seq1=new("seq1");
fork
begin
seq1.starting_phase = phase;
seq1.start(env0.sqr);
end
begin
for(int i=0;i<10;i++) begin
seq1.write();
seq1.read();
end
end
join_none
endtask
endclass

结果如下:

1
2
3
4
5
6
7
8
9
10
11
27:UVM_INFO seq/sequence1.sv(13) @ 0: uvm_test_top.env0.sqr@@seq1 [sequence1] write
28:UVM_INFO seq/sequence1.sv(25) @ 0: uvm_test_top.env0.sqr@@seq1 [sequence1] pre_body
29:UVM_INFO seq/sequence1.sv(28) @ 0: uvm_test_top.env0.sqr@@seq1 [sequence1] body
54:UVM_INFO seq/sequence1.sv(19) @ 0: uvm_test_top.env0.sqr@@seq1 [sequence1] read
79:UVM_INFO seq/sequence1.sv(13) @ 0: uvm_test_top.env0.sqr@@seq1 [sequence1] write
....
454:UVM_INFO seq/sequence1.sv(19) @ 0: uvm_test_top.env0.sqr@@seq1 [sequence1] read
479:UVM_INFO seq/sequence1.sv(13) @ 0: uvm_test_top.env0.sqr@@seq1 [sequence1] write
504:UVM_INFO seq/sequence1.sv(19) @ 0: uvm_test_top.env0.sqr@@seq1 [sequence1] read
529:UVM_INFO seq/sequence1.sv(33) @ 1000000: uvm_test_top.env0.sqr@@seq1 [sequence1] post_body
543:[sequence1] 23

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!