uvm_primer ch16 在testbench中使用analysis_port
本章是一个单线程内部不同object通信的例子 使用analysis_port完成;
observer模式适用于monitor连接使用
在interface中加入句柄,这个用法之前没有用过;
在interface中import tinyalu_pkg::*;
interface tinyalu_bfm;
import tinyalu_pkg::*;
command_monitor command_monitor_h; //定义句柄
result_monitor result_monitor_h;
byte unsigned A;
byte unsigned B;
bit clk;
bit reset_n;
wire [2:0] op;
bit start;
wire done;
wire [15:0] result;
operation_t op_set;
assign op = op_set;
task reset_alu();
reset_n = 1'b0;
@(negedge clk);
@(negedge clk);
reset_n = 1'b1;
start = 1'b0;
endtask : reset_alu
...
always @(posedge clk) begin : cmd_monitor
bit new_command;
if (!start)
new_command = 1;
else
if (new_command) begin
command_monitor_h.write_to_monitor(A, B, op); //重点
new_command = (op == 3'b000); // handle no_op
end
end : cmd_monitor
always @(negedge reset_n) begin : rst_monitor
command_monitor_h.write_to_monitor(A, B, rst_op);
end : rst_monitor
always @(posedge clk) begin : rslt_monitor
if (done)
result_monitor_h.write_to_monitor(result);
end : rslt_monitor
....
endinterface
在pkg中include 所有的component;
package tinyalu_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
typedef enum bit[2:0] {no_op = 3'b000,
add_op = 3'b001,
and_op = 3'b010,
xor_op = 3'b011,
mul_op = 3'b100,
rst_op = 3'b111} operation_t;
typedef struct {
byte unsigned A;
byte unsigned B;
operation_t op;
} command_s;
`include "coverage.svh"
`include "base_tester.svh"
`include "random_tester.svh"
`include "add_tester.svh"
`include "scoreboard.svh"
`include "command_monitor.svh"
`include "result_monitor.svh"
`include "env.svh"
`include "random_test.svh"
`include "add_test.svh"
endpackage : tinyalu_pkg
在monitor中将对象赋值给interface中的句柄
class command_monitor extends uvm_component;
`uvm_component_utils(command_monitor);
uvm_analysis_port #(command_s) ap;//重点
function void build_phase(uvm_phase phase);
virtual tinyalu_bfm bfm;
if(!uvm_config_db #(virtual tinyalu_bfm)::get(null, "*","bfm", bfm))
$fatal("Failed to get BFM");
bfm.command_monitor_h = this; //连接句柄和对象
ap = new("ap",this);//重点
endfunction : build_phase
function void write_to_monitor(byte A, byte B, bit[2:0] op);
command_s cmd;
cmd.A = A;
cmd.B = B;
cmd.op = op2enum(op);
$display("COMMAND MONITOR: A:0x%2h B:0x%2h op: %s", A, B, cmd.op.name());
ap.write(cmd); //重点
endfunction : write_to_monitor
function operation_t op2enum(bit[2:0] op);
case(op)
3'b000 : return no_op;
3'b001 : return add_op;
3'b010 : return and_op;
3'b011 : return xor_op;
3'b100 : return mul_op;
3'b111 : return rst_op;
default : $fatal($sformatf("Illegal operation on op bus: %3b",op));
endcase // case (op)
endfunction : op2enum
function new (string name, uvm_component parent);
super.new(name,parent);
endfunction
endclass : command_monitor
coverage
class coverage extends uvm_subscriber #(command_s);//重点
`uvm_component_utils(coverage)
byte unsigned A;
byte unsigned B;
operation_t op_set;
covergroup op_cov;
coverpoint op_set {
bins single_cycle[] = {[add_op : xor_op], rst_op,no_op};
bins multi_cycle = {mul_op};
bins opn_rst[] = ([add_op:no_op] => rst_op);
bins rst_opn[] = (rst_op => [add_op:no_op]);
bins sngl_mul[] = ([add_op:xor_op],no_op => mul_op);
bins mul_sngl[] = (mul_op => [add_op:xor_op], no_op);
bins twoops[] = ([add_op:no_op] [* 2]);
bins manymult = (mul_op [* 3:5]);
bins rstmulrst = (rst_op => mul_op [= 2] => rst_op);
bins rstmulrstim = (rst_op => mul_op [-> 2] => rst_op);
}
endgroup
function new (string name, uvm_component parent);
super.new(name, parent);
op_cov = new();
zeros_or_ones_on_ops = new();
endfunction : new
//因为extend uvm_subscriber, 包含了analysis_export
function void write(command_s t);//重点 通过write函数得到A B op
A = t.A;
B = t.B;
op_set = t.op;
op_cov.sample();
zeros_or_ones_on_ops.sample();
endfunction : write
endclass : coverage
需要订阅多个analysis_port
使用uvm_tlm_analysis_fifo解决这个问题
uvm_tlm_analysis_fifo在一端有一个analysis_export,在另外一端游一个try_get() 的方法;我们使用try_get() 从fifo中取数据;如果fifo为空 则返回0;
class scoreboard extends uvm_subscriber #(shortint);
`uvm_component_utils(scoreboard);
uvm_tlm_analysis_fifo #(command_s) cmd_f;
function void build_phase(uvm_phase phase);
cmd_f = new ("cmd_f", this);
endfunction : build_phase
function void write(shortint t); //重点 取shortint
shortint predicted_result;
command_s cmd;
cmd.op = no_op;
do
//重点 取 command_s
if (!cmd_f.try_get(cmd)) $fatal(1, "No command in self checker");
while ((cmd.op == no_op) || (cmd.op == rst_op));
case (cmd.op)
add_op: predicted_result = cmd.A + cmd.B;
and_op: predicted_result = cmd.A & cmd.B;
xor_op: predicted_result = cmd.A ^ cmd.B;
mul_op: predicted_result = cmd.A * cmd.B;
endcase // case (op_set)
if (predicted_result != t)
$error (
"FAILED: A: %2h B: %2h op: %s actual result: %4h expected: %4h",
cmd.A, cmd.B, cmd.op.name(), t, predicted_result);
endfunction : write
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
endclass : scoreboard
连接monitor
class env extends uvm_env;
`uvm_component_utils(env);
function void connect_phase(uvm_phase phase);
result_monitor_h.ap.connect(scoreboard_h.analysis_export);
command_monitor_h.ap.connect(scoreboard_h.cmd_f.analysis_export); //连接fifo
command_monitor_h.ap.connect(coverage_h.analysis_export);
endfunction : connect_phase
endclass
使用uvm_analysis_port来连接不同的对象;这个是线程内部的通信机制(intra-tread);
函数的调用发生在一个线程内部;当cmd_monitor always调用write_to_monitor()时,调用的是同一个线程的subscriber里边的write()函数;