Verilog HDL-同步技术

同步技术

在芯片设计中,数据同步和在不同时钟域之间进行数据传输会经常出现。为避免任何差错、系统故障和数据破坏,正确的同步和数据传输就显得格外重要。这些问题的出现往往比较隐蔽,不易被发现,因此正确进行跨时钟域处理就显得极为重要。实现数据同步有许多种方式,在不同的情况下进行恰当的同步方式选择非常重要。以计算机中的南桥芯片为例,它通过不同的接口(如PCIe USB 吉比特以太网等)与外部设备相连。南桥通过不同的接口与外围设备相连,它与北桥之间是一个通用数据接口。南桥芯片中需要使用数据同步技术,目前,常用的同步技术主要分为以下几类:

  • 在不同的时钟域之间使用FIFO

  • 在不同的时钟域之间使用握手信号

  • 相位差固定的同步域内部的数据传输

  • 准同步域之间的数据传输

使用FIFO进行的数据同步

当存在两个异步时钟域并且二者之间进行数据包传输时,双端口FIFO最为适合。FIFO有两个端口,一个端口写入输入数据,另一个端口读出数据,如图6.34所示。两个端口工作在相互独立的时钟域内,通过各自的指针(地址)来读写数据。由于每个端口工作在相互独立的时钟域内,因此读写操作可以独立实现并且不会出现任何差错。当FIFO变满时,应停止写操作,直到FIFO中出现空闲空间。同样,当FIFO为空时,应停止读操作,直到有新的数据被写入FIFO中。

握手同步方式

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

所谓握手,即通信双方使用了专用控制信号进行状态指示,这个控制信号既有发送域给接受域的也有接收域给控制域的,有别于单向控制信号方式。

使用握手协议方式处理跨时钟域数据传输时,只需要对双方的握手信号(req 和 ack)分别使用脉冲检测方法进行同步,在具体实现中,假设req ,ack, data,总线在初始化时都处于无效状态,发送域先把数据放入总线,随后发送有效的req信号给接收域;接收域在检测到有效的req信号后锁存数据总线,然后会送一个有效的ack信号表示读取完成应答;发送域在检测到有效ack信号后撤销当前的req信号,接收域在检测到req撤销后也相应撤销ack信号,此时完成一次正常握手通信,此后,发送域可以继续开始下一次握手通信,如此循环,该方式能够使接收到的数据稳定可靠,有效的避免了亚稳态的出现,但是控制信号握手检测会消耗通信双方较多的时间。

以下是握手同步机制的工作步骤:

  • 用后缀_t表示发送端,用后缀_r表示接收端。发送时钟用tclk表示,接收时钟用rclk表示。数据从tclk域向rclk域传输;

  • 当需要发送的数据准备好后,发送端将t_rdy信号置为有效,该信号必须在tclk下降沿时釆样输出;

  • 在t_rdy有效期间,t_data必须保持稳定;

  • 接收端在rclk域中采用双同步器同步t_rdy控制信号,并把同步后的信号命名为t_rdy_rclk;

  • 接收端在发现t_rdy_rclk信号有效时,tdata已经安全地进入了rclk域,使用rclk对其进行采样,可以得到t_data_rclk。由于由于数据已经在rclk域进行了正确采样,所以此后在rclk域使用该数据是安全的;

  • 接收端将r_ack信号置位1,信号必须在rclk下降沿输出;

  • 发送端通过双同步器在tclk域内同步r_ack信号,同步后的信号称为r_ack_tclk;

  • 以上所有步骤称为半握手。tclk1,这是因为发送端在输出下一数据之前,不会等r_ack_tclk被置为0;

  • 半握手机制工作速度快,但是,使用半握手机制时需要谨慎,一旦使用不当,会导致操作错误;

  • 从低频时钟域向高频时钟域传输数据时,半握手机制较为适用,这是由于接收端可以更快地完成操作。然而,如果从高频时钟域向低频时钟域传输数据,则需要采用全握手机制;

  • 当r_ack_tclk为高电平时,发送端将t_rdy置为0;

  • 当t_rdy_rclk为低电平时,接收端将r_ack置为0;

  • 当发送端发现r_ack_tclk为低电平后,全握手过程结束,传输端可以发送新的数据;

  • 显然,全握手过程耗时较长,数据传输速度较慢。然而,全握手机制稳定可靠,可以在两个任意频率的时钟域内安全地进行数据传输。如图6.35所示为全握手机制工作波形。

