デジタル回路のスイッチ入力の同期化_10msecパルス生成

今回は1msecパルスを元にして10msecパルスを作ります。初心者で犯しがちなのが、1msecパルス幅の10msecを作ってしまう間違いです。クロックに同期する必要がありますので、パルス幅は10MHzにします。今回設計するブロックを次に示します。

前のブロック図とちょっと変わったのがわかるでしょうか? 黄色い部分が1msecを包み込んでいます。今回、1msecパルスの回路を子にして親を10msecパルス生成回路にしてみます。
このようにした理由ですが、この方が分かりやすい。というのもあるのですが、一番の理由は設計検証です。設計検証はシミュレータで行う訳ですが、まだ1msec生成回路をシミュレーションしていません。もちろん、この回路だけをシミュレートして、別に10msecの回路をシミュレートしても問題ありません。
しかし、シミュレーションする際に、上のブロック図で赤矢印の部分を実際の回路のHDLでシミュレートするでしょうか? シミュレートのHDLで上の赤矢印を書くと、それは実際の回路ではなくシミュレートの仮想回路になってしまいます。
赤矢印のワイヤーを次のように宣言したとします。
signal wtim1msec: std_logic; — 1msec pulse
さて、このsignal宣言はVHDLのテストベンチに書いていますか? テストされる側の回路で書いていますか? の違いです。もし、テストベンチでこのワイヤーを書いてしまうと、それはせっかくテストしたワイヤーが実際の回路に入らない。という事になります。もちろん、このワイヤーが無いと動きませんので、どこかで接続するのですが、そのワイヤーをテストしていない事になります。
テストしていないワイヤーがある。というのは非常に危険な事です。それゆえ、先のsignal wtim1msec: std_logic;はテストされる側のファイルに記載したいのです。
今回のように1本のワイヤーなので、間違うことはまず無いでしょう。しかし、人はどこで間違うのかわかりません。間違いを起こしにくい設計というのを心掛けるのは大切な事です。
私はこの時間計測の回路のような汎用性の高い回路はなるべく階層構造で一つのモジュールとして仕上げるようにしています。そうすれば、別の回路を設計するときに使いまわしできますし、なによりそのモジュールを使った製品がある。というのは検証を行う上で大変心強いものです。いわゆる実証済み。とか言われる回路ブロックになるわけです。
-- TITLE: "Count10msec.vhd" -- MODULE NAME: -- PROJECT CODE: -- AUTHOR: (****@nakaharagiken.com) -- CREATION DATE: 2020.7.4 -- SOURCE: -- LICENSE: Copyright (c) 2020 nakaharagiken All rights reserved. -- DESCRIPTION: -- NOTES TO USER: 入力10MHzで10msecのカウントパルス生成 -- SOURCE CONTROL: -- REVISION HISTORY: v0.1 2020.7.1 First edition -- -- -- 入力クロック 10MHz = 100nsec -- enable入力 1msec -- 出力パルス 10msec -- -- 10msec / 1msec = 10(dec) = a(hex) -- -- library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_unsigned.all; use IEEE.std_logic_arith.all; entity Count10msec is port ( clock: in std_logic; -- system clock 10MHz nreset: in std_logic; -- asynchronous reset ____reset___|~~~normal~~~ tim1msec: out std_logic; -- 1msec pulse ___________|~|___________ tim10msec: out std_logic -- 10msec pulse _______|~|_______________ ); end Count10msec; architecture rtl of Count10msec is --------------------------------------------------------------- -- SIGNALS --------------------------------------------------------------- signal rcounter: std_logic_vector(3 downto 0); -- カウンター signal rPls_out: std_logic; -- 出力用レジスタ signal wtim1msec: std_logic; -- 1msec pulse constant c10MSEC: std_logic_vector(3 downto 0) := X"9"; -- 10msec / 1msec component Count1msec port ( clock: in std_logic; -- system clock 10MHz nreset: in std_logic; -- asynchronous reset ____reset___|~~~normal~~~ tim1msec: out std_logic -- 1msec信号 _______|~|_______________ ); end component; begin ------------------------------------------ -- 1msecカウンタ ------------------------------------------ inst_Count1msec : Count1msec port map ( clock => clock, -- in std_logic; system clock 10MHz nreset => nreset, -- in std_logic; asynchronous reset ____reset___|~~~normal~~~ tim1msec => wtim1msec -- out std_logic 1msec信号 _______|~|_______________ ); ------------------------------------------ -- カウンタ ------------------------------------------ process (clock,nreset) begin if nreset = '0' then -- リセット初期化 rcounter <= (others => '0'); rPls_out <= '0'; elsif clock' event and clock = '1' then -- clock 立ち上がり指示 if (wtim1msec = '1') then if (rcounter = c10MSEC) then rPls_out <= '1'; rcounter <= (others => '0'); -- reset counter else rPls_out <= '0'; rcounter <= rcounter +'1'; end if; else rPls_out <= '0'; end if; end if; end process; tim1msec <= rPls_out; tim10msec <= wtim1msec; end architecture rtl;
// TITLE: "Count10msec.v" // MODULE NAME: // PROJECT CODE: // AUTHOR: (****@nakaharagiken.com) // CREATION DATE: 2020.7.4 // SOURCE: // LICENSE: Copyright (c) 2020 nakaharagiken All rights reserved. // DESCRIPTION: // NOTES TO USER: 入力10MHzで10msecのカウントパルス生成 // SOURCE CONTROL: // REVISION HISTORY: v0.1 2020.7.4 First edition // // // // 入力クロック 10MHz = 100nsec // enable入力 1msec // 出力パルス 10msec // // 10msec / 1msec = 10(dec) = a(hex) // // // // `timescale 1ns/1ps module Count10msec ( clock, // in system clock 10MHz nreset, // in asynchronous reset ____reset___|~~~normal~~~ tim1msec, // out 1msec pulse ___________|~|___________ tim10msec // out 10msec pulse _______|~|_______________ ); input clock; // in system clock 10MHz input nreset; // in asynchronous reset ____reset___|~~~normal~~~ output tim1msec; // out 1msec pulse ___________|~|___________ output tim10msec; // out 1msec信号 _______|~|_______________ /////////////////////////////////////////////////////////////// // SIGNALS /////////////////////////////////////////////////////////////// reg [3:0] rcounter; // カウンター reg rPls_out; // 出力用レジスタ wire wtim1msec; // 1msec pulse parameter [3:0] c10MSEC = 4'h9; // 10msec / 1msec ////////////////////////////////////////// // 1msecカウンタ ////////////////////////////////////////// Count1msec inst_Count1msec( .clock (clock), // in system clock 10MHz .nreset (nreset), // in asynchronous reset ____reset___|~~~normal~~~ .tim1msec (wtim1msec) // out 1msec信号 _______|~|_______________ ); ////////////////////////////////////////// // カウンタ ////////////////////////////////////////// always @ (posedge clock, negedge nreset) begin if (!nreset) begin rcounter <= 4'h0; rPls_out <= 1'b0; end else begin if (wtim1msec == 1'b1) begin if (rcounter == c10MSEC) begin rPls_out <= 1'b1; rcounter <= 0; // reset counter end else begin rPls_out <= 1'b0; rcounter <= rcounter + 1; end end else begin rPls_out <= 1'b0; end end end assign tim10msec = rPls_out; assign tim1msec = wtim1msec; endmodule
VHDLファイルで解説していきます。59行目で先に設計した1msecカウンタを子として階層構造にしています。wtim1msecが1msecのパルスです。
69行目から10msecのカウンタですが、クロックは10MHzで動作していることに注意してください。そのカウンタを74行目のwtim1msecが1のif文と82行目のelseで動作をON/OFFしている事が読み取れるでしょうか? つまりwtim1msec=0の時にはカウンタ(rcounter)は停止しています。そして、出力信号であるrPls_outは0になっています。このようにすることで1msecに一回カウンタが数を数える回路になっています。
初心者がやりがちなのはwtim1msecをカウントのクロックにしてしまう事です。たとえば、
process (wtim1msec,nreset) begin という記載です。こうすれば、1msecに一回カウンタは動作します。
これは同期設計ではありません。同期設計では1msecカウンタも10msecカウンタも同じクロックを使う必要があります。たしかに、1msecカウンタは10MHzに同期しているので、10MHzの同期の同期は10msecというように思われがちですが、同期の同期は「遅延付きの同期」なのです。テクニックで遅延付きの同期を動かす。という事をやることがありますが、それは非同期設計だ。という思いで検証が必要になります。
さて、一気にテストベンチも記載しましょう。
-- TITLE: "TB_Count10msec.vhd" -- MODULE NAME: TB_Count10msec -- PROJECT CODE: -- AUTHOR: (****@nakaharagiken.com) -- CREATION DATE: 2020.7.4 -- SOURCE: -- LICENSE: Copyright (c) 2020 nakaharagiken All rights reserved. -- DESCRIPTION: Count10msec.vhdのテストベンチ -- NOTES TO USER: -- SOURCE CONTROL: -- REVISION HISTORY: v0.1 2020.7.4 First edition -- -- -- -- -- -- -- -- library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_unsigned.all; use IEEE.std_logic_arith.all; entity TB_Count10msec is end TB_Count10msec; architecture sim of TB_Count10msec is component Count10msec is port ( clock: in std_logic; -- system clock 10MHz nreset: in std_logic; -- asynchronous reset ____reset___|~~~normal~~~ tim1msec: out std_logic; -- 1msec pulse ___________|~|___________ tim10msec: out std_logic -- 10msec pulse _______|~|_______________ ); end component; constant SYSCLK_PERIOD : time := 100 ns; -- 10MHz signal i_clock: std_logic; -- system clock signal i_nreset: std_logic; -- asynchronous reset ____reset___|~~~normal~~~ signal otim1msec: std_logic; -- 1msec出力 signal otim10msec: std_logic; -- 10msec出力 begin inst_Count10msec : Count10msec port map( clock => i_clock, -- in std_logic; system clock 10MHz nreset => i_nreset, -- in std_logic; asynchronous reset ____reset___|~~~normal~~~ tim1msec => otim1msec, -- out std_logic; 1msec pulse ___________|~|___________ tim10msec => otim10msec -- out std_logic 10msec pulse _______|~|_______________ ); ------------------------------------ -- Clock Driver ------------------------------------ process begin i_clock <= '1'; wait for SYSCLK_PERIOD / 2; i_clock <= '0'; wait for SYSCLK_PERIOD / 2; end process; ------------------------------------ -- 信号作成 ------------------------------------ process begin i_nreset <= '0'; wait for (SYSCLK_PERIOD / 10); wait for (SYSCLK_PERIOD * 50); i_nreset <= '1'; wait; end process; end sim;
// TITLE: "TB_Count10msec.v" // MODULE NAME: TB_Count10msec // PROJECT CODE: // AUTHOR: (****@nakaharagiken.com) // CREATION DATE: 2020.7.4 // SOURCE: // LICENSE: Copyright (c) 2020 nakaharagiken All rights reserved. // DESCRIPTION: Count10msec.vのテストベンチ // NOTES TO USER: // SOURCE CONTROL: // REVISION HISTORY: v0.1 2020.7.4 First edition // // // // // // // // `timescale 1ns/1ps module TB_Count10msec; parameter SYSCLK_PERIOD = 100; // 10MHz reg i_clock; // system clock reg i_nreset; // asynchronous reset ____reset___|~~~normal~~~ wire o_1msec; // 1msec 出力信号 wire o_10msec; // 10msec 出力信号 ////////////////////////////////////////////////////////////////////// // Clock Driver ////////////////////////////////////////////////////////////////////// always @(i_clock) #(SYSCLK_PERIOD / 2.0) i_clock <= !i_clock; ////////////////////////////////////////////////////////////////////// // Main ////////////////////////////////////////////////////////////////////// initial begin i_clock <= 0; i_nreset <= 0; // 初期値 repeat(50) @(posedge i_clock); i_nreset <= 1; repeat(100) @(posedge i_clock); end Count10msec inst_Count10msec ( .clock (i_clock), // in system clock 10MHz .nreset (i_nreset), // in asynchronous reset ____reset___|~~~normal~~~ .tim1msec (o_1msec), // out 1msec pulse ___________|~|___________ .tim10msec (o_10msec) // out 10msec pulse _______|~|_______________ ); endmodule
そして、その結果は次のようになります。


見えにくいかも知れません。上の動作波形は10msecのカウンタであるrcounterが0~9まで数えているのかを確認しています。下の波形は1msecのカウンタが数えているのかを確認しています。
下の波形の方が分かりやすいかも知れません。1msecも10msecも同じ10MHzのクロック幅でHiになっているのが確認できます。また、1msec回路と10msec回路の各々のrcounterもテストベンチで表示しました。テストベンチではこのようにピンに出していないレジスタやワイヤーを観測することができます。
rcounterの値を見てみると、きちんと0に戻っている事がわかります。このようにしてテストベンチを使ってカウンタの動作を途中途中で観測して動作の確認を行います。
このようにカウンタを並べて動作させても10MHzのクロック幅を保つ。というのが同期設計の基本になります。
-
前の記事
デジタル回路のスイッチ入力の同期化_1msecパルス生成 2020.07.06
-
次の記事
デジタル回路のスイッチ入力の同期化_同期微分 2020.07.08

コメントを書く