题解 | #数据累加输出#
数据累加输出
https://www.nowcoder.com/practice/956fa4fa03e4441d85262dc1ec46a3bd
`timescale 1ns/1ns 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 ); assign ready_a= !valid_b |ready_b;//根据题目要求+波形ready_b拉高reay_a立刻拉高,无气泡无损耗传输 reg [1:0] cnt; always@(posedge clk or negedge rst_n)begin if(!rst_n) cnt<=0; else if (valid_a && ready_a)begin//仅限允许输入时调整计数器计数 if(cnt==3)//cnt==3指的是已经计满了三次,再来一次就计0(新的一轮的第一个) cnt<=0; else cnt<=cnt+1; end else cnt<=cnt; end always@(posedge clk or negedge rst_n)begin if(!rst_n) valid_b<=0; else if (ready_a && valid_a && cnt==3) //cnt为3还未实际执行什么东西的时候,所以还需要再赋值一次,需要ready_a && valid_a进行判断 valid_b<=1; else if (ready_b && valid_b) valid_b<=0; else valid_b<=valid_b; end always@(posedge clk or negedge rst_n)begin if(!rst_n) data_out<=0; //else if(cnt==0 && valid_a && (ready_b || (ready_b==0 & valid_b==0))) else if(cnt==0 && valid_a && ready_a) //仅限这种比较特殊的情况,也就是尚未向外输出导致data_in不动的时候,有段时间data_out也需要等ready_b data_out<=data_in; else if(valid_a && ready_a ) data_out<=data_out+data_in; else data_out<=data_out; end endmodule
这道题是做到的最痛苦的题了,首先要找清楚输出valid_b, ready_a和data_out
valid_b:验证已经赋值了四个,需要cnt==3的时候立即拉高,因为要求高速传输没有气泡,所以第四次实际的赋值和valid_b拉高是同时进行的
ready_a:ready_a有解答表示目前器件有空闲,可以接收上游信号,实际上这种理解是误导。这里的定义就是需要等待一轮四个数据传输完成以后才可以置1,也就是valid_d拉高而ready_b还没有拉高这段时间,ready_a需要置零才能保证赋值等待。为了刚好这次握手周期结束和下一次握手周期开始重合,需要ready_a在valid_b刚拉高时置零,而reay_b高电平,valid_b高电平(实现对下游输出)同时ready_a需要被拉高,存入下一轮的第一个信号。所以assign ready_a=!valid_b | ready_b;(这个地方是这个电路最难的点,需要根据波形图和题目要求一起去理解)
data_out:题目里说是接收四个再输出,实际上根据波形图就是4个以内直接赋值给data_out做求和,第四个赋值后等待valid_b和rady_b同时拉高给下游传输数据后,再给data_out<=data_in新一轮赋值。看似不难,但是这个地方让我纠结的点是cnt==0时需要考虑每一轮赋值结束和刚初始化后两种情况,我开始想通过ready_b高信号解决,但是考虑到初始化时ready_b为低,不能处理,后来发现自己多虑了,因为ready_a会随着ready_b变化,并且刚初始化后,ready_a作为输入也是持续高电平,所以直接用ready_a && valid_a && cnt==0就可以解决了