題目描述
當A、B兩組的信号請求通路某個子產品時,為了保證正确的通路,需要對這些信号進行仲裁。請用Verilog實作一個仲裁器,對兩組請求信号進行仲後,要求:
協定如圖所示,請求方發送req(request)信号1表示有請求給仲裁器,仲裁器響應grant信号為1表示請求成功:
通過參數定義在沖突情形下,響應A/B的比例
(舉例,一段時間内,有若幹次A請求和若幹次B請求,其中A&B發生沖突的有N次,這N次中先響應A 3次,後響應B 1次,循環反複。舉例中的3和1可配置。);
添加必要的注釋,增加代碼可讀性。
解題思路
根據題目描述,很容易想到子產品arbiter的端口應該如下:
module arbiter (
input wire clk, // 時鐘信号
input wire rst, // 複位信号
input wire reqA, // A組請求信号
input wire reqB, // B組請求信号
output reg grantA, // A組響應信号
output reg grantB // B組響應信号
);
時鐘、複位信号。兩個請求信号和兩個相應響應信号。隻有在沖突時,AB沖突響應比為3:1。其他情況下,正常響應。是以可以寫一個計數器,僅在沖突時加1。計數器=0,1,2時響應A,計數器=3時響應B,于此同時将計數器置0。
對于仲裁部分,可以将總線的請求信号reqA和reqB拼接成一個2bit信号,這樣使用case語句就能避免多級if-else嵌套導緻的長組合邏輯鍊。
在case語句中,把{reqA, reqB}的所有可能2'b00,2'b01,2'b10,2'b11。全都規劃到就行,當{reqA, reqB}==2'b11時,判斷沖突計數器範圍,落在[0,A_ratio-1],則沖突時A獲得總線;若counter落在[A_ratio,A_ratio+B_ratio-2]時,沖突時B獲得總線。這樣子產品滿足題目要求可自定義沖突配置設定比例。
代碼
module arbiter #(
parameter [7:0] A_ratio = 3 , // A grant ratio
parameter [7:0] B_ratio = 1 // B grant ratio
)(
input wire clk , // 時鐘信号
input wire rstn , // 複位信号
input wire reqA , // A組請求信号
input wire reqB , // B組請求信号
output grantA , // A組響應信号
output grantB // B組響應信号
);
// 定義計數器和比例參數
reg [7:0] counter = 0; // belongs to [0, A_ratio + B_ratio - 1]
reg grantA_reg,grantB_reg;
assign grantA = grantA_reg;
assign grantB = grantB_reg;
always @(posedge clk) begin
if (!rstn) begin
grantA_reg <= 0;
grantB_reg <= 0;
end
else begin
case({reqA,reqB})
2'b00:begin
grantA_reg <= 1'b0;
grantB_reg <= 1'b0;
end
2'b01:begin
grantA_reg <= 1'b0;
grantB_reg <= 1'b1;
end
2'b10:begin
grantA_reg <= 1'b1;
grantB_reg <= 1'b0;
end
default:begin
if(counter <= (A_ratio - 1) )begin
grantA_reg <= 1'b1;
grantB_reg <= 1'b0;
end
else begin
grantA_reg <= 1'b0;
grantB_reg <= 1'b1;
end
end
endcase
end
end
always @(posedge clk)begin
if(!rstn)begin
counter <= 8'd0;
end
if( (reqA&&reqB) && (counter <= (A_ratio + B_ratio - 2)) )begin
counter <= counter + 1'b1;
end
else
counter <= 8'd0;
end
endmodule
tb
module arbiter_tb;
// 定義時鐘和複位信号
reg clk;
reg rstn;
// 定義A組和B組請求信号
reg reqA;
reg reqB;
// 定義A組和B組響應信号
wire grantA;
wire grantB;
// 執行個體化被測試的子產品
arbiter dut (
.clk(clk),
.rstn(rstn),
.reqA(reqA),
.reqB(reqB),
.grantA(grantA),
.grantB(grantB)
);
// 時鐘信号發生器
always #5 clk = ~clk;
// 測試用例1:A組優先
initial begin
// 初始化信号
rstn = 0;
clk = 0;
reqA = 0;
reqB = 0;
// 複位
#15 rstn = 1;
#1
// 發送A組請求
#10 reqA = 1;
// 發送B組請求
#10 reqA = 0;reqB = 1;
// 發送A and B組請求 eight times
repeat(8) begin
#10 reqA = 1;reqB = 1;
end
repeat(2) begin
#10 reqA = 1;reqB = 0;
end
repeat(2) begin
#10 reqA = 0;reqB = 1;
end
#10 reqA = 0;reqB = 0;
// 停止測試
#100 $finish;
end
initial begin
$fsdbDumpfile("arbiter.fsdb");
$fsdbDumpvars(0);
end
endmodule
波形圖
在tb裡,先分别讓A、B各請求總線一次,然後讓他們出現請求沖突8次,最後再讓A、B分别請求總線兩次,從圖中可以看到,在A、B請求沖突的時候,A_grant、B_grant拿到總線的比例是3:1,我們在module定義開頭給了兩個parameter,定義了A_grant、B_grant拿到總線的比例A_ratio和B_ratio,如果要修改子產品代碼,修改module傳入的parameter的值即可。
更多手撕代碼題可以前往 數字IC手撕代碼--題庫