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

デジタル回路のスイッチ入力の同期化_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のクロック幅を保つ。というのが同期設計の基本になります。