Chapter 18 Put and Get Ports in Action
上一章,我们介绍了使用put和get port进行线程间通信的机制,本章我们将put/get port机制假如TinyALU测试平台。从学习UVM开始,UVM就不断地把测试平台拆解成一个个小的功能模块,这样易于debug和重用。本章我们将tester按照功能进行拆分。
- tester的两个功能:
- 选择操作类型op_type;
- 将操作类型和操作数送至BFM;
- 我们将tester分成两个部分,一个专注于产生操作类型的opcode,另一个专注于和BFM交互,此时测试环境的框图如下所示:
- 其中driver代表tester中和BFM交互的模块,driver从tester获取opcode,并以信号的方式送给BFM。random_tester个driver通过TLM FIFO连接,现在我们的tester被分为random_tester和driver两个部分。
18.1 未修改前的base_tester
- 如我们在UVM_ENV中所记录一致,虚类base_tester中需要定义了get_op和get_data两个纯虚方法,出新方法必须在子类(如random_tester)中重载;
- base_tester好处,我们只需要修改base中的run_phase,random_tester和add_tester中继承的run_phase也会更新,不需要再多处代码修改。
- base_tester中bfm,操作类型,数据集中处理,功能划分不够清晰;
virtual class base_tester extends uvm_component;
`uvm_component_utils(base_tester)
virtual tinyalu_bfm bfm;
function void build_phase(uvm_phase phase);
if(!uvm_config_db #(virtual tinyalu_bfm)::get(null, "*","bfm", bfm))
$fatal("Failed to get BFM"); //在tester中get bfm
endfunction : build_phase
pure virtual function operation_t get_op();
pure virtual function byte get_data();
task run_phase(uvm_phase phase);
byte unsigned iA;
byte unsigned iB;
operation_t op_set;
shortint result;
phase.raise_objection(this); //同时完成:激励产生 & 数据传输
bfm.reset_alu();
repeat (1000) begin : random_loop
op_set = get_op(); //产生op_type
iA = get_data(); //产生操作数A
iB = get_data(); //产生操作数B
bfm.send_op(iA, iB, op_set, result); //将产生的信号送往BFM
end : random_loop
#500;
phase.drop_objection(this);
endtask : run_phase
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
endclass : base_tester
- 现在我们将tester分为tester和driver两个部分:
18.2 修改后的base_tester
- 不再使用bfm,但是需要在tester中定义put port
- 现在tester中只负责激励产生,并将数据送至put port端口,其他一概不负责;
- base_tester和driver之间的port传输,不影响get_op和get_data,因此random_tester和add_tester不用更改
virtual class base_tester extends uvm_component;
`uvm_component_utils(base_tester)
virtual tinyalu_bfm bfm; //这里不声明也可以,尊重原书保留
uvm_put_port #(command_s) command_port; //定义put port,指定传输的数据类型
function void build_phase(uvm_phase phase);
command_port = new("command_port", this); //build phase不使用bfm,所以只需要例化put ports
endfunction : build_phase
pure virtual function operation_t get_op(); //纯虚函数,在其子类中重载
pure virtual function byte get_data(); //纯虚函数不改变,所以子类不用更改代码
task run_phase(uvm_phase phase); //run phase不再负责数据送至BFM的工作
byte unsigned iA;
byte unsigned iB;
operation_t op_set;
command_s command;
phase.raise_objection(this);
command.op = rst_op;
command_port.put(command); //将reset opcode送至put port,blocking port
repeat (1000) begin : random_loop
command.op = get_op();
command.A = get_data();
command.B = get_data();
command_port.put(command); //将激励送至put port,数据进入TLM FIFO,再也不管这些数据了,这是其他人的工作
end : random_loop //在比较复杂的系统中,tester只需要关注激励产生,不在关注信号级的数据传输工作
#500;
phase.drop_objection(this);
endtask : run_phase
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
endclass : base_tester
18.3 新增的driver
- 新增的driver负责将tester产生的激励送至BFM,因此需要再次例化bfm;
- 新增的dirver需要从TLM FIFO中取数据,需要例化uvm_get_port;
- driver不负责生产激励,只是激励的搬运工;
- driver模块代码如下:
class driver extends uvm_component;
`uvm_component_utils(driver)
virtual tinyalu_bfm bfm; //定义bfm
uvm_get_port #(command_s) command_port; //定义get port
function void build_phase(uvm_phase phase); //在build phase例化get port和bfm
if(!uvm_config_db #(virtual tinyalu_bfm)::get(null, "*","bfm", bfm))
$fatal("Failed to get BFM"); //通过configdb机制获得传入的bfm
command_port = new("command_port",this); //例化get port
endfunction : build_phase
task run_phase(uvm_phase phase); //run phase中不进行数据处理,只能搬运数据
command_s command;
shortint result;
forever begin : command_loop //forever循环,不断尝试从FIFO获取数据,送至bfm
command_port.get(command); //通过get port的get函数从TLM FIFO获取数据
bfm.send_op(command.A, command.B, command.op, result); //通过bfm的send_op函数将数据送至BFM
end : command_loop
endtask : run_phase
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
endclass : driver
18.4 修改后的env
- 既然测试环境增加了组件和FIFO,那么环境的structure必定需要重新连接,参考测试平台的框图。
- 现在测试平台的env变得复杂了些,梳理一下:
- 基本组件:random_tester, coverage, scoreboard
- 和analysis port相关的组件:command_monitor, result_monitor
- 和get port相关的组件:driver
- 连接tester和driver的TLM FIFO
- 注意:analysis fifo位于scoreboard内部,所以env是看不到的
- uvm_env模块的代码如下:
class env extends uvm_env;
`uvm_component_utils(env);
random_tester random_tester_h; //环境组件定义
driver driver_h;
uvm_tlm_fifo #(command_s) command_f;
coverage coverage_h;
scoreboard scoreboard_h;
command_monitor command_monitor_h;
result_monitor result_monitor_h;
function void build_phase(uvm_phase phase); //在build phase例化各个组件,factory机制
command_f = new("command_f", this);
random_tester_h = random_tester::type_id::create("random_tester_h",this);
driver_h = driver::type_id::create("drive_h",this);
coverage_h = coverage::type_id::create ("coverage_h",this);
scoreboard_h = scoreboard::type_id::create("scoreboard_h",this);
command_monitor_h = command_monitor::type_id::create("command_monitor_h",this);
result_monitor_h = result_monitor::type_id::create("result_monitor_h",this);
endfunction : build_phase
function void connect_phase(uvm_phase phase);
driver_h.command_port.connect(command_f.get_export); //driver的get port <-> get export in TLM FIFO
random_tester_h.command_port.connect(command_f.put_export); //tester的put port <-> put export in TLM FIFO
result_monitor_h.ap.connect(scoreboard_h.analysis_export); //result monitor的analysis port <-> analysis export in scoreboard;
command_monitor_h.ap.connect(scoreboard_h.cmd_f.analysis_export); //command monitor的analysis port <-> analysis export in uvm_tlm_analysis_fifo in scoreboard
command_monitor_h.ap.connect(coverage_h.analysis_export); //commad monitor的analysis port <-> analysis export in coverage
endfunction : connect_phase
//记住所有连接比较难,只需要记住port总是连接在同类型的export上,export总是作为同类型的port的connect函数的参数。
function new (string name, uvm_component parent);
super.new(name,parent);
endfunction : new
endclass
- 本章中我们通过put/get port将tester分离为taster和driver两个部分,各部分实现单一的功能。现在测试平台被分解为:仿真&分析 两个层次,仿真又分为:激励产生(tester) & 激励传输(driver).
- 现在我们可以更方便的驱动成千上万的激励到DUT,并通过analysis layer对DUT的行为进行分析。但是测试平台需要将DUT运行时的信息告诉我们。当然,我们可以使用SV的 d i s p l a y , display, display,error,$fatal等函数,但是当系统很复杂的时候,将会有海量信息需要我们处理,这是我们需要信息分级,SV内含的函数是帮不了我们的。
- 因此UVM提供了reporting tools帮助我们解决这个问题,下一章我们学习UVM Reports。