多bit信号的跨时钟域处理

前言

常用的多bit信号的跨时钟域处理方法有两种:
①使用异步FIFO进行数据同步。
②采用握手方式进行数据同步。

使用FIFO进行的数据同步

当存在两个异步时钟域并且二者之间需要进行数据包传输时,双端口异步FIFO最为合适。

FIFO有两个端口,一个端口写入输入数据,另一个端口读出数据。两个端口工作在相互独立的时钟域内,通过各自的指针(地址)来读写数据。由于每个端口工作在相互独立的时钟域内,因此读写操作可以独立实现并且不会出现任何差错。当FIFO变满时,应停止写操作,直到FIFO中出现空闲空间。同样,当FIFO为空时,应停止读操作,直到有新的数据被写入FIFO中。
异步FIFO的具体代码可以参见我的另一篇文章:
异步FIFO代码

握手同步方式

FIFO可用于在不同的时钟域之间进行数据包的传输,但是在一些应用中,需要在不同时钟域之间进行少量数据的传输。FIFO占用的硬件资源较大,此时可以考虑使用握手同步机制。

以下是握手同步机制的工作步骤:
●用后缀_t表示数据发送端,用后缀_r表示数据接收端。发送端时钟用tclk表示,接收端时钟用rclk表示;
●当需要发送的数据准备好后,发送端将t_rdy信号置为有效;
●在t_rdy有效期间,t_data必须保持稳定;
●接收端在rclk时钟域内采用双同步器同步t_rdy控制信号,并把同步后的信号命名为t_rdy_rclk;
●接收端在发现t_rdy_rclk信号有效时,t_data已经安全地进入了rclk时钟域内,使用rclk对其进行采样,可以得到t_data_rclk。由于数据已经在rclk域进行了正确采样,所以此后在rclk域使用该数据是安全的;
●接收端将r_ack信号置为1;
●发送端通过双同步器在tclk时钟域内同步r_ack信号,同步后的信号命名为r_ack_tclk;
●以上所有步骤被称为“半握手”。这是因为发送端在输出下一个数据之前,不会等到r_ack_tclk被置为0;
●半握手机制工作速度快,但是使用半握手机制时需要谨慎,一旦使用不当,会导致操作错误;
●从低频时钟域向高频时钟域传输数据时,半握手机制较为适用,这是由于接收端可以更快地完成操作。然而,如果从高频时钟域向低频时钟域传输数据,则需要采用全握手机制;
●当r_ack_tclk为高电平时,发送端将t_rdy置为0;
●当t_rdy_rclk为低电平时,接收端将r_ack置为0;
●当发送端发现r_ack_tclk为低电平后,全握手过程结束,发送端可以发送新的数据;
●显然,全握手过程耗时较长,数据传输速率较慢。然而,全握手机制稳定可靠,可以在两个任意频率的时钟域内安全地进行数据传输。

下图所示为全握手机制工作波形图:
全握手机制工作波形
下面是一段采用全握手机制的同步桥代码,代码中采用的信号命名方式与以上讲述中略有不同,但整体思路与以上讲述的一致,可以直接例化此同步桥进行跨时钟域的数据同步:

module sync_bridge(
       input                  clk_t   ,
       input                  clk_r   ,
       input                  rst_n   ,
       input                  data_vld,   //表示发送端数据有效(已经准备好)
       input         [31:0]   data_t  ,
       output  reg   [31:0]   data_r
                  );
reg   req, req_syn1, req_syn2;
reg   ack, ack_syn1, ack_syn2;

always @(posedge clk_t, negedge rst_n) begin
  if(rst_n == 1'b0) begin
    req <= #1 1'b0;
  end
  else if(ack_syn2 == 1'b1) begin
    req <= #1 1'b0;
  end
  else if(ack_syn2 == 1'b0 && data_vld == 1'b1) begin
    req <= #1 1'b1;
  end
end

always @(posedge clk_t, negedge rst_n) begin
  if(rst_n == 1'b0) begin
    ack_syn1 <= #1 1'b0;
    ack_syn2 <= #1 1'b0;
  end
  else begin
    ack_syn1 <= #1 ack;
    ack_syn2 <= #1 ack_syn1;
  end
end

always @(posedge clk_r, negedge rst_n) begin
  if(rst_n == 1'b0) begin
    req_syn1 <= #1 1'b0;
    req_syn2 <= #1 1'b0;
  end
  else begin
    req_syn1 <= #1 req;
    req_syn2 <= #1 req_syn1;
  end
end

always @(posedge clk_r, negedge rst_n) begin
  if(rst_n == 1'b0) begin
    ack <= #1 1'b0;
  end
  else if(req_syn2 == 1'b1) begin
    ack <= #1 1'b1;
  end
  else if(req_syn2 == 1'b0) begin
    ack <= #1 1'b0;
  end
end

always @(posedge clk_r, negedge rst_n) begin
  if(rst_n == 1'b0) begin
    data_r <= #1 32'h0;
  end
  else if(req_syn2 == 1'b1) begin
    data_r <= #1 data_t;
  end
end

endmodule
  • 13
    点赞
  • 155
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值