实现串行输入数据累加输出,输入端输入8bit数据,每当模块接收到4个输入数据后,输出端输出4个接收到数据的累加结果。输入端和输出端与上下游的交互采用valid-ready双向握手机制。要求上下游均能满速传输时,数据传输无气泡,不能由于本模块的设计原因产生额外的性能损失。
电路的接口如下图所示。valid_a用来指示数据输入data_in的有效性,valid_b用来指示数据输出data_out的有效性;ready_a用来指示本模块是否准备好接收上游数据,ready_b表示下游是否准备好接收本模块的输出数据;clk是时钟信号;rst_n是异步复位信号。
波形为:
顶层:
module valid_ready(
input clk ,
input rst_n ,
input [7:0] data_in ,
input valid_a ,
input ready_b ,
output ready_a ,
output reg valid_b ,
output reg [9:0] data_out
);
endmodule
答案解析
我最后提交的RTL呢,data_out是reg输出的,valid_b不是reg输出,后面也提交了一版valid_b reg输出的代码。
既然在累加四次的下一拍valid_b就必须要输出有效,那么可见这个题的核心就在于记录累加次数的计数器,所以我们就先做这个计数器好了:
wire hand_a = (valid_a && ready_a);
wire hand_b = (valid_b && ready_b);
//add num cnt
wire cnt_en;
wire [3 -1:0]cnt_d;
wire [3 -1:0]cnt_q;
assign cnt_en = hand_a || hand_b;
assign cnt_d = (hand_a && hand_b) ? 3'd1 :
hand_b ? 3'd0 :
cnt_q + 3'd1;
dffre #(.WIDTH(3)) u_cnt_dffre(
.clk(clk),
.rst_n(rst_n),
.d(cnt_d),
.en(cnt_en),
.q(cnt_q)
);
这个cnt的寄存器位3bit,更新的条件必定是输入握手成功或输出握手成功,逻辑也很清晰:1.当只有输入握手时,cnt + 1;2.当只有输出握手时,cnt归零;3.当同一拍输入握手&&输出握手时,cnt置1,显然,在这种条件下是没有性能瓶颈的。
ok,cnt的条件已经做完,其他的就变得简单了。
累加寄存器的逻辑很类似,就不赘述了:
wire value_en;
wire [10 -1:0]value_d;
wire [10 -1:0]value_q;
assign value_en = cnt_en;
assign value_d = (hand_a && hand_b) ? {2'b0, data_in} :
hand_b ? 10'd0 :
value_q + {2'b0, data_in};
dffre #(.WIDTH(10)) u_value_dffre(
.clk(clk),
.rst_n(rst_n),
.d(value_d),
.en(value_en),
.q(value_q)
);
进一步的,通过cnt_q的值来推到握手信号,对于valid_b,如果是逻辑输出的话可以如此:
assign valid_b = (cnt_q == 3'd4);
如果要将valid_b作为寄存器输出可以这样:
wire valid_b_en = cnt_en;
wire valid_b_d = (cnt_d == 3'd4);
dffre #(.WIDTH(10)) u_valid_b_dffre(
.clk(clk),
.rst_n(rst_n),
.d(valid_b_d),
.en(valid_b_en),
.q(valid_b)
);
valid_b的输出时比较容易做的,对于ready_a则容易出错,误做为:
assign ready_a = (cnt_q != 3'd4);
因为不能有空泡,所以输入的ready要看输出的握手,这样虽然性能更好,但是时序更差:
assign ready_a = (cnt_q != 3'd4) || hand_b;
完整代码
`timescale 1ns/1ns
module dffre#(
parameter WIDTH = 1
)(
input clk,
input rst_n,
input [WIDTH -1:0] d,
input en,
output [WIDTH -1:0] q
);
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 valid_ready(
input clk ,
input rst_n ,
input [7:0] data_in ,
input valid_a ,
input ready_b ,
output ready_a ,
output valid_b ,
output [9:0] data_out
);
parameter MAX = 4;
wire hand_a = (valid_a && ready_a);
wire hand_b = (valid_b && ready_b);
//add num cnt
wire cnt_en;
wire [3 -1:0]cnt_d;
wire [3 -1:0]cnt_q;
assign cnt_en = hand_a || hand_b;
assign cnt_d = (hand_a && hand_b) ? 3'd1 :
hand_b ? 3'd0 :
cnt_q + 3'd1;
dffre #(.WIDTH(3)) u_cnt_dffre(
.clk(clk),
.rst_n(rst_n),
.d(cnt_d),
.en(cnt_en),
.q(cnt_q)
);
//add num value
wire value_en;
wire [10 -1:0]value_d;
wire [10 -1:0]value_q;
assign value_en = cnt_en;
assign value_d = (hand_a && hand_b) ? {2'b0, data_in} :
hand_b ? 10'd0 :
value_q + {2'b0, data_in};
dffre #(.WIDTH(10)) u_value_dffre(
.clk(clk),
.rst_n(rst_n),
.d(value_d),
.en(value_en),
.q(value_q)
);
//gen hand
wire valid_b_en = cnt_en;
wire valid_b_d = (cnt_d == 3'd4);
dffre #(.WIDTH(10)) u_valid_b_dffre(
.clk(clk),
.rst_n(rst_n),
.d(valid_b_d),
.en(valid_b_en),
.q(valid_b)
);
//assign valid_b = (cnt_q == 3'd4);
assign ready_a = (cnt_q != 3'd4) || hand_b;
assign data_out = value_q;
endmodule