1.?策略模式
策略模式是一種行為設(shè)計(jì)模式, 它能讓你定義一系列算法, 并將每種算法分別放入獨(dú)立的類中, 以使算法的對(duì)象能夠相互替換。
在RTL設(shè)計(jì)中可能包含了復(fù)雜的多個(gè)訪問仲裁邏輯,使用了多種算法來確定訪問內(nèi)存優(yōu)先級(jí)順序,包括規(guī)定優(yōu)先級(jí)、輪詢仲裁等等。仲裁器的輸入是多個(gè)請(qǐng)求者信號(hào),以及選擇要使用的仲裁算法的配置。根據(jù)選擇的類型和請(qǐng)求者信號(hào)的值,仲裁器確定具有最高優(yōu)先級(jí)的請(qǐng)求源,并授予它訪問內(nèi)存的權(quán)利。如下圖所示,仲裁類型可以動(dòng)態(tài)配置,這就是為什么該特性適合使用策略設(shè)計(jì)模式進(jìn)行建模。
在該模式中,可以在testcase運(yùn)行中從提供的一系列算法中選擇要應(yīng)用的特定算法。此外,還可以直接為仲裁添加新算法,而無需修改之前代碼。值得注意的是,之前講到的裝飾器設(shè)計(jì)模式也可用于動(dòng)態(tài)更改行為,關(guān)鍵的區(qū)別在于,裝飾器模式在原功能基礎(chǔ)上添加額外的功能,而策略者模式直接更改原先功能??偟脕碚f,策略模式可以讓你改變對(duì)象的內(nèi)部結(jié)構(gòu),裝飾器模式允許你更改對(duì)象的皮膚。
策略設(shè)計(jì)模式主要包括以下幾個(gè)組件:
策略(Strategy):定義了所有具體策略(Concrete Strategies)的通用接口,它聲明了一個(gè)上下文(Context)用于執(zhí)行策略的方法。在這個(gè)例子中,策略定義了仲裁的接口函數(shù)arb_winner()。
具體策略(Concrete Strategies):實(shí)現(xiàn)了上下文所用算法的各種不同變體。在這個(gè)例子中,對(duì)于每個(gè)必要的算法,定義了一個(gè)具體策略類,提供特定算法的實(shí)現(xiàn)。將每個(gè)算法包裝到一個(gè)單獨(dú)的類中可以提高代碼的可讀性和可擴(kuò)展性。
上下文(Context):維護(hù)指向具體策略的引用,且僅通過策略接口與該對(duì)象進(jìn)行交流。比如UVM scoreboard組件依賴于Strategy,檢查RTL內(nèi)的仲裁邏輯是否正確實(shí)現(xiàn),那么UVM scoreboard可以被認(rèn)為是Context。
客戶端(Client):會(huì)創(chuàng)建一個(gè)特定策略對(duì)象并將其傳遞給上下文。上下文則會(huì)提供一個(gè)設(shè)置函數(shù)以便客戶端在運(yùn)行時(shí)替換相關(guān)聯(lián)的策略。當(dāng)上下文需要運(yùn)行算法時(shí),它會(huì)在其已連接的策略對(duì)象上調(diào)用執(zhí)行方法。上下文并不清楚其所涉及的策略類型與算法的執(zhí)行方式。本例的客戶端是example_application。
下圖為策略設(shè)計(jì)模式在仲裁中應(yīng)用的UML類圖。
策略模式讓你能將不同行為抽取到一個(gè)獨(dú)立類層次結(jié)構(gòu)中, 并將原始類組合成同一個(gè), 從而減少重復(fù)代碼。而且讓你能將各種算法的代碼、 內(nèi)部數(shù)據(jù)和依賴關(guān)系與其他代碼隔離開來。不同客戶端可通過一個(gè)簡單接口執(zhí)行算法, 并能在運(yùn)行時(shí)進(jìn)行切換。
2.?參考代碼
仲裁處理的策略設(shè)計(jì)模式參考代碼如下:
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類兩個(gè)算法。
在strategy_low_priority類中,輸入信號(hào)中,req0=0,req1=1,req2=1,req1輸入口被選中,因此輸出的結(jié)果是1。
在strategy_high_priority類中,輸入信號(hào)中,req0=0,req1=1,req2=1,req2輸入口被選中,因此輸出的結(jié)果是2。
微信號(hào)|c(diǎn)hip_yes,微信公眾號(hào)|專芯致志er