01signal.com

Quartus: 将寄存器(registers)装入 I/O cells

通常我更喜欢通过确保将所有寄存器放入 I/O 单元来处理 I/O 时序。时序(timing)很重要。

I/O register packing 似乎不是 Quartus的默认设置。无论如何,这是懒人应对这种情况的秘诀。

在这篇文章的前一个版本中,我建议在所有 I/Os上禁用时序 checking (timing checking)。这会在实现(implementation)期间使 unconstrained path warning 静音,特别是它可以防止 Quartus报告窗格中的“TimeQuest Timing Analyzer”部分变红:

set_false_path -from [get_ports]
set_false_path -to [get_ports]

事实证明,这不是一个好主意,尤其是关于输入端口。这将在下面进一步详述。

然而,需要说服 fitter 将寄存器放入 I/O block。在 QSF中,添加

set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to *
set_instance_assignment -name FAST_INPUT_REGISTER ON -to *
set_instance_assignment -name FAST_OUTPUT_ENABLE_REGISTER ON -to *

绝对在每个寄存器(register)上制作这些 assignments 有点激进,但它确实完成了工作。 fitter 为 I/O elements 发出 warnings ,它未能强制执行这些约束(constraints),这实际上是一件好事。

要查看它的运行情况,请查看 fitter 报告的“资源 Section(Resource Section)”(可能在 Quartus的报告 pane(reports pane)中找到它)并寻找“输入寄存器(Input Registers)”等,无论适用。

在涉及 I/O cells的路径(paths)的时序报告(timing reports)中,差异很明显。例如,比较这个涉及 I/O 寄存器的路径:

+----------------------------------------------------------------------------------+
; Data Arrival Path                                                                ;
+---------+---------+----+------+--------+-----------------------+-----------------+
; Total   ; Incr    ; RF ; Type ; Fanout ; Location              ; Element         ;
+---------+---------+----+------+--------+-----------------------+-----------------+
; 2.918   ; 2.918   ;    ;      ;        ;                       ; data path       ;
;   0.000 ;   0.000 ;    ;      ; 1      ; DDIOOUTCELL_X3_Y0_N32 ; rst             ;
;   0.465 ;   0.465 ; RR ; CELL ; 1      ; DDIOOUTCELL_X3_Y0_N32 ; rst|q           ;
;   0.465 ;   0.000 ; RR ; IC   ; 1      ; IOOBUF_X3_Y0_N30      ; RESETB~output|i ;
;   2.918 ;   2.453 ; RR ; CELL ; 1      ; IOOBUF_X3_Y0_N30      ; RESETB~output|o ;
;   2.918 ;   0.000 ; RR ; CELL ; 0      ; PIN_P3                ; RESETB          ;
+---------+---------+----+------+--------+-----------------------+-----------------+

请注意 DDIOOUTCELL 元素,以及寄存器和 IOOBUF之间的布线(routing)中的 zero increment 。

作为比较,这里有一条路径(path)没有应用 I/O 寄存器(因为它被逻辑阻止了):

+--------------------------------------------------------------------------------+
; Data Arrival Path                                                              ;
+---------+---------+----+------+--------+-----------------+---------------------+
; Total   ; Incr    ; RF ; Type ; Fanout ; Location        ; Element             ;
+---------+---------+----+------+--------+-----------------+---------------------+
; 8.284   ; 8.284   ;    ;      ;        ;                 ; data path           ;
;   0.000 ;   0.000 ;    ;      ; 1      ; FF_X3_Y0_N17    ; Dir_flop_sig        ;
;   0.496 ;   0.496 ; RR ; CELL ; 8      ; FF_X3_Y0_N17    ; Dir_flop_sig|q      ;
;   2.153 ;   1.657 ; RR ; IC   ; 1      ; IOOBUF_X3_Y0_N9 ; DATA[7]~output|oe   ;
;   8.284 ;   6.131 ; RF ; CELL ; 1      ; IOOBUF_X3_Y0_N9 ; DATA[7]~output|o    ;
;   8.284 ;   0.000 ; FF ; CELL ; 1      ; PIN_T3          ; DATA[7]             ;
+---------+---------+----+------+--------+-----------------+---------------------+

