在异步通信中数据位的提取至关重要,如果能够准确的把数据位提取出来,在后面的字同步,帧同步以及数据包同步都会迎刃而解。如果熟悉计算机异步串行口通信的原理,我们就知道要想准确的接受串行口的数据帧(基本数据帧如图1),首先要准确识别start位,这就对start位准确采样提出要求。关于UART的详细内容请参照本教材的相关章节。

图1 UART 基本数据帧
在对异步串行数据采样中,由于采用本地时钟,因此对接受的串行数据流是异步采样,对于异步采样及同步处理可以按照如下步骤进行。
- 首先要时钟同步化处理,消除亚稳态的影响,
- 提取串行数据流中的边沿脉冲(上升沿,下降沿,双边沿),
- 数据采样定位,
- 同步数据为输出。
在上几节内容中介绍了时钟同步化原理及处理办法,以及串行数据流边沿提取的方法,本节内容着重介绍数据采样定位以及数据位同步输出。
1.采样定位原理
采样定位的原理,如图2所示,利用本地时钟获取数据流的边沿后,通过延迟将对数据流(bit流)中的某个bit进行中心定位并采样。由于延迟的原因,因此采样对应的原数据也要对应的延迟。在高速串行通信中最理想的就是能做到对每个数据位(bit)进行相位矫正,但由于原始数据传输时不可能每个bit都含有时钟信息,因此在对没有边沿变化的数据位采样时,按照过采样时钟的倍数进行延迟。如图2中成功得到上升沿后,延迟3个时钟对RX_rrr进行采样,但相邻的下一个bit的采样(采样点2)就只能依赖本地时钟的计数器实现,如4倍过采样就需要延迟4个过采样时钟进行采样。

