コード中に制約を書くときの注意点 のバックアップ(No.1)

更新


公開メモ

コード中に制約を埋め込む

次のコードは [[非同期信号を扱うための危ういVerilogライブラリ>電気回路/HDL/非同期信号を扱うための危ういVerilogライブラリ#非同期信号を2段FFで受ける]] で紹介している、非同期信号を同期化するためのコードです。

LANG:verilog
module double_ff #(
    parameter INIT = 1'b0
) (
    (* TIG="TRUE" *) input wire idata,
    input wire oclk,
    output wire odata
);
    wire temp;

    (* IOB="FALSE", RLOC="X0Y0", ASYNC_REG="TRUE" *)
    FDRSE #(
        .INIT(INIT)     // Initial value of register (1'b0 or 1'b1)
    ) temp0 (
        .Q(temp),       // Data output
        .C(oclk),       // Clock input
        .CE(1'b1),      // Clock enable input
        .D(idata),      // Data input
        .R(1'b0),       // Synchronous reset input
        .S(1'b0)        // Synchronous set input
    );

    (* IOB="FALSE", RLOC="X0Y0" *)
    FDRSE #(
        .INIT(INIT)     // Initial value of register (1'b0 or 1'b1)
    ) temp1 (
        .Q(odata),      // Data output
        .C(oclk),       // Clock input
        .CE(1'b1),      // Clock enable input
        .D(temp),       // Data input
        .R(1'b0),       // Synchronous reset input
        .S(1'b0)        // Synchronous set input
    );
endmodule

このモジュールは1つのプロジェクト中で何十回、何百回も使うことになるため、 上記のように TIG や IOB, ASYNC_REG, RLOC といった制約をコード中に埋め込めれば 大きな助けになります。

そうでなければ、何百もの double_ff のインスタンスに対応する個別の制約を .ucf ファイルに別途記述しなければならないからです。

上記制約は次の意味を持っています。

  • idata から temp0 へのパスは遅延解析が必要ないため idata に TIG を付けています
  • temp0 や temp1 が IOB に入らないように IOB を付けています
  • temp0 は非同期信号を受け取る事が分かっているので ASYNC_REG を付けています
  • temp0 と temp1 とが同じスライスに入るように RLOC を付けています

この書き方は ISE コードエディタの Language Templates の記述をほぼ踏襲しているのですが、 そのままつかうにはいくつか問題がありそうで、いろいろ調べてみました。

異なるパラメータでインスタンス化する

問題をはっきりさせるため、下記のあまり意味はないですがシンプルな例で見てみます。

LANG:verilog
module main1 (
    input clk_125MHz_in,

    input idata,
    output odata
);

    DCM_SP #(
        .CLKIN_PERIOD(8.0),         // Specify period of input clock
        .CLK_FEEDBACK("1X")         // Specify clock feedback of NONE, 1X or 2X
    ) DCM_SP_inst (
        .CLK0(clk_125MHz),          // 0 degree DCM CLK output
        .CLK90(clk_125MHz90),       // 90 degree DCM CLK output
        .CLKFB(clk_125MHz),         // DCM clock feedback
        .CLKIN(clk_125MHz_in)       // Clock input (from IBUFG, BUFG or DCM)
    );

    double_ff double_ff0  ( .idata(idata),      .oclk(clk_125MHz),   .odata(clk0_idata) );
    double_ff double_ff90 ( .idata(clk0_idata), .oclk(clk_125MHz90), .odata(clk90_idata) );
    assign odata = clk90_idata;

endmodule

対応する ucf ファイル

NET "clk_125MHz_in" TNM_NET = "clk_125MHz_in";
TIMESPEC TS_clk_125MHz_in = PERIOD "clk_125MHz_in" 8 ns HIGH 50 %;

これは、idata という非同期入力を clk_125MHz_in に同期化し、 さらにその信号を clk_125MHz_in と 90 度ずれた位相を持つクロックに同期化して、 出力します。

入力クロック clk_125MHz_in タイミング制約が掛かっているので、 clk_125MHz, clk_125MHz90 も 125MHz で動作するよう制約が掛かります。

PERIOD 制約について

この記事の趣旨から言えば、クロックに対する制約も .ucf ファイルに書くのではなく、

LANG:verilog
module main(
    (* PERIOD="125MHz" *) input clk_125MHz_in,

    input idata,
    output odata
);

のように書いてしまえばよいはずなのですが、これだと、インプリメント中のメッセージで

WARNING:Xst:1577 - Converting constraint on signal clk_125MHz_in: 'PERIOD=125MHz' 
to XCF style constraint NET clk_125MHz_in PERIOD = 125.000000 MHz HIGH 50 %

となって、一見うまく行っているように見えるものの、

WARNING:ConstraintSystem:3 - Constraint <TIMESPEC TS_clk_125MHz = PERIOD
  "clk_125MHz" TS_clk_125MHz_in HIGH 50%>: This constraint will be ignored
  because the relative clock constraint named 'TS_clk_125MHz_in' was not found.
WARNING:ConstraintSystem:3 - Constraint <TIMESPEC TS_clk_125MHz90 = PERIOD
  "clk_125MHz90" TS_clk_125MHz_in PHASE 2 ns HIGH 50%>: This constraint will be
  ignored because the relative clock constraint named 'TS_clk_125MHz_in' was
  not found.

となって、正しく制約が掛かりませんでした。

実際 .pcf にはクロック周期制約の記載がなく、どうなっているのかまだ不明です。

main1 に対する .pcf ファイル

上記コードをインプリメントすると、.pcf ファイルは次のようになりました。

SCHEMATIC START;
TIMEGRP clk_125MHz90    = BEL "double_ff90/double_ff_temp1" 
                          BEL "double_ff90/double_ff_temp0";
TIMEGRP clk_125MHz90_0  = BEL "double_ff90/double_ff_temp1" 
                          BEL "double_ff90/double_ff_temp0";
TIMEGRP clk_125MHz      = BEL "double_ff0/double_ff_temp1" 
                          BEL "double_ff0/double_ff_temp0";
TIMEGRP clk_125MHz_0    = BEL "double_ff0/double_ff_temp1" 
                          BEL "double_ff0/double_ff_temp0";
PIN DCM_SP_inst_pins<3> = BEL "DCM_SP_inst" PINNAME CLKIN;
TIMEGRP clk_125MHz_in = PIN "DCM_SP_inst_pins<3>";
TS_clk_125MHz_in  = PERIOD TIMEGRP "clk_125MHz_in"                         8 ns HIGH 50%;
TS_clk_125MHz     = PERIOD TIMEGRP "clk_125MHz"     TS_clk_125MHz_in            HIGH 50%;
TS_clk_125MHz90   = PERIOD TIMEGRP "clk_125MHz90"   TS_clk_125MHz_in PHASE 2 ns HIGH 50%;
TS_clk_125MHz_0   = PERIOD TIMEGRP "clk_125MHz_0"   TS_clk_125MHz_in            HIGH 50%;
TS_clk_125MHz90_0 = PERIOD TIMEGRP "clk_125MHz90_0" TS_clk_125MHz_in PHASE 2 ns HIGH 50%;
PIN "idata_IBUF_pins<1>"                 = BEL "idata_IBUF"                 PINNAME OUT;
PIN "double_ff0/double_ff_temp1_pins<2>" = BEL "double_ff0/double_ff_temp1" PINNAME Q;
PIN "idata_IBUF_pins<1>"                 TIG;
PIN "double_ff0/double_ff_temp1_pins<2>" TIG;
SCHEMATIC END;

クロックに対する記述を除くと、

PIN "idata_IBUF_pins<1>"                 = BEL "idata_IBUF"                 PINNAME OUT;
PIN "double_ff0/double_ff_temp1_pins<2>" = BEL "double_ff0/double_ff_temp1" PINNAME Q;
PIN "idata_IBUF_pins<1>"                 TIG;
PIN "double_ff0/double_ff_temp1_pins<2>" TIG;

の部分が double_ff 関連の制約です。

TIG は出力ピンまで遡ってかかる

.pcf の記述から、double_ff の入力ピンに記述した TIG 制約は、 そこに接続された出力ピンまで遡って付いています。

これはかなり困ったことで、その出力ピンに double_ff 以外のモジュールが接続されていると、 そちらへのパスも遅延解析から除かれてしまうことになります。

[添付]


Counter: 19825 (from 2010/06/03), today: 2, yesterday: 0