接續UVM基礎入門文章。
前言
重點講述UVM常用的接口連接方式。
寄存器模型:
UVM寄存器模型(Register Model)是一組高級抽象的類,用于對DUT(Design Under Test)中具有地址映射的寄存器和存儲器進行建模,通過提供寄存器操作的抽象接口進行讀寫操作,這樣簡化了對寄存器的驗證。
寄存器模型模擬了DUT內部的存儲器件,一個簡單的寄存器模型如圖:
上述的圖中可以看出寄存器模型的大致框架,內部含有眾多寄存器和存儲器,這些元件通過地址圖與DUT內部的寄存器形成映射關系。
不過寄存器的保留域可以不用管,無法寫入,讀出的數據是復位值,在提取寄存器功能點時也常常忽略保留域。
寄存器讀寫:
寄存器模型與DUT的數據交互復雜多樣,整體方式其實就是通過前門訪問和后門訪問。
前門訪問過程:
1.測試用例或者參考模型會調用寄存器模型的read() 或者 write() 方法,指定為前門訪問方式。一旦寄存器模型被上層調用后,這個寄存器模型就會產生一個有關讀寫操作事務。
2.寄存器模型通過調用adapter的reg2bus函數將寄存器讀寫事務轉換成總線事務。
3.該總線事務會交給關聯的sequencer發送給driver,然后再驅動給DUT。
4.monitor監測響應,然后給到端口傳出去,這個端口可以是analysis_port端口。
5.寄存器模型通過analysis_port端口獲取響應后調用adapter的bus2reg函數將事務轉換成寄存器操作
6.寄存器模型自動更新鏡像值
后門訪問過程:
1. 在寄存器模型中,通過?add_hdl_path()
?或?add_hdl_path_slice()
?為內部的每個寄存器或字段域指定對應的RTL信號路徑,這個過程理解為映射。
2. 調用寄存器模型的read() 或者? write() 函數,方式指定為后門路徑。
3.通過DPI-C接口或者force/release直接修改或者讀取對應路徑的寄存器值.
4.寫入的數值或者讀出的數值都會自動更新到寄存器鏡像里。
如圖所示:
紅色線條表示的是參考模型寫數據到DUT內部的路徑,綠色線條表示參考模型從DUT寄存器里讀出數據,黑色線條表示后門訪問。
參考模型一般都不會寫數據,想要讀寄存器數據可以通過前門后門或者讀取鏡像值來獲取數據。
通道機制:
在UVM的端口連接機制中,最為常用的有3種端口連接機制。
一對一接口:
1.put_port:在完成transaction的打包后,通過port.put(trans)將事務發送出去。(blocking_put_port接口也屬于put_port的一種,屬于阻塞接口,不支持反向)
2.get_port:在需要transaction的時候,通過port.get(trans)獲取事務。(get_blocking_prot就是get_port的一種,它屬于阻塞端口)
一對多接口:
analysis_port接口為廣播接口,可以向所有組件發送事務信息。
接口轉接:
tlm_fifo作為中間傳輸的緩存器,可以用于不同接口之間的連接。當存在兩個接口的定義不相同時,通過將接口連接到fifo對應的接口上實現轉接。
tlm_fifo分為有ap端口的fifo 和沒有ap端口的fifo。
通常agent發送的transaction不止一個組件使用,因此在agent中通常使用的analysis_port實現廣播,而在參考模型或者記分板中并不需要廣播功能。
因此在不需要廣播功能的組件中,通常可以不用analysis_port端口。
以agent到scoreboard的端口連接為例簡單的講述一下常用的接口連接機制:
1.多對一連接:
analysis_port------env.tlm_fifo------scoreboard.get_blocking_port。
這屬于不同類型的接口連接,由于scoreboard不是時刻都需要事務的,因此需要一個緩沖功能的接口。
analysis_port--------scoreboard.tlm_fifo
由于fifo中自帶了get()函數,通常也可以省略get_blocking_port接口,直接在scoreboard里面實例化兩個fifo存儲數據用于對比。
雖然使用fifo簡單易懂,但是很多時候一個組件需要獲取多組數據的情況,這種情況下例化很多fifo是不現實的。
原因在于如果不同agent之間發送事務的phase不同,而scoreboard里面沒有選擇合適的phase,那么環境的調試將會變得十分困難。
而且想要從fifo里面獲取數據需要在特定的phase里面執行。
analysis_port ------------- scoreboard.imp
imp,我在B站上看到的視頻解釋為通道的意思,如果組件中有多個imp接口的話,需要使用·uvm_analysis_imp_decl宏定義創建多個imp類去分別對應不同的通道,還要再組件中定義具有相同后綴名的函數。
2.一對一連接:
常見一對一的端口連接方式有:
阻塞式單向傳輸:uvm_blocking_put_port(發送)
?+?uvm_blocking_put_imp(接收)
阻塞式雙向傳輸:uvm_blocking_transport_port(發送)
?+?uvm_blocking_transport_imp(接收)
非阻塞式單向傳輸:uvm_nonblocking_put_port(發送)
?+?uvm_nonblocking_put_imp(接收)
例如:
class scoreboard extends uvm_component;`uvm_component_param_utils(scoreboard #(transaction))// 定義兩個IMP端口:一個接收預期數據,一個接收實際數據uvm_blocking_put_imp #(transaction, scoreboard #(transaction)) expected_imp;uvm_blocking_put_imp #(transaction, scoreboard #(transaction)) actual_imp;// 存儲預期和實際數據的隊列transaction expected_queue[$];transaction actual_queue[$];function new(string name = "scoreboard", uvm_component parent = null);super.new(name, parent);expected_imp = new("expected_imp", this);actual_imp = new("actual_imp", this);endfunction// 實現expected_imp的put方法virtual task put(transaction tr);`uvm_info("SCOREBOARD", $sformatf("Received Expected Data: %0h", tr.data), UVM_MEDIUM);expected_queue.push_back(tr);endtask// 實現actual_imp的put方法virtual task put(transaction tr);`uvm_info("SCOREBOARD", $sformatf("Received Actual Data: %0h", tr.data), UVM_MEDIUM);actual_queue.push_back(tr);endtask// 比對邏輯(可在run_phase或單獨的任務中調用)task compare();if (expected_queue.size() != actual_queue.size()) begin`uvm_error("SCOREBOARD", "Mismatch in number of transactions!");end else beginforeach (expected_queue[i]) beginif (expected_queue[i].data !== actual_queue[i].data) begin`uvm_error("SCOREBOARD", $sformatf("Data mismatch at index %0d!", i));endendendexpected_queue.delete();actual_queue.delete();endtask
endclass
總結
不同種類的端口一般不能直接相連,需要FIFO轉接,或者將兩個要相連的端口類型聲明為一致的。