上面过程比较绕口,因此我制作了一张流程图,如下所示:

全握手机制代码及仿真结果如下。

发送端:使用三个状态的状态机,其跳转关系如下:

module transmit(tclk,reset_tclk,t_rdy,data_avail,transmit_data,t_data,r_ack);
input tclk;
input reset_tclk;
input data_avail;
input [31:0]transmit_data;
input r_ack;
output t_rdy;
output t_data;

localparam IDLE_T = 2'd0,
    ASSERT_T_RDY = 2'd1,
    DEASSERT_T_RDY = 2'd2;
    
reg [1:0] t_hndshk_state,t_hndshk_state_nxt;
reg t_rdy,t_rdy_nxt;
reg [31:0] t_data,t_data_nxt;
reg r_ack_tclk;

always@(*)begin

 t_hndshk_state_nxt = t_hndshk_state;
 t_rdy_nxt = 1'b0;
 t_data_nxt = t_data;
 
 case(t_hndshk_state)
  IDLE_T:begin
   if(data_avail) begin
    t_rdy_nxt = 1'b1;
    t_hndshk_state_nxt = ASSERT_T_RDY;
    t_data_nxt = transmit_data;
   end
  end
  
  ASSERT_T_RDY:begin
   if(r_ack_tclk)begin
    t_rdy_nxt = 1'b0;
    t_hndshk_state_nxt = DEASSERT_T_RDY;
    t_data_nxt = 'd0;
   end
   else begin
    t_rdy_nxt = 1'b1;
    t_data_nxt = transmit_data;
   end
  end
  
  DEASSERT_T_RDY:begin
   if(!r_ack_tclk)begin
    if(data_avail)begin
     t_rdy_nxt = 1'b1;
     t_hndshk_state_nxt = ASSERT_T_RDY;
     t_data_nxt = transmit_data;
    end
    else begin
     t_hndshk_state_nxt = IDLE_T;
    end
   end
  end
  
 endcase
end
always@(posedge tclk or negedge reset_tclk)begin
 if(!reset_tclk)begin
  t_rdy <= 1'b0;
  t_hndshk_state <= IDLE_T;
  t_data <= 32'h00000000;
  r_ack_tclk <= 1'b0;
 end
 
 else begin
  t_rdy <= t_rdy_nxt;
  t_hndshk_state <= t_hndshk_state_nxt;
  t_data <= t_data_nxt;
  r_ack_tclk <= r_ack;
 end
 
end
endmodule 

接收端:使用两个状态的状态机,跳转关系如下:

module receiver(rclk,reset_rclk,t_rdy,t_data,r_ack);
input rclk,reset_rclk;
input t_rdy;
input[31:0] t_data;
output r_ack;

reg r_hndshk_state,r_hndshk_state_nxt;
reg t_rdy_rclk;
reg[31:0] t_data_rclk,t_data_rclk_nxt;
reg r_ack,r_ack_nxt;

localparam IDLE_R = 1'b0,
    ASSERT_ACK = 1'b1;
    
always@(*)begin
 r_hndshk_state_nxt = r_hndshk_state;
 r_ack_nxt = 1'b0;
 t_data_rclk_nxt = t_data_rclk;
 case(r_hndshk_state)
  IDLE_R:begin
   if(t_rdy_rclk)begin
    r_hndshk_state_nxt = ASSERT_ACK;
    t_data_rclk_nxt = t_data;
    r_ack_nxt = 1'b1;
   end
  end
  
  ASSERT_ACK:begin
   if(!t_rdy_rclk)begin
    r_hndshk_state_nxt = IDLE_R;
    r_ack_nxt = 1'b0;
   end
   else begin
    r_ack_nxt = 1'b1;
   end
  end
  
 endcase
end

always@(posedge rclk or negedge reset_rclk)begin
 if(!reset_rclk)begin
  r_hndshk_state <= IDLE_R;
  t_data_rclk <= 1'b0;
  t_rdy_rclk <= 1'b0;
  r_ack <= 1'b0;
 end
 
 else begin
  r_hndshk_state <= r_hndshk_state_nxt;
  t_data_rclk <= t_data_rclk_nxt;
  t_rdy_rclk <= t_rdy;
  r_ack <= r_ack_nxt;
 end
end

endmodule

测试代码

