1. uvm_component ,uvm_object
从图上给可以看出,除了driver, monitor, agent, model, scoreboard, env, test之外的几乎所有类本质都是uvm_object。
uvm_component相比uvm_object多出来的功能有两点:
一是增加了new函数的parent参数,使其可以构成UVM树的结构;
二是有phase的自动执行的特点。
与uvm_object相关的factory宏有:
uvm_object_utils:用于将一个直接或间接派生自uvm_object的类注册到factory中。
uvm_object_utils_begin:使用这个宏来开启field_automation机制。
uvm_object_utils_end:与uvm_object_*_begin成对出现,作为factory注册的结束标志。
与uvm_component相关的factory宏有:
uvm_component_utils:用于将一个直接或间接派生自uvm_component的类注册到factory中。
uvm_component_utils_begin/uvm_component_utils_end:同上。
https://www.aldec.com/en/company/blog/149--understanding-the-inner-workings-of-uvm
2. clocking
模块端口和接口默认不指定信号之间的任何时序要求或同步方案。在时钟和结束时钟之间定义的时钟块正是如此。它是一组与特定时钟同步的信号集合,有助于指定时钟和信号之间的时序要求。
[default] clocking [identifier_name] @ [event_or_identifier]
default input #[delay_or_edge] output #[delay_or_edge]
[list of signals]
endclocking
clocking ckb @ (posedge clk);
default input #1step output negedge;
input ...;
output ...;
endclocking
clocking ck1 @ (posedge clk);
default input #5ns output #2ns;
input data, valid, ready = top.ele.ready;
output negedge grant;
input #1step addr;
endclocking
注意以下内容:
创建了一个名为ck1的时钟块,该块将在clk的正沿激活;
默认情况下,时钟块内的所有输入信号将在时钟clk的正沿前5ns进行采样,所有输出信号将在时钟clk的正沿后2ns驱动;
data、valid和ready被声明为块的输入,因此将在clk的正沿前5ns进行采样;
grant是块的输出信号,具有自己的时间要求。在这里,grant将在clk的负沿驱动,而不是默认的正沿。
NOTE: 如果clocking block 没有指定 输入和输出的采样时间,默认采样时间是
default input #1step output #0ns
SystemVerilog Clocking Tutorial
3. sequence

* mid_do and post_do are functions, All other are tasks
UVM Sequence - Verification Guide
UVM Sequence - Verification Guide
Communication between the Sequence and driver involves below steps,
1.create_item() / create req.
2.wait_for_grant().
3.randomize the req.
4.send the req.
5.wait for item done.
6.get response.
* Step 5 and 6 are optional.

