對于一個驗證平臺而言,最重要的角色是激勵的產(chǎn)生,最開始,driver是集合了數(shù)據(jù)的產(chǎn)生、發(fā)送于一體這么一個重要的角色(后面到進(jìn)入真正UVM會將功能分離)。對于整個驗證平臺而言,產(chǎn)生激勵,將激勵送到DUT,收集DUT產(chǎn)生的數(shù)據(jù),進(jìn)行對比,這是最基本的功能。后面一系列的UVM機(jī)制,都只是為了能夠更好的實現(xiàn)這個基本功能。
這里也強(qiáng)調(diào)一點,學(xué)習(xí)UVM,我個人覺得最重要的是搞清楚,what,where,how。你應(yīng)該產(chǎn)生什么樣的激勵數(shù)據(jù)(what),你的激勵是在哪里產(chǎn)生(where),能各個組件之間是怎么連接,數(shù)據(jù)怎么發(fā)送,怎么收集,怎么對比,這就是how的問題。數(shù)據(jù)在哪里對比,這又是where的問題。能夠從數(shù)據(jù)產(chǎn)生的源頭,一路追,追到數(shù)據(jù)在各個組件里頭的流轉(zhuǎn)。就像你找到了一條小溪的源頭,你能夠沿著這條小溪,一路追下去,直到你明白這條小溪會流經(jīng)那些村莊,目的地是哪里。
學(xué)過verilog的應(yīng)該知道,verilog里面進(jìn)行驗證,只有兩個部分,一個是DUT,一個是tb,這兩個組件,組成了最最基本的驗證平臺,其中,它包含了激勵部分,以及代碼部分。在UVM中,最基本的驗證平臺也是由這兩部分組成,但是多了一個top_tb.sv的組件。下面,先認(rèn)識一下這個簡單的平臺,從代碼進(jìn)行剖析,代碼全部來自張強(qiáng)老師的《UVM》實戰(zhàn)的源碼,張強(qiáng)老師如果覺得我侵權(quán)了,請聯(lián)系我。
module dut(clk,
rst_n,
rxd,
rx_dv,
txd,
tx_en);
input clk;
input rst_n;
input[7:0] rxd;
input rx_dv;
output [7:0] txd;
output tx_en;
reg[7:0] txd;
reg tx_en;
always @(posedge clk) begin
if(!rst_n) begin
txd <= 8'b0;
tx_en <= 1'b0;
end
else begin
txd <= rxd;
tx_en <= rx_dv;
end
end
endmodule
這個dut確實是很簡單。當(dāng)復(fù)位也就是rst_n=0時,將輸出數(shù)據(jù)(txd)和使能(tx_en)都設(shè)置為0,否則,將輸入給輸出。
看到這個DUT,如果讓你自己寫一個Tb.v,你會怎么寫呢?不妨自己寫一個,然后再對照下面這個。其實思路也很簡答,就是產(chǎn)生相應(yīng)的數(shù)據(jù)給四根輸入信號,經(jīng)過dut,看輸出的數(shù)據(jù)。產(chǎn)生時鐘給clk,產(chǎn)生復(fù)位信號給rst_n,產(chǎn)生數(shù)據(jù)給rxd,產(chǎn)生使能信號給rx_dv。下面我們來看看driver和tb。
`ifndef MY_DRIVER__SV
`define MY_DRIVER__SV
//這個`ifndef和`define其實就是相當(dāng)于一個判斷條件,便于調(diào)用
class my_driver extends uvm_driver;
function new(string name = "my_driver", uvm_component parent = null);
super.new(name, parent);
endfunction
extern virtual task main_phase(uvm_phase phase);
endclass
//這里出現(xiàn)了extern,你翻翻SV的書就知道,只是因為把代碼全部寫在這個類里面顯得臃腫
//所以,在這里用extern留下了一個索引,具體的內(nèi)容放到class外面
task my_driver::main_phase(uvm_phase phase);//::作用域符號,這個寫法就是個規(guī)則,記住就行
top_tb.rxd <= 8'b0;
top_tb.rx_dv <= 1'b0;
//這兩句相當(dāng)于初始化
while(!top_tb.rst_n)//這里其實是等待復(fù)位結(jié)束,否則一直在這里循環(huán)
@(posedge top_tb.clk);
for(int i = 0; i < 256; i++)begin// 復(fù)位結(jié)束之后開始for循環(huán),產(chǎn)生256個數(shù)據(jù)
@(posedge top_tb.clk);
top_tb.rxd <= $urandom_range(0, 255);//產(chǎn)生隨機(jī)數(shù),賦值給rxd
top_tb.rx_dv <= 1'b1;
`uvm_info("my_driver", "data is drived", UVM_LOW)
end
@(posedge top_tb.clk);//賦值結(jié)束以后,等待時鐘上升沿,將rx_dv,重新歸零
top_tb.rx_dv <= 1'b0;
endtask
`endif
`timescale 1ns/1ps //時鐘精度
`include "uvm_macros.svh"//導(dǎo)入uvm的庫文件,才能識別定義的宏變量
import uvm_pkg::*; // 導(dǎo)入uvm的包
`include "my_driver.sv"
module top_tb;
reg clk;
reg rst_n;
reg[7:0] rxd;
reg rx_dv;
wire[7:0] txd;
wire tx_en;
dut my_dut(.clk(clk),
.rst_n(rst_n),
.rxd(rxd),
.rx_dv(rx_dv),
.txd(txd),
.tx_en(tx_en));
//這是信號的例化,相當(dāng)于把這個文件中的信號,和DUT的輸入信號連接起來
initial begin
my_driver drv;//指定一個類的指針,你可以理解為用drv代替了driver.
drv = new("drv", null);//實例化一個driver,不實例化的driver相當(dāng)于一張圖紙
drv.main_phase(null);
$finish();
end
initial begin
clk = 0;
forever begin // forever,永遠(yuǎn)發(fā)生,你還能想到其他方法嗎?
#100 clk = ~clk; //這是產(chǎn)生時鐘的地方,#是延遲,意思是每隔100個時鐘單位,clk進(jìn)行翻轉(zhuǎn)
end
end
initial begin
rst_n = 1'b0;
#1000; //復(fù)位持續(xù)了1000個時鐘單位
rst_n = 1'b1;
end
endmodule
用vcs進(jìn)行仿真
看一下波形:
從波形圖中,我們怎么看呢? 看復(fù)位 ,是不是在1000個時間單位以前在復(fù)位; 看采樣 ,復(fù)位之后的時鐘上升沿是否開始采樣,采的樣是不是復(fù)位之后的有效數(shù)據(jù), 看數(shù)據(jù) ,數(shù)據(jù)個數(shù),對照輸入輸出的數(shù)據(jù)是否一致;我們就基本可以判定,DUT的功能有沒有實現(xiàn)。
-
仿真器
+關(guān)注
關(guān)注
14文章
1018瀏覽量
83744 -
UVM
+關(guān)注
關(guān)注
0文章
182瀏覽量
19171 -
CLK
+關(guān)注
關(guān)注
0文章
127瀏覽量
17171 -
DUT
+關(guān)注
關(guān)注
0文章
189瀏覽量
12384
發(fā)布評論請先 登錄
相關(guān)推薦
評論