图2 4倍过采样时序图
图2中 RX为原始接收数据,RX_r,RX_rr,RX_rrr分别经过过采样时钟锁存的三个信号,双边沿脉冲为RX_r与RX_rr异或产生。采样点分布在RX_rrr上。图2中的时序图显示的各个信号之间的关系可以由如下Verilog代码实现。
module recv_bit
(
input rst,
input clk_400M , //400Hz
input rx,
output reg rx_bit,
output reg rx_bit_vld,
output reg rx_r, //r---register
output reg rx_rr, //rr--- rx_r registered
output reg rx_dg //dg----double (dual) edge, 上升沿或下降沿
);
////=========rx_r^rx_rr==1,dual edge
wire rx_out;
reg rx_rrr;
reg [ 1: 0 ] sample_count = 0;
always@( posedge clk_400M or posedge rst )
if ( rst )
begin
rx_r <= 0;
rx_rr <= 0;
rx_rrr <= 0;
rx_dg <= 0;
end
else
begin
rx_r <= rx_out;
rx_rr <= rx_r;
rx_rrr <= rx_rr;
rx_dg <= rx_r ^ rx_rr; //dual edge
end
always@( posedge clk_400M or posedge rst )
if ( rst )
begin
rx_bit_vld <= 0;
rx_bit <= 0;
sample_count <= 0;
end
else
begin
rx_bit_vld <= 0;
if ( sample_count == 2 ) //0,1,2,3,0,1,2,3...
begin
rx_bit <= rx_rrr; //
rx_bit_vld <= 1'b1;
end
if ( rx_dg ) // 2 3 0 0
sample_count <= 0;
else
sample_count <= sample_count + 1'b1;
end
IO_RX IO_RX_inst
(
.rx_in ( rx ),
.rx_out ( rx_out )
);
endmodule
仿真程序:
`timescale 10 ps / 1 ps
module tb_recv_bit
(
);
parameter PERIOD_ORG = 1000 ;
parameter PERIOD_OVER = 250;
reg org_clk = 0;
reg over_sample_clk = 0;
always # ( PERIOD_ORG / 2 ) org_clk = ~org_clk;
always # ( PERIOD_OVER / 2 ) over_sample_clk = ~over_sample_clk;
wire rx_bit;
wire rx_bit_vld;
wire rx_dg;
reg rst;
reg rx;
initial
begin
rst = 1;
rx = 0;
#1000
rst = 0;
#1000
rx = 0;
#2000
rx = 1;
#4000
rx = 0;
#8000
rx = 1;
#8000
rx = 0;
#8000
rx = 1;
#1000
rx = 0;
#4000
rx = 1;
#2000
$stop;
end
recv_bit recv_bit_inst
(
.rst ( rst ),
.clk_400M ( over_sample_clk ),
.rx ( rx ),
.rx_bit ( rx_bit ),
.rx_bit_vld ( rx_bit_vld ),
.rx_r (),
.rx_rr (),
.rx_dg ( rx_dg )
);
endmodule
仿真波形:

图3
2. 采样相位校准
从图3的仿真波形可以看出,在采样点S1之前由于没有边沿矫正,采样完全按照本地过采样时钟进行,因此S1采样点距离rx_rrr的边沿比较近,而且这个距离是随机的,取决于本地时钟稳定的时刻,以及远程时钟与本地时钟频率的差异。S6是第二个边沿的校准采样点,由于收发时钟频率相同,因此经过S1校正后,后面都同步跟随,因此S6点实际上没有校准。以下情况会发生校准情况。
(1) 收发时钟频率不同
虽然目前无论晶振还是有源震荡器的精度都比较高(如 50ppm),但收发双方的时钟频率不可能完全相等,因此随着时间的累积,在捕获边沿后必然会发生调整。
(2)由于直流不平衡,PCB布线不等长等诸多因素眼图没有完全张开,使数据流中边沿的位置发生偏移,此时会出现采样点的调整。
不管是什么原因发生了边沿调整,都会将采样点调整到做合适的位置采样,而且采样点调整又是自动实现的,因此称为自适应位同步信号提取。
可以改变仿真程序测试采样点的调整过程,
`timescale 10 ps / 1 ps
module tb_recv_bit
(
);
parameter PERIOD_ORG = 1000 ;
parameter PERIOD_OVER = 250;
reg org_clk = 0;
reg over_sample_clk = 0;
always # ( PERIOD_ORG / 2 ) org_clk = ~org_clk;
always # ( PERIOD_OVER / 2 ) over_sample_clk = ~over_sample_clk;
wire rx_bit;
wire rx_bit_vld;
wire rx_dg;
reg rst;
reg rx;
initial
begin
rst = 1;
rx = 0;
#1000
rst = 0;
#1000
rx = 0;
#2000
rx = 1;
#3800 //缩短2ns,
rx = 0;
#8200
rx = 1;
#8000
rx = 0;
#8000
rx = 1;
#1000
rx = 0;
#4000
rx = 1;
#2000
$stop;
end
recv_bit recv_bit_inst
(
.rst ( rst ),
.clk_400M ( over_sample_clk ),
.rx ( rx ),
.rx_bit ( rx_bit ),
.rx_bit_vld ( rx_bit_vld ),
.rx_r (),
.rx_rr (),
.rx_dg ( rx_dg )
);
endmodule

图4
图4中可以看出有两处调整,一处是在初始化阶段rx输入都是0,一直没有边沿,因此在第一个边沿处发生调整,可以看到正常sample_count的计数都是0,1,2,3,0而在调整的地方变成0,1,2,0,因此直接影响下一次的采样间隔。
3. 位采样输出
由于使用过采样的方法,每个位的输出宽度是过采样时钟的过采样倍数。如4倍过采样的位输出宽度基本上为4个时钟周期, 同时伴随着的有rx_bit_vld信号指示采样时刻,该信号的宽度为一个时钟周期,如图4所示,实现代码如下,
rx_bit_vld<=0;
if(sample_count==2) //0,1,2,3,0,1,2,3…
begin
rx_bit<=rx_rrr; //
rx_bit_vld<=1’b1;
end
关于收发时钟频率差异的处理,将在介绍帧同步的文章中详细探讨。
rx 和 rx_r的信号为什么没有延后一个完整的时钟周期?延后的时钟周期不应该最少是1个时钟周期及以上吗?怎么看仿真波形,延后了半个时钟周期也行呢?
因为rx是一个异步的数据,和同步时钟无关。用同步时钟抓取后得到的rx_r并不能保证延后一个完整的时钟周期。
可以参考相关文章Verilog信号边沿检测电路
采集异步串行信号:
1.新手:通过边沿判断,获得到采样起始点之后,后面的信号都是在本地时钟下按照约定的时间间隔来采集信号;
2.老手:通过边沿判断,获得到采样起始点之后,如果信号没有翻转,在本地时钟下按照约定的时间间隔来采集信号;如果信号有翻转,重新获得采样起始点,这样可以不断地校准和对齐异步串行信号。