在这里,我们看到通用触发器(flip-flop)如何生成信号,导致布线时延(routing delay)相当于 1.657 ns。主要问题是每个实现(implementation)的布线时延都不同,所以如果主板有信号 integrity (signal integrity)问题, FPGA 可能会受到指责,因为不同的 FPGA 设计版本似乎可以解决问题或使其重新出现。

时序约束(Timing constraints)

输入端口和输出端口(output ports)都应该有紧时序约束,所以除了充分利用 I/O 寄存器之外是无法满足的。如果所需的寄存器 packing(register packing)出现问题,这不仅会生成时序 failure (timing failure),而且还需要实现最小的 input-to-register 时序,如下所述。

下面的讨论仅适用于驱动寄存器的时钟与外部时钟直接相关的情况(即,使用将时钟与整数相乘的锁相环(PLL))。如果驱动寄存器的时钟实际上与外部时钟无关,那么事情就会变得更加复杂,正如本文中所讨论的

要演示此问题,请考虑以下 Verilog 代码:

module top
  (
   input        clk,
   input        in,
   output reg   out
   );

   reg 		in_d, in_d2;
   wire  	pll_clk;

   always @(posedge pll_clk)
     begin
	in_d <= in;
	in_d2 <= in_d;
	out <= in_d2;
     end

  /* Here comes an instantiation of a phase-compensating PLL, which
     doesn't change the frequency */
endmodule

还要考虑 SDC file中的以下约束:

create_clock -name main_clk -period 10 -waveform { 0 5 } [get_ports {clk}]

derive_pll_clocks
derive_clock_uncertainty

set_input_delay -clock main_clk -max 8.5 [get_ports in*]
set_input_delay -clock main_clk -min 0 [get_ports in*]

正如这篇文章所解释的, set_input_delay 是信号源的最大时延(delay),从时钟到有效的逻辑状态。由于时钟的时间段设置为 10 ns,因此将时延约束(delay constraint)设置为 8.5 ns 允许 1.5 ns 直到下一个时钟到达(在 10 ns处)。换句话说, FPGA的引脚上的 setup time 有一个约束(constraint),强制它不能超过 1.5 ns。

请注意, set_max_delay 也可以用于此目的(在某些情况下这是唯一的方法),如本文中所述。

这次编译(compilation)(连同上面显示的 FAST_INPUT_REGISTER ON QSF 分配)在时序报告中产生以下段:

+----------------------------------------------------------------------------------+
; Data Arrival Path                                                                ;
+---------+---------+----+------+--------+-------------------+---------------------+
; Total   ; Incr    ; RF ; Type ; Fanout ; Location          ; Element             ;
+---------+---------+----+------+--------+-------------------+---------------------+
; 0.000   ; 0.000   ;    ;      ;        ;                   ; launch edge time    ;
; 0.000   ; 0.000   ;    ;      ;        ;                   ; clock path          ;
;   0.000 ;   0.000 ; R  ;      ;        ;                   ; clock network delay ;
; 8.500   ; 8.500   ; F  ; iExt ; 1      ; PIN_F2            ; in                  ;
; 9.550   ; 1.050   ;    ;      ;        ;                   ; data path           ;
;   8.500 ;   0.000 ; FF ; IC   ; 1      ; IOIBUF_X0_Y22_N15 ; in~input|i          ;
;   9.308 ;   0.808 ; FF ; CELL ; 1      ; IOIBUF_X0_Y22_N15 ; in~input|o          ;
;   9.308 ;   0.000 ; FF ; IC   ; 1      ; FF_X0_Y22_N17     ; in_d|d              ;
;   9.550 ;   0.242 ; FF ; CELL ; 1      ; FF_X0_Y22_N17     ; in_d                ;
+---------+---------+----+------+--------+-------------------+---------------------+

与输出寄存器(output register)的情况不同,列表中没有类型为“DDIOINCELL”的触发器,但有一些看起来像普通触发器的东西。但是,请注意,此触发器的接线有 zero 时延(标记为红色),这清楚地表明触发器和输入缓冲区已熔合在一起。

此输入的数据手册报告(datasheet report)表示:

