1.sequence不属于验证平台的任何一部分,出于比较特殊的位置:
1).transaction 像一颗单独的子弹,包含数据元素。
2).sequence 是transaction 的 ”序列“。
3).sequencer 是一把枪,负责发射。
4).sequencer是一个uvm_component,sequence 是一个uvm_object。
5).sequence 生命周期比my_transaction 要长,因为是”序列“嘛!
2.sequence 如下:
`ifndef MY_SEQUENCE__SV
`define MY_SEQUENCE__SV
class my_sequence extends uvm_sequence #(my_transaction);
my_transaction m_trans;
function new(string name= "my_sequence");
super.new(name);
endfunction
virtual task body();
repeat (10) begin
`uvm_do(m_trans)
end
#1000;
endtask
`uvm_object_utils(my_sequence)
endclass
`endif
1)循环 10次,弄出一个序列。
2)要指定transaction 类型。
3)每一个sequence都有一个body 任务(main_phase 是属于components之类的),当一个sequence 启动之后,会自动执行body 的代码。
4)uvm_do 宏:1)创建一个my_transaction 的实例m_trans;2)将其随机化;3)最终送给sequencer。
5)如果不使用uvm_do 宏,可以直接使用start_item 与finish_item 产生transaction。
3.sequencer过程
1)一个sequence 再向sequencer发送请求前,要先向sequencer发送一个请求(只是个请求,不是真的transaction),sequencer把这个请求放在一个仲裁队列中。
2)sequencer做两件事:1)检验队列是否有sequence发送transaction的请求;2)检验driver是否申请transaction。
3)如果仲裁队列有请求,但driver没有申请transaction,那sequencer会一直等待driver。如果driver申请,sequencer 同意sequence的发送请求,sequence得到sequencer的批准后,产生transaction 交给sequencer,sequencer把这个transaction 交给driver。
4) 反正也一样:仲裁队列没请求,driver申请,那么一直等sequence。
5)如果两边同时,也一样。
4.driver 如何向sequencer 申请transaction呢?
1)在uvm_driver 中有变量seq_item_port:
2)在uvm_sequencer 中有变量seq_item_export:
在这两者中建立通道。
5.在my_agent中的connect 把两者连接到一起:
function void my_agent::connect_phase(uvm_phase phase);
super.connect_phase(phase);
if (is_active == UVM_ACTIVE) begin
drv.seq_item_port.connect(sqr.seq_item_export);
end
ap = mon.ap;
endfunction
好像永远是port 主动连接export。
6.在driver中通过get_next_item 任务向sequencer 申请新的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);
seq_item_port.item_done();
end
endtask
1)因为driver负责驱动transaction,不负责生产,只要拿到transaction 就驱动,所以必须是无限循环while(1)。
2)item_done:当driver 使用get_next_item得到transaction 时,sequencer 自己也保留一份刚刚送出去的transaction。当sequencer 发出transaction,而driver 并没有得到时,sequencer会把保留的这份transaction在发送出去(这一过程和sequence无关)。
3)sequencer怎么知道driver 是否成功得到transaction 呢?在下次调用get_next_item 之前,item_done 被调用,sequencer认为driver已得到transaction,会把这个transaction删除。握手机制。
7.sequence中的uvm_do 什么时候返回呢?uvm_do产生一个transaction 后交给sequencer,driver区总这个transaction后,uvm_do不会立刻返回执行下一次uvm_do宏,一直等待,直到driver返回item_done 信号。此时,uvm_do才执行完毕返回,开始下一次uvm_do,产生新的transaction。
1)不是之前想的产生所有的10个后,一起交给sequencer。
2)占内存的应该只是sequence,sequencer 和 driver 都应该是指针取地址吧!!
8.虽然在driver的seq_item_port 中使用了get_next_item 和 item_done,但是我没在seq_item_port 中见到它们的定义。后来在sequencer 中找到了:
怎么实现的呢?
应该是my_agent的connect_phase。
9.sequence 如何向sequencer中送出transaction呢?前面已定义sequence,只要在某个component(如my_sequencer,my_env)的main_phase中启动sequence即可。另一侧的driver一直想sequencer取transaction,只要这边对接好就行,以my_env为例:
task my_env::main_phase(uvm_phase phase);
my_sequence seq;
phase.raise_objection(this);
seq = my_sequence::type_id::create("seq");
seq.start(i_agt.sqr);
phase.drop_objection(this);
endtask
1)首先创建。
2)然后调用start任务,参数是一个sequencer指针。
3)在UVMZ中,objection一般伴随着sequence,之前的driver中也是这样。
10.也可以在sequencer中启动sequence:
task my_sequencer::main_phase(uvm_phase phase);
my_sequence seq;
phase.raise_objection(this);
seq = my_sequence::type_id::create("seq");
seq.start(this);
phase.drop_objection(this);
endtask
start的参数变成了this。
11代码使用了get_next_item,还可以使用try_next_item。get_next_item 是阻塞的,它会一直等;try_next_item是非阻塞的,如果没有新的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.try_next_item(req);
if(req == null)
@(posedge vif.clk);
else begin
drive_one_pkt(req);
seq_item_port.item_done();
end
end
endtask
try_next_item更加接近真实driver 行为:有数据,就驱动,否则总线一直处于空闲状态。(不懂啊,就算get 阻塞住,也没对总线有影响。。????)