特に断らない限り環境は Xilinx 社の FPGA + ISE とします。
以下のように IBUFG の primitive を instantiation し、GCLK ピンに 割り当てる信号 (ここでは CLK_IN) を IBUFG を通し、内部ではその出力 (CLK) を用いる。
entity ... is port ( CLK_IN : in std_logic; -- GCLK ピンに割り当てる信号 ... ); end ...; begin architecture ... is component IBUFG port ( I : in std_logic; O : out std_logic ); end component; signal CLK : std_logic; begin IBUFG_0 : IBUFG port map ( I => CLK_IN, O => CLK ); -- 以下、クロックには CLK を使う。 process (CLK) begin ... end ...;
process (CLK) begin if (CLK'event and CLK = '1') then if (A = '1' and B = '0') then C <= '1'; end if; end if; end process;のように記述すると、C は常に (if 文の条件が真でない時でも) 1 を出力してしまう。 これを回避するには、条件が成り立っていない場合を明確にすればよい。すなわち、
process (CLK) begin if (CLK'event and CLK = '1') then if (A = '1' and B = '0') then C <= '1'; else C <= '0'; end if; end if; end process;で解決する。
-- ASYNC_SIGNAL 非同期信号 process (CLK) begin if (CLK'event and CLK = '1') then CURRENT_ASYNC_SIGNAL <= ASYNC_SIGNAL; PREVEOUS_ASYNC_SIGNAL <= CURRENT_ASYNC_SIGNAL; if (CURRENT_ASYNC_SIGNAL = '0' and PREVEOUS_ASYNC_SYGNAL = '1') then -- 非同期信号の立ち下がりで行う処理 -- 実際には立ち下がりから 2 クロック遅れる end if; end if; end process;
入力信号として 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;
これを論理合成すると下図の様になった。
もし、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 の値はどうなるのだろうか。かなり謎である。
ちなみに、この回路記述の論理合成結果はこのようになった。
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 とか余計なのが入っているけど、多分これで論理合 成通ると思う(試してないけど)。