寄存器模型的常規方法
mirrored、 desired 和 actual value
在應用寄存器模型時, 除了利用它的寄存器信息, 還可以利用它來跟蹤寄存器的值。
跟蹤寄存器的值, 一方面是建立 mirrored value, 另一方面是為建立 desired value。
寄存器模型中的每一個寄存器都應該有兩個值,一個是鏡像值(mirrored value),一個是期望值 (desired value) 。期望值是先利用寄存器模型修改軟件對象值, 而后利用該值更新硬件值;鏡像值是表示當前硬件的已知狀態值。鏡像值往往由模型預測給出,關于預測,在前門訪問時通過觀察總線或在后門訪問時通過自動預測等方式來給出鏡像值。
然而,鏡像值有可能與硬件實際值 (hardware actual value)不一致,例如狀態寄存器的鏡像值就無法與硬件實際值保持同步更新。另外, 如果其他訪問寄存器的通路修改了寄存器, 那么可能由于那一路總線沒有被監測, 導致寄存器的鏡像值未得到及時更新。
UVM 提供了兩種用來跟蹤寄存器值的方式, 我們將其分為自動預測 (auto prediction)和顯式預測 (explicit)。如果讀者想使用自動預測的方式,還需要調用函數 uvm_reg_map: :set_auto_predict () 。兩種預測方式的顯著差別在于, 顯式預測對寄存器數值預測更為準確, 我們可以通過下面對兩種模式的分析得出具體原因。
自動預測 (auto prediction) :如果讀者沒有在環境中集成獨立的 predictor, 而是利用寄存器的操作來自動記錄每一次寄存器的讀寫數值, 并在后臺自動調用 predict()方法的話, 則這種方式稱為自動預測。這種方式簡單有效, 然而需要注意, 如果出現其他一些sequence 直接在總線層面上,對寄存器進行操作(跳過寄存器級別的 write()/read()操作), 或通過其他總線來訪問寄存器等這些額外的情況, 都無法自動得到寄存器的鏡像值和預期值。
顯式預測 (explicit prediction):更為可靠的一種方式是在物理總線上通過監視器來捕捉總線事務, 并將捕捉到的事務傳遞給外部例化的 predictor (預測器),該 predictor 由 UVM 參數化類 uvm_reg_predictor 例化并集成在頂層環境中。 如下圖, 在集成的過程中需要將 adapter 與 map 的句柄也一并傳遞給 predictor, 同時將 monitor 采集的事務通過 analysis port 接入到 predictor一側。這種集成關系可以使得 monitor一旦捕捉到有效事務, 會發送給 predictor, 再由其利用adapter 的橋接方法, 實現事務信息轉換, 并將轉化后的寄存器模型有關信息更新到 map 中。默認情況下,系統將采用顯式預測的方式, 這就要求集成到環境中的總線 UVC monitor 需要具備捕捉事務 的功能和對應的 analysis port, 以便于同 predictor 連接。
關于 predictor 在頂層環境中的集成, 讀者可以通過下面的一段例碼片段來掌握集成時的幾個要素:
class mcdf_bus_env extends uvm_env; mcdf_bus_agent agent; mcdf_rgm rgm; reg2mcdf_adapter reg2mcdf; uvm_reg_predictor #(mcdf_bus_trans) mcdf2reg_predictor; `uvm_component_utils(mcdf_bus_env) ... function void build_phase(uvm_phase phase); agent=mcdf_bus_agent::create("agent", this); if(!uvm_config_db#(mcdf_rgm)::get(this, "", "rgm", rgm)) begin `uvm_info("GETRGM", "no top-down RGM handle is assigned", UVM_LOW) rgm = mcdf_rgm::create("rgm", this); `uvm_info("NEWRGM", "created rgm instance locally", UVM_LOW) end rgm. build() ; reg2mcdf = reg2mcdf_adapter::create("reg2mcdf"); mcdf2reg_predictor = uvm_reg_predictor#(mcdf_bus_trans)::type_ id: : create ("mcdf2reg_predcitor", this); mcdf2reg_predictor.map = rgm.map; mcdf2reg_predictor.adapter = reg2mcdf; endfunction function void connect_phase(uvm_phase phase); rgm.map.set_sequencer(agent.sequencer, reg2mcdf); agent.monitor.ap.connect(mcdf2reg_predictor.bus_in); endfunction endclass
uvm_reg的訪問方法
在給出寄存器模型的常見應用模式之前,首先從下表中更全面地了解uvm_reg_ block、 uvm_reg 和uvm_reg_ field 三個類提供的用于訪問寄存器的方法。
uvm_reg_ sequence 提供的方法(均是針對寄存器對象的, 而不是寄存器塊或寄存器域)如下表。
結合mirrored value、 desired value 和 actual value, 我們需要理解這4種方法在調用時, 三種數值的變化時序關系:
? 對于前門訪問的 read()和 write(),在總線事務完成時, 鏡像值和期望值才會更新為與總線上相同的值,這種預測方式是顯式預測。
? 對于 peek()和 poke(), 以及后門訪問模式下的 read()和 write(),由于不通過總線,默認采取自動預測的方式,因此在方法調用返回后,鏡像值和期望值也相應修改。
關于 reset()和 get_reset()的用法, 下面也給出部分例碼。例如硬件在復位觸發時, 會將內部寄存器值復位, 而寄存器模型在捕捉到復位事件時, 為了保持同硬件行為一致, 也應當對其復位。這里注意的是, 復位的對象是寄存器模型, 而不是硬件。
@(negedge p_sequencer.vif.rstn);
rgm. reset(); / / register block reset for mirrored value and desired value
rgm.chnl0_ctrl_reg.reset(); // register level reset
rgm.chnl0_ctrl_reg.pkt_len.reset(); // register field reset
在復位之后, 用戶也可以通過讀取寄存器模型的復位值(與寄存器描述文件一致), 與前門訪問獲取的寄存器復位值進行比較, 以此判斷硬件各個寄存器的復位值是否按照寄存器描述去實現。這里的 get_reset()方法指的也是寄存器模型的復位值, 而不是硬件。
// register model reset value get and check
rstval = rgm.chnl0_ctrl_reg. get_reset() ;
rgm.chnl0_ctrl_reg.read (status, data, UVM_BACKDOOR, .parent(this));
if(rstval != data)
`uvm_error ("RSTERR", "reset value read is not the desired reset value")
mirror()方法與 read()方法類似, 也可以選擇前門訪問或后門訪問, 不同的是, mirror()不會返回讀回的數值, 但是會將對應的鏡像值修改。在修改鏡像值之前, 用戶還可以選擇是否將讀回的值與模型中的原鏡像值進行比較。下面的例碼在更新鏡像值之前, 首先將讀回的值與上一次鏡像值做了比對, 隨后再更新鏡像值。比如, 對于配置寄存器, 可以采用這種方法來檢查上一次的配置是否生效, 又或者對于狀態寄存器可以選擇只更新鏡像值不做比較, 這是因為狀態寄存器隨時可能被硬件內部邏輯修改。
// get register value and check
rgm.chnl0_ctrl_reg.mirror(status, UVM_CHECK, UVM_FRONTDOOR,parent(this));
下面的方法是運用 set()和 update()對寄存器做批量修改。首先 set()方法的對象是寄存器模型自身,通過set()可以修改期望值, 而在寄存器配置時不妨先對其模型隨機化,再配置個別寄存器或域, 當寄存器的期望值與鏡像值不相同時,可以通過update()方法來將不相同的寄存器通過前門訪問或后門訪問的方式做全部修改。
這種 set()和 update()的方式較 write()和 poke()的寫寄存器方式更為靈活的是,它可以實現隨機化寄存器配置值(先隨機化寄存器模型,后將隨機值結合某些域的指定值寫入到寄存器),繼而模擬更多不可預知的寄存器應用場景。另外,update()強大的批量操作寄存器功能使得修改寄存器更為便捷。
// randomize register model, set register/field value and update to // hardware actual value void'(rgm. chnlO_ctrl_reg. randomize()); rgm.chnlO ctrl reg.pkt len.set('h3); rgm.chnlO ctrl reg. update (status, UVM FRONTDOOR, .parent (this)); void'(rgm.chnll_ctrl_reg.randomize()); rgm. chnl0_ctrl_reg. set ('h22); rgm.update(status, UVM FRONTDOOR, .parent(this));
mem與reg的聯系和差別
UVM寄存器模型也可以用來對存儲建模。uvm_mem 類可以用來模擬RW (讀寫)、 RO(只讀)和WO(只寫)類型的存儲,并且可以配置存儲模型的數據寬度和地址范圍。uvm_mem不同于uvm_reg 的地方在于,考慮到物理存儲一旦映射到 uvm_mem 會帶來更大的資源消耗,因此 uvm_mem 并不支待預測和影子存儲(shadow storage) 功能, 即沒有鏡像值和期望值。
uvm_mem 可以提供的功能就是利用自帶的方法去訪問硬件存儲, 相比于直接利用硬件總線 UVC進行訪問,這么做的好處在于:
? 類似于寄存器模型訪問寄存器,利用存儲模型訪問硬件存儲便于維護和復用。
? 在訪問過程中,可以利用模型的地址范圍來測試硬件的地址范圍是否全部覆蓋。
? 由于 uvm_mem 也同時提供前門訪問和后門訪問,這使得存儲測試可以考慮先通過后門訪問預先加載存儲內容,而后通過前門訪問讀取存儲內容,繼而做數據比對,這樣做不但節省時間,同時也在測試方式上保持了一致性。同時這種方式相比于傳統前后測試方式(利用系統函數或仿真器函數實現存儲加載),要在UVM測試框架中更為統一。
與 uvm_reg 相比, uvm_mem不但擁有常規的訪問方法read() 、 write()、peek()和 poke(),也提供了 burst_read()和 burst_write() 。之所以額外提供這兩種方法, 不但是為了可以更高速地通過物理總線的BURST方式連續存儲,也是為了盡可能貼合實際訪問存儲中的場景。要實現BURST訪問形式, 需要考慮下面這些因素:
? 目前掛載的總線UVC是否支持BURST形式訪問,例如APB不能支持BURST訪問模式。
? 與 read()、 write()方法相比,burst_read()和 burst_write()的參數列表中的一項uvm_reg_ data _t value[]采用的是數組形式, 不再是單一變量, 即表示用戶可以傳遞多個數據。而在后臺,這些數據首先需要裝載到 uvm_reg_item 對象中,裝載時 value 數組可以直接寫入, 另外兩個成員需要分別指定為 element_kind = UVM_MEM , kind = UVM_BURST_READ。
? 在 adapter 實現中, 也需要考慮到存儲模型 BURST 訪問的情形, 實現4 種訪問類型(uvm _access_ e) 的轉換,即UVM_READ、UVM_WRITE、UVM_BURS_READ 和UVM_BURST_ WRITE。對于 UVM_READ 和 UVM_WRITE 的橋接, 已經在寄存器模型訪問中實現, 而 UVM_BURST_READ 和 UVM_BURST_WRITE的轉換,往往需要考慮寫入的數據長度,例如長度是否是 4、 8、 16 或其他。比如 AHB 總線, 支持連續 4 個、8 個、 16 個數據的讀寫 (INCR4 、 INCR8、 INCR16), 但是如果數據長度不是這些固定長度時, adapter 還需要自己處理來實現 INCR 的連續訪問方式。
? 此外還需要考慮不同總線的其他控制參數, 例如 AHB 支持 WRAP 模式, AXI 支持out-of-order 模式等, 如果想要將更多的總線控制封裝在 adapter 的橋接功能里, 需要將更多的配置作為擴展配置, 在調用訪問方法時作為擴展信息類,傳入到形式參數uvm_object_extension。待傳入后, adapter 將可以在橋接方法中抽取出擴展信息類,作為更準確的協議訪問的限定依據。
? 對于更為復雜的 BURST 形式, 如果需要實現更多的協議配置要求, 那么筆者推薦直接在總線 UVC 層面去驅動。這樣做的靈活性更大, 且更能充分全面的測試存儲接口的協議層完備性。因此, 驗證師在為存儲模型訪問實現 adapter 方法時, 需要考慮的是, uvm_mem 層面的方法應該盡員便捷、必要的參數應該少量, 以便于使用和維護;而另一方面, 如果要首先測試存儲接口協議, 則應該在總線 UVC 的層面上完成更充分的驗證。
內建sequences
不少有經驗的 UVM 用戶可能會忽略 UVM 針對寄存器模型內建的(built-in) 一些sequence, 實際上, 將這些自建的序列作為驗證項目開始前的健康檢查必選項, 對整個項目的平穩運行會有不小的貢獻。
這是因為,在項目的開始階段,設計內部的邏輯尚不穩定,驗證師要跟上設計的進度,可以展開驗證的部分無外乎是系統控制信號(時鐘、復位、 電源)和寄存器的驗證。在項目早期,寄存器模型的驗證可以為后期各個功能點驗證打下良好的基礎。比如,通過內建的寄存器序列可以實現完善的寄存器復位值檢查,又比如檢查讀寫寄存器的讀寫功能是否正常等。
不過有一些寄存器即便可以測試, 也建議將其作為例外而過濾出去, 例如一些重要的系統控制信號(時鐘、復位、電源), 當寫入某些值以后, 會使得系統全部或局部復位、時鐘也可能被關閉 , 這就可能阻礙寄存器的下一步檢查。所以 UVM 提供了一些特殊域, 用來禁止sequence 檢查這些寄存器或存儲。接下來,我們從下表來分別瀏覽整理出的寄存器和存儲相關的自建sequence 。
寄存器模型內建序列
存儲器模型內建序列
接下來我們給出一段例碼,來演示如何利用內建序列完成MCDF寄存器測試一開始的健康檢查。下面的例碼分別添加了 uvm_reg_ hw _reset_ seq、 uvm_reg_ bit_ bash_ seq和 uvm _reg_ access_ seq 來測試寄存器模型, 從代碼的整潔性來看, 用戶并不需要額外再添加什么, 這種使用方式非常方便, 且又能完成寄存器的大規模集成測試。
mcdf_rgm rgm; `uvm_object_utils(mcdf_example_seq) `uvm_declare_p_sequencer(mcdf_bus_sequencer) ... task body(); uvm_status_e status; uvm_reg_data_t data; uvm_reg_hw_reset_seq reg_rst_seq = new(); uvm_reg_bit_bash_seq reg_bit_bash_seq = new(); uvm_reg_access_seq reg_acc_seq = new(); if (! uvm_config_db# (mcdf_rgm)::get (null, get_full_name (), "rgm", rgm)) begin `uvm_error("GETRGM", "no top-down RGM handle is assigned") end // wait reset asserted and release @(negedge p_sequencer.vif.rstn); @(posedge p_sequencer.vif.rstn); `uvm_info ("BLTINSEQ", "register reset sequence started", UVM LOW) reg_rst_seq.model = rgm; reg_rst_seq.start(m_sequencer); `uvm_info ("BLTINSEQ", "register reset sequence finished", UVM_LOW) `uvm_info("BLTINSEQ", "register bit bash sequence started", UVM_LOW) //reset hardware register and register model reg_bit_bash_seq.model = rgm; reg_bit_bash_seq.start(m_sequencer); `uvm_info("BLTINSEQ", "register bit bash sequence finished", UVM_LOW) `uvm_info("BLTINSEQ", "register access sequence started", UVM_LOW) //reset hardware register and register model reg_acc_seq.model = rgm; `uvm info ("BLTINSEQ", "register access sequence finished", UVM LOW ) endtask endclass如果想將一些寄存器排除在某些內建序列測試范圍之外, 可以額外添加上面列表中提到的“禁止域名 ”。由于 uvm_reg_block 和 uvm_reg 均是 uvm_object 類而不是 uvm_ component 類,所以可以使用 uvm_resource_ db 來配置 “ 禁止域名 “。下面的代碼摘自 mcdf_rgm::build()方法, 這相當于寄存器模型在自己的建立階段設定了一些屬性。當然, uvm_resource_ db 的配置也可以在更高層指定, 只不過考慮到 uvm_resource_ db 不具備層次化的覆蓋屬性, 我們建議只在 一個地方進行 “ 禁止域名 ” 的配置。
class mcdf_rgm extends uvm_reg_block; ... virtual function build(); // disable built-in seq attributes uvm_resource_db#(bit)::set ({"REG::", this. chnlO stat reg. get full name () } , NO REG ACCESS TEST", 1); uvm_resource_db# (bit)::set ({ "REG: : ", this. chnll stat reg.get full name () } , "NO REG ACCESS TEST", 1); uvm_resource_db#(bit):: set ({"REG: : ", this. chnl2 stat reg. get full name () } , "NO REG ACCESS TEST", l); endfunction endclass
審核編輯:劉清
-
寄存器
+關注
關注
31文章
5325瀏覽量
120054 -
UVM
+關注
關注
0文章
181瀏覽量
19144 -
uvc
+關注
關注
1文章
126瀏覽量
14507
原文標題:IC學霸筆記 | UVM寄存器模型的常規方法
文章出處:【微信號:IC修真院,微信公眾號:IC修真院】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論