`timescale 1ns/1ns
module testbench;
reg tclk_tb,rclk_tb;
reg [31:0] transmit_data_tb;
reg reset_tclk_tb,reset_rclk_tb;
reg data_avail_tb;
wire t_rdy_tb;
wire [31:0] t_data_tb;
wire r_ack_tb;

parameter CLK_HALF_PERIOD1 = 5;
parameter CLK_HALF_PERIOD2 = 8;
parameter RESET_DELAY = 100;

initial begin
 tclk_tb = 0;
 rclk_tb = 0;
end

always #CLK_HALF_PERIOD1 tclk_tb = ~tclk_tb;
always #CLK_HALF_PERIOD2 rclk_tb = ~rclk_tb;

initial begin
 reset_rclk_tb = 0;
 reset_tclk_tb = 0;
 #RESET_DELAY reset_rclk_tb = 1;
 reset_tclk_tb = 1;
end

initial begin
 #500;
 data_avail_tb = 1;
 transmit_data_tb = 32'h96431346;
end

transmit transmit_test
 (.tclk(tclk_tb),
 .reset_tclk(reset_tclk_tb),
 .t_rdy(t_rdy_tb),
 .data_avail(data_avail_tb),
 .transmit_data(transmit_data_tb),
 .t_data(t_data_tb),
 .r_ack(r_ack_tb));
 
receiver receiver_test
 (.rclk(rclk_tb),
 .reset_rclk(reset_rclk_tb),
 .t_rdy(t_rdy_tb),
 .t_data(t_data_tb),
 .r_ack(r_ack_tb));
endmodule

仿真结果

脉冲同步器

脉冲同步器在源时钟域内接收一个脉冲,在目的时钟域内产生一个脉冲。脉冲同步器内部通常采用全握手机制来产生输出脉冲。在讨论脉冲同步器工作原理之前,我们先讨论它的用途。有时状态机希望更新不同时钟域内寄存器的数值,它可以采用全握手同步机制来达到这一目的,但全握手同步机制存在同步延迟大的问题,在全握手完成之前,状态机都将处于等待对方响应的状态。

为了解决这一问题,可以引入脉冲同步器电路。引入脉冲同步器后,状态机在源时钟域内产生更新脉冲,此后继续执行其他操作。脉冲同步器可以接收脉冲并完成剩余的同步和输出脉冲产生工作。需要注意的是,脉冲同步器完成全握手操作需要消耗多个时钟周期,因此状态机发出的两个脉冲之间需要足够的时间间隔,否则就会出现逻辑错误。下面是脉冲同步器的工作步骤、Verilog代码及仿真结果。

步骤:

  • 当源脉冲(pulse_src)有效时,在源时钟域中生成一个信号,并且保持有效(该信号称为sig_stretched);

  • 使用同步器在目的时钟域中对sig_stretched信号进行同步,称为sig_stretched_dest;

  • sig_stretched_dest信号被送回到源时钟域并进行同步称为sig_stretched_ack;

  • 如果sig_stretched_ack=1,则产生一个脉冲,根据这一反馈脉冲来将sig_stretched置为0(完成全握手);

  • 基于sig_stretched_dest,在目的时钟域中产生一个脉冲称为pulse_dest。

module pulse_synchronizer
(clksrc,
resetb_clksrc,
clkdest,
resetb_clkdest,
pulse_src,
pulse_dest);

//*******************************************************
input     clksrc;
input     resetb_clksrc;
input     clkdest;
input     resetb_clkdest;
input     pulse_src;    // pulse in source clock domain
output    pulse_dest; // pulse in destination clock domain


reg    sig_stretched;
wire   sig_stretched_nxt;
reg    sig_stretched_syncl, sig_stretched_dest;
reg    sig_stretched_dest_dl;
reg    sig_stxetched_ack_pre, sig_stretched_ack,
reg    sig_stretched_ack_d1;
wire   sig_stretched_ack_edge;
wire   pulse_dest;


assign sig_stretched_nxt = sig_stretched_ack_edge ? 1'b0: (pulse_src ? l'bl : sig_stretched);


always @(posedge clksrc or negedge resetb_clksrc)
begin
if (!resetb_clksrc)

 sig_stretched <= 1'bO;

else
 sig_stretched <= sig_stretched_nxt;
end

//First two flops for synchronizing and the third one for pulse generation
always @(posedge clkdest or negedge resetb_clkdcst)
begin
if (!resetb_clkdest)
begin
 sig_stretched_sync <= 1'b0;
 sig_stretchcd_dest <= 1'b0;
 sig_stretched_dest_d1 <= 1'b0;
end
else
begin
sig_stretchcd_sync1 <= sig_stretched;
sig_stretched_dest  <= sig_stretched_sync1;
sig_stretched_dest_d1 <= sig_stretched_dest;
end
end

// First two flops are for synchronizing back to source clock domain.
// third flop is for edge detection
always @(posedge clksrc or negedge resetb_clksrc)
begin
if (!resetb_clksrc)
begin
sig_strctched_ack_pre <= 1'b0;
sig_stretchecl_ack   <= 1'b0;
sig_stretched_ack_d1  <= 1'b0;
end
else
begin
sig_stretched_ack_pre <= sig_stretched_dest;
sig_stretched_ack     <= sig_stretched_ack_pre;
sig_stretchcd_ack_d1  <= sig_strctched_ack;
end
end
assign sig_stretched_ack_edge = sig_stretched_ack & !sig_stretched_ack_d1;

// Pulse generation in destination clock domain

assign pulse_dest = sig_stretched_dest & !sig_stxctched_dest_d1;

endmodule

相位、频率关系固定时的跨时钟域数据传输

如果两个时钟具有相同或者整数倍的频率关系,上升沿之间有固定、明确的相位关系,那么在不使用FIFO或者握手协议的情况下,可以进行数据传输。此时固定明确的相位关系非常重要,数据传递时的建立时间和保持时间必须满足要求,如果相位关系不固定、不明确,则无法采用这种机制进行跨时钟域数据传递。在系统复位之后,需要调整数据延迟值(使用延迟链电路),从而确保跨时钟域数据传递时可以进行正确采样。

这种机制与使用FIFO或者握手机制相比具有更小的延迟。例如,DDR数据总线上可以使用单倍的时钟实现双倍的数据传输。前面讲过的数据位宽调整电路,也要求双方的时钟频率和相位具有间定、明确的关系。

准同步时钟域

如果两个时钟具有相同的标称频率和指定范围内的时钟精度误差,那么我们说这两个时钟源是准同步的。在实际应用中,通常数据发送端的本地时钟和接收端的本地时钟是独立产生的,通常都使用晶体振荡器这类高精度时钟源,二者往往具有相同的标称值和规定范围内的精度误差。例如,PCIe要求发送和接收时钟误差在300ppm以内。这就意味着在一个相对较长的时间里(例如,对PCIe来说,超过1300个时钟周期),两个时钟将产生1个时钟周期的偏差。下面我们将讨论此时如何进行数据传输同步。

PCIe,SATA等串行通信协议中广泛使用了准同步通信机制。在数据收发电路中,弹性缓冲区(FIFO)被用于进行跨时钟域数据传输。此时FIFO不仅用于跨时钟域的同步,还需要与一定的外部电路配合,解决长时间通信时,由于时钟偏差造成的FIFO内部数据上溢或下溢的问题。PCIe和SATA要求发送端周期性地将null字符插入传输数据流中;在接收端,根据FIFO内部的数据深度,这些null字符会被丢弃或添加到FIFO中。

NOW

学习Xilinx FPGA最好的资料其实就是官方手册,下表总结了部分手册的主要介绍内容,关注我,持续更新中......

文件名主标题内容简单介绍是否有中文版
UG4767 Series FPGAs GTX/GTH  TransceiversGTX和GTH介绍,PCIe、serdes等学习必备
UG4717 Series FPGAs SelectIO Resources描述 7 系列 FPGA 中可用的 SelectIO资源。
UG1114PetaLinux Tools DocumentatonPetaLinux 工具文档 参考指南是,V2019.2
UG949UltraFAST 设计方法指南(适用于 Vivado  Design Suite)赛灵思® UltraFast™  设计方法是用于为当今器件优化设计进程的一套最佳实践。这些设计的规模与复杂性需要执行特定的步骤与设计任务,从而确保设计每一个阶段的成功开展。依照这些步骤,并遵循最佳实践,将帮助您以最快的速度和最高的效率实现期望的设计目标是,V2018.1
IP手册pg057FIFO GeneratorFIFO生成器IP使用手册
pg104Complex Multiplier复数乘法器IP使用手册
pg122RAM-Based Shift Register 移位寄存器IP使用手册

推荐阅读

【Vivado那些事】如何查找官网例程及如何使用官网例程

【Vivado使用误区与进阶】总结篇

【Vivado那些事】Vivado中常用的快捷键(二)其他常用快捷键

SystemVerilog数字系统设计_夏宇闻 PDF

图书推荐|ARM Cortex-M0 全可编程SoC原理及实现

简谈:如何学习FPGA

1202年了,还在使用虚拟机吗?Win10安装Ubuntu子系统及图形化界面详细教程

Github 上有哪些优秀的 VHDL/Verilog/FPGA 项目

AD936x+ZYNQ搭建收音机(一)

AD936x+ZYNQ搭建OpenWIFI

无招胜有招-Vivado非工程模式下的详细设计

追寻ARM的起源-Acorn电脑简史及FPGA实现

面试中经常会遇到的FPGA基本概念,你会几个?

Xilinx FPGA MIPI 接口简单说明

介绍一些新手入门FPGA的优秀网站

Vivado ML(机器学习) 2021尝鲜

推荐一些可以获取免费的国外的原版书籍(电子版)网站

【Vivado那些事】FPGA的配置方式

FPGA 的重构

浅析FPGA局部动态可重构技术

ISP(图像信号处理)算法概述、工作原理、架构、处理流程

国产CPU概括

从电子游戏历史看IC发展的助推剂

80年代电子游戏及电脑游戏的发展历史

PCIe总线的基础知识

万字长文带你回顾电子游戏的七十多年历史(完整版)

FPGA中异步复位,同步释放的理解

OpenFPGA系列文章总结

用Verilog设计一个16 位 RISC 处理器

介绍一些新手入门FPGA的优秀网站(新增)

Verilog数字系统基础设计-CRC

FPGA 的布局规划艺术

Verilog数字系统基础设计-奇偶校验

建立和保持时间及时序简单理解

(Xilinx)FPGA中LVDS差分高速传输的实现

Xilinx Multiboot实例演示

高速串行通信常用的编码方式-8b/10b编码/解码
Verilog计时(微秒、毫秒和秒)脉冲的产生及同步整形电路

  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: 《用Verilog HDL进行高级数字设计》是一本关于数字电路设计的教材。本书首先介绍了数字电路设计的基本原理和概念,包括数字系统、逻辑门、寄存器和存储器。接着,本书详细介绍了Verilog HDL语言的基础知识和语法,以及如何使用Verilog HDL进行数字电路设计。 该书的重要内容包括设计概念、模拟和验证、前端设计、时序分析和后端设计,这些知识点将逐步引领读者掌握数字电路设计的整个过程。另外,该书还涵盖了多个实际的数字系统设计示例。这些实例充分说明了作者介绍的概念和技术,以及在实际应用中的演示。 《用Verilog HDL进行高级数字设计》的主要优点是将数字电路设计和Verilog HDL教程融合在一起。读者可以通过学习该书,了解数字电路设计的基本概念和原理,以及如何使用Verilog HDL来实现数字系统。本书适合正在学习或实践数字电路设计的学生、专业人士和研究人员,还适合初学者作为参考书籍。 ### 回答2: 《Advanced Digital Design with the Verilog HDL》是一本涵盖Verilog硬件描述语言的高级数字设计的重要教材,适用于学习和实践数字系统设计的学生和工程师。本书主要陈述了基础的数字电路和数字系统设计知识,以及通过使用Verilog HDL实现数字设计的实践技能。作者通过许多例子和项目来指导读者学习如何使用Verilog HDL构建复杂的数字系统以及如何进行仿真和验证。本书还涵盖了如何设计和优化数字电路、同步和异步电路设计、接口设计、存储器和存储器控制器、以及FPGA和ASIC实现等方面的知识。总的来说,这本书适合已经掌握数字电路和计算机体系结构基础知识的人,能够更深入地了解数字系统设计和Verilog HDL语言。 ### 回答3: 《Advanced Digital Design with the Verilog HDL》是一本关于使用Verilog硬件描述语言进行高级数字设计的书籍,其中包含了硬件描述语言的基本概念、语法和使用方法,还提供了诸多实践案例。通过学习这本书,读者可以掌握Verilog HDL的各种技术,能够应用于复杂数字设计项目中,并能够独立设计出符合特定要求的数字系统。此外,该书还介绍了运用Verilog HDL进行仿真和验证的技术,帮助读者进行正确性验证和结果分析,确保数字系统的正确性和稳定性。总的来说,《Advanced Digital Design with the Verilog HDL》是一本非常实用和全面的书籍,对于数字设计工程师和学生都具有很高的参考价值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OpenFPGA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值