目錄
(一)Factory工廠機制
1. 工廠機制核心邏輯:“注冊 - 創建 - 覆蓋”
2. 代碼映射:從概念到實現
3. 實驗目標:用?dadd_fixen_driver?固定?data_en=1
4. 工廠機制的價值:“靈活驗證的基石”
5. 常見問題與調試
6. 總結
(二)Phase階段運行機制
1. 核心規則:“三類 phase 執行順序”
2. 實驗驗證:代碼與日志的映射
3. 關鍵細節與易錯點
4. 實驗價值與應用場景
5. 總結
(三)sequence 激勵產生與交互執行機制
1.?sequence 核心機制概述
2. sequence_item的發送
2.1 核心規則:sequence_item?發送的三種方法
2.2 逐類解析:代碼邏輯與實驗驗證
2.3 關鍵細節與對比
2.4 實驗價值與總結
3. sequence 的發送
3.1 核心規則:子?sequence?發送的三類方法
3.2 逐類解析:結合?dadd?代碼與腳本
3.3 關鍵細節與對比
3.4 實驗價值與總結
(一)Factory工廠機制
1. 工廠機制核心邏輯:“注冊 - 創建 - 覆蓋”
????????UVM 工廠機制本質是?“對象創建的集中管控”,核心解決兩個問題:
(1)解耦創建邏輯:讓對象的 “創建” 與 “使用” 分離,無需硬編碼?new()
,便于后續替換實現(如用?dadd_fixen_driver
?替換?dadd_driver
?)。
(2)支持動態替換:通過?set_type_override
?或?set_inst_override
,可在不修改代碼的前提下,替換組件類型(如把?dadd_driver
?換成?dadd_fixen_driver
?),靈活控制驗證行為。
2. 代碼映射:從概念到實現
(1) 注冊(uvm_component_utils
)
- 作用:把類 “登記” 到 UVM 工廠的 “查找表”,讓工廠能識別并創建它。
- 代碼示例(
dadd_driver
?注冊):
class dadd_driver extends uvm_driver #(dadd_item);`uvm_component_utils(dadd_driver) // 注冊 component 到工廠// ... 類定義 ...
endclass
- 關鍵:
uvm_component_utils
?是注冊?component
(繼承?uvm_component
?的類,如?driver
、monitor
?)的宏;若為?object
(繼承?uvm_object
?的類,如?sequence_item
?),則用?uvm_object_utils
。- 注冊后,類會被加入 UVM 工廠的 “類型映射表”,后續可通過?
type_id::create()
?創建實例。
(2)創建(type_id::create
)
- 作用:通過工廠創建對象,自動檢查是否有 “類型覆蓋”,再決定實例化原類還是替換類。
- 代碼示例(
dadd_iagent
?中創建?dadd_driver
?):
function dadd_iagent::new(string name = "dadd_iagent", uvm_component parent);super.new(name, parent);// 通過工廠創建 driver,而非直接 new(dadd_driver::new(...))drv = dadd_driver::type_id::create("drv", this); // ... 其他組件創建 ...
endfunction
- 關鍵:
type_id::create("drv", this)
?會先查工廠是否有?dadd_driver
?的覆蓋類型(如?dadd_fixen_driver
?),若有則創建覆蓋類,否則創建原類。- 替換?
new()
?為?create()
?是實現 “動態覆蓋” 的核心:后續只需調用?set_type_override
,無需修改?dadd_iagent
?代碼,就能替換?driver
?類型。
(3)覆蓋(set_type_override
)
- 作用:告訴工廠 “用類 B 替換類 A 的創建”,實驗中把?
dadd_driver
?換成?dadd_fixen_driver
,固定?data_en=1
。 - 代碼示例(在?
dadd_rand_test
?中覆蓋 ):
class dadd_rand_test extends uvm_test;`uvm_component_utils(dadd_rand_test)function void build_phase(uvm_phase phase);// 全局覆蓋:所有 dadd_driver 類型都替換為 dadd_fixen_driverdadd_driver::type_id::set_type_override(dadd_fixen_driver::get_type()); super.build_phase(phase);endfunction
endclass
- 關鍵:
set_type_override
?需在?build_phase
?調用,確保工廠在創建組件前生效。- 替換后,
dadd_iagent
?中?drv = dadd_driver::type_id::create(...)
?會自動創建?dadd_fixen_driver
?實例,實現 “無代碼修改替換組件”。
3. 實驗目標:用?dadd_fixen_driver
?固定?data_en=1
????????原?dadd_driver
?中?data_en
?是隨機的,實驗通過?“工廠覆蓋”?替換為?dadd_fixen_driver
,強制?data_en=1
:
(1)dadd_fixen_driver
?邏輯
task dadd_fixen_driver::main_phase(uvm_phase phase);wait(tb_dadd.dadd_if.reset_n);forever beginseq_item_port.get_next_item(req);@(posedge tb_dadd.dadd_if.clk);// 固定 data_en=1(與原 driver 的隨機邏輯區分)tb_dadd.dadd_if.mcb.dadd_in_en <= 1'b1; tb_dadd.dadd_if.mcb.dadd_in <= req.data;tb_dadd.dadd_if.mcb.dadd_in_addr<= req.addr;seq_item_port.item_done();end
endtask
- 關鍵:
dadd_in_en <= 1'b1
?硬編碼為 1,覆蓋原?driver
?的隨機行為。
(2)覆蓋流程
- 在?
dadd_rand_test
?的?build_phase
?調用?set_type_override
,替換?dadd_driver
?為?dadd_fixen_driver
。 dadd_iagent
?中通過?dadd_driver::type_id::create()
?創建?driver
?時,工廠自動實例化?dadd_fixen_driver
。- 仿真時,
driver
?驅動 DUT 的?data_en
?恒為 1,實現實驗目標。
4. 工廠機制的價值:“靈活驗證的基石”
(1)解耦與復用:組件創建邏輯與使用邏輯分離,dadd_iagent
?無需關心?driver
?具體類型,只需通過工廠創建,復用性更高。
(2)動態配置:通過一行?set_type_override
,就能切換?driver
?行為(隨機 / 固定?data_en
?),無需修改?agent
、env
?代碼。
(3)可維護性:驗證需求變化時(如替換?driver
?協議),只需修改?driver
?子類和覆蓋邏輯,不影響上層組件。
5. 常見問題與調試
(1)覆蓋不生效:
- 檢查?
set_type_override
?是否在?build_phase
?調用(main_phase
?調用無效,因組件已創建 )。 - 檢查?
create()
?是否用?type_id::create
,而非直接?new()
(直接?new()
?會繞過工廠,覆蓋失效 )。
(2)類型注冊遺漏:
- 若子類(如?
dadd_fixen_driver
?)未注冊(忘記?uvm_component_utils
?宏 ),工廠無法識別,覆蓋會失敗。
(3)層次化覆蓋:
- 若需只替換某個?
agent
?中的?driver
(而非全局替換 ),可用?set_inst_override
,指定實例路徑(如?uvm_test_top.env.iagt.drv
?)。
6. 總結
????????UVM 工廠機制通過?“注冊 - 創建 - 覆蓋”?三步,實現了:
- 動態替換組件:實驗中用?
dadd_fixen_driver
?替換?dadd_driver
,無需修改?agent
?代碼。 - 解耦創建邏輯:組件創建由工廠統一管控,上層組件只需關注接口,無需關心具體實現。
- 靈活驗證配置:一行代碼切換驗證行為(隨機 / 固定?
data_en
?),讓驗證平臺可適配不同場景。
????????這是 UVM 實現 “可復用、可配置驗證平臺” 的核心機制,掌握后能大幅提升驗證環境的擴展性與維護性。
(二)Phase階段運行機制
1. 核心規則:“三類 phase 執行順序”
????????UVM phase 執行順序分為?“組件內順序”、“樹形結構順序”、“同級組件順序”?三類,需結合實驗場景理解:
(1)同一組件內的 phase 順序(縱向順序)
- 規則:同一組件(如?
dadd_iagent
?)內,phase 按?“功能階段”?順序執行,從?build_phase
?開始,到?final_phase
?結束,流程為:
build_phase → connect_phase → end_of_elaboration_phase → start_of_simulation_phase →
reset_phase → configure_phase → run_phase → main_phase → shutdown_phase →
extract_phase → check_phase → report_phase → final_phase
- 實驗映射:若在?
dadd_driver
?的所有 phase 中加入打印,會看到該組件內 phase 嚴格按上述順序執行。
(2) 樹形結構中的 phase 順序(橫向跨組件)
????????UVM 組件構成?樹形層次結構(uvm_test_top → env → agent → driver/monitor
?),同一類型 phase 在樹形結構中的執行順序分兩種:
phase 類型 | 執行方向 | 典型 phase 舉例 | 實驗流程映射(以?build_phase ?和?connect_phase ?為例) |
---|---|---|---|
自上而下(Top-Down) | 從根到葉子 | build_phase 、final_phase | uvm_test_top.build_phase ?→?env.build_phase ?→?agent.build_phase ?→?driver.build_phase |
自下而上(Bottom-Up) | 從葉子到根 | connect_phase 、report_phase ?等 | driver.connect_phase ?→?agent.connect_phase ?→?env.connect_phase ?→?uvm_test_top.connect_phase |
(3)同級組件的 phase 順序(橫向同層)
- 規則:同一父組件下的?同級組件(如?
agent
?內的?driver
、monitor
、sequencer
?),同一 phase 的執行順序按?“實例化名稱的字典序”?執行。 - 實驗映射:若?
agent
?中?driver
?命名為?drv
、monitor
?命名為?imon
、sequencer
?命名為?sqr
,則?build_phase
?執行順序為:
drv.build_phase → imon.build_phase → sqr.build_phase
(因字典序?drv
?<?imon
?<?sqr
?,按字母順序排列 )
2. 實驗驗證:代碼與日志的映射
(1)代碼中加入 phase 打印(以?dadd_driver
?為例)
class dadd_driver extends uvm_driver #(dadd_item);`uvm_component_utils(dadd_driver)function new(string name="dadd_driver", uvm_component parent);super.new(name, parent);endfunctiontask build_phase(uvm_phase phase);`uvm_info("DRV", "build_phase executed", UVM_LOW)super.build_phase(phase);endtasktask connect_phase(uvm_phase phase);`uvm_info("DRV", "connect_phase executed", UVM_LOW)super.connect_phase(phase);endtask// ... 其他 phase 同理加入打印 ...
endclass
(2)日志分析(以?build_phase
?和?connect_phase
?為例)
build_phase
?日志(自上而下):
UVM_INFO dadd_driver.sv(10) @ 0: uvm_test_top.env.iagt.drv [DRV] build_phase executed
UVM_INFO dadd_imonitor.sv(10) @ 0: uvm_test_top.env.iagt.imon [MON] build_phase executed
UVM_INFO dadd_sequencer.sv(10) @ 0: uvm_test_top.env.iagt.sqr [SQR] build_phase executed
UVM_INFO dadd_oagent.sv(10) @ 0: uvm_test_top.env.oagt [OAGT] build_phase executed
- 邏輯:先執行?
uvm_test_top
?的?build_phase
(未完整打印 ),再執行?env
?的?build_phase
,然后按字典序執行?iagt
?內的?drv
→imon
→sqr
,最后執行?oagt
(因?iagt
?字典序小于?oagt
?)。
connect_phase
?日志(自下而上):
UVM_INFO dadd_driver.sv(15) @ 0: uvm_test_top.env.iagt.drv [DRV] connect_phase executed
UVM_INFO dadd_imonitor.sv(15) @ 0: uvm_test_top.env.iagt.imon [MON] connect_phase executed
UVM_INFO dadd_sequencer.sv(15) @ 0: uvm_test_top.env.iagt.sqr [SQR] connect_phase executed
UVM_INFO dadd_oagent.sv(15) @ 0: uvm_test_top.env.oagt [OAGT] connect_phase executed
UVM_INFO dadd_env.sv(15) @ 0: uvm_test_top.env [ENV] connect_phase executed
UVM_INFO dadd_test.sv(15) @ 0: uvm_test_top [TEST] connect_phase executed
- 邏輯:先執行葉子組件(
drv
→imon
→sqr
?),再執行父組件(oagt
→env
→uvm_test_top
?),符合 “自下而上” 規則。
3. 關鍵細節與易錯點
(1)?run_phase
?與?main_phase
?的關系
run_phase
?是?task phase
?的父 phase,main_phase
?是?run_phase
?的子 phase(屬于?task phase
?類別 )。- 執行順序:
run_phase
?啟動后,main_phase
?會自動執行,且遵循?自下而上?規則(如先?driver.main_phase
,再?agent.main_phase
?等 )。
(2)字典序的具體表現
- 同級組件的 phase 執行順序,嚴格按?“new 時的名稱字符串比較”,如:
- 名稱為?
a_drv
?和?b_drv
?→?a_drv
?先執行(因?a
?的 ASCII 碼小于?b
?)。 - 名稱為?
drv1
?和?drv2
?→?drv1
?先執行(數字?1
?的 ASCII 碼小于?2
?)。
- 名稱為?
(3)phase
?阻塞與 objection 機制
task phase
(如?main_phase
、run_phase
?)需要通過?phase.raise_objection
?和?phase.drop_objection
?控制仿真進度,否則仿真會直接結束。function phase
(如?build_phase
、connect_phase
?)是純函數,無需 objection 機制,執行完立即退出。
4. 實驗價值與應用場景
(1)驗證平臺構建
build_phase
?自上而下:確保父組件先完成 “資源分配”(如創建子組件 ),子組件再初始化(如?driver
?在?agent.build_phase
?中被創建 )。connect_phase
?自下而上:確保子組件先完成 “端口連接”(如?driver.seq_item_port
?連接?sequencer
?),父組件再做全局連接(如?agent
?連接?scoreboard
?)。
(2)調試與問題定位
- 若子組件的?
build_phase
?未執行,需檢查父組件是否在?build_phase
?中正確創建了它(因?build_phase
?自上而下,父組件未創建則子組件無法執行 )。 - 若?
connect_phase
?邏輯異常,需檢查是否因 “自下而上” 順序導致,子組件的端口未準備好時父組件已開始連接。
(3) 復雜場景控制
- 對于多?
agent
、多?sequence
?的驗證平臺,利用?字典序控制同級組件執行順序,可確保特定組件優先執行(如?monitor
?先采樣,driver
?后驅動 )。
5. 總結
????????UVM phase 執行順序的核心邏輯可歸納為:
- 同一組件內:按功能階段順序(
build
→connect
→run
?等 )依次執行。 - 樹形結構中:
build_phase
?和?final_phase
?自上而下,其余 phase 自下而上。 - 同級組件間:按實例化名稱的字典序執行。
????????理解這三類順序,能精準控制驗證平臺的?組件初始化流程、端口連接時機、激勵注入順序,是構建復雜 UVM 驗證環境的基礎。實驗中通過打印各 phase 執行日志,可直觀驗證這些規則,為調試和優化驗證平臺提供依據。
(三)sequence 激勵產生與交互執行機制
1.?sequence 核心機制概述
????????在 UVM 中,sequence
?機制是激勵產生、調度與驅動的核心,通過?sequence
、sequencer
、driver
?的協作,實現 “激勵生成→仲裁調度→信號驅動→結果反饋” 的完整閉環。以下從?執行規則、代碼映射、實驗驗證?三個維度解析其核心邏輯:
(1)執行規則
- 角色分工:
sequence
:作為 “激勵生成器”,負責創建、隨機化?sequence_item
(事務包),并通過?sequencer
?發送給?driver
。sequencer
:作為 “調度中心”,接收多個?sequence
?的請求,通過仲裁算法(如 FIFO、優先級)決定發送順序,再轉發給?driver
。driver
:作為 “執行者”,從?sequencer
?獲取?sequence_item
,轉換為物理信號驅動 DUT,并可通過?response
?反饋結果。
- 交互流程(完整握手):
sequence
?生成?sequence_item
?并隨機化,通過?start_item
/finish_item
?提交給?sequencer
。sequencer
?仲裁后將?item
?放入?REQ_FIFO
,driver
?通過?get_next_item
?取走并驅動 DUT。- 若需反饋,
driver
?生成?response
?放入?RSP_FIFO
,sequence
?通過?get_response
?獲取結果,完成生命周期。
(2)總結
sequence
?專注于 “產生什么激勵”,支持隨機化和約束,覆蓋多樣化測試場景。sequencer
?專注于 “何時發送激勵”,通過仲裁算法協調多?sequence
?競爭。driver
?專注于 “如何驅動激勵”,將抽象事務轉換為物理信號,確保時序正確
2. sequence_item的發送
sequence 的執行必須在 task body 中執行,task body 是在 task phase 中自動調用的。
2.1 核心規則:sequence_item
?發送的三種方法
????????在 UVM 中,sequence
?發送?sequence_item
(事務包,如?dadd_item
?)有三類典型方法,本質都是?“實例化→隨機化→發送給?sequencer
”?的流程封裝,但語法和復雜度不同:
方法分類 | 核心語法 | 封裝層級 | 適用場景 |
---|---|---|---|
基礎方法 | start_item ?+?finish_item | 最底層,無封裝 | 需精準控制發送流程(如調試) |
宏封裝方法 | uvm_create ?+?uvm_send | 封裝?new ?+?start_item/finish_item | 需靈活指定?sequencer |
高級宏(常用) | uvm_do ?系列宏 | 封裝?uvm_create ?+ 完整流程 | 日常驗證(簡潔高效) |
2.2 逐類解析:代碼邏輯與實驗驗證
????????以下結合?dadd
?驗證平臺的?dadd_rand_sequence.sv
?代碼和?Makefile
?腳本,說明每種方法的細節:
(1)方法 1:start_item
?+?finish_item
(基礎流程)
執行規則:手動完成?sequence_item
?的?“實例化→連接?sequencer
→隨機化→發送”?全流程,每一步需顯式調用:
步驟 | 代碼邏輯 | 作用說明 |
---|---|---|
1. 實例化?item | item = new("item"); | 創建?dadd_item ?事務對象,準備承載激勵數據 |
2. 連接?sequencer | start_item(item); | 讓?item ?與?iagt.sqr (輸入?agent ?的?sequencer ?)建立調度連接 |
3. 隨機化?item | item.randomize(); | 隨機生成?addr 、data 、data_en ?等字段,模擬真實激勵 |
4. 發送給?driver | finish_item(item); | 通知?sequencer ?完成調度,將?item ?轉發給?driver ?驅動 DUT |
代碼映射(dadd_sequence.sv
?中?START_ITEM
?分支 ):
`ifdef START_ITEM
task body();if(starting_phase != null) starting_phase.raise_objection(this); // 阻止仿真提前結束repeat(20) begin // 發送20個事務item = new("item"); // 1. 實例化start_item(item); // 2. 連接sequenceritem.randomize(); // 3. 隨機化finish_item(item); // 4. 發送給driverendif(starting_phase != null) starting_phase.drop_objection(this); // 允許仿真結束
endtask : body
`endif
實驗驗證(執行?make send_item_start_item
?):
- 日志顯示 20 次?
item
?發送,每次含隨機化的?addr
、data。
- 波形中?
dadd_if
?接口的信號(如?addr
、data
?)與日志匹配,證明?driver
?正確驅動 DUT。
UVM_INFO dadd_sequence.sv(12) @ 100ns: uvm_test_top.env.iagt.sqr [SEQ] Sent item: addr=0x12, data=0x34, data_en=1
(2)方法 2:uvm_create
?+?uvm_send
(宏封裝基礎流程)
執行規則:用?uvm_create
?替代?new
?實例化?item
,用?uvm_send
?替代?start_item/finish_item
?發送,本質是?封裝了基礎方法的語法糖,但更靈活(可指定?sequencer
?):
步驟 | 代碼邏輯 | 作用說明 |
---|---|---|
1. 實例化?item | uvm_create(item); ?或?uvm_create_on(item, sqr); | 不僅創建?item ,還可指定發送到哪個?sequencer (如?iagt.sqr ?或?oagt.sqr ?) |
2. 隨機化?item | item.randomize(); | 同方法 1 |
3. 發送給?driver | uvm_send(item); | 封裝?start_item/finish_item ,簡化發送流程 |
代碼映射(dadd_sequence.sv
?中?UVM_CREATE
?分支 ):
`elsif UVM_CREATE
task body();if(starting_phase != null) starting_phase.raise_objection(this);repeat(20) begin`uvm_create(item); // 1. 實例化(可指定sequencer)item.randomize(); // 2. 隨機化`uvm_send(item); // 3. 發送(封裝start_item/finish_item)endif(starting_phase != null) starting_phase.drop_objection(this);
endtask : body
`endif
實驗驗證(執行?make send_item_uvm_create
?):
- 日志與方法 1 類似,但代碼更簡潔,
uvm_create
/uvm_send
?隱式完成連接和發送。 - 若修改為?
uvm_create_on(item, env.oagt.sqr);
,item
?會發送到?oagt
?的?sequencer
,波形中?oagt
?接口信號變化,驗證跨?agent
?發送。
(3)方法 3:uvm_do
?系列宏(高級封裝,最常用)
執行規則:
uvm_do
?是?“一站式” 宏,直接封裝 “實例化→隨機化→發送” 全流程,甚至可帶約束(uvm_do_with
?)或優先級(uvm_do_pri
?),是日常驗證最常用的方法:
宏類型 | 語法示例 | 作用說明 |
---|---|---|
基礎發送 | uvm_do(item); | 自動完成實例化、隨機化、發送 |
帶約束發送 | uvm_do_with(item, {item.data_en==1;}); | 隨機化時固定?data_en=1 ,其他字段隨機 |
指定?sequencer ?發送 | uvm_do_on(item, env.iagt.sqr); | 強制?item ?發送到?iagt ?的?sequencer |
代碼映射(dadd_sequence.sv
?中?UVM_DO
?分支 ):
`else//UVM_DO
task body();if(starting_phase != null) starting_phase.raise_objection(this);repeat(20) begin`uvm_do(item) // 一站式完成實例化、隨機化、發送endif(starting_phase != null) starting_phase.drop_objection(this);
endtask : body
`endif
實驗驗證(執行?make send_item_uvm_do
?):
- 日志與前兩種方法一致,但代碼行數最少,
uvm_do
?隱式完成所有步驟。 - 若修改為?
uvm_do_with(item, {item.addr==0x5a5a;});
,日志中?addr
?固定為?0x5a5a
,驗證約束生效。
2.3 關鍵細節與對比
(1)方法選擇建議
- 調試階段:用?
start_item/finish_item
,逐行控制流程,方便定位問題。 - 跨?
agent
?發送:用?uvm_create_on
?或?uvm_do_on
,明確指定?sequencer
,避免發送到錯誤?agent
。 - 日常驗證:優先用?
uvm_do
?系列宏,代碼簡潔,減少樣板代碼。
(2)易錯點
starting_phase
?為空:若?sequence
?未關聯?phase
(如未在?test
?中設置?starting_phase
?),raise_objection
?會報錯,需確保?sequence.starting_phase = phase;
。uvm_create
?未指定?sequencer
:若未用?uvm_create_on
?且?p_sequencer
?未正確連接,item
?可能發送到?null
?sequencer
,觸發?UVM_ERROR
。
2.4 實驗價值與總結
通過?Makefile
?腳本切換宏定義(START_ITEM
/UVM_CREATE
/UVM_DO
?),可直觀對比三種方法的執行流程:
- 證明三類方法本質是同一流程的不同封裝,
uvm_do
?是最簡潔的高階用法。 - 驗證平臺可靈活切換發送方式,適配不同測試場景(如調試、跨?
agent
?、帶約束發送 )。
3. sequence 的發送
3.1 核心規則:子?sequence
?發送的三類方法
????????當?sequence
?需發送子?sequence
(如?dadd_fixen_sequence
?調用?dadd_rand_sequence
?)時,本質是?“父?sequence
?調度子?sequence
?的生命周期”,三類方法的核心差異在于 “調度的封裝層級”:
方法分類 | 核心語法 | 封裝層級 | 適用場景 |
---|---|---|---|
start ?函數 | seq.start(p_sequencer); | 最底層,手動控制實例化、隨機化、啟動 | 需精準控制子?sequence ?流程 |
uvm_create/uvm_send | uvm_create(seq); ?+?uvm_send(seq); | 封裝?start ?函數,簡化調用 | 需顯式控制隨機化步驟 |
uvm_do ?宏 | uvm_do_with(seq, {約束;}) | 封裝 “實例化 + 隨機化 + 啟動” 全流程 | 日常驗證(簡潔高效) |
3.2 逐類解析:結合?dadd
?代碼與腳本
以下基于?dadd_fixen_sequence.sv
?和?dadd_rand_sequence.sv
?代碼,說明每種方法的細節:
(1)方法 1:start
?函數(手動調度子?sequence
)
執行規則:
手動完成子?sequence
?的?“實例化→隨機化約束→啟動”?全流程,需顯式調用?start
?函數關聯?p_sequencer
(父?sequence
?所在的?sequencer
?)。
步驟 | 代碼邏輯 | 作用說明 |
---|---|---|
1. 實例化子?seq | seq = dadd_rand_sequence::type_id::create("seq"); | 創建子?sequence ?對象(dadd_rand_sequence ?) |
2. 隨機化約束 | seq.randomize() with {data_en_rand == 1;}; | 固定子?sequence ?的?data_en_rand ?為 1,間接約束?dadd_item.data_en=1 |
3. 啟動子?seq | seq.start(p_sequencer); | 讓子?sequence ?掛載到父?sequence ?的?sequencer (iagt.sqr ?)上 |
代碼映射(dadd_fixen_sequence.sv
?中?SEND_SEQ
?分支 ):
`ifdef SEND_SEQ
`ifdef START
task body();if(starting_phase != null) starting_phase.raise_objection(this); // 阻止仿真提前結束// 1. 實例化子 sequenceseq = dadd_rand_sequence::type_id::create("seq"); // 2. 約束子 sequence 的 data_en_rand 為 1seq.randomize() with {data_en_rand == 1;}; // 3. 啟動子 sequence,掛載到 p_sequencer(iagt.sqr)seq.start(p_sequencer); if(starting_phase != null) starting_phase.drop_objection(this); // 允許仿真結束
endtask : body
`endif
`endif
實驗驗證(執行?make send_seq_start
?):
- 日志顯示子?
sequence
?被啟動,且?data_en
?固定為 1:plaintext
UVM_INFO dadd_rand_sequence.sv(20) @ 100ns: uvm_test_top.env.iagt.sqr [SEQ] Sent item: data_en=1, addr=0x12, data=0x34
- 波形中?
dadd_if.data_en
?恒為 1,證明約束生效。
(2)方法 2:uvm_create
?+?uvm_send
(封裝?start
?函數)
執行規則:
用?uvm_create
?替代手動?new
?實例化子?sequence
,用?uvm_send
?替代?start
?函數,封裝部分流程,但仍需手動隨機化。
步驟 | 代碼邏輯 | 作用說明 |
---|---|---|
1. 實例化子?seq | uvm_create(seq); | 隱式創建子?sequence ,并關聯到?p_sequencer |
2. 隨機化約束 | seq.randomize() with {data_en_rand == 1;}; | 同方法 1 |
3. 啟動子?seq | uvm_send(seq); | 封裝?start ?函數,簡化發送流程 |
代碼映射(dadd_fixen_sequence.sv
?中?UVM_CREATE
?分支 ):
`elsif UVM_CREATE
task body();if(starting_phase != null) starting_phase.raise_objection(this);// 1. 實例化子 sequence(隱式關聯 p_sequencer)`uvm_create(seq); // 2. 約束子 sequenceseq.randomize() with {data_en_rand == 1;}; // 3. 發送子 sequence(封裝 start 函數)`uvm_send(seq); if(starting_phase != null) starting_phase.drop_objection(this);
endtask : body
實驗驗證(執行?make send_seq_uvm_create
?):
- 日志與方法 1 類似,但代碼更簡潔,
uvm_create/uvm_send
?隱式完成部分流程。 - 若刪除?
seq.randomize()
,data_en_rand
?會隨機化,驗證?uvm_create
?不自動隨機化,需手動調用。
(3)方法 3:uvm_do
?系列宏(一站式封裝)
執行規則:
uvm_do_with
?宏?一站式封裝“實例化 + 隨機化 + 啟動” 全流程,甚至可在宏內直接寫約束,無需手動調用?randomize
。
步驟 | 代碼邏輯 | 作用說明 |
---|---|---|
1. 實例化 + 隨機化 + 啟動 | uvm_do_with(seq, {data_en_rand == 1;}); | 隱式完成 “創建→隨機化(帶約束)→啟動”,最簡潔 |
代碼映射(dadd_fixen_sequence.sv
?中?UVM_DO
?分支 ):
`else//UVM_DO
task body();if(starting_phase != null) starting_phase.raise_objection(this);// 一站式完成:實例化+隨機化(約束 data_en_rand=1)+啟動`uvm_do_with(seq, {data_en_rand == 1;}); if(starting_phase != null) starting_phase.drop_objection(this);
endtask : body
實驗驗證(執行?make send_seq_uvm_do
?):
- 日志與前兩種方法一致,但代碼行數最少,
uvm_do_with
?隱式完成所有步驟。 - 若修改約束為?
{data_en_rand == 0;}
,波形中?data_en
?恒為 0,驗證宏內約束生效。
3.3 關鍵細節與對比
(1)方法選擇建議
- 調試子?
sequence
:用?start
?函數,逐行控制實例化、隨機化、啟動,方便定位問題(如約束不生效時,檢查?randomize
?調用 )。 - 需顯式隨機化:用?
uvm_create/uvm_send
,手動控制隨機化時機(如先隨機化部分字段,再覆蓋其他約束 )。 - 日常嵌套發送:優先用?
uvm_do_with
,代碼最簡潔,減少樣板代碼,適合高頻使用。
(2)易錯點
p_sequencer
?未關聯:若子?sequence
?未通過?uvm_declare_p_sequencer
?關聯父?sequencer
,p_sequencer
?會空指針報錯,需確保:systemverilog
`uvm_declare_p_sequencer(dadd_sequencer) // 在子 sequence 中聲明
- 約束未生效:若?
uvm_do_with
?中約束語法錯誤(如?data_en_rand = 1;
?少寫?==
?),約束會被忽略,需檢查約束表達式。
3.4 實驗價值與總結
通過?Makefile
?腳本切換方法(send_seq_start
/send_seq_uvm_create
/send_seq_uvm_do
?),可直觀對比三類方法的執行流程:
- 證明三類方法本質是同一流程的不同封裝,
uvm_do
?是最簡潔的高階用法。 - 驗證平臺可靈活切換子?
sequence
?的調度方式,適配不同測試場景(如調試、高效開發、復雜約束 )。
掌握這三類方法,可高效實現 “父?sequence
?調度子?sequence
” 的嵌套邏輯,是構建復雜驗證場景(如 “先復位子?sequence
,再隨機讀寫子?sequence
” )的基礎。