文章目录
本文目标
使用 TLM端口 实现 scoreboard
组件与 reference_model
组件之间的连接!
一、总体 / 局部结构
UVM验证平台总体框图如下所示,紫色圆圈包围的即是本文探讨的唯一重点:
1、总体结构分析
自动比对
是验证流程中不可或缺的一项重要功能。其具体 实现方法 结合图示为:
1. 在 master_agent 中的 monitor 是将 DUT 端口上的输入信号进行采集并在 reference_model中通过计算输出结果;
2. 得到的输出结果经过 r2s_fifo (reference to scoreboard 各取首字母发音即 r2s ) 缓存并送至 Scoreboard ;
3. 另一方面在 slave_agent 中的 monitor 是将 DUT 端口上的输出信号进行采集并缓存至 s_a2s_fifo (slave_agent to scoreboard 各取首字母发音即 s_a2s );
4. 将 s_a2s_fifo 中 DUT 的输出数据也传入至 Scoreboard 并与第 2 步的数据进行自动比对
。
2、局部结构分析
Tips:(编号1~12后文会使用)
- m2s_port [ monitor(out) to scoreboard—port]
- s_a2s_export [slave_agent to scoreboard—export]
- s_a2s_fifo.blocking_put_export [此处是 put 模式—注意是
import
] - s_a2s_fifo.blocking_get_export [此处是 get 模式—注意是
import
] - s_a2s_port [slave_agent to scoreboard—port]
- m2r_port [ monitor(in) to reference_model—port]
- m_a2r_export [master_agent to reference_model—export]
- m_a2r_imp [master_agent to reference_model—import]
- ref_model.r2s_port [reference_model to scoreboard—port]
- r2s_fifo.blocking_put_export [此处是 put 模式—注意是
import
] - r2s_fifo.blocking_get_export [此处是 get 模式—注意是
import
] - sb.r2s_port [reference_model to scoreboard—port]
一、
输入
端口路线分析(紫色箭头)
- < ① > DUT -> monitor 是由 interface 产生的 virtual 句柄进行的数据传递,此处不在讨论范围内;
- < ② > 我们应首先主动地将数据发送至 reference_model 中,此时 monitor 为主动发起者, reference_model 为被动接收方,控制流
为 monitor (port)流向 reference_model ,数据流
为 monitor 流向 reference_model ,在数据由 monitor 传向 reference_model 的过程中,需穿越 master_agent (export),因此需在 master_agent.sv 中将二者进行连接,即m_moni.m2r_port.connect(this.m_a2r_export);
【编号6~7】;
- < ③ > 将经过 master_agent (export)传出的数据传入至 reference_model (import)中,因其二者需在 env.sv 中进行连接,即m_agent.m_a2r_export.connect(ref_model.i_m2r_imp)
【编号7~8】;
- < ④ > 从 reference_model (port)中传出的数据通过put()
方法传入至 r2s_fifo (import)中,此时 reference_model 为主动发起者, r2s_fifo 为被动接收方,控制流
为 reference_model (port)流向 r2s_fifo (import),数据流
为 reference_model 流向 r2s_fifo ,因此这里使用put()
方法。因其二者是在 env.sv 中进行连接,即ref_model.r2s_port.connect(this.r2s_fifo.blocking_put_export)
【编号9~10】;
- < ⑤ > 存放在 r2s_fifo 中的数据不会主动传递至 scoreboard ,而是需等待(阻塞)scoreboard 的命令后才会将数据发送出去,此时 scoreboard 是主动方, r2s_fifo 为被动方,控制流
为 scoreboard (port)流向 r2s_fifo (import),数据流
为r2s_fifo 流向 scoreboard ,因此应使用get()
方法。其二者是在 env.sv 中进行连接,即sb.r2s_port.connect(this.r2s_fifo.blocking_get_export);
【编号11~12】。
二、
输出
端口路线分析(绿色箭头)
- < ① > DUT -> monitor 是由 interface 产生的 virtual 句柄进行的数据传递,此处不在讨论范围内;
- < ② > 我们应首先主动将数据存入至 FIFO 中,此时 monitor 为主动发起者,s_a2s_fifo 为被动接收方,控制流
为 monitor (port)流向 s_a2s_fifo ,数据流
为 monitor 流向 s_a2s_fifo ,在数据由 monitor 传向 s_a2s_fifo 的过程中,需穿越 slave_agent (export),因此在 slave_agent.sv 中将二者进行连接,即m_moni.m2s_port.connect(this.s_a2s_export);
【编号1~2】;
- < ③ > 从 slave_agent (export)中传出的数据通过put()
方法传入至 s_a2s_fifo (import)中,其二者是在 env.sv 中进行连接,即s_agent.s_a2s_export.connect(this.s_a2s_fifo.blocking_put_export);
【编号2~3】;
- < ④ > 存放在 s_a2s_fifo 中的数据不会主动传递至 scoreboard ,而是需等待(阻塞)scoreboard 的命令后才会将数据发送出去,此时 scoreboard 是主动方, s_a2s_fifo 为被动方,控制流
为 scoreboard (port)流向 s_a2s_fifo (import),数据流
为 s_a2s_fifo 流向 scoreboard ,因此应使用get()
方法。其二者是在 env.sv 中进行连接,即sb.s_a2s_port.connect(this.s_a2s_fifo.blocking_get_export);
【编号4~5】。
二、通信步骤
1、输入路线
1)新建in_monitor.sv文件(输入路线①)
代码如下(示例):
//in_monitor扩展于uvm_monitor
class in_monitor extends uvm_monitor;
//添加port(参数为传输事务的类型)
uvm_blocking_put_port #(my_transaction) m2r_port;
//实例化port对象
function new(string name = "",uvm_component parent);
super.new(name , parent);
this.m2r_port = new("m2r_imp",this);
endfunction
//将monitor发送的事务打印出来
virtual task run_phase(uvm_phase phase);
....//循环发送至reference_model
`uvm_info("Monitor","Now monitor send the transaction to the reference model!",UVM_MEDIUM)
//调用put()方法发送transaction
this.m2r_port.put(tr);
endtask
endclass
2)新建master_agent.sv文件(输入路线②)
代码如下(示例):
//master_agent扩展于uvm_agent
class master_agent extends uvm_agent;
...
//添加export(参数为传输事务的类型)
uvm_blocking_put_export #(my_transaction) m_a2r_export;
//实例化export对象
function new(string name = "",uvm_component parent);
super.new(name , parent);
this.m_a2r_export = new("m_a2r_export",this);
endfunction
//连接monitor的port和master_agent的export
virtual function void connect_phase(uvm_phase phase);
if(is_active == UVM_ACTIVE)
m_driv.seq_item_port.connect(m_seqr.seq_item_export);
m_moni.m2r_port.connect(this.m_a2r_export);//核心
endtask
endclass
3)新建my_reference_model.sv文件(输入路线③)
class my_reference_model extends uvm_component;
`uvm_component_utils(my_reference_model)
uvm_blocking_put_imp #(my_transaction,my_reference_model) i_m2r_imp;
uvm_blocking_put_port #(my_transaction) r2s_port;
my_transaction item;
function new(string name = "",uvm_component parent);
super.new(name,parent);
this.i_m2r_imp = new("i_m2r_imp",this) ;
this.r2s_port = new("r2s_port",this) ;
endfunction
task put(my_transaction tr);
`uvm_info("REF_REPORT",{"\n","master agent have been sent a transaction :\n",tr.sprint()},UVM_MEDIUM);
this.r2s_port.put(tr);
endtask
endclass
4)新建my_env.sv文件(输入路线④、⑤)
代码如下(示例):
class my_env extends uvm_env;
`uvm_component_utils(my_env)
master_agent m_agent ;
slave_agent s_agent ;
env_config m_env_cfg ;
my_reference_model ref_model;
my_scoreboard sb;
uvm_tlm_analysis_fifo#(my_transaction) r2s_fifo ;
uvm_tlm_analysis_fifo#(my_transaction) s_a2s_fifo ;
function new(string name = "",uvm_component parent);
super.new(name,parent);
this.r2s_fifo = new("r2s_fifo",this) ;
this.s_a2s_fifo = new("s_a2s_fifo",this);
endfunction
virtual function void build_phase(uvm_phase phase);
super.bulid_phase(phase);
if(!uvm_config_db#(env_config)::get(this,"","env_cfg",m_env_cfg))begin
`uvm_fatal("CONFIG_FATAL","ENV can not get the configuration !!!")
end
uvm_config_db#(agent_config)::set(this,"m_agent","m_agent_cfg",m_env_cfg.m_agent_cfg);
uvm_config_db#(agent_config)::set(this,"s_agent","m_agent_cfg",m_env_cfg.m_agent_cfg);
if(m_env_cfg.is_coverage)begin
`uvm_info("COVERAGE_ENABLE","The function coverage is enable for this testcase",UVM_MEDIUM)
end
if(m_env_cfg.is_check)begin
`uvm_info("CHECK_ENABLE","The check function is enable for this testcase",UVM_MEDIUM)
sb = my_scoreboard::type_id::create("sb",this);
end
m_agent = master_agent::type_id::create("m_agent",this);
s_agent = slave_agent::type_id::create("s_agent",this);
ref_model = my_reference_model::type_id::create("ref_model",this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
`uvm_info("ENV","Connect the agent and reference model...",UVM_MEDIUM)
m_agent.m_a2r_export.connect(ref_model.i_m2r_imp);
s_agent.s_a2s_export.connect(this.s_a2s_fifo.blocking_put_export);
ref_model.r2s_port.connect(this.r2s_fifo.blocking_put_export); //输入4
if(m_env_cfg.is_check)begin
sb.r2s_port.connect(this.r2s_fifo.blocking_get_export); //输入5
sb.s_a2s_port.connect(this.s_a2s_fifo.blocking_get_export);
end
endfunction
endclass
2、输出路线
1)新建out_monitor.sv文件(输出路线①)
代码如下(示例):
class out_monitor extends uvm_monitor; //out_monitor:DUT输出端口传输至的组件
`uvm_component_utils(out_monitor) //是组件,就要注册
//用于连接DUT的输出端口
virtual dut_interface m_vif;
//添加port
uvm_blocking_put_port #(my_transaction) m2s_port; //monitor to scoreboard
function new(string name = "",uvm_component parent);
super.new(name,parent);
this.m2s_port = new("m2s_port",this);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("TRACE",$sformatf("%m"),UVM_HIGH)
if(!uvm_config_db#(virtual dut_interface)::get(this,"","vif",m_vif))begin
uvm_fatal("CONFIG_FATAL","Out Monitor can not get the interface !!!")
end
endfunction
//监测DUT输出信号
virtual task run_phase(uvm_phase phase);
my_transaction tr;
int active_port ;
logic [7:0] temp ;
int count ;
forever begin
active_port = -1;
count = 0 ;
tr = my_transaction::type_id::create("tr");
//wait for bus active
while(1)begin
@(m_vif.omonitor_cb);
foreach(m_vif.omonitor_cb.frameo_n[i])begin
if(m_vif.omonitor_cb.frameo_n[i] == 0)begin
active_port = i;
end
end
if(active_port != -1)begin
break;
end
end
//Active port has been Detected
tr.da = active_port;
end
forever begin
if(m_vif.omonitor_cb.valido_n[tr.da] == 0)begin
temp[count] = m_vif.omonitor_cb.dout[tr.da];
count++;
if(count == 8)begin
tr.payload.push(temp);
count = 0;
end
end
if(m_vif.omonitor_cb.frameo_n[tr.da])begin
if(count != 0)begin
tr.payload.push_back(temp);
`uvm_warning("PAYLOAD_WARNING","Payload not byte aliged!!!")
end
break;
end
@(m_vif.omonitor_cb);
end
`uvm_info("Out_Monitor",{"\n","Out monitor Got An Output Transaction: \n",tr.sprint()},UVM_MEDIUM)
`uvm_info("Out_Monitor","Now Out monitor send the transaction to the Scoreboard",UVM_MEDIUM)
this.m2s_port.put(tr);
endtask
endclass
2)新建salve_agent.sv文件(输出路线②)
class slave_agent extends uvm_agent;
`uvm_component_utils(slave_agent) //注册
out_monitor m_moni; //声明 out_monitor 句柄
agent_config m_agent_cfg;
uvm_blocking_put_export #(my_transaction) s_a2s_export; //slave_agent to scoreboard
function new(string name = "",uvm_component parent);
super.new(name,parent);
this.s_a2s_export = new("s_a2s_export",this);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(agent_config)::get(this,"","m_agent_cfg",m_agent_cfg))begin
`uvm_fatal("CONFIG_FATAL","slave_agent can not get the configuration !!!")
end
uvm_config_db#(virtual dut_interface)::set(this,"m_moni","vif",m_agent_cfg.vif);
m_moni = out_monitor::type_id::create("m_moni",this);
endfunction
virtual function void connect_phase(uvm_phase phase);
m_moni.m2s_port.connect(this.s_a2s_export);
endfunction
endclass
3)新建my_scoreboard.sv文件(输出路线④)
class my_scoreboard extends uvm_scoreboard;
`uvm_component_utils(my_scoreboard)
uvm_blocking_get_port #(my_transaction) r2s_port ;
uvm_blocking_get_port #(my_transaction) s_a2s_port ;
function new(string name = "",uvm_component parent);
super.new(name,parent);
r2s_port = new("r2s_port",this) ;
s_a2s_port = new("s_a2s_port",this);
endfunction
virtual task run_phase();
my_transaction dut_output_tr ;
my_transaction expected_tr ;
forever begin
`uvm_info("SCOREBOARD","Now waiting for getting the transaction from slave agent and reference model",UVM_MEDIUM)
fork
r2s_port.get(expected_tr);
s_a2s_port.get(dut_output_tr);
join
`uvm_info("CHECK","Dut has completed a transaction.Now check the output ...",UVM_MIDIUM)
if(expected_tr.compare(dut_output_tr))begin
`uvm_info("CHECK","Check is done.The result matches the expected value!",UVM_MEDIUM)
end
else begin
`uvm_info("CHECK_ERROR","Checking is done.But the result does not match the expected value!Please check your DUT!",UVM_MEDIUM)
end
end
endtask
endclass
4)新建my_env.sv文件(输出路线③、④)
代码如下(示例):
class my_env extends uvm_env;
`uvm_component_utils(my_env)
master_agent m_agent ;
slave_agent s_agent ;
env_config m_env_cfg ;
my_reference_model ref_model;
my_scoreboard sb;
uvm_tlm_analysis_fifo#(my_transaction) r2s_fifo ;
uvm_tlm_analysis_fifo#(my_transaction) s_a2s_fifo ;
function new(string name = "",uvm_component parent);
super.new(name,parent);
this.r2s_fifo = new("r2s_fifo",this) ;
this.s_a2s_fifo = new("s_a2s_fifo",this);
endfunction
virtual function void build_phase(uvm_phase phase);
super.bulid_phase(phase);
if(!uvm_config_db#(env_config)::get(this,"","env_cfg",m_env_cfg))begin
`uvm_fatal("CONFIG_FATAL","ENV can not get the configuration !!!")
end
uvm_config_db#(agent_config)::set(this,"m_agent","m_agent_cfg",m_env_cfg.m_agent_cfg);
uvm_config_db#(agent_config)::set(this,"s_agent","m_agent_cfg",m_env_cfg.m_agent_cfg);
if(m_env_cfg.is_coverage)begin
`uvm_info("COVERAGE_ENABLE","The function coverage is enable for this testcase",UVM_MEDIUM)
end
if(m_env_cfg.is_check)begin
`uvm_info("CHECK_ENABLE","The check function is enable for this testcase",UVM_MEDIUM)
sb = my_scoreboard::type_id::create("sb",this);
end
m_agent = master_agent::type_id::create("m_agent",this);
s_agent = slave_agent::type_id::create("s_agent",this);
ref_model = my_reference_model::type_id::create("ref_model",this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
`uvm_info("ENV","Connect the agent and reference model...",UVM_MEDIUM)
m_agent.m_a2r_export.connect(ref_model.i_m2r_imp);
s_agent.s_a2s_export.connect(this.s_a2s_fifo.blocking_put_export);//输出3
ref_model.r2s_port.connect(this.r2s_fifo.blocking_put_export);
if(m_env_cfg.is_check)begin
sb.r2s_port.connect(this.r2s_fifo.blocking_get_export);
sb.s_a2s_port.connect(this.s_a2s_fifo.blocking_get_export);//输出4
end
endfunction
endclass