class my_sequence extends uvm_sequence #(seq_item);
`uvm_object_utils(my_sequence)
function new (string name = "my_sequence")
super.new(name);
endfunction
task body();
req = seq_item::type_id::create("req");
start_item(req);
assert(req.randomize());
finish_item(req);
endtask
endclass
① sequence 内部的task body()内:
创建transaction
start_item()
transaction 随机化
finish_item()
class my_sequence extends uvm_sequence #(seq_item);
`uvm_object_utils(my_sequence)
function new (string name = "my_sequence")
super.new(name);
endfunction
task body();
req = seq_item::type_id::create("req");
wait_for_grant();
assert(req.randomize());
send_request(req);
wait_for_item_done();
get_respose(rsp);
endtask
endclass
②
1.create_item() / create req.
2.wait_for_grant().
3.randomize the req.
4.send the req.
5.wait for item done.
6.get response.
class my_sequence extends uvm_sequence #(seq_item);
`uvm_object_utils(my_sequence)
function new (string name = "my_sequence")
super.new(name);
endfunction
task body();
`uvm_do_with(req, {req.<variable> == 0;}); // any constraint
endtask
endclass
③ 使用 uvm macros :
`uvm_do(sequence)
`uvm_do_with()
How to write a sequence
An intention is to create a seq_item, randomize it, and then send it to the driver. To perform this operation any one of the following approaches is followed in the sequence.
- Using macros like `uvm_do , `uvm_create, `uvm_send etc
- Using existing methods from the base class
a. Using wait_for_grant(), send_request(), wait_for_item_done() etc
b. Using start_item/finish_item methods.
4. m_sequencer ,p_sequencer
m_sequencer
The m_sequencer handle contains the reference to the sequencer(default sequencer) on which the sequence is running.
m_sequencer is the generic uvm_sequencer pointer. it will always exist for the uvm_sequence and is initialized when the sequence is started.
This is determined by,
- the sequencer handle provided in the start method
- the sequencer used by the parent sequence
- the sequencer that was set using the set_sequencer method
p_sequencer
p_sequencer is a typed-specific sequencer pointer, created by registering the sequence to the sequencer using macros. It will not exist if we have not registered the sequence with macros.
As it is type-specific we can access almost everything added to the sequencer.
The p_sequencer is a variable, used as handle to access the sequencer properties.
p_sequencer is defined using the macro `uvm_declare_p_sequencer(SEQUENCER_NAME)
To put it simply, a P-sequencer (port-sequencer) is to drive transaction to a specific port interface on the DUT.
m_sequencer is the default handle for uvm_vitual_sequencer and p_sequencer is the hook up for child sequencer.
5. modport
在sv中,对一系列接口的方向做出限制的接口列表。 可简化模块间的链接方式,易于测试激励编写。
先在interface中描述modport ,此时描述的接口方向与dut的接口方向一致。 slave 接受输入data 和addr ,master 接受sready 响应。
interface ms_if (input clk);
logic sready; // Indicates if slave is ready to accept data
logic rstn; // Active low reset
logic [1:0] addr; // Address
logic [7:0] data; // Data
modport slave ( input addr, data, rstn, clk,
output sready);
modport master ( output addr, data,
input clk, sready, rstn);
endinterface
定义master module 功能
// This module accepts an interface with modport "master"
// Master sends transactions in a pipelined format
// CLK 1 2 3 4 5 6
// ADDR A0 A1 A2 A3 A0 A1
// DATA D0 D1 D2 D3 D4
module master ( ms_if.master mif);
always @ (posedge mif.clk) begin
// If reset is applied, set addr and data to default values
if (! mif.rstn) begin
mif.addr <= 0;
mif.data <= 0;
// Else increment addr, and assign data accordingly if slave is ready
end else begin
// Send new addr and data only if slave is ready
if (mif.sready) begin
mif.addr <= mif.addr + 1;
mif.data <= (mif.addr * 4);
// Else maintain current addr and data
end else begin
mif.addr <= mif.addr;
mif.data <= mif.data;
end
end
end
endmodule
定义slave 功能
module slave (ms_if.slave sif);
reg [7:0] reg_a;
reg [7:0] reg_b;
reg reg_c;
reg [3:0] reg_d;
reg dly;
reg [3:0] addr_dly;
always @ (posedge sif.clk) begin
if (! sif.rstn) begin
addr_dly <= 0;
end else begin
addr_dly <= sif.addr;
end
end
always @ (posedge sif.clk) begin
if (! sif.rstn) begin
reg_a <= 0;
reg_b <= 0;
reg_c <= 0;
reg_d <= 0;
end else begin
case (addr_dly)
0 : reg_a <= sif.data;
1 : reg_b <= sif.data;
2 : reg_c <= sif.data;
3 : reg_d <= sif.data;
endcase
end
end
assign sif.sready = ~(sif.addr[1] & sif.addr[0]) | ~dly;
always @ (posedge sif.clk) begin
if (! sif.rstn)
dly <= 1;
else
dly <= sif.sready;
end
endmodule
top层模块链接
module d_top (ms_if tif);
// Pass the "master" modport to master
master m0 (tif.master);
// Pass the "slave" modport to slave
slave s0 (tif.slave);
endmodule
测试用例
module tb;
reg clk;
always #10 clk = ~clk;
ms_if if0 (clk);
d_top d0 (if0);
// Let the stimulus run for 20 clocks and stop
initial begin
clk <= 0;
if0.rstn <= 0;
repeat (5) @ (posedge clk);
if0.rstn <= 1;
repeat (20) @ (posedge clk);
$finish;
end
endmodule
6. analysis port
- The uvm_analysis_port is a TLM-based class that provides a write method for communication. TLM analysis port broadcasts transactions to one or multiple components.
- uvm_analysis_port can be open without any implementation of uvm_analysis_imp or uvm_analysis_export.
- Single uvm_analysis_port can have a connection with uvm_analysis_imp or uvm_analysis_export. No errors will be reported.
- Multiple uvm_analysis_port can be connected to a single uvm_analysis_imp or uvm_analysis_export.
- uvm_subscriber already has analysis_export so that it can directly receive transactions from the connected analysis port. Generally, it is useful to write a coverage collector that attaches to the monitor.
- Valid Connections:
a. port to port
b. port to export
c. port to imp
d. export to export
e. export to imp (没有imp to imp)

uvm 参考
http://cluelogic.com/2011/07/uvm-tutorial-for-candy-lovers-agent/
sequence
How to create and use a sequence
UVM Sequence item - Verification Guide
uvm_sequence_base methods - VLSI Verify
UVM Sequence macros - VLSI Verify
sequencer
UVM Sequencer | The Octet Institute
https://verificationacademy.com/forums/t/p-sequencer-and-m-sequencer/31081/3
https://www.youtube.com/watch?v=OdHVk3Pc3Gw
modport
analysis port
TLM Analysis port Analysis imp port - Verification Guide
TLM-10 A Single Port - EDA Playground



2万+

被折叠的 条评论
为什么被折叠?



