SV系統整合篇之五(終):初論環境的複用性
本文轉自:http://www.eetop.cn/blog/html/28/1561828-2331492.html
伴隨著模組驗證和MCDF設計的子系統應用,在最終的晶片級驗證中,MCDF會跟其它的模組一併組合在一起形成一個統一的晶片級驗證環境。在之前的底層驗證過程中,主要將功能側重於模組或者子系統一級,而在晶片級驗證中則側重於子系統之間的連線和互動測試上面。伴隨著驗證系統的逐層提高,我們的驗證環境也會發生變化,這一點可以在之前的《驗證環境的組裝》中得到說明。但是不管驗證什麼子系統,它的驗證環境都會包括stimulator、monitor、checker等。為了最大限度的降低驗證環境構建的工作量,我們期望可以用於便於複用的測試元件(《靈活化的配置》)和易於組合排程的測試向量(《測試場景的生成》)。有了可複用的驗證元件,這會在減少重複工作的同時,補充驗證師們在驗證場景構成和檢查的時間。
重用的策略
重用的好處就是避免“重複製造輪子”,而是用車去做更緊迫更有效的事情,讓驗證團隊減低重複開發驗證環境的成本,多投入到驗證場景和檢查中去。從MCDF的例子來看,一個有規模的公司在同一時間段很有可能執行兩個以上的專案,如果MCDF的設計複用性好,那麼設計的整合時間是很快的,而這對於驗證的要求就更緊迫了。
在上面的示圖中,晶片A和晶片B的結構中都會整合MCDF,在晶片A中的MCDF slave介面是三個,輸入資料位寬是32位,對應著之前的設計,而在晶片B中的MCDF slave介面是四個,輸入資料位寬是64位。那麼晶片B的設計組如果已經擁有一個引數化的設計,那麼對於調整slave介面數量和資料位寬不會是難事,而一旦MCDF的介面發生了變化,這對驗證環境也會帶來新的要求。在晶片B中MCDF的介面變化,會要求MCDF子系統驗證的環境也要相應做調整,如果像設計一樣使得環境可以引數化配置,做出快速的響應和驗證檢查,那麼在不同專案之間,驗證環境的遷移就可以完成水平復用了。
而無論是在晶片A中MCDF設計,還是晶片B中的MCDF設計,一旦整合到晶片系統結構中,那麼它的介面即完全“浸入”在晶片結構中,原來的MCDF子系統環境不再需要提供任何激勵了。那麼MCDF的子系統驗證環境是否在晶片一級中還有用武之地,可以繼續發揮它的功能來檢查MCDF自身功能呢?
這就需要考慮從低一層的驗證環境到高一層的驗證環境複用性了,即垂直複用。
在之前《驗證環境的組裝》中我們討論了通過agent元件來組裝stimulator和monitor的必要性,這會使得模組級元件的初衷複用可以跨接到MCDF子系統一級。類似地,我們也可以同參考《靈活化的配置》一節中通過絕對結構化的變數來完成MCDF從子系統一級到晶片一級的垂直複用,讓MCDF的子系統驗證環境在晶片級驗證中繼續發揮它的作用。
下面的兩段例碼用來初步討論環境在水平復用和垂直複用方向上的一些考慮,希望對讀者有一點啟發。在後面的篇章中我們也會深入討論驗證環境的可複用性。
水平復用的應用
下面的這段程式碼是經過簡化後的,以此來說明MCDF中的模組arbiter在MCDF的slave介面從晶片A中的3個變化為晶片B中的4個,以及資料寬度從32位拓寬為64位的水平復用環境。
package mcdf_global_pkg;
parameter data_width_p = 64;
parameter slave_num_p = 4;
endpackage
interface arb_ini_if;
parameter data_width_p = mcdf_global_pkg::data_width_p;
logic [data_width_p-1 :0] data;
endinterface
interface arb_rsp_if;
parameter data_width_p = mcdf_global_pkg::data_width_p;
logic [data_width_p-1 :0] data;
endinterface
首先是定義了MCDF的引數包mcdf_global_pkg,其中規定了資料的寬度為64,MCDF slave介面數目是4。
接下來是對arbiter的兩個介面arb_ini_if和arb_rsp_if的定義,關於訊號埠data的寬度通過引數進行位寬的可變控制,且來自於mcdf_global_pkg::data_width_p。
package arb_pkg;
import mcdf_global_pkg::*;
class arb_ini_trans; endclass
class arb_ini_stm;
virtual interface arb_ini_if #(.data_width_p(data_width_p)) vif;
task run(); endtask
endclass
class arb_ini_mon;
virtual interface arb_ini_if #(.data_width_p(data_width_p)) vif;
mailbox #(arb_ini_trans) mb;
task run(); endtask
endclass
class arb_ini_agent;
virtual interface arb_ini_if #(.data_width_p(data_width_p)) vif;
arb_ini_stm stm;
arb_ini_mon mon;
function new();
stm = new();
mon = new();
endfunction
task run(); endtask
function void assign_vi(virtual interface arb_ini_if #(.data_width_p(data_width_p)) intf);
vif = intf;
stm.vif = intf;
mon.vif = intf;
endfunction
endclass
class arb_rsp_trans; endclass
class arb_rsp_stm;
virtual interface arb_rsp_if #(.data_width_p(data_width_p)) vif;
task run(); endtask
endclass
class arb_rsp_mon;
virtual interface arb_rsp_if #(.data_width_p(data_width_p)) vif;
mailbox #(arb_rsp_trans) mb;
task run(); endtask
endclass
class arb_rsp_agent;
virtual interface arb_rsp_if #(.data_width_p(data_width_p)) vif;
arb_rsp_stm stm;
arb_rsp_mon mon;
function new();
stm = new();
mon = new();
endfunction
task run(); endtask
function void assign_vi(virtual interface arb_rsp_if #(.data_width_p(data_width_p)) intf);
vif = intf;
stm.vif = intf;
mon.vif = intf;
endfunction
endclass
class arb_checker;
mailbox #(arb_ini_trans) ini_mb[slave_num_p];
mailbox #(arb_rsp_trans) rsp_mb;
function new();
foreach(ini_mb[i]) ini_mb[i] = new();
rsp_mb = new();
endfunction
function void connect(); endfunction
task run(); endtask
endclass
class arb_env;
arb_ini_agent arb_ini_agt[slave_num_p];
arb_rsp_agent arb_rsp_agt;
arb_checker chk;
function new();
foreach(arb_ini_agt[i]) arb_ini_agt[i] = new();
arb_rsp_agt = new();
chk = new();
endfunction
function void connect();
foreach(arb_ini_agt[i]) begin
arb_ini_agt[i].mon.mb = chk.ini_mb[i];
end
arb_rsp_agt.mon.mb = chk.rsp_mb;
endfunction
task run(); endtask
endclass
endpackage
上面的arb_pkg中定義了各個元件,而最後arb_env將各個元件進行了例化和連線。值得注意的是,由於介面arb_ini_if和arb_rsp_if是引數化的介面,所以在各個元件中宣告虛介面時,應該使用的是
- virtual interface arb_ini_if #(.data_width_p(data_width_p)) vif
- virtual interface arb_rsp_if #(.data_width_p(data_width_p)) vif
來宣告,而不是
- virtual interface arb_ini_if vif
- virtual interface arb_rsp_if vif
因為SV關於虛介面傳遞的規定是,傳遞的介面型別必須嚴格一致,如果是引數介面,則虛介面的型別中也應該表明介面的引數傳遞值與隨後被傳入的物理介面的引數值是一致的。
再來看看由於MCDF slave介面數量的變化導致的arbiter輸入埠組數的變化。這一變化使得arbiter arb_env中的arb_ini_agt[slave_num_p]和arb_checker中的ini_mb[slave_num_p]也變為引數化了,並且在arb_env中monitor與checker的通訊管道連線也通過foreach完成了全部連線。
module arb_tb;
import arb_pkg::*;
event build_end_e;
event connect_end_e;
arb_env env;
arb_ini_if arb_ini_if0();
arb_ini_if arb_ini_if1();
arb_ini_if arb_ini_if2();
arb_ini_if arb_ini_if3();
arb_rsp_if arb_rsp_if();
... // DUT例化
initial begin : build
env = new();
-> build_end_e;
end
initial begin : connect
wait(build_end_e.triggered());
env.arb_ini_agt[0].assign_vi(arb_ini_if0);
env.arb_ini_agt[1].assign_vi(arb_ini_if1);
env.arb_ini_agt[2].assign_vi(arb_ini_if2);
env.arb_ini_agt[3].assign_vi(arb_ini_if3);
env.arb_rsp_agt.assign_vi(arb_rsp_if);
env.connect();
->connect_end_e;
end
initial begin : run
wait(connect_end_e.triggered());
fork
env.run();
join_none
end
endmodule
最後看看arb_env在頂層arb_tb中的例化和連線關係。在例化了4個arb_ini_if介面之後,需要在connect過程語句塊中連線對應的介面到正確的agent。而其它的非引數化的例化和連線則不需要做出變動。從上面的arb_tb實現中,完成了從之前的晶片A MCDF arbiter模組環境到晶片B MCDF arbiter模組環境的水平復用。與之類似地,我們相信可以在MCDF子系統一級的驗證環境中做出同樣的水平復用,使得在晶片A到晶片B的MCDF複用中,不但設計可以很好地完成複用,對於驗證環境也可以貢獻同樣的複用價值。
垂直複用的應用
下面的例碼是從之前《驗證環境的組裝》中移植過來的程式碼,經過稍加改善,我們已經可以使得mcdf_env的環境結構同時支援MCDF_TB模式(MCDF子系統驗證)和CHIP_TB模式(晶片系統驗證)兩種模式。做的更新在於新增模式變數mcdf_env::tb_mode,並且在例化mcdf_env的時候傳遞模式值。如果是MCDF_TB模式,那麼會在其後對子元件的例化中,令子元件都變為active模式;如果是CHIP_TB模式,那麼會在子元件的例化中,讓其也都例化為passive模式。
通過這種層次化的模式傳遞,可以在頂層例化時,靈活地控制整體的環境結構。而在後面的connect()和run()階段則無需做出更新,因為對於mcdf_env子元件而言,各個agent會在內部進一步基於active模式還是passive模式對自身的結構做出調整。
typedef enum {MCDF_TB, CHIP_TB} tb_mode_t;
class mcdf_env;
regs_ini_agent regs_ini_agt;
slv_ini_agent slv_ini_agt[3];
fmt_rsp_agent fmt_rsp_agt;
mcdf_checker chk;
tb_mode_t tb_mode;
virtual interface mcdf_if vif;
function new(tb_mode_t m = MCDF_TB);
bit active_mode;
tb_mode = m;
if(m == MCDF_TB)
active_mode = 1;
else
active_mode = 0;
regs_ini_agt = new(.mod(active_mode));
foreach(slv_ini_agt[i]) slv_ini_agt[i] = new(.mod(active_mode));
fmt_rsp_agt = new(.mod(active_mode));
chk = new();
endfunction
function void connect();
chk.connect();
regs_ini_agt.mon.mb = chk.regs_ini_mb;
foreach(slv_ini_agt[i]) slv_ini_agt[i].mon.mb = chk.slv_ini_mb[i];
fmt_rsp_agt.mon.mb = chk.rsp_mb;
endfunction
task run();
fork
regs_ini_agt.run();
foreach(slv_ini_agt[i]) slv_ini_agt[i].run();
fmt_rsp_agt.run();
chk.run();
join_none
endtask
function void assign_vi(virtual interface mcdf_if intf);
vif = intf;
chk.vif = intf;
endfunction
endclass
至此,關於SV的系統整合篇就結束了。在這一篇中,讀者懂得了package的意義,懂得如何集簇底層模組的package。在整理了各個模組環境的之後,將其各個模組環境的元件通過MCDF子系統環境的組織可以進一步搭建成為子系統驗證結構。有了房子之後,如何“通水通電”,讓這個大房子能夠正常運轉,則是要考慮測試場景如何生成和排程的問題。最後,考慮到驗證環境的水平復用和垂直複用,我們需要引入更靈活的結構變數和模式變數來提供便捷的配置方式。