class uvm_sequence extends uvm_sequence_item; // uvm_sequence 继承于 uvm_sequence_item
// case属于sequence类型,case本质是个sequence
// case0_sequence 在 my_sequencer上启动:case0_sequence.start(my_sequencer)
class case0_sequence extends uvm_sequence #(my_transaction);
my_trasaction m_trans;
`uvm_object_utils(case0_squence)
...
virtual task body();
...
repeat(10)
begin
... //如何得到dmac和smac?
end
...
endclass
// 创建继承于 uvm_sequencer 的 my_sequencer
`uvm_do_with 宏,这个宏完成了三个步骤:
• sequence 或 item 的创建;
• sequence 或 item 的随机化;
• sequence 或 item 的传送
uvm_do_with(sequencer, {sequencer.seq_item = values}
class case0_sequence extends uvm_sequence #(my_transaction);
my_trasaction m_trans;
`uvm_object_utils(case0_squence)
...
virtual task body();
my_sequencer x_sequencer; // class my_sequencer extends uvm_sequencer #(my_transaction); 声明uvm_sequencer类型变量x_sequencer
...
$cast(x_sequencer, m_sequencer); // 将m_sequencer转换为x_sequencer
...
endtask: body
repeat(10)
begin
`uvm_do_with(m_trans,{m_trans.dmac==x_sequencer.dmac;
m_trans.smac==x_sequencer.smac;}) // `uvm_do_with宏创建item,并调用item.randomize()对item进行约束,(item.randomize with {m_trans.dmac==x_sequencer.dmac; m_trans.smac==x_sequencer.smac;})
end
...
endclass
方法一:在 case0_sequence 的 body 中创建 my_sequencer 类型变量 x_sequencer,再 $cast(x_sequencer, m_sequencer),m_sequencer 通过 cast 转换成 x_sequencer,相当于 my_sequencer m_sequencer? sequence 通过自己的成员变量挂载在 sequencer上?
// 使用宏定义 声明一个 my_sequencer 类型的成员变量 p_sequencer
class case0_sequence extends uvm_sequence #(my_transaction);
my_trasaction m_trans;
`uvm_object_utils(case0_squence)
`uvm_declare_p_sequencer(my_sequencer) // UVM内建宏`uvm_declare_p_sequencer(SEQUENCER),UVM会自动将m_sequencer通过$cast() 转换成 my_sequencer 类型的 p_sequencer,在pre_body() 之前完成
...
virtual task body();
...
repeat(10)
begin
`uvm_do_with(m_trans,{m_trans.dmac==p_sequencer.dmac;
m_trans.smac==p_sequencer.smac;})
end
...
endclass
`uvm_declare_p_sequencer(my_sequencer)宏的作用:
- 声明了一个my_sequencer类型的句柄p_sequencer
- 将 m_sequencer 句柄通过 $cast(p_sequencer,m_sequencer) 转化为 p_sequencer 类型的句柄。
equence/item一旦“挂载”到某一个sequencer上,该sequencer的句柄即被赋值于m_sequencer(uvm_sequencer_base类);而p_sequencer通常需要通过在定义sequence类使,通过宏`uvm_declare_p_sequencer(SEQUENCER)声明,间接定义p_sequencer成员变量。m_sequencer与p_sequencer均指向同一个挂载的sequencer句柄,只是前者使父类句柄,后者往往是所挂载sequencer句柄(子类句柄)
对于两个具有继承关系的class,若对应父类的instance为father_tr,而子类的instance为child_tr,那么如果只是让子类的指针指向父类,有以下几种操作方法:
1、简单赋值:fathrer_tr = child_tr;
2、使用copy函数:father_tr = child_tr.copy();
3、使用cast函 数:cast(father_tr, child_tr);
以上3种方法均可实现数据的传递,但有本质区别:(1)通过简单赋值操作,使两个handle father_tr和child_tr指向了同一块memory,因此,father_tr的类型也会变成子类的类型。(2)使用copy函数,是将数值进行简单的copy,即将child_tr对应的memory中的数据放入father_tr对应的memory中,这种数据的copy方式,不会带来类型的变化,因此,father_tr仍然是父类的类型,但使用copy函数要求father_tr得先new一块memory, 如果没有memory,则需要使用clone函数。(3)此处的$cast操作与(1)中的简单赋值很类似,father_tr的类型也会变为子类的类型。
上面子类向父类进行数据传递时,显示不出cast操作向下类型转换的优势,但父类向子类传递数据时,无法使用child_tr = father_tr,则必须要使用$cast操作。
直接使用cast(child_tr, father_tr)是非法的,此时则要求基类的handle必须指向派生类的对象,即father_tr必须指向child_tr类型的对象,因此一般会有如下几步操作:
1、定义一个派生类类型的对象:child1_tr = new;
2、father_tr = child1_tr;
3、$cast(child_tr, father_tr);
以上第(3)步,$cast会做类型检查,若类型兼容返回1,否则返回0。
m_sequencer是uvm_sequencer_base类型的句柄,默认情况下在每个sequence中都可用。要访问正在运行sequence 的真实sequencer ,我们需要将m_sequencer类型转化为真实sequencer ,通常称为p_sequencer。
// 下面是一个简单的示例,sequence 希望访问sequencer的 clock monitor组件
class test_sequencer_c extends uvm_sequencer;
clock_monitor_c clk_monitor;
endclass
class test_sequence_c extends uvm_sequence;
test_sequencer_c p_sequencer; // sequencer/monitor在sequence中声明
clock_monitor_c my_clock_monitor;
task pre_body() //Typecast the m_sequencer base type to p_sequencer
if(!$cast(p_sequencer, m_sequencer)) begin // m_sequencer转p_sequencer,sequence挂载sequencer
`uvm_fatal("Sequencer Type Mismatch:", " Worng Sequencer");
end
//get access to clock monitor
my_clock_monitor = p_sequencer.clk_monitor; // sequence中声明的monitor连接sequencer的monitor
endtask
endclass