UVM scoreboard是一个检查DUT功能的组件。它用analysis export从monitor接受transaction事务以进行检查。
uvm_scoreboard class declaration:
virtual class uvm_scoreboard extends uvm_component
User-defined scoreboard class declaration:
用户定义的scoreboard是从 uvm_scoreboard 扩展而来的,而 uvm_scoreboard 又派生自 uvm_component。
class <scoreboard_name> extends uvm_scoreboard;
1 uvm_scoreboard class hierarchy
3 Scoreboard Usage
- 使用analysis export从monitor接收transaction以进行检查。
- scoreboard有一个参考模型reference model可以与design行为进行比较。reference model也称为预测器predictor,它实现design行为,以便scoreboard可以将 DUT 结果与相同驱动刺激的参考模型reference model结果进行比较。
3 How to write scoreboard code in UVM?
- 创建一个从 uvm_scoreboard 扩展的用户定义的scoreboard类,并将其注册到工厂中。
- 声明analysis export以从monitor接收sequence item或transaction。
- 编写标准 new() 函数。由于记分板是一个 uvm_component。new() 函数有两个参数:字符串名称name和 uvm_component 父类parent。
- 实现 build_phase 并创建 TLM analysis export实例。
- 实现一个 write 方法来接收来自monitor的事务。
- 实现run_phase 以在整个仿真时间内检查 DUT 功能。
3.1 Scoreboard Example
class scoreboard extends uvm_scoreboard;
uvm_analysis_imp #(seq_item, scoreboard) item_collect_export;
seq_item item_q[$];
`uvm_component_utils(scoreboard)
function new(string name = "scoreboard", uvm_component parent = null);
super.new(name, parent);
item_collect_export = new("item_collect_export", this);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
endfunction
function void write(seq_item req);
`uvm_info(get_type_name, $sformatf("Received transaction = %s", req), UVM_LOW);
item_q.push_back(req);
endfunction
task run_phase (uvm_phase phase);
seq_item sb_item;
forever begin
wait(item_q.size > 0);
if(item_q.size > 0) begin
sb_item = item_q.pop_front();
// Checking comparing logic
...
end
end
endtask
endclass
4 UVM Scoreboad types
根据设计功能,记分板可以通过两种方式实现。
- in-order scoreboard
- out-of-order scoreboard
4.1 In-order scoreboard
有序记分板(in-order scoreboard)对于输出顺序与stimuli激励相同的设计很有用。比较器将以相同的顺序比较预期输出流和实际输出流。他们将独立抵达。因此,评估必须阻塞,直到预期transaction和实际transaction都存在。
要实现此类scoreboard,更简单的方法是实现 TLM analysis FIFO。有关更多详细信息,请访问TLM analysis FIFO部分。在下面的示例中,有两个monitor,其分析端口连接到记分板以提供输入和输出事务。
class inorder_sb extends uvm_scoreboard;
`uvm_component_utils(inorder_sb)
uvm_analysis_export #(txn) in_export, out_export;
uvm_tlm_analysis_fifo #(txn) in_fifo, out_fifo;
function new (string name = "inorder_sb" , uvm_component parent = null) ;
super.new(name, parent);
endfunction
function void build_phase (uvm_phase phase);
in_fifo = new("in_fifo", this);
out_fifo = new("out_fifo", this);
in_export = new("in_export", this);
out_export = new("out_export", this);
endfunction
function void connect_phase (uvm_phase phase);
in_export.connect(in_fifo.analysis_export);
out_export.connect(out_fifo.analysis_export);
endfunction
task run_phase( uvm_phase phase);
txn in_txn;
txn exp_txn, act_txn;
forever begin
in_fifo.get(in_txn);
process_data(in_txn, exp_txn);
out_fifo.get(act_txn);
if (!exp_txn.compare(act_txn)) begin
`uvm_error(get_full_name(), $sformat("%s does not match %s", exp_txn.sprint(), act_txn.sprint()), UVM_LOW);
end
end
endtask
// Reference model
task process_data(input txn in_txn, output txn exp_txn);
// Generate expected txn for driven stimulus
...
...
endtask
endclass
4.2 Out-of-order scoreboard
无序记分板out-of-order scoreboard对于输出顺序与驱动输入激励stimuli不同的设计很有用。基于输入激励参考模型将生成 DUT 的预期结果,并且实际输出预计以任何顺序出现。因此,需要存储从输入激励生成的此类不匹配事务,直到从待比较的 DUT 接收到相应的输出为止。为了存储此类事务,关联数组被广泛使用。根据索引值,transactions存储在预期和实际关联数组中。当匹配的数组索引发生比较时,关联数组中的条目将被删除。
class txn extends uvm_sequence_item;
int id;
// other class properties
//...
endclass
class out_of_order_sb extends uvm_scoreboard;
`uvm_component_utils(out_of_order_sb)
uvm_analysis_export #(txn) in_export, out_export;
uvm_tlm_analysis_fifo #(txn) in_fifo, out_fifo;
// associative array of class type as txn and indexed by int
txn expected_out_array[int];
txn actual_out_array[int];
// Store idx in separate queues.
int expected_out_q[$], actaul_out_q[$];
function new (string name = "out_of_order_sb" , uvm_component parent = null) ;
super.new(name, parent);
endfunction
function void build_phase (uvm_phase phase);
in_fifo = new("in_fifo", this);
out_fifo = new("out_fifo", this);
in_export = new("in_export", this);
out_export = new("out_export", this);
endfunction
function void connect_phase (uvm_phase phase);
in_export.connect(in_fifo.analysis_export);
out_export.connect(out_fifo.analysis_export);
endfunction
task run_phase( uvm_phase phase);
txn in_txn, out_txn;
forever begin
fork
begin
in_fifo.get(in_txn);
process_data(in_txn);
end
begin
out_fifo.get(out_txn);
actual_out_array[out_txn.id] = out_txn;
actaul_out_q.push_back(out_txn.id);
end
join
compare_data();
end
endtask
// check_phase to check whether any entry is pending in queues.
function void check_phase(uvm_phase phase);
super. check_phase(phase);
if(expected_out_q.size() != 0) `uvm_info (get_full_name(), $sformatf("expected_out_q size = %0d", expected_out_q.size()), UVM_LOW);
if(actaul_out_q.size() != 0) `uvm_info (get_full_name(), $sformatf("actaul_out_q size = %0d", actaul_out_q.size()), UVM_LOW);
endfunction
task process_data(txn in_txn);
txn exp_out_txn;
// Using reference models, generate output for input stimulus.
// store expected output (exp_out_txn) in expected_out_array
...
...
expected_out_array[in_txn.id] = exp_out_txn;
expected_out_q.push_back(in_txn.id);
endtask
task compare_data();
int idx;
txn exp_txn, act_txn;
if(expected_out_q.size() > && actaul_out_q.size() > 0) begin
idx = expected_out_q.pop_front();
// Look for idx in actual_out_array to see whether the output has been received for a driven stimulus or not.
if(actual_out_array.exists(idx)) begin
exp_txn = expected_out_array[idx];
act_txn = actual_out_array[idx];
if(!exp_txn.compare(act_txn)) begin
`uvm_error(get_full_name(), $sformat("%s does not match %s", exp_txn.sprint(), act_txn.sprint()), UVM_LOW);
end
else begin
expected_out_array.delete(idx);
actual_out_array.delete(idx);
end
end
else expected_out_q.push_back(idx); // exp_idx is not found in actual_out_array.
end
endtask
endclass