8.1 复位电路设计
复位信号是数字电路中重要的组成部分,它可以使电路在开始工作或出现错误时,让电路系统进入初始状态,以保证电路正确、稳定地工作。复位又可以分为同步复位和异步复位两种。
8.1.1 同步复位
同步复位D触发器的代码如下:
module sync_reset
(
input wire I_clk ,
input wire I_rst ,
input wire I_data_in ,
output reg O_data_out
);
always@(posedge I_clk) begin
if(I_rst)
O_data_out <= 1'b0;
else
O_data_out <= I_data_in;
end
endmodule | 在TD软件中PH1A90器件综合的结果如下图所示。
在Vivado软件中XC7K325T器件综合的结果如下图所示。
安路FPGA器件综合的结果中多使用了一个MUX,这是因为该器件的D触发器只有异步复位接口,而Xilinx的FPGA器件中的D触发器可以配置为同步复位或者异步复位接口。所以在部分器件中,使用同步复位会增加逻辑资源的消耗。在使用同步复位时,需要注意复位信号必须大于一个时钟周期,并且满足恢复时间和撤销时间的要求,否则可能复位失败。
8.1.2 异步复位
异步复位D触发器的代码如下:
module async_reset
(
input wire I_clk ,
input wire I_rst ,
input wire I_data_in ,
output reg O_data_out
);
always@(posedge I_clk or posedge I_rst) begin
if(I_rst)
O_data_out <= 1'b0;
else
O_data_out <= I_data_in;
end
endmodule | 在TD软件中PH1A90器件综合的结果如下图所示。
在Vivado软件中XC7K325T器件综合的结果如下图所示。
8.1.3 异步复位同步释放
同步复位的优点:
·由于复位信号和时钟同步,可以过滤毛刺。
·复位信号在时钟边沿到达时才有效,更容易被时序分析工具分析,使得时序更容易预测和控制。
同步复位的缺点:
·对于D触发器只有异步复位接口的器件,会带来更大的逻辑资源消耗。
·复位动作有一个周期的延迟,并且复位信号有效时间要大于一个时钟周期。
异步复位的优点:
·对于D触发器只有异步复位接口的器件,能够节约逻辑资源。
·复位信号随时有效,不需要等待时钟边沿的到来,可以让系统快速恢复到正常工作状态。
异步复位的缺点:
·复位信号容易收到毛刺的影响。
·复位信号可能不满足恢复时间(recovery time)和撤销时间(removal time)的要求,导致亚稳态。
对于一个D触发器来说,数据和时钟需要满足建立时间和保持时间才能让数据正确地传输,同样的,复位和时钟需要满足恢复时间和撤销时间才能正确地进行复位操作。恢复时间是指复位信号释放复位时,下一个时钟上升沿到来之前复位信号保持非激活状态的最短时间。撤销时间是指释放复位之前,时钟上升沿到来之后复位信号需要保持激活状态的时间。
为了消除以上同步复位和异步复位的缺点,可以采用异步复位、同步释放的电路,即复位信号异步复位D触发器,并且在释放的时候与时钟同步,其电路由两级触发器构成。
理想情形下的时序图如下:
假设在时钟上升沿时I_rst刚好释放掉,则第一级触发器的输出为亚稳态,第二级触发器不会立刻更新输出,即输出值还是为复位有效的高电平。当下一个时钟上升沿到来后,才会变为低电平,实现同步释放。
异步复位、同步释放电路的RTL代码如下:
module reset_module
(
input wire I_clk ,
input wire I_rst ,
input wire I_data_in ,
output reg O_data_out
);
reg rst_d0;
reg rst_d1;
always@(posedge I_clk or posedge I_rst) begin
if(I_rst) begin
rst_d0 <= 1'b1;
rst_d1 <= 1'b1;
end
else begin
rst_d0 <= 1'b0;
rst_d1 <= rst_d0;
end
end
always@(posedge I_clk or posedge rst_d1) begin
if(rst_d1)
O_data_out <= 1'b0;
else
O_data_out <= I_data_in;
end
endmodule | 综合的结果如下:
8.2 跨时钟域设计
信号从一个时钟域发送到另一个时钟域称为跨时钟域。如果两个时钟的相位关系不确定,则这两个时钟是异步时钟。一个设计中经常有多个时钟域,如果不能合理地处理跨时钟域的信号,会产生亚稳态,从而可能使整个系统崩溃,如何正确采集到跨时钟域的信号是FPGA设计中的重点。
8.2.1 单bit信号跨时钟域
单bit信号跨时钟域分为慢时钟域到快时钟域和快时钟域到慢时钟域两种情况。对于慢时钟域到快时钟域的情况,一般直接采用打两拍的方法,即信号经过两级D触发器来实现跨时钟域,可以极大降低亚稳态的发生概率。
注意在两级同步D触发器之前,不可以有组合逻辑,组合逻辑会带来额外的毛刺,因此数据在同步之前,必须在当前时钟域下经过一个D触发器打拍来消除毛刺。
对于快时钟域到慢时钟域的情形,或者慢时钟到快时钟域而快时钟的频率比慢时钟大不到2倍的情形,由于直接打两拍可能采不到数据,所以需要对数据进行展宽,在快时钟域下将数据展宽到大于慢时钟域的两个周期即可,满足奈奎斯特采样定理,再经过两级D触发器跨时钟域采集到数据。
单bit信号跨时钟域除了打两拍的方法,还能使用握手协议,这种方法可以使用在任意时钟域下,一般用于控制信号的跨时钟域处理。握手跨时钟域的代码如下:
module cdc_process
(
input wire I_CLKA ,
input wire I_RSTA ,
input wire I_DATA_A ,
input wire I_CLKB ,
input wire I_RSTB ,
output reg O_DATA_B
);
reg req_a;
reg ack_a_f;
reg ack_a_ff;
reg req_b_f;
reg req_b_ff;
wire req_b_pos;
assign req_b_pos = req_b_f && ~req_b_ff;
always@(posedge I_CLKA or posedge I_RSTA) begin
if(I_RSTA)
req_a <= 1'b0;
else if(I_DATA_A)
req_a <= 1'b1;
else if(ack_a_ff)
req_a <= 1'b0;
end
always@(posedge I_CLKA or posedge I_RSTA) begin
if(I_RSTA) begin
ack_a_f <= 1'b0;
ack_a_ff <= 1'b0;
end
else begin
ack_a_f <= req_b_ff;
ack_a_ff <= ack_a_f;
end
end
always@(posedge I_CLKB or posedge I_RSTB) begin
if(I_RSTB) begin
req_b_f <= 1'b0;
req_b_ff <= 1'b0;
end
else begin
req_b_f <= req_a;
req_b_ff <= req_b_f;
end
end
always@(posedge I_CLKB or posedge I_RSTB) begin
if(I_RSTB)
O_DATA_B <= 1'b0;
else if(req_b_pos)
O_DATA_B <= 1'b1;
else
O_DATA_B <= 1'b0;
end
endmodule | 首先将DATA_A信号展宽,通过两级触发器同步到CLKB时钟域下,通过捕获上升沿得到DATA_B,B时钟域下接收到数据后,返回一个ACK信号给A时钟域,A时钟域下展宽的信号拉低,即完成一次握手跨时钟域。
快时钟域到慢时钟域的仿真波形如下图所示。
慢时钟域到快时钟域的仿真波形如下图所示。
8.2.2 多bit信号跨时钟域
多bit信号跨时钟域不能采用打两拍的方法,这是因为每1bit的信号路径延时不同,不能保证每1bit在时钟域变化后都是同步的。格雷码的特点是每计数加一,只有1位信号跳变,所以可以先在发送时钟域下将数据转换成格雷码,再打两拍处理。格雷码一般用在计数器或者RAM地址跨时钟域的情形下。
多bit信号跨时钟域最常用、最简单的方法是使用异步FIFO或者双口RAM处理数据,通过调用厂商提供的IP核,即可安全、高效地处理跨时钟域的数据。
8.3 代码模块的拆分
在设计verilog代码时,经常需要用到加法器、比较器、乘法器等模块。部分工程师在模块设计时,对资源消耗不敏感,或是为了方便,在定义计数器时给了很大的位宽或在一个时钟周期内完成多次加法运算,在低速设计中,这样的写法可能不会造成什么影响,但是在高速设计中,这样的写法是不能存在的。下面是一个32位累加器的代码:
reg [31:0] cnt;
always@(posedge clk) begin
cnt <= cnt + 1;
end | 这几行代码看似简单,实际上我们查看时序报告,仅仅一个累加器便带来了8个逻辑级数,如果在加上一些判断逻辑,这样的代码就很难跑到200MHz。
在程序设计中,应当注意这类计算模块的设计,一般可以使用专用逻辑资源比如DSP,或是将代码拆分成多级流水,把原来一个时钟周期内完成的动作放在几个时钟周期里完成。
8.4 时钟组
对于同步电路而言,满足采样沿的建立时间和保持时间要求,数据就能在特定时间内稳定传输并正确被捕获。
然而,在异步时钟中,无法确定多个时钟之间的相位关系,因此无法直接使用常规的静态时序分析方法进行时序路径分析。对于没有明确相位关系的时钟信号,一个有效的策略是让时序工具在分析时忽略它们。默认情况下,vivado分析各时钟下所有的路径(除clock group 或false path约束)。
clock group是将多个时钟放到1个组内,工具不分析指定时钟组之间的时序路径,依旧分析是时钟组各时钟的关系,使用set_clock_groups约束。如果要设置两个时钟之间的路径不分析,使用set_false_path约束。
set_clock_groups [- name group_name]
[- group clock_list]
[- logically_exclusive ]
[- physically_exclusive ]
[- asynchronous ]
[- allow_paths ] | ·name:定义时钟组名
·group:定义单个时钟属于一组
·logically_exclusive:设计中共存多个时钟,并且电路仅选择一个
·physically_exclusive:设计中不能共存多个时钟
·asynchronous :设计中共存多个时钟,却没有相位关系
·allow_paths :只可和asynchronous使用,表示串扰分析时不禁用时钟之间的时序分析
logically_exclusive 、physically_exclusive、asynchronous这三个命令互斥,只能选一个使用,对于异步电路,通常使用asynchronous。
若两个异步时钟通过引脚引入,且时钟相位不固定:
create_clock -name clka-period 10 -waveform {0 5} [get_ports CLKA]
create_clock -name clkb-period 20 -waveform {0 10} [get_ports CLKB]
set_clock_groups -asynchronous -group {clka} -group {clkb} | 两个group时钟组不进行时序分析,上面的命令也可以用false path约束:
create_clock -name clka-period 10 -waveform {0 5} [get_ports CLKA]
create_clock -name clka-period 20 -waveform {0 10} [get_ports CLKB]
set_false_path -from [get_clocks clka] -to [get_clocks clkb]
set_false_path -from [get_clocks clkb] -to [get_clocks clka] | 若两个异步时钟通过clk引脚引入,clkb生成一个同步时钟DIVCLKB,它们与CLKA为异步时钟:
create_clock -name clka-period 10 -waveform {0 5} [get_ports CLKA]
create_clock -name clka-period 20 -waveform {0 10} [get_ports CLKB]
set_clock_groups -asynchronous -group [get_clocks clka] -group [get_clocks {clkb divclkb}] |
|