VHDL について

特に断らない限り環境は Xilinx 社の FPGA + ISE とします。


種々雑多な事


「ERROR:NgdBuild:755 - Line xxx in '*.ucf': Could not find net(s) 'xxx' in the design...」

入力信号として entity に宣言しているにもかからわず、このようなエラーが 出るときは、その入力信号で駆動しているロジックが外部に出力されているかを 確認する。外部に出力されない内部ロジックはすべて削除され、結果として 入力信号も削除される。ここで、User Constraints に入力信号に対する ピン番号を制約していれば、上記のようなエラーが発生する。

上記のエラーを抑制する方法として、「Implement Design」の「Translate」の「Properties」 にて、「Allow Unmatched LOC Constraints」にチェックをする。こうすれば、UCF に書いた 信号のピン番号制約のうち、ネットリストに存在しない信号の制約は無視される。 つまり、「Synthesize」で入力信号とその信号が駆動する内部ロジックが自動的に削除され、 そのピン番号の制約を UCF に書いていたとしても無視されるので、「ERROR:NgdBuild:755 ...」 のようなエラーは起きない。

そもそも合成のときにロジックを trim しないような設定ができればいいのだが...。 (schema 設計だとできるらしい。)


順次実行?

「process 文中は順次実行をする」とかいう説明をどこかの本で見かけたので、 実際に論理合成して調べてみた。 最初に結論を述べると、順次実行されるのは if 文などの条件判定であり、 Flip-Flop への代入は並列に実行される。以下、これを検証する。

まず、以下のような回路記述を論理合成する。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity TEST_1 is
        port (
                CLKIN : in std_logic;
                DIN : in std_logic;
                DOUT : out std_logic
        );
end TEST_1;

architecture Behavioral of TEST_1 is

        signal CLK : std_logic;
        signal A, B : std_logic;

        component IBUFG
                port (
                        I : in std_logic;
                        O : out std_logic
                );
        end component;
        attribute box_type : string;
        attribute box_type of IBUFG : component is "black_box";

