打拍是進(jìn)行時(shí)需優(yōu)化最常用和最簡(jiǎn)單的方式之一,不過握手型協(xié)議的打拍和通常的使能型協(xié)議是不同的。使能型只需要把data/enable或者xoff使用寄存器正常打拍即可,而握手型由于自身的特殊性(必須在握手當(dāng)拍做出響應(yīng)),所以單純打拍肯定是不行的。
握手型協(xié)議的時(shí)序優(yōu)化分為三種情況:對(duì)發(fā)送端進(jìn)行優(yōu)化(即valid打拍,或稱forward打拍),對(duì)接受端進(jìn)行優(yōu)化(即ready打拍,或稱backward打拍),對(duì)兩端均優(yōu)化(即valid-ready打拍,或稱forward-backword打拍)。本篇對(duì)forward打拍進(jìn)行說(shuō)明。
fw_pipe的打拍對(duì)象不僅是valid也包括data,道理是顯而易見的,data和valid一樣是發(fā)送端驅(qū)動(dòng)的且需要與valid保持時(shí)序上的一致,不能valid打拍延時(shí)了data沒變,那就差拍了。
對(duì)于valid和data打拍,我們要借助兩個(gè)寄存器實(shí)現(xiàn)。通常來(lái)說(shuō)數(shù)據(jù)如果不參與控制邏輯,是沒有必要進(jìn)行復(fù)位的,因此使用兩種不同的寄存器:
module dffre #(
parameter WIDTH = 1
)(
input clk,
input rst_n,
input [WIDTH -1:0] d,
input en,
output reg[WIDTH -1:0] q
);
always @(posedge clk or negedge rst_n)begin
if(~rst_n) q <= {WIDTH{1'b0}};
else if(en) q <= d;
end
endmodule
module dffe#(
parameter WIDTH = 1
)(
input clk,
input [WIDTH -1:0] d,
input en,
output reg[WIDTH -1:0] q
);
always @(posedge clk)begin
if(en) q <= d;
end
endmodule
寄存器選好了,接下來(lái)確定fw_pipe的接口,data_in側(cè)數(shù)輸入端,data_out為輸出端。:
module fw_pipe #(
parameter WIDTH = 8)
(
input clk,
input rst_n,
input [WIDTH -1:0]data_in,
input data_in_valid,
output data_in_ready,
output[WIDTH -1:0]data_out,
output data_out_valid,
input data_out_ready
);
endmodule
接下來(lái)就是借助dffre對(duì)data_in_valid打拍的邏輯了。邏輯其實(shí)比較簡(jiǎn)單,u_in_valid_dffre就是專門用來(lái)緩存data_in_valid,那么當(dāng)上一個(gè)data_in_valid還沒有被下游握手時(shí)顯然當(dāng)前的data_in_valid是不能寫入u_in_valid_dffre的。
那么問題就變成了,如何預(yù)期下一拍的u_in_valid_dffre是空的,當(dāng)前拍的data_in_valid可以使能寄存器并在下一拍寫入到u_in_valid_dffre中呢??jī)煞N情況:
- 當(dāng)拍的u_in_valid_dffre就是空的;
- u_in_valid_dffre不空,當(dāng)當(dāng)拍的data_out_ready為1,u_in_valid_dffre在下一拍必然為空;
于是精簡(jiǎn)代碼之后的代碼,如下所示:
wire in_valid_en = data_in_ready;
wire in_valid_d = data_in_valid;
wire in_valid_q;
dffre #(.WIDTH(1))
u_in_valid_dffre(
.clk(clk),
.rst_n(rst_n),
.d(in_valid_d),
.en(in_valid_en),
.q(in_valid_q)
);
assign data_in_ready = data_out_ready || (~in_valid_q);
而后是通過無(wú)復(fù)位的dffe對(duì)data_in進(jìn)行打拍,u_in_data_dffe的更新邏輯完全跟隨data_in_valid一致就可以,data_in_valid可以寫進(jìn)寄存器時(shí)data_in也必須跟著寫進(jìn)寄存器,當(dāng)然了為了避免x態(tài)的傳播造成困擾,可以選擇在輸入握手時(shí)寫入寄存器:
wire data_en = data_in_valid && data_in_ready;
wire [WIDTH -1:0]data_d = data_in;
wire [WIDTH -1:0]data_q;
dffe #(.WIDTH(WIDTH))
u_in_data_dffe(
.clk(clk),
.d(data_d),
.en(data_en),
.q(data_q)
);
最后就是輸出邏輯:
assign data_out_valid = in_valid_q;
assign data_out = data_q;
整個(gè)fw_pipe的代碼就完成了:
module fw_pipe #(
parameter WIDTH = 8)
(
input clk,
input rst_n,
input [WIDTH -1:0]data_in,
input data_in_valid,
output data_in_ready,
output[WIDTH -1:0]data_out,
output data_out_valid,
input data_out_ready
);
wire in_valid_en = data_in_ready;
wire in_valid_d = data_in_valid;
wire in_valid_q;
dffre #(.WIDTH(1))
u_in_valid_dffre(
.clk(clk),
.rst_n(rst_n),
.d(in_valid_d),
.en(in_valid_en),
.q(in_valid_q)
);
wire data_en = data_in_valid && data_in_ready;
wire [WIDTH -1:0]data_d = data_in;
wire [WIDTH -1:0]data_q;
dffe #(.WIDTH(WIDTH))
u_in_data_dffe(
.clk(clk),
.d(data_d),
.en(data_en),
.q(data_q)
);
assign data_in_ready = data_out_ready || (~in_valid_q);
assign data_out_valid = in_valid_q;
assign data_out = data_q;
endmodule
借助auto_testbench驗(yàn)證一下代碼的正確性,在出口頻繁反壓的情況下仿真了100000ns:
數(shù)據(jù)比對(duì)全部通過:
-
寄存器
+關(guān)注
關(guān)注
31文章
5294瀏覽量
119816 -
時(shí)序優(yōu)化
+關(guān)注
關(guān)注
0文章
4瀏覽量
1446
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論