我有一个利用同步复位的fpga设计(我更喜欢同步复位到异步,原因在别处讨论)。我在设计中有四个不同的时钟域,我利用一个按钮来产生我的复位信号,这当然是完全异步的(保存我的手指)。我在四个时钟域中的每一个中去抖按钮信号,以从单个源生成四个域的同步复位。我的去抖模块基本上计算了被置位的复位按钮的N个时钟周期。如果复位断言超过N个周期,则生成复位信号(下面粘贴此模块的代码)。
第一个问题 - 有没有比这种方法更好的方法来产生重置?
第二个(更有趣的问题):当我查看时序报告(使用xilinx工具)时,我发现一致的限制信号都与模块有关。例如,限制路径是从复位发生器(去抖动器)到某个状态机的状态寄存器。复位信号的扇出非常高(它们触及各自时钟域中的所有内容)。虽然我的速度受到重置的限制,但我有点惊讶。我发现我只限于8.5 nS,其中~50%是路由,其中约50%是逻辑。关于如何做得更好的任何建议?你如何处理fpga设计中的同步复位生成?
这是重置生成的代码。请注意,信号复位信号类似于去抖动输出(例如,当我实例化模块时,debounced
输出是该特定时钟域的复位。)
module button_debouncer(/*AUTOARG*/
// Outputs
debounced,
// Inputs
clk, button
);
/* Parameters */
parameter WIDTH = 1;
parameter NUM_CLKS_HIGH = 12000000;
parameter log2_NUM_CLKS = 24;
/* Inputs */
input clk;
input [WIDTH-1:0] button;
/* Outputs */
output [WIDTH-1:0] debounced;
/* Regs and Wires */
reg [WIDTH-1:0] b1, b2;
reg [log2_NUM_CLKS-1:0] counter;
/* Synched to clock domain */
always @(posedge clk) begin
b1 <= button;
b2 <= b1;
end
/* Debounce the button */
always @(posedge clk) begin
if(~b2)
counter <= 0;
else if(counter < {log2_NUM_CLKS{1'b1}})
counter <= counter + 1;
end
/* Assign the output */
//wire [WIDTH-1:0] debounced = counter > NUM_CLKS_HIGH;
reg [WIDTH-1:0] debounced;
always @(posedge clk) begin
debounced <= counter > NUM_CLKS_HIGH;
end
endmodule //button_debouncer
答案 0 :(得分:2)
在使用重置时改善时序分数的一个非常好的方法是限制最大扇出。然后,工具将缓冲信号,以便没有一个lut试图被路由并用于驱动每个寄存器。这可以通过这种方式实现:
(* max_fanout = <arbitrary_value> *)
wire reset;
所以我们这里有一个vivado合成工具使用的约束(或者如果你仍在使用ISE,那么该工具)。此外,如果需要注意,这只会影响网络的下一个声明,因此在此之前或之后声明的其他网络(电线,注册,分机)不受影响。
xilinx网站上有一个很好的约束用户指南。您可能还需要查看其他一些内容,它们是:IBUF或BUFG。
答案 1 :(得分:0)
您不需要四个debouncer实例。在主时钟上放入一个去抖器,然后使用三个亚稳态滤波器将其输出同步到其他三个域。
此外,当您分发重置时,您应该使用Cliff Cummings所称的“同步重置分发树”。检查他的网站上的一些论文。