apb协议
写时序
地址、写信号、PSEL、写数据信号同时发生变化,即传输的第一个时钟被称为SETUP周期。在下个时钟上升沿,PENABLE信号拉高,表示ENABLE周期,在该周期内,数据、地址以及控制信号都必须保持有效。整个写传输在这个周期结束时完成:
读时序
地址、写信号、PSEL信号同时发生变化,在下个时钟上升沿,PENABLE信号拉高,从机必须在ENABLE周期内提供读数据:
Sequencer
充当激励环节的路由器作用,管理sequence,也传递数据,仲裁某一时刻传递哪个sequence的数据
- uvm_sequencer#(REQ,RSP)
REQ:request;RSP:response//一般相同 - default_sequence
决定接数据的类型 - seq_item_export
决定送数据的driver
sequencer的实现
c
class apb_sequencer extends uvm_sequencer #(apb_trans);
`uvm_component_utils(apb_sequencer);
function new(string name,uvm_component parent);
super.new(name,parent);
endfunction
endclass
c
简便方法(不建议使用):
typedef uvm_sequencer #(apb_trans) apb_sequencer;
virtual sequencer
- 不同的sequence需要不同的sequencer
- 控制其他的sequencer
- 不和任何driver相连
- 本身不处理item
实现与sequencer相同,其中可以定义子sequencer
Driver
driver和monitor都是数据转换类型,但方向相反
driver的方法:
get_next_item(阻塞)
try_next_item(不阻塞)
item_done(不阻塞)
c
driver的使用
uvm_driver(REQ,REP)
seq_item_port.get_next_item(req);//get上述括号内传来的句柄
driver的实现
c
class apb_driver extends uvm_driver #(apb_trans);
`uvm_component_utils(apb_driver)
//定义虚接口
virtual apb_interface vif;
function new(string name,uvm_component parent);
super.new(name,parent);
endfunction
//config_db连接虚接口
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
if(!uvm_config_db #(virtual apb_interface)::get(this,"","vif",vif);
`uvm_fatal("NOvif","NOvif for apb_driver");
end
endfunction
task run_phase(uvm_phase phase);
super.run_phase(phase);
vif.paddr<=0;
vif.pwdata<=0;
vif.pwrite<=0;
vif.psel<=0;
vif.penable<=0;
tx_driver();
endtask
//从seq_item_port获取transaction
task tx_driver();
forever begin
seq_item_port.get_next_item(req);
send(req);
seq_item_port.item_done();
end
endtask
//给vif传送transaction
task send(apb_trans tr);
case(tr.dir)
apb_trans::RD:begin
@(posedge vif.clk);
vif.paddr<=tr.addr;
vif.pwrite<=0;
vif.psel<=1;
@(posedge vif.clk);
vif.penable<=1;
@(posedge vif.clk);
tr.data=vif.prdata;
vif.psel<=0;
vif.penable<=0;
end
apb_trans::WR:begin
@(posedge vif.clk);
vif.paddr<=tr.addr;
vif.pwdata<=tr.data;
vif.psel<=1;
vif.pwrite<=1;
@(posedge vif.clk);
vif.penable<=1;
@(posedge vif.clk);
tr.data=vif.prdata;
vif.psel<=0;
vif.penable<=0;
end
endcase
endclass
Monitor
源码中没有定义port
monitor不用像driver一样按时序操作的原因:
driver:transaction→DUT,DUT时序要求正确
monitor:DUT→transaction,验证没有时序的概念,只关注行为
monitor的实现
c
class apb_monitor extends uvm_monitor;
`uvm_component_utils(apb_moitor);
//定义虚接口
virtual apb_interface vif;
//利用uvm_analysis_port创建apb_mon_port
uvm_analysis_port #(apb_trans) apb_mon_port;
function new(string name,uvm_component parent);
super.new(name,parent);
endfunction
config_db连接虚接口,new apb_mon_port
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db #(virtual apb_interface)::get(this,"","vif",vif);
`uvm_fatal("NOvif","NOvif for apb_monitor");
end
apb_mon_port=new("apb_mon_port",this);
endfunction
//从vif获取transaction,要create transaction,写入apb_mon_port中,base_test中会与scoreboard的uvm_analysis_imp连接
task run_phase(uvm_phase phase);
super.run_phase(phase);
forever begin
apb_trans tr = apb_trans::type_id::create("tr",this);
@(posedge vif.clk);
if(vif.psel==1 && vif.penable==1)begin
tr.dir=(vif.pwrite)?apb_trans::WR:apb_trans::RD;
tr.addr=vif.paddr;
tr.data=(vif.pwrite)?vif.pwdata:vif.prdata;
apb_mon_port.write(tr);
end
end
endtask
endclass