FPGA工程师面试资料【22】—— 握手机制的实现
FPGA工程师面试资料【22】—— 握手机制的实现握手机制核心握手机制vld 与 rdy逐级反压 (Backpressure) 的实现设计要点与常见实现总结不带存储器的逐级反压数据累加输出握手机制核心握手机制vld 与 rdy这两个信号共同构成了一个双向握手协议用于控制数据在模块间的可靠传输。vld (输出信号)由数据发送方上游模块产生。当vld 1时表示当前时钟沿上输出端口data上的数据是有效的、可用的;rdy (输入信号)由数据接收方下游模块产生。当rdy 1时表示接收方当前时钟周期内可以接收新数据。rdy 0表示接收方内部缓冲区已满或正忙无法接收。数据传输成功的条件在一个时钟上升沿只有当vld rdy同时为高时数据才被成功从发送方传递到接收方。我们通常称此时发生了一次握手成功或数据传递。时序行为示例发送方有数据要发 (vld1)但接收方没准备好 (rdy0) - 数据保持不动等待下一个周期。发送方没数据 (vld0)接收方准备好了 (rdy1) - 无数据传输。双方都就绪 (vld1, rdy1) - 数据成功传输发送方可以在下一个周期更新数据或保持。这种机制确保了数据不会因为接收方未准备好而丢失发送方也不会在接收方忙碌时盲目发送无效数据。逐级反压 (Backpressure) 的实现在实际的流水线或处理链中模块往往是串联的。一个模块同时是上游模块的“接收方”又是下游模块的“发送方”。rdy 信号可以像波浪一样从数据流的末端最终消费者如DDR控制器、输出接口逐级向前传递直到最源头数据生产者如传感器接口、DMA从而控制整个数据流的启停。这就是“逐级反压”。关键点一个模块的输出 rdy告诉上游“我是否可以接收”通常取决于两件事模块自身内部的状态如 FIFO 是否将满、处理单元是否忙碌。模块的下游是否就绪即来自下游的输入 rdy 信号。举例一个三级流水线处理链假设有三个模块串联A - B - C。数据从 A 流到 C。C 是最终端。它的rdy_out可能由外部决定如输出FIFO满信号;B 的rdy_out输出给 A的计算逻辑通常是B_rdy_out (B 内部有空间) (C_rdy_out 1)。即B 只有在自己能存数据并且下游 C 也能接收时才会告诉 A “我准备好了”;A 的rdy_out可能连接更上游计算逻辑A_rdy_out (A 内部有空间) (B_rdy_out 1)。反压传播过程如果 C 因为输出堵塞而拉低rdy_out (C_rdy_out0)。则 B 发现下游不就绪即使自己内部有空间也会立即拉低自己的B_rdy_out为 0。A 看到B_rdy_out0随即也拉低自己的A_rdy_out。瞬间整个数据流从 C 到 A 的源头都暂停了。直到 C 的堵塞解除rdy信号才会从 C 到 B 再到 A 逐级恢复为高数据流重新开始。设计要点与常见实现组合逻辑路径rdy信号通常是组合逻辑产生的如rdy !fifo_almost_full而vld通常是寄存器输出。需要注意rdy信号组合逻辑路径过长可能成为时序瓶颈因为它需要在一个周期内从末端传播到源头。FIFO 是解耦反压的利器在两级处理模块之间插入一个 FIFO如 AXI-Stream 中的 TREADY 信号通常与 FIFO 的空满状态相关。FIFO 提供了一定的数据缓冲能力允许生产者和消费者短暂地独立工作缓解了反压的严格性。只有当 FIFO 快满时才会向上游发出反压信号 (rdy0)。Skid Buffer一种非常常用且高效的实现方式用于在rdy突然变低时临时缓存一个来不及收回的数据。它本质上是一个深度为1或2的微型 FIFO用少量寄存器实现了握手机制且时序很好。性能与面积权衡反压机制保证了正确性但可能降低峰值吞吐率。通过增加缓冲FIFO深度可以提高吞吐但会增加资源消耗。设计时需要平衡。标准接口工业标准如 AXI-Stream 的核心就是基于 TVALID 和 TREADY 的握手机制。理解了这个就理解了 AXI-Stream 的精髓。总结vld/rdy握手机制是 FPGA 数据流设计的“交通规则”它通过简单的信号交互确保了数据在异步时钟域或不同处理速度模块间可靠传输。逐级反压是该机制的自然结果它使得系统能够根据最慢环节的动态状态进行自我调节是构建可伸缩、高鲁棒性数字系统的基石。在设计时结合 FIFO 或 Skid Buffer 来优化反压路径是达到高性能的关键。不带存储器的逐级反压module hand_shake#( parameter DW 8 )( input clk, input rst_n, output data_i_ready, input data_i_valid, input [DW-1:0] data_i, input data_o_ready, output data_o_valid, output [DW-1:0] data_o ); // ------------------- // signals def // ------------------- reg valid_l1; wire ready_l1; reg [DW-1:0] data_l1; reg valid_l2; wire ready_l2; reg [DW-1:0] data_l2; reg valid_l3; wire ready_l3; reg [DW-1:0] data_l3; // ------------------- // pipe logic // ------------------- // l0 assign data_i_ready ~valid_l1 || ready_l1; // l1 always (posedge clk or negedge rst_n) begin if(!rst_n) valid_l1 0; else if(data_i_ready) valid_l1 data_i_valid; end assign ready_l1 ~valid_l2 || ready_l2; always (posedge clk) begin if(data_i_ready data_i_valid) data_l1 data_i 1; end // l2 always (posedge clk or negedge rst_n) begin if(!rst_n) valid_l2 0; else if(ready_l1) valid_l2 valid_l1; end assign ready_l2 ~valid_l3 || ready_l3; always (posedge clk) begin if(ready_l1 valid_l1) data_l2 data_l1 * 2; end // l3 always (posedge clk or negedge rst_n) begin if(!rst_n) valid_l3 0; else if(ready_l2) valid_l3 valid_l2; end assign ready_l3 data_o_ready; always (posedge clk) begin if(ready_l2 valid_l2) data_l3 data_l2 3; end // ------------------- // output // ------------------- assign data_o_valid valid_l3; assign data_o data_l3; endmodule数据累加输出实现串行输入数据累加输出输入端输入8bit数据每当模块接收到4个输入数据后输出端输出4个接收到数据的累加结果。输入端和输出端与上下游的交互采用valid-ready双向握手机制。要求上下游均能满速传输时数据传输无气泡不能由于本模块的设计原因产生额外的性能损失。其中valid_a用来指示数据输入data_in的有效性valid_b用来指示数据输出data_out的有效性ready_a用来指示本模块是否准备好接收上游数据ready_b表示下游是否准备好接收本模块的输出数据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 ); reg [1:0] data_cnt; assign ready_a (!valid_b) | ready_b;//如果下游ready_b拉高表示下游可以接收模块输出数据那么此时ready_a应拉高即本模块可以接收上游数据如果没有接收够4个数据即valid_b未拉高那么表示本模块仍可以接收上游数据此时ready_a应拉高。 always (posedge clk or negedge rst_n ) begin if(!rst_n) data_cnt d0; else if(valid_a ready_a) data_cnt (data_cnt 2d3) ? d0 : (data_cnt 1d1); end // 当和上游正常通讯时即valid_a和ready_a均为高数据正常接收但注意计数了4个就得加起来输出一次所以data_cnt 2d3时拉高valid_b而等待下游接收即当ready_b也拉高表示接收完成则拉低valid_b保证只有在四个数之和的时候才拉高。 always (posedge clk or negedge rst_n ) begin if(!rst_n) valid_b d0; else if(data_cnt 2d3 valid_a ready_a) valid_b 1d1; else if(valid_b ready_b) valid_b 1d0; end // 同理当和上游正常通讯时即valid_a和ready_a均为高数据正常接收数据累加 always (posedge clk or negedge rst_n ) begin if(!rst_n) data_out d0; else if(valid_a ready_a (data_cnt 2d0)) data_out data_in; else if(valid_a ready_a) data_out data_out data_in; end endmodule ~ 本节完结 ~ 关注以下公众号了解更多