异步四相位异步握手模块学习

背景

由于EDA工具的广泛支持,采用同步电路设计的方法是目前SoC设计的基本要求,但在一个功能复杂的SoC系统中,难免会有异步信号与同步电路交互的问题。特别是在许多核组成的SoC中,通常采用全局异步局部同步 (GALS) 的设计。

最近项目中需要跨时钟域同步数据。

数据跨时钟同步通常可以采用异步FIFO、异步握手信号来进行。

正好看到cdc_4phase这个模块,今天来分析一下代码和时序。

四相位异步握手协议

常用的握手协议有2种:两相位握手协议、四相位握手协议


摘自《异步电路设计 -- 陈虹》

四相位握手协议也称为归零 (return-to-zero, RTZ)握手协议。

这里的相位指的是通信动作的次数:

  1. 发送端准备好数据后会将请求信号 (Req)置高
  2. 接收端接收数据后将应答信号 (Ack)置高
  3. 发送端将请求信号(Req)置低作为响应
  4. 接收端通过将应答信号(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状态
posted @   一鸣惊人_001  阅读(99)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 百万级群聊的设计实践
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
点击右上角即可分享
微信分享提示