异步四相位异步握手模块学习
背景
由于EDA工具的广泛支持,采用同步电路设计的方法是目前SoC设计的基本要求,但在一个功能复杂的SoC系统中,难免会有异步信号与同步电路交互的问题。特别是在许多核组成的SoC中,通常采用全局异步局部同步 (GALS) 的设计。
最近项目中需要跨时钟域同步数据。
数据跨时钟同步通常可以采用异步FIFO、异步握手信号来进行。
正好看到cdc_4phase这个模块,今天来分析一下代码和时序。
四相位异步握手协议
常用的握手协议有2种:两相位握手协议、四相位握手协议
摘自《异步电路设计 -- 陈虹》
四相位握手协议也称为归零 (return-to-zero, RTZ)握手协议。
这里的相位指的是通信动作的次数:
- 发送端准备好数据后会将请求信号 (Req)置高
- 接收端接收数据后将应答信号 (Ack)置高
- 发送端将请求信号(Req)置低作为响应
- 接收端通过将应答信号(Ack)置低来做出应答
而两相位握手协议在Req和Ack的任何一对跳变沿组合处均可实现一次数据通信。
两相位握手协议比四相位握手协议效率要高,但会造成电路死锁,其稳定性要远远低于四相位握手协议。
这里如何造成死锁后续补充
框图
代码
module cdc_4phase #(
parameter type T = logic,
parameter bit DECOUPLED = 1'b1,
parameter bit SEND_RESET_MSG = 1'b0,
parameter T RESET_MSG = T'('0)
)(
input logic src_rst_ni,
input logic src_clk_i,
input T src_data_i,
input logic src_valid_i,
output logic src_ready_o,
input logic dst_rst_ni,
input logic dst_clk_i,
output T dst_data_o,
output logic dst_valid_o,
input logic dst_ready_i
);
// Asynchronous handshake signals.
(* dont_touch = "true" *) logic async_req;
(* dont_touch = "true" *) logic async_ack;
(* dont_touch = "true" *) T async_data;
// The sender in the source domain.
cdc_4phase_src #(
.T(T),
.DECOUPLED(DECOUPLED),
.SEND_RESET_MSG(SEND_RESET_MSG),
.RESET_MSG(RESET_MSG)
) i_src (
.rst_ni ( src_rst_ni ),
.clk_i ( src_clk_i ),
.data_i ( src_data_i ),
.valid_i ( src_valid_i ),
.ready_o ( src_ready_o ),
.async_req_o ( async_req ),
.async_ack_i ( async_ack ),
.async_data_o ( async_data )
);
// The receiver in the destination domain.
cdc_4phase_dst #(.T(T), .DECOUPLED(DECOUPLED)) i_dst (
.rst_ni ( dst_rst_ni ),
.clk_i ( dst_clk_i ),
.data_o ( dst_data_o ),
.valid_o ( dst_valid_o ),
.ready_i ( dst_ready_i ),
.async_req_i ( async_req ),
.async_ack_o ( async_ack ),
.async_data_i ( async_data )
);
endmodule
cdc_4phase由cdc_4phase_src和cdc_4phase_dst组成,分别维护了自己的状态机来切换Req和Ack的值。
Req和Ack会通过打拍的方式同步到对方的时钟域:
module sync #(
parameter int unsigned STAGES = 2
) (
input logic clk_i,
input logic rst_ni,
input logic serial_i,
output logic serial_o
);
logic [STAGES-1:0] reg_q;
always_ff @(posedge clk_i, negedge rst_ni) begin
if (!rst_ni) begin
reg_q <= 'h0;
end else begin
reg_q <= {reg_q[STAGES-2:0], serial_i};
end
end
assign serial_o = reg_q[STAGES-1];
endmodule
状态机
状态机比较简单
对于发送方:
在IDLE状态,当接收到上游的valid,则切换到等待Ack状态
在等待Ack状态,当接收到Ack后,则切换到等待Ack取消状态
在等待Ack取消状态,当接收到Ack取消后,切换到IDLE状态
对于接收方:
在IDLE状态:
- 当下游ready有效,可以接收时
- 接收到发送方同步过来后的Req后,进入等待Req取消状态
- 当下游ready无效,不能接收时
- 进入等待下游ready状态
在等待下游ready状态,当接收到下游ready后,也进入等待Req取消状态
在等待Req取消状态,当接收到Req取消后,进入IDLE状态
可以看到和上面四相位握手的波形图行为一致。
仿真
initial
begin
src_data_i <= 64'h12345678;
dst_ready_i <= 1;
#32
src_valid_i <= 1;
#(SRC_PERIOD)
src_valid_i <= 0;
#(SRC_PERIOD*200)
$finish;
end
写一个简单的testbench,下游ready,然后上游发送valid
valid有效后,下个时钟沿(src):
- 发送端会切换到等待Ack状态
- req有效
当req_synced (req同步到接收端,打2拍后) 为高,下个时钟沿(dst):
- 接收端切换到等待Req取消状态
当在等待Req取消状态时,下个时钟沿(dst):
- 发出Ack
当ack_synced (ack同步到发送端,打2拍后) 为高,下个时钟沿(src):
- 切换到等待Ack取消状态
- 取消Req
当req_synced (req同步到接收端,打2拍后) 为低,下个时钟沿(dst):
- 接收端切换到IDLE状态
- 取消Ack
当ack_synced (ack同步到发送端,打2拍后) 为低,下个时钟沿(src):
- 切换到IDLE状态
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 百万级群聊的设计实践
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期