+---------------------------------------------------------------------------------------------------+
; Setup Times                                                                                       ;
+-----------+------------+-------+-------+------------+---------------------------------------------+
; Data Port ; Clock Port ; Rise  ; Fall  ; Clock Edge ; Clock Reference                             ;
+-----------+------------+-------+-------+------------+---------------------------------------------+
; in        ; main_clk   ; 1.282 ; 1.461 ; Rise       ; altpll_component|auto_generated|pll1|clk[0] ;
+-----------+------------+-------+-------+------------+---------------------------------------------+

+-----------------------------------------------------------------------------------------------------+
; Hold Times                                                                                          ;
+-----------+------------+--------+--------+------------+---------------------------------------------+
; Data Port ; Clock Port ; Rise   ; Fall   ; Clock Edge ; Clock Reference                             ;
+-----------+------------+--------+--------+------------+---------------------------------------------+
; in        ; main_clk   ; -0.683 ; -0.862 ; Rise       ; altpll_component|auto_generated|pll1|clk[0] ;
+-----------+------------+--------+--------+------------+---------------------------------------------+

根据要求, FPGA 所需的 setup time 低于约束设置的 1.5 ns 限制。

现在让我们用 2 ns松开输入 setup delay (input setup delay),其余一切保持原样,重新运行编译(compilation):

set_input_delay -clock main_clk -max 6.5 [get_ports in*]
set_input_delay -clock main_clk -min 0 [get_ports in*]

时序报告中的部分现在显示:

+----------------------------------------------------------------------------------+
; Data Arrival Path                                                                ;
+---------+---------+----+------+--------+-------------------+---------------------+
; Total   ; Incr    ; RF ; Type ; Fanout ; Location          ; Element             ;
+---------+---------+----+------+--------+-------------------+---------------------+
; 0.000   ; 0.000   ;    ;      ;        ;                   ; launch edge time    ;
; 0.000   ; 0.000   ;    ;      ;        ;                   ; clock path          ;
;   0.000 ;   0.000 ; R  ;      ;        ;                   ; clock network delay ;
; 6.500   ; 6.500   ; F  ; iExt ; 1      ; PIN_F2            ; in                  ;
; 8.612   ; 2.112   ;    ;      ;        ;                   ; data path           ;
;   6.500 ;   0.000 ; FF ; IC   ; 1      ; IOIBUF_X0_Y22_N15 ; in~input|i          ;
;   7.308 ;   0.808 ; FF ; CELL ; 1      ; IOIBUF_X0_Y22_N15 ; in~input|o          ;
;   8.370 ;   1.062 ; FF ; IC   ; 1      ; FF_X0_Y22_N17     ; in_d|d              ;
;   8.612 ;   0.242 ; FF ; CELL ; 1      ; FF_X0_Y22_N17     ; in_d                ;
+---------+---------+----+------+--------+-------------------+---------------------+

嗯?互连突然上升到 1.062 ns?!请注意,寄存器的位置没有改变,因此毫无疑问 in_d 是 I/O 寄存器。那么这个延迟是从哪里来的呢?

要回答这个问题,需要仔细研究设计。完整的编译,选择工具 > Netlist Viewers > Technology Map Viewer (Post-Fitting)(Tools > Netlist Viewers > Technology Map Viewer (Post-Fitting))后,出现如下 diagram (部分如下图,点击放大):

Design diagram

右键单击 in_d (寄存器)并选择 Locate Note > Locate in Resource Property Editor 显示以下内容(点击放大):

Property editor view

在此图的右侧(上面未显示),属性“输入 Pin to Input Register Delay(Input Pin to Input Register Delay)”设置为 2。这就是时延的原因。在约束松动之前,它被设置为 0。直接的教训是:

如果 setup 约束未设置为技术的最佳价值, Quartus 可能会增加时延的费用。

但是为什么, Quartus,为什么?

所以有人可能会奇怪为什么 Quartus 会在输入 pad (input pad)和寄存器之间插入这个时延(delay)。尽快取样不是重点吗?要回答这个问题,让我们看一下更新后的数据手册报告:

