在异步串行通信中,能准确的把时钟信息提取出来是后面正确进行数据解调的的保证。下面以4倍过采样为例介绍边沿提取的Verilog程序。由于利用本地异步时钟对接收信号进行采样,会带来亚稳态等不确定因素,因此在采样前需要对被采样信号进行同步化。同步化的结果可以较好的消除亚稳态影响,但对于像glitch(短脉冲干扰)等噪声却不能很好的免疫,当glitch的脉冲宽度可以与过采的时钟相比拟时,glitch信号可能会得到有效的传播,严重影响数据解调效果。对于这个问题我们会在专门的文章中讲解。
1.采样信号同步化
采样信号同步化,一般是利用本地时钟对接收信号采样(寄存器时钟锁存)以消除亚稳态的影响,建议对被采样信号做两级锁存,最低要求也要做一级锁存。
接收信号同步化程序如下:
module sync_rx
(
input loc_clk, //本地时钟,与rx是异步时钟的关系
input rx,
output reg sync_rx, //与时钟同步后的信号
output reg nsync_rx, //反相同步信号
input rst
);
reg rx_r;
always@(posedge loc_clk or posedge rst)
if(rst) begin
rx_r <= 0;
sync_rx <= 0;
nsync_rx <= 0; //或者nsync_rx <=1'b1; 根据需要
end
else begin
rx_r <= rx; //first level registered;
sync_rx <= rx_r; //second level registered;
nsync_rx <= ~rx_r; //inversed second level registered signal;
end
endmodule
上例中输出了两个与loc_clk同步的信号,分别为sync_rx及反相的nsync_rx。
2.上升沿单脉冲提取
方法1 :逻辑电路实现
同步化后的信号如果上一次的采样为0,本次采样为1,则可以得到上升沿的脉冲。上升沿单脉冲提取程序如下:
module rising_pulse
(
input loc_clk,
input rx,
output reg rx_rising_pulse,
input rst
);
reg rx_r;
reg nsync_rx;
always@(posedge loc_clk or posedge rst)
if(rst) begin
rx_r <= 0;
nsync_rx <= 0;
rx_rising_pulse <= 0;
end
else begin
rx_r <= rx; //first level registered ;
nsync_rx <= ~rx_r; //second level registered ;
rx_rising_pulse <= rx_r & nsync_rx;
end
endmodule
仿真程序 tb_rising_pulse.v 如下:
`timescale 1 ns / 1 ps
module tb_rising_pulse
(
);
parameter PERIOD = 10 ; //周期10 ns
reg inclk = 0;
initial begin
inclk = 1'b0;
#(PERIOD/2);
forever
#(PERIOD/2) inclk = ~inclk;
end
reg rst;
reg rx;
wire rx_rising_pulse;
initial begin
rst = 1;
rx = 0;
#2
rx = 1'b0;
#10
rst = 0;
#10
rx = 0;
#40
rx = 1;
#80
rx = 0;
#120
rx = 1;
#40
rx = 0;
#80
rx = 0;
#160
rx = 1;
#40
rx = 0;
$stop;
end
rising_pulse rising_pulse_inst
(
.loc_clk (inclk),
.rx (rx),
.rx_rising_pulse (rx_rising_pulse),
.rst (rst)
);
endmodule
仿真波形如图1所示:

图1
图1中的波形可以看出,nsync_rx是rx_r的取反锁存,时间上之后rx_r一个过采样时钟,因此二者相与即可得到一个过采样时钟宽度的单脉冲信号。
由于rx_r仅一次锁存就参与了运算,对亚稳态的消除不够理想,因此上述代码也可以再加一级寄存器,修改的程序如下,
module rising_pulse
(
input loc_clk,
input rx,
output reg rx_rising_pulse,
input rst
);
reg rx_r;
reg rx_rr;
reg nsync_rrx;
always@(posedge loc_clk or posedge rst)
if(rst) begin
rx_r <= 0;
rx_rr <= 0;
nsync_rrx <= 0;
rx_rising_pulse <= 0;
end
else begin
rx_r <= rx; //first level registered ;
rx_rr <= rx_r; //second level registered ;
nsync_rx <= ~rx_rr; //inverted registered ;
rx_rising_pulse <= rx_rr & nsync_rx; //rising edge pulse;
end
endmodule
方法2 :移位寄存器相邻位为2’b01
相同的功能还可以用更容易理解的方式实现,即采用if语句判断前一个时钟采样为低电平,后一个时钟采样为高电平就可以得到上升沿单脉冲信号。
module rising_pulse_1
(
input loc_clk,
input rx,
output reg rx_rising_pulse,
input rst
);
reg [2:0] rx_r;
always@(posedge loc_clk or posedge rst)
if(rst) begin
rx_r <= 0;
rx_rising_pulse <= 0;
end
else begin
rx_r <= {rx_r[1:0], rx}; //left shift register;
if(rx_r[2:1] == 2'b01)
rx_rising_pulse <= 1'b1; //rising edge pulse;
else
rx_rising_pulse <= 1'b0;
end
endmodule
上例利用了移位寄存器的方式实现亚稳态的消除,在if语句中使用了rx_r[2:1],rx_r[2]和rx_r[1]都是经过两级或两级以上本地时钟锁存的寄存器,对于亚稳态的消除有理想的效果。由于移位寄存器的原因,当前时刻rx_r等于2’b01时,下一时刻无论rx_r[0]为0还是为1,rx_r[2:1]更新后都不可能再为2’b01,因此rx_rising_pulse只能维持一个时钟宽度的脉冲。
3. 下降沿脉冲提取
下降沿的分析原理与上升沿非常类似,仅作非常简单分析,后面给出实现实例
方法1:逻辑电路法
逻辑电路如图2,

图2
verilog程序如下:
module falling_pulse
(
input loc_clk,
input rx,
output reg rx_falling_pulse,
input rst
);
reg rx_r;
reg nrx_r;
reg nrx_rr;
always@(posedge loc_clk or posedge rst)
if(rst) begin
rx_r <= 0;
nrx_r <= 0;
nrx_rr <= 0;
rx_falling_pulse <= 0;
end
else begin
rx_r <= rx; //left shift register;
nrx_r <= ~rx_r;
nnrx_r <= ~nrx_r;
rx_falling_pulse <= nrx_r & nnrx_r; //falling edge pulse;
end
endmodule
方法2:移位寄存器法
直接利用if语句判断上寄存器rx_r[2:1]是否为2’b10(一个时钟为高电平,下一个时钟为低电平),来设置下降沿信号rx_falling_pulse, 程序如下,
module falling_pulse_shift
(
input loc_clk,
input rx,
output reg rx_falling_pulse,
input rst
);
reg [2:0] rx_r;
always@(posedge loc_clk or posedge rst)
if(rst) begin
rx_r <= 0;
rx_falling_pulse <= 0;
end
else begin
rx_r <= {rx_r[1:0], rx}; //left shift register;
if(rx_r[2:1] == 2'b10)
rx_falling_pulse <= 1'b1; //falling edge pulse;
else
rx_falling_pulse <= 1'b0;
end
endmodule
4. 双边沿提取
双边沿提取就是将上升沿与下降沿的提取合并,可以将上述程序合并提取,但如果只想获得双边沿,而不区分双边沿的中的上升沿还是下降沿,可以用异或的方式实现。程序如下,
module dual_edge_pulse_shift
(
input loc_clk,
input rx,
output reg rx_dual_edge_pulse,
input rst
);
reg [2:0] rx_r;
always@(posedge loc_clk or posedge rst)
if(rst) begin
rx_r <= 0;
rx_dual_edge_pulse <= 0;
end
else begin
rx_r <= {rx_r[1:0], rx}; //left shift register;
rx_dual_edge_pulse <= rx_r[2] ^ rx_r[1]; //dual edge pulse;
end
endmodule
边沿提取对于异步采样的同步化具有非常重要的意义,下节内容将介绍位同步信号提取。