我是从牛客网上第一次接触valid_ready机制,想总结这两天自己解题的思路。
VL31
描述
实现串行输入数据累加输出,输入端输入8bit数据,每当模块接收到4个输入数据后,输出端输出4个接收到数据的累加结果。输入端和输出端与上下游的交互采用valid-ready双向握手机制。要求上下游均能满速传输时,数据传输无气泡,不能由于本模块的设计原因产生额外的性能损失。
电路的接口如下图所示。valid_a用来指示数据输入data_in的有效性,valid_b用来指示数据输出data_out的有效性;ready_a用来指示本模块是否准备好接收上游数据,ready_b表示下游是否准备好接收本模块的输出数据;clk是时钟信号;rst_n是异步复位信号。
握手协议:
握手指的是两个设备之间通信的一种方式,用来通信的信号就是握手信号。最简单的握手信号是valid和ready,也可以叫request和grant。假设设备1向设备2发送数据,设备1不知道设备2什么时候可以接收数据,设备2也不知道设备1什么时候会发送数据,那么它们之间如果用握手通信可能是这样的顺序:
1、设备1将valid信号置1,告诉设备2,数据准备就绪了,请查收
2、设备2此刻正处于忙碌状态无法接收数据,设备2将ready信号保持为0
3、设备2空闲了,将ready信号置1接收设备1的数据
4、设备1看到设备2的ready为1,它知道设备2已经接收好数据了,将valid置0同时撤销数据,准备下一次发送。
可以看到因为有握手机制,可以确保数据的正确传输,不会丢失。跨时钟域的握手设计就是利用握手控制这种优势,从而避免因为跨时钟域引起的数据传输错误。
解题
此题目中描述的时主机和从机交换数据中间的一个握手bridge
当valid_a&&ready_a = 1时,表示上游数据成功的被bridge接收到
当valid_b&&ready_b = 1时,表示bridge发送的数据被下游设备成功接收到
注意:ready_a和valid_b信号都是根据其他信号来产生的。
wire ready_a,
1、当下游设备表示可以接收数据时(即ready_b = 1),此时bridge也应该可以接收数据,其输出的ready_a拉高
2、当valid_b拉低时,表示bridge还没有准备好发送数据,说明此时bridge正在接收数据,ready_a=1
assign ready_a =(ready_b || !valid_b)?1'b1:1'b0;
reg valid_b
valid_b在时序模块中被赋值,在时序电路图中data_out=10时,valid_b=1
即,当bridge接收到了4个数据后,valid_b拉高,直到数据被下游设备成功接收到,即valid_b&&ready_b = 1后,valid_b立马拉低,data_out立即接收下一轮的第一个数据,可满足数据传输的过程中无气泡
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
valid_b <= 1'b0;
else begin
if(ready_a && valid_a && cnt==2'd3)
valid_b<=1'b1;
else if(valid_b && ready_b)
valid_b<=1'b0;
end
end
reg [9:0]data_out
data_out在时序逻辑块中被赋值,当valid_a&&ready_a = 1时,数据成功被接收到data_out中,当cnt=3时,表示4个数据被成功接收,cnt=0时,表示开始下一轮数据传输
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
data_out <= 'b0;
end
else begin
if(ready_a && valid_a && cnt == 2'd0)
data_out <= data_in;
else if(ready_a && valid_a)
data_out <= data_in + data_out;
else
data_out<=data_out;
end
end