---------------------+
; Data Port ; Clock Port ; Rise  ; Fall  ; Clock Edge ; Clock Reference                             ;
+-----------+------------+-------+-------+------------+---------------------------------------------+
; in        ; main_clk   ; 2.205 ; 2.523 ; Rise       ; altpll_component|auto_generated|pll1|clk[0] ;
+-----------+------------+-------+-------+------------+---------------------------------------------+

+-----------------------------------------------------------------------------------------------------+
; Hold Times                                                                                          ;
+-----------+------------+--------+--------+------------+---------------------------------------------+
; Data Port ; Clock Port ; Rise   ; Fall   ; Clock Edge ; Clock Reference                             ;
+-----------+------------+--------+--------+------------+---------------------------------------------+
; in        ; main_clk   ; -1.570 ; -1.882 ; Rise       ; altpll_component|auto_generated|pll1|clk[0] ;
+-----------+------------+--------+--------+------------+---------------------------------------------+

回想一下, 2 ns 是从时延约束缩减而来的。因此,最大允许 setup time 从 1.5 ns 上升到 3.5 ns。很容易看出满足了这个要求, slack 几乎是 1 ns。

所以 Quartus 说“我可以轻松满足 setup 的要求,有多余的 2 ns。让我们给 setup time额外的 1 ns ,给 hold time requirement 额外的 1 ns (即 0 ns)”。事实上,通过在时延中添加 1.062 ns , hold time 从 -0.683 ns 改进到 -1.570 ns (请不要指责我为什么差异不准确)。

底线: Quartus 扩大了 setup 和 hold的余量,使输入对抖动(jitter)更加稳健。虽然这是一件相当明智的事情,但这通常是不希望发生的,也不期望发生。

结论: 如果你想从输入到寄存器获得绝对最小的时延,运行一次编译与一个失败的时延约束,然后松开约束刚好足以解决这个失败。这确保了 Quartus 不会为了更好的 hold time而添加这个输入时延(input delay)来尝试“改进”时序。

使用 DDR primitives

Intel的 FPGAs 在 I/O cells 上或附近有逻辑,专用于在双时钟 rate(clock rate)上产生输出以及对输入进行采样。此主题在相关用户指南 ug_altddio.pdf中有详细说明。实例化 DDR primitive (或使用 ALTDDIO_BIDIR megafunction)是强制工具将寄存器放入 I/O cells的一种有吸引力的方式。但是,这不一定是一个好主意。

例如,像这样的例化(instantiation):

