|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?注册
x
本帖最后由 杰克淡定 于 2016-6-21 19:37 编辑
看到坛子里不少朋友,对于基本数字电路存在这样那样的疑惑,本人决定开贴,介绍数字电路最常见的模块单元,希望给初学者带来帮助,也欢迎大佬们前来拍砖。如果想要做数字设计,下面这些电路是一定会碰到的,也是所有大型IP,SOC设计必不可少的基础,主要包括异步信号的同步处理,同步FIFO,异步FIFO,时钟无缝切换,信号滤波debounce等等,后面会根据大家反馈情况再介绍新电路。
首先介绍异步信号的跨时钟域同步问题。一般分为单bit的控制信号同步,以及多bit的数据信号同步。多bit的信号同步会使用异步FIFO完成,而单bit的信号同步,又是时钟无缝切换电路以及异步FIFO电路的设计基础,这里先介绍单bit信号同步处理。
clka域下的信号signal_a,向异步的clkb域传递时,会产生亚稳态问题。所有的亚稳态,归根结底就是setup/hold时间不满足导致。在同一个时钟域下的信号,综合以及布线工具可以在data路径或者clock路径上插入buffer使得每一个DFF的setup/hold时间都满足;但是当signal_a在clkb域下使用时,由于clka与clkb异步,它们的相位关系不确定,那么在clkb的时钟沿到来时,无法确定signal_a此时是否处于稳定无变化状态,也即setup/hold时间无法确定,从而产生亚稳态。这种异步信号在前后端流程里面是无法做时序分析的,也就是静态时序分析里常说的false_path。
消除亚稳态,就是采用多级DFF来采样来自另一个时钟域的信号,级数越多,同步过来的信号越稳定。对于频率很高的设计,建议至少用三级DFF,而两级DFF同步则是所有异步信号处理的最基本要求。
单bit的信号跨时钟域同步,又分成电平信号同步以及脉冲信号同步。电平信号,就是说clka下的信号signal_a在clkb看来,是一个很宽的信号,会保持多个clkb的时钟周期,一定能被clkb采到。这种情况,只需要使用clkb用至少两级DFF连续抓signal_a即可,特别需要强调的是,此时signal_a必须是clka下的寄存器信号,如果signal_a是clka下的组合逻辑信号,一定要先在clka下用DFF抓一拍,再使用两级DFF向clkb传递。这是因为clka下的组合逻辑信号会有毛刺,在clka下使用时会由setup/hold时间保证毛刺不会被clka采到,但由于异步相位不确定,组合逻辑的毛刺却极有可能被clkb采到。电平信号的同步处理,一般用于知道确定的时钟频率大小关系或者极慢时钟下的信号向极快时钟域传递时使用,简单处理如下:
always @ (posedge clkb or negedge rst_n)
begin
if (!rst_n) begin
levl_b_d1 <= #DLY 1'b0;
levl_b_d2 <= #DLY 1'b0;
levl_b_d3 <= #DLY 1'b0;
end
else begin
levl_b_d1 <= #DLY levl_a_in;
levl_b_d2 <= #DLY levl_b_d1;
levl_b_d3 <= #DLY levl_b_d2;
end
end
assign puls_b_pos = levl_b_d2 & (~levl_b_d3);
assign puls_b_neg = levl_b_d3 & (~levl_b_d2);
assign levl_b_out = levl_b_d2;
上面三个输出分别是经过同步之后,clkb下可以使用的0变1脉冲信号,1变0脉冲信号以及电平信号。再次强调:levl_a_in必须是clka的DFF信号!
下面是更常见的,clka下的脉冲信号,同步到clkb时钟域下,它对于clka与clkb的时钟频率关系没有任何限制,快到慢,慢到快都没问题。其主要原理就是先把脉冲信号在clka下展宽,变成电平信号,再向clkb传递,当确认clkb已经“看见”信号同步过去之后,再清掉clka下的电平信号。脉冲信号同步处理电路,有两个地方使用了上面的电平信号同步处理原则,请仔细揣摩原因。详细见下面的RTL,其中省略了信号定义声明:
module sync_pulse (
// input
rst_n , // system reset
clka , // clockA
clkb , // clockB
puls_a_in , // pulse input from clka
// output
puls_b_out , // pulse output in clkb
levl_b_out // level output in clkb
);
parameter DLY = 1 ; //
always @ (posedge clka or negedge rst_n)
begin
if (rst_n == 1'b0)
signal_a <= # DLY 1'b0 ;
else if (puls_a_in)
signal_a <= # DLY 1'b1 ;
else if (signal_b1_a2)
signal_a <= # DLY 1'b0 ;
else ;
end
always @ (posedge clkb or negedge rst_n)
begin
if (rst_n == 1'b0)
signal_b <= # DLY 1'b0 ;
else
signal_b <= # DLY signal_a ;
end
always @ (posedge clkb or negedge rst_n)
begin
if (rst_n == 1'b0) begin
signal_b_b1 <= # DLY 1'b0 ;
signal_b_b2 <= # DLY 1'b0 ;
end
else begin
signal_b_b1 <= # DLY signal_b ;
signal_b_b2 <= # DLY signal_b_b1 ;
end
end
always @ (posedge clka or negedge rst_n)
begin
if (rst_n == 1'b0) begin
signal_b1_a1 <= # DLY 1'b0 ;
signal_b1_a2 <= # DLY 1'b0 ;
end
else begin
signal_b1_a1 <= # DLY signal_b_b1 ;
signal_b1_a2 <= # DLY signal_b1_a1 ;
end
end
assign puls_b_out = signal_b_b1 & (~signal_b_b2) ;
assign levl_b_out = signal_b_b1 ;
endmodule
下一篇讲时钟切换电路。
留下一个思考题:clka下的同一个寄存器信号signal_a,电平宽度对clkb而言足够长,如果同时调用两个相同的电平同步模块向clkb时钟传递,分别得到levl_b1和levl_b2,那么在clkb时钟域下看到的lev_b1和levl_b2信号是否一样?
这个问题是实际设计中一不小心就会犯错的,如果能够想明白正确回答这个问题,异步信号的理解就可以过关了。 |
|