1.?策略模式
策略模式是一種行為設計模式, 它能讓你定義一系列算法, 并將每種算法分別放入獨立的類中, 以使算法的對象能夠相互替換。
在RTL設計中可能包含了復雜的多個訪問仲裁邏輯,使用了多種算法來確定訪問內(nèi)存優(yōu)先級順序,包括規(guī)定優(yōu)先級、輪詢仲裁等等。仲裁器的輸入是多個請求者信號,以及選擇要使用的仲裁算法的配置。根據(jù)選擇的類型和請求者信號的值,仲裁器確定具有最高優(yōu)先級的請求源,并授予它訪問內(nèi)存的權(quán)利。如下圖所示,仲裁類型可以動態(tài)配置,這就是為什么該特性適合使用策略設計模式進行建模。
在該模式中,可以在testcase運行中從提供的一系列算法中選擇要應用的特定算法。此外,還可以直接為仲裁添加新算法,而無需修改之前代碼。值得注意的是,之前講到的裝飾器設計模式也可用于動態(tài)更改行為,關(guān)鍵的區(qū)別在于,裝飾器模式在原功能基礎(chǔ)上添加額外的功能,而策略者模式直接更改原先功能。總得來說,策略模式可以讓你改變對象的內(nèi)部結(jié)構(gòu),裝飾器模式允許你更改對象的皮膚。
策略設計模式主要包括以下幾個組件:
策略(Strategy):定義了所有具體策略(Concrete Strategies)的通用接口,它聲明了一個上下文(Context)用于執(zhí)行策略的方法。在這個例子中,策略定義了仲裁的接口函數(shù)arb_winner()。
具體策略(Concrete Strategies):實現(xiàn)了上下文所用算法的各種不同變體。在這個例子中,對于每個必要的算法,定義了一個具體策略類,提供特定算法的實現(xiàn)。將每個算法包裝到一個單獨的類中可以提高代碼的可讀性和可擴展性。
上下文(Context):維護指向具體策略的引用,且僅通過策略接口與該對象進行交流。比如UVM scoreboard組件依賴于Strategy,檢查RTL內(nèi)的仲裁邏輯是否正確實現(xiàn),那么UVM scoreboard可以被認為是Context。
客戶端(Client):會創(chuàng)建一個特定策略對象并將其傳遞給上下文。上下文則會提供一個設置函數(shù)以便客戶端在運行時替換相關(guān)聯(lián)的策略。當上下文需要運行算法時,它會在其已連接的策略對象上調(diào)用執(zhí)行方法。上下文并不清楚其所涉及的策略類型與算法的執(zhí)行方式。本例的客戶端是example_application。
下圖為策略設計模式在仲裁中應用的UML類圖。
策略模式讓你能將不同行為抽取到一個獨立類層次結(jié)構(gòu)中, 并將原始類組合成同一個, 從而減少重復代碼。而且讓你能將各種算法的代碼、 內(nèi)部數(shù)據(jù)和依賴關(guān)系與其他代碼隔離開來。不同客戶端可通過一個簡單接口執(zhí)行算法, 并能在運行時進行切換。
2.?參考代碼
仲裁處理的策略設計模式參考代碼如下:
virtual class strategy;
pure virtual function int arb_winner(ref bit req_arr[3]);
endclass : strategy
class strategy_low_priority extends strategy;
virtual function int arb_winner(ref bit req_arr[3]);
for (int i=0; i<3; i++) begin
if (req_arr[i] == 1) begin
return i;
end
end
return -1;
endfunction : arb_winner
endclass : strategy_low_priority
class strategy_high_priority extends strategy;
virtual function int arb_winner(ref bit req_arr[3]);
for (int i=2; i>=0; i++) begin
if (req_arr[i] == 1) begin
return i;
end
end
return -1;
endfunction : arb_winner
endclass : strategy_high_priority
class my_context;
local strategy m_strategy;
function void set_strategy(strategy _m);
m_strategy = _m;
endfunction : set_strategy
function int execute_strategy(ref bit req_arr[3]);
return m_strategy.arb_winner(req_arr);
endfunction : execute_strategy
endclass : my_context
模擬測試代碼如下:
class example_application;
rand bit low_priority;
rand bit high_priority;
constraint p_cons { low_priority + high_priority >= 1; }
function void main();
int result;
bit req_arrary[3] = '{1'b0, 1'b1, 1'b1};
strategy stg;
my_context m_ctx = new();
`uvm_info("strategy", $psprintf("low_priority:%b, high_priority:%b", low_priority, high_priority), UVM_LOW)
$display("The input req0:%b, req1:%b, req2:%b", req_arrary[0], req_arrary[1], req_arrary[2]);
if ( low_priority ) begin
stg = strategy_low_priority::new();
m_ctx.set_strategy(stg);
result = m_ctx.execute_strategy(req_arrary);
$display("For low priority, the result is: %0d", result);
end
if ( high_priority ) begin
stg = strategy_high_priority::new();
m_ctx.set_strategy(stg);
result = m_ctx.execute_strategy(req_arrary);
$display("For high priority, the result is: %0d", result);
end
endfunction : main
endclass : example_application
輸出仿真日志如下:
| # [strategy] low_priority:1, high_priority:1
| # The input req0:0, req1:1, req2:1
| # For low priority, the result is: 1
| # For high priority, the result is: 2
從仿真結(jié)果可知,low_priority為1,high_priority為1,因此example_application類選取了strategy_low_priority類和strategy_high_priority類兩個算法。
在strategy_low_priority類中,輸入信號中,req0=0,req1=1,req2=1,req1輸入口被選中,因此輸出的結(jié)果是1。
在strategy_high_priority類中,輸入信號中,req0=0,req1=1,req2=1,req2輸入口被選中,因此輸出的結(jié)果是2。
微信號|chip_yes,微信公眾號|專芯致志er