目录
sequence相关宏及其实现
uvm_do系列宏
uvm_do系列宏主要有以下8个:
`uvm_do(SEQ_OR_ITEM)
`uvm_do_pri(SEQ_OR_ITEM, PRIORITY)
`uvm_do_with(SEQ_OR_ITEM, CONSTRAINTS)
`uvm_do_pri_with(SEQ_OR_ITEM, PRIORITY, CONSTRAINTS)
`uvm_do_on(SEQ_OR_ITEM, SEQR)
`uvm_do_on_pri(SEQ_OR_ITEM, SEQR, PRIORITY)
`uvm_do_on_with(SEQ_OR_ITEM, SEQR, CONSTRAINTS)
`uvm_do_on_pri_with(SEQ_OR_ITEM, SEQR, PRIORITY, CONSTRAINTS)
其中uvm_do、uvm_do_with、uvm_do_pri、uvm_do_pri_with在前面已经提到过。UVM实战 卷I学习笔记9——UVM中的sequence(1)
介绍另4个:
uvm_do_on用于显式地指定使用哪个sequencer发送此transaction。它有两个参数:transaction的指针和sequencer的指针。当在sequence中使用uvm_do等宏时,其默认sequencer就是此sequence启动时为其指定的sequencer,sequence将这个sequencer的指针放在其成员变量m_sequencer中。事实上uvm_do等价于:`uvm_do_on(tr, this.m_sequencer)
在这里看起来指定使用哪个sequencer似乎并没有用,它的真正作用要在virtual sequence中得到体现(见后文)。
uvm_do_on_pri,有三个参数:transaction的指针、sequencer的指针和优先级;
uvm_do_on_with,有三个参数:transaction的指针、sequencer的指针和约束;
uvm_do_on_pri_with,四个参数:transaction的指针、sequencer的指针、优先级和约束;
如:`uvm_do_on_pri_with(tr, this, 100, {tr.pload.size == 100;}) //注意约束得加花括号
uvm_do系列的其他七个宏其实都是用uvm_do_on_pri_with宏来实现的。如uvm_do宏:
`define uvm_do(SEQ_OR_ITEM) \
`uvm_do_on_pri_with(SEQ_OR_ITEM, m_sequencer, -1, {})
*uvm_create与uvm_send
除了使用uvm_do宏产生transaction,还可使用uvm_create宏与uvm_send宏来产生:
class case0_sequence extends uvm_sequence #(my_transaction);
…
virtual task body();
int num = 0;
int p_sz;
…
repeat (10) begin
num++;
`uvm_create(m_trans)
assert(m_trans.randomize());
p_sz = m_trans.pload.size();
{m_trans.pload[p_sz - 4],
m_trans.pload[p_sz - 3],
m_trans.pload[p_sz - 2],
m_trans.pload[p_sz - 1]}
= num;
`uvm_send(m_trans)
end
…
endtask
…
endclass
uvm_create宏的作用是实例化transaction。当一个transaction被实例化后,可以对其做更多的处理,处理完毕后使用uvm_send宏发送出去。这种使用方式比uvm_do系列宏更加灵活。如在上例中,就将pload的最后4个byte替换为此transaction的序号。
事实上,在上述代码中也完全可以不使用uvm_create宏,而直接调用new进行实例化:
virtual task body();
…
m_trans = new("m_trans");
assert(m_trans.randomize());
p_sz = m_trans.pload.size();
{m_trans.pload[p_sz - 4],
m_trans.pload[p_sz - 3],
m_trans.pload[p_sz - 2],
m_trans.pload[p_sz - 1]}
= num;
`uvm_send(m_trans)
…
endtask
除了uvm_send外还有uvm_send_pri宏,作用是将transaction交给sequencer时设定优先级:
virtual task body();
…
m_trans = new("m_trans");
assert(m_trans.randomize());
p_sz = m_trans.pload.size();
{m_trans.pload[p_sz - 4],
m_trans.pload[p_sz - 3],
m_trans.pload[p_sz - 2],
m_trans.pload[p_sz - 1]}
= num;
`uvm_send_pri(m_trans, 200)
…
endtask
*uvm_rand_send系列宏
uvm_rand_send系列宏有如下几个:
`uvm_rand_send(SEQ_OR_ITEM)
`uvm_rand_send_pri(SEQ_OR_ITEM, PRIORITY)
`uvm_rand_send_with(SEQ_OR_ITEM, CONSTRAINTS)
`uvm_rand_send_pri_with(SEQ_OR_ITEM, PRIORITY, CONSTRAINTS)
uvm_rand_send宏与uvm_send宏类似,唯一的区别是它会对transaction进行随机化。这个宏使用的前提是transaction已经被分配了空间,即已经实例化了:
m_trans = new("m_trans");
`uvm_rand_send(m_trans)
uvm_rand_send_pri宏用于指定transaction的优先级。它有两个参数:transaction的指针和优先级;
uvm_rand_send_with宏用于指定使用随机化时的约束,它有两个参数:transaction的指针和约束;
uvm_rand_send_pri_with宏用于指定优先级和约束,它有三个参数:transaction的指针、优先级和约束;
如:m_trans = new(“m_trans”);
`uvm_rand_send_pri_with(m_trans, 100, {m_trans.pload.size == 100;}) //注意约束得加花括号
uvm_rand_send系列宏及uvm_send系列宏的意义主要在于如果一个transaction占用的内存比较大,那么很可能希望前后两次发送的transaction都使用同一块内存,只是其中的内容可以不同,这样比较节省内存。
*start_item与finish_item
前面一直使用宏产生transaction。宏隐藏了细节,方便了用户的使用,但也给用户带来了困扰:宏到底做了什么事情?
不使用宏产生transaction的方式要依赖于start_item和finish_item。在使用这两个任务前,必须要先实例化transaction后才可以调用这两个任务:
tr = new("tr");
start_item(tr);
finish_item(tr);
完整使用如上两个任务构建的一个sequence如下:
virtual task body();
repeat(10) begin
tr = new("tr");
start_item(tr);
finish_item(tr);
end
endtask
代码并没有对tr进行随机化。可以在transaction实例化后、finish_item调用前对其进行随机化:
class case0_sequence extends uvm_sequence #(my_transaction);
…
virtual task body();
…
repeat (10) begin
tr = new("tr");
assert(tr.randomize() with {tr.pload.size == 200;});
start_item(tr);
finish_item(tr);
end
…
endtask
…
endclass
上述assert语句也可以放在start_item之后、finish_item之前。uvm_do系列宏其实是将下述动作封装在了一个宏中:
virtual task body();
…
tr = new("tr");
start_item(tr);
assert(tr.randomize() with {tr.pload.size() == 200;});
finish_item(tr);
…
endtask
如果要指定transaction的优先级,那么要在调用start_item和finish_item时都要加入优先级参数:
virtual task body();
…
start_item(tr, 100);
finish_item(tr, 100);
…
endtask
如果不指定优先级参数,默认的优先级为-1。
*pre_do、 mid_do与post_do
uvm_do宏封装了从transaction实例化到发送的一系列操作,封装的越多则其灵活性越差。
为了增加uvm_do系列宏的功能,UVM提供了三个接口:
- pre_do是在start_item中被调用的任务,在start_item返回前执行的最后一行代码,在它执行完毕后才对transaction进行随机化。
- mid_do是位于finish_item最开始的函数,执行完此函数后finish_item才进行其他操作。
- post_do也是位于finish_item中的函数,它是finish_item返回前执行的最后一行代码。
它们的执行顺序大致为:
wait_for_grant、send_request及wait_for_item_done都是UVM内部的一些接口。这三个接口函数/任务的使用示例如下:
class case0_sequence extends uvm_sequence #(my_transaction);
my_transaction m_trans;
int num;
…
virtual task pre_do(bit is_item);
#100;
`uvm_info("sequence0", "this is pre_do", UVM_MEDIUM)
endtask
virtual function void mid_do(uvm_sequence_item this_item);
my_transaction tr;
int p_sz;
`uvm_info("sequence0", "this is mid_do", UVM_MEDIUM)
void'($cast(tr, this_item));
p_sz = tr.pload.size();
{tr.pload[p_sz - 4],
tr.pload[p_sz - 3],
tr.pload[p_sz - 2],
tr.pload[p_sz - 1]} = num;
tr.crc = tr.calc_crc();
tr.print();
endfunction
virtual function void post_do(uvm_sequence_item this_item);
`uvm_info("sequence0", "this is post_do", UVM_MEDIUM)
endfunction
virtual task body();
…
repeat (10) begin
num++;
`uvm_do(m_trans)
end
…
endtask
…
endclass
pre_do的参数:用于表明uvm_do宏是在对一个transaction还是在对一个sequence进行操作。
mid_do和post_do的两个参数是正在操作的sequence或者item的指针,但是其类型是uvm_sequence_item类型。通过cast可以转换成目标类型(示例中为my_transaction) 。