altddio_bidir ioddr
 (
 .padio(pin),
 .aclr (1'b0),
 .datain_h(datain_h),
 .datain_l(datain_l),
 .inclock(clk),
 .oe(oe),
 .outclock(clk),
 .dataout_h(dataout_h),
 .dataout_l(dataout_l),
 .oe_out (),
 .aset (1'b0),
 .combout(),
 .dqsundelayedout(),
 .inclocken(1'b1),
 .outclocken(1'b1),
 .sclr(1'b0),
 .sset(1'b0));
 defparam
   ioddr.extend_oe_disable = "OFF",
   ioddr.implement_input_in_lcell = "OFF",
   ioddr.intended_device_family = "Cyclone IV E",
   ioddr.invert_output = "OFF",
   ioddr.lpm_hint = "UNUSED",
   ioddr.lpm_type = "altddio_bidir",
   ioddr.oe_reg = "REGISTERED",
   ioddr.power_up_high = "OFF",
   ioddr.width = 1;

这确实导致逻辑实现了双向 DDR 接口,但就时序而言,至少在 Cyclone IV上取得了部分成功。 clock-to-output 时序与封装在 I/O cell中的普通输出寄存器完全相同,但输入路径(input path)的延迟实际上与上面的例化相比更差。结果可能与其他 Intel FPGA 系列不同。

请注意,为了用 DDR primitive模仿普通的 SDR 寄存器,它的 datain_h 端口和 datain_l 端口必须连接到同一根电线,因此时钟的下降沿(falling edge)不会改变任何东西。同样,应该忽略 dataout_l 端口的值,因为它是在下降沿上采样的。另请注意,输出使能端口(output enable port)(oe) 是 SDR 输入— 据我所知,无法通过 DDR rate 和 Intel FPGAs打开和关闭 high-Z 。至少不使用提供的逻辑 primitives(logic primitives)。

现在来解释为什么它在输出寄存器上运行良好,而不是输入: 提示在上面的时序报告中: 即使对于普通的 I/O cell 寄存器, DDIOOUTCELL_Xn_Ym_Nk 组件也用作寄存器。换句话说, DDR output 寄存器甚至用于 single-rate 输出,但仅与一个时钟边沿(clock edge)一起使用。至于输入路径,上面的时序报告表明使用了逻辑阵列寄存器(logic fabric register)(FF_Xn_Ym_Nk)。这是症结所在: DDR input 逻辑也在逻辑阵列(logic fabric)中实现。更糟糕的是,在 DDR的情况下,组合 blocks (combinatorial blocks)被挤在 I/O cell 和触发器之间。坦率地说,我不明白为什么,因为每个这样的组合 block (combinatorial block)只是单个输入到单个输出之间的传递。

这些观察得到了时序报告以及 Quartus的 Post-Fit Technology Map Viewer显示的图纸的支持。尤其是那些无用的组合 blocks ,在这些信息源中更是一目了然。

整个问题很可能从一 FPGA family 到另一个不同。至于 Cyclone IV,只有将 DDR primitives 用于输出才有意义。

更重要的是,当需要输出寄存器时使用 DDR primitive 输出的事实允许生成与其他输出对齐的输出时钟(output clock)。为此,请分别在 datain_h 端口和 datain_l 端口上为 DDR output primitive 提供常数 '1' 和 '0' 。至于其他输出,则需要使用输出寄存器封装。因此,其他输出的切换与来自 DDR 输出的时钟的上升沿(rising edge)对齐。

嗯,差不多。输出时钟的时序分析(timing analysis)不同,因为时钟切换多路选择器(mux),选择两个输出寄存器(output registers)中的哪一个提供输出(水平滚动查看详细信息):

+------------------------------------------------------------------------------------------------------------------------------------+
; Data Arrival Path                                                                                                                  ;
+---------+---------+----+------+--------+-------------------------+-----------------------------------------------------------------+
; Total   ; Incr    ; RF ; Type ; Fanout ; Location                ; Element                                                         ;
+---------+---------+----+------+--------+-------------------------+-----------------------------------------------------------------+
; 0.000   ; 0.000   ;    ;      ;        ;                         ; launch edge time                                                ;
; 0.000   ; 0.000   ;    ;      ;        ;                         ; clock path                                                      ;
;   0.000 ;   0.000 ; R  ;      ;        ;                         ; clock network delay                                             ;
; 0.000   ; 0.000   ; R  ;      ; 1      ; PIN_B12                 ; osc_clock                                                       ;
; 5.610   ; 5.610   ;    ;      ;        ;                         ; data path                                                       ;
;   0.000 ;   0.000 ; RR ; IC   ; 1      ; IOIBUF_X19_Y29_N8       ; osc_clock~input|i                                               ;
;   0.667 ;   0.667 ; RR ; CELL ; 2      ; IOIBUF_X19_Y29_N8       ; osc_clock~input|o                                               ;
;   0.853 ;   0.186 ; RR ; IC   ; 1      ; CLKCTRL_G12             ; osc_clock~inputclkctrl|inclk[0]                                 ;
;   0.853 ;   0.000 ; RR ; CELL ; 165    ; CLKCTRL_G12             ; osc_clock~inputclkctrl|outclk                                   ;
;   1.971 ;   1.118 ; RR ; IC   ; 1      ; DDIOOUTCELL_X16_Y29_N11 ; sram_controller_ins|ddr_clk|auto_generated|ddio_outa[0]|muxsel  ;
;   3.137 ;   1.166 ; RR ; CELL ; 1      ; DDIOOUTCELL_X16_Y29_N11 ; sram_controller_ins|ddr_clk|auto_generated|ddio_outa[0]|dataout ;
;   3.137 ;   0.000 ; RR ; IC   ; 1      ; IOOBUF_X16_Y29_N9       ; sram_clk~output|i                                               ;
;   5.610 ;   2.473 ; RR ; CELL ; 1      ; IOOBUF_X16_Y29_N9       ; sram_clk~output|o                                               ;
;   5.610 ;   0.000 ; RR ; CELL ; 0      ; PIN_E10                 ; sram_clk                                                        ;
+---------+---------+----+------+--------+-------------------------+-----------------------------------------------------------------;

请注意,这不是 register-to-pin 分析,而是 clock-to-pin。不过, set_output_delay 约束将包含此路径。但是,从寄存器到端口的 set_max_delay 约束,如果使用的话,不会包含这条路径,所以需要单独处理。换句话说,如果使用 set_max_delay ,它必须是以下形式:

set_max_delay -from [get_clocks main_clk] -to [get_ports sram_clk] 3.8

现在,将此与另一个引脚与相同的电压标准(voltage standard)等进行比较,仅由寄存器驱动:

+----------------------------------------------------------------------------------------------------------------------+
; Data Arrival Path                                                                                                    ;
+---------+---------+----+------+--------+-------------------------+---------------------------------------------------+
; Total   ; Incr    ; RF ; Type ; Fanout ; Location                ; Element                                           ;
+---------+---------+----+------+--------+-------------------------+---------------------------------------------------+
; 0.000   ; 0.000   ;    ;      ;        ;                         ; launch edge time                                  ;
; 2.507   ; 2.507   ;    ;      ;        ;                         ; clock path                                        ;
;   0.000 ;   0.000 ;    ;      ;        ;                         ; source latency                                    ;
;   0.000 ;   0.000 ;    ;      ; 1      ; PIN_B12                 ; osc_clock                                         ;
;   0.000 ;   0.000 ; RR ; IC   ; 1      ; IOIBUF_X19_Y29_N8       ; osc_clock~input|i                                 ;
;   0.667 ;   0.667 ; RR ; CELL ; 2      ; IOIBUF_X19_Y29_N8       ; osc_clock~input|o                                 ;
;   0.853 ;   0.186 ; RR ; IC   ; 1      ; CLKCTRL_G12             ; osc_clock~inputclkctrl|inclk[0]                   ;
;   0.853 ;   0.000 ; RR ; CELL ; 165    ; CLKCTRL_G12             ; osc_clock~inputclkctrl|outclk                     ;
;   1.970 ;   1.117 ; RR ; IC   ; 1      ; DDIOOUTCELL_X37_Y29_N11 ; sram_controller_ins|dq_wr_data[6]|clk             ;
;   2.507 ;   0.537 ; RR ; CELL ; 1      ; DDIOOUTCELL_X37_Y29_N11 ; sram_controller:sram_controller_ins|dq_wr_data[6] ;
; 5.645   ; 3.138   ;    ;      ;        ;                         ; data path                                         ;
;   2.717 ;   0.210 ;    ; uTco ; 1      ; DDIOOUTCELL_X37_Y29_N11 ; sram_controller:sram_controller_ins|dq_wr_data[6] ;
;   3.182 ;   0.465 ; RR ; CELL ; 1      ; DDIOOUTCELL_X37_Y29_N11 ; sram_controller_ins|dq_wr_data[6]|q               ;
;   3.182 ;   0.000 ; RR ; IC   ; 1      ; IOOBUF_X37_Y29_N9       ; sram_dq[6]~output|i                               ;
;   5.645 ;   2.463 ; RR ; CELL ; 1      ; IOOBUF_X37_Y29_N9       ; sram_dq[6]~output|o                               ;
;   5.645 ;   0.000 ; RR ; CELL ; 1      ; PIN_G14                 ; sram_dq[6]                                        ;
+---------+---------+----+------+--------+-------------------------+---------------------------------------------------;

clock-to-output 的总时间相差不超过 35 ps,尽管后者的路径在表面上完全不同。这不是巧合。 FPGA 显然是为产生这种相似性而设计的。具体来说,上面的时序分析是 100°C温度下的 slow 1200 mV ,但这个微小的差异在其他分析条件下也是一致的。

此页面由英文自动翻译。 如果有不清楚的地方,请参考原始页面
Copyright © 2021-2024. All rights reserved. (b4b9813f)