begin

        IBUFG_0 : IBUFG
                port map (
                        I => CLKIN,
                        O => CLK
                );

        process (CLK)
        begin
                if (CLK'event and CLK = '1') then
                        A <= DIN;    -- (1)
                        B <= A;      -- (2)
                end if;
        end process;
       
        DOUT <= B
       
end Behavioral;

これを論理合成すると下図の様になった。

TEST_1.png

もし、process 文の中が順次実行されるのであれば、(1) と (2) の処理が順番に実 行され、B の出力は A と同じ値の DIN になるはずである。 つまり、DIN が右の FD の D 入力にも入力されるはずである。 しかし、この論理合成結果からは、A への DIN の代入と B への A の代入は 同時に行われている。 よって、process 文の中が順次実行されるというのは誤りである。 すなわち、process 文の中は同時実行のはずである。

次は process 文の中が順次実行だと思い込んで作った 10 進カウンタの誤例で ある。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity test01 is
        port (
                CLKIN : in std_logic;
                DOUT : out std_logic
        );
end test01;

architecture Behavioral of test01 is

        signal CLK : std_logic;
        signal COUNT_REG : std_logic_vector(3 downto 0);

        component IBUFG
                port (
                        I : in std_logic;
                        O : out std_logic
                );
        end component;
        attribute box_type : string;
        attribute box_type of IBUFG : component is "black_box";

begin

        IBUFG_0 : IBUFG
                port map (
                        I => CLKIN,
                        O => CLK
                );

        process (CLK)
        begin
                if (CLK'event and CLK = '1') then
                        COUNT_REG <= COUNT_REG + 1;    -- (1)
                        if (COUNT_REG = "1010") then
                                COUNT_REG <= "0000";   -- (2)
                        end if;
                end if;
        end process;
       
        DOUT <= COUNT_REG(3);

end Behavioral;

上記の回路記述は、COUNT_REG をインクリメントし、値が "1010" (10 進数の 10) になったら "0000" (10 進数の 0) にリセットすることを意図しているが、 この記述には問題がある。 まず、先程の実検結果から process 文の中は同時実行であるはずなので、 上の例のように、インクリメントしてその結果が "1010" だったらリセットする、 といった動作をする回路を生成しない。 正しくは、クロック(CLK)の立ち上がりで、COUNT_REG のインクリメントとい う処理(1)と if 文の処理が同時に実行される。いま、COUNT_REG の値が "1010" の時にクロックが立ち上がった、つまり if 文が実行されると仮定す る。クロックの立ち上がりによって、(1) が実行され、なおかつ (2) も実行され るのである。この時、一体 COUNT_REG の値はどうなるのだろうか。かなり謎である。

ちなみに、この回路記述の論理合成結果はこのようになった。

TEST_2.png TEST_2_1.png
(実際には、加算器の入力の A(3:0) は Q(3:0) と接続されている)
TEST_2_2.png

Xilinx の ECS (RTL Viewer) で階層構造を flatten にできないので、 それぞれ個別に表示している(やっぱ Synplify がいいなぁ...)。 これを見る限り、COUNT_REG の値が "1010" の時にクロックが立ち上がると、 COUNT_REG が "0000" にリセットされるという 11 進カウンタが生成されてい るようだ。COUNT_REG が multiple drive にならないのがかなり謎だ。

通常、10 進カウンタは以下のような記述になると思う。

process (CLK)
begin
        if (CLK'event and CLK = '1') then
                if (COUNT_REG = "1001") then
                        COUNT_REG <= "0000";
                else
                        COUNT_REG <= COUNT_REG + 1;
                end if;
        end if;
end process;

クロックが立ち上がったとき、COUNT_REG が "1001" (10 進数の 9) なら "0000" (10 進数の 0) にリセットし、そうでなければインクリメントする、 という記述になっている。


スイッチ入力のチャタリング除去回路

チャタリングを除去するにはいろいろ方法があるらしいが、チャタリングがお さまる時間以上の時間間隔にてスイッチの入力信号をサンプリングする方法が 簡単だと思う。 スイッチの物理的特性によるかもしれないが、だいたい 1 [ms] ~ 10 [ms] 前後をサンプリング間隔とすれば十分だと思う。何遍も必要となる処理なので、 一応記載しておくことにする。

具体的には、10 [ms] でベースクロック 1 波長分のパルスを出力する回路を 作り、そのパルスを条件にスイッチ入力をラッチすればよい。 HDL で書けばこんな感じか。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity TEST is
        generic (
                constant BASE_CLK : integer := 27000000  -- ベースクロックは 27 [MHz] とする
        );

        port (
                CLK_IN : in std_logic;
                SW_IN : in std_logic;
                LED_OUT : out std_logic
        );
end TEST;

architecture Behavioral of TEST is

        signal CLK : std_logic;
        constant COUNT_10MSEC_LAST : integer := (BASE_CLK/100-1);
        signal COUNT_10MSEC : integer range 0 to COUNT_10MSEC_LAST;
        signal SW_CUR, SW_PREV : std_logic;
        signal LED_REG : std_logic;

        component IBUFG
                port (
                        I : in std_logic;
                        O : out std_logic
                );
        end component;
        attribute box_type : string;
        attribute box_type of IBUFG : component is "black_box";

begin

        IBUFG_0 : IBUFG
                port map (
                        I => CLK_IN,
                        O => CLK
                );

        process (CLK)
        begin
                if (CLK'event and CLK = '1') then
                        if (COUNT_10MSEC = COUNT_10MSEC_LAST) then
                                SW_CUR <= SW_IN;
                                COUNT_10MSEC <= 0;
                        else
                                COUNT_10MSEC <= COUNT_10MSEC + 1;
                        end if;
                        SW_PREV <= SW_CUR;
                        if (SW_CUR = '0' and SW_PREV = '1') then
                                -- スイッチが押された時の処理
                                LED_REG <= not LED_REG; -- LED を反転させる、とか
                        end if;
                end if;
        end process;

        LED_OUT <= LED_REG;

end Behavioral;

要するに微分回路だやね。 IBUFG の instantiation とか余計なのが入っているけど、多分これで論理合 成通ると思う(試してないけど)。


一石 <isseki@susutawari.org>
Last modified: Wed Sep 6 08:11:37 2006