【MEMSマイクアレイ】 データ収集システムの構成

FPGA

制御信号の発生回路

このシステムでは,FPGAに接続したスイッチにより動作の開始やリセットを制御しています。またFT232Hとデータをやり取りするための制御信号を生成します。これらの制御信号の発生回路について説明します。

PROCESS(clk_60MHz, reset) -- mclkからSTART信号生成
BEGIN
	IF(reset='0')THEN
		mclk_D <= '0';
	ELSIF(clk_60MHz'EVENT AND clk_60MHz='1')THEN
		mclk_D <= mclk;
	END IF;
END PROCESS;

START <= NOT(mclk AND NOT(mclk_D) AND RUN);

PROCESS(START_SW, reset) -- 外部スイッチで動作開始
BEGIN
	IF(reset='0')THEN
		RUN <= '0';
	ELSE
		IF(START_SW = '0')THEN
			RUN <= '1';
		END IF;
		--IF(Samp_cnt = Samp_n)THEN
		--    RUN <= '0';
	    --END IF;
	END IF;
END PROCESS;
  • PROCESS(clk_60MHz, reset) — mclkからSTART信号生成
    ・・・・
    このプロセス文から,3MHzのマイク用クロック信号mclkからデータ転送を開始するための制御信号STARTを生成させる回路の記述が続く。このプロセス文でやっていることはシンプルなもので,60MHzのクロックの立上りに同期して信号mclk_Dにmclkの値を代入している。この結果,mclk_Dはmclkを60MHzのクロック信号clk_60MHzの1周期分だけ遅延させた信号となる。
  • START <= NOT(mclk AND NOT(mclk_D) AND RUN);
    mclkとそれを遅延させたmclk_Dの否定(NOT(mclk_D))のAND演算をすると,mclkの立上りエッジの位置に,クロック60MHzのクロック1周期分の幅を持つパルス信号が得られる。さらに動作状態で1となる信号RUNとAND演算することで,RUN=1の間に3MHz周期で発生するパルスとしてSTART信号が得られる。
  • PROCESS(START_SW, reset) — 外部スイッチで動作開始
    ・・・
    このプロセス文はRUN信号発生回路を記述している。RUN信号はreset=0で0となる。START_SWが0になると,RUN=0となり,reset=0とならない限り0を保ち続ける。
    信号START_SWはFPGA外部のタクトスイッチを使って発生する。タクトスイッチは,2つの端子が通常は開放つまり切れている状態であり,ボタンを押すと短絡される。スイッチを押すことでSTART_SWは0になる。

なお,“–”によりコメントアウトされている記述では,サンプル数カウント信号Samp_cntがSamp_nにより設定された値に達するとRUN=0となるようにしている。つまり,転送状態は開始するとリセットをしない限り継続することになる。

書込み制御信号WRの発生

FPGAはデータを書き込んでいる間はFT232Hへの書き込み制御信号WRを0に保持します。信号WRはFT232Hの内部FIFOの状態を表すフラグ信号TXFの値も考慮して生成しなければなりません。そこで少し記述が長くなるのですが,読み出し可能な状態を表す信号WR_busyをステートマシンにより生成し,その値に基づいてWRを生成します。

PROCESS(clk_60MHz,reset) --State transition for WR_busy
BEGIN
	IF(reset='0')THEN
		WR_busy &lt;= '0';
	ELSIF(clk_60MHz'EVENT AND clk_60MHz='1')THEN
		CASE WR_busy is
			WHEN '0' =>
				IF(START = '0' AND TXE = '0')THEN
					WR_busy &lt;= '1';
				ELSE
					WR_busy &lt;= '0';
				END IF;
			WHEN '1' => 
				IF(Ch_cnt = Ch_n-1)THEN
					WR_busy &lt;= '0';
				ELSE
					WR_busy &lt;= '1';
				END IF;
			WHEN OTHERS =>
					WR_busy &lt;='0';
		END CASE;
	END IF; 
END PROCESS; 

-- WR control
WR &lt;=	'1' 	WHEN (TXE = '1' OR WR_busy ='0') ELSE
		'0' 	WHEN (TXE = '0' AND WR_busy = '1');
  • PROCESS(clk_60MHz,reset) –State transition for WR_busy
    このプロセス文でWR_busyを生成します。WR_busyは以下の遷移ルールに従って生成されます。
    リセット:reset=0のとき0とする
    WR_busyが0のとき:TXE=0のときSTART = 0であれば1にする それ以外では0を保つ
    WR_busyが1のとき:Ch_cnt = Ch_n-1に達していれば0にする それ以外では1を保つ
    上記以外の場合:0とする
  • WR <= ‘1’ WHEN (TXE = ‘1’ OR WR_busy =’0′) ELSE
    ‘0’ WHEN (TXE = ‘0’ AND WR_busy = ‘1’);
    この2行の記述は,WHENを使っていますがプロセス文ではなく,「条件付き代入文」と呼ばれる同時処理文です。TXE = 1つまりFIFOが満杯で書き込みできない状態か,WR_busy=0つまり書き込みできない状態のときはWRを1にします。一方,TX=0でWR_busy=1のときはWR=0にしてFT232Hにデータを読み込むよう指示します。

その他 LED表示

この記述は,データ転送とは関係ありません。FPGA内部の信号を基板上のLED表示でモニタするために8ビット幅の信号LEDを用意しています。以下の記述では,LED(7)がRUN=1のとき点灯するようにしています。別の信号をモニタしたいときは,このVHDL記述を変更します。なお,LEDの各ビットは0のとき点灯し1のときは消灯です。このため,LED(7) <= NOT(RUN);のように,RUNの否定を代入しています。

PROCESS(mic_clk)
BEGIN
	IF(mic_clk'EVENT AND mic_clk = '1')THEN
		--LED &lt;= mic_in(0);
		LED &lt;= (others =>'1');
		LED(7) &lt;= NOT(RUN);
	END IF;
END PROCESS;

なお,この記述はセンシティビティリストにmic_clkを指定したプロセス文で記述しています。単なる代入文だけでも目的は達成できます。オシロスコープを持っていなかった開発の初期にmic_clk信号が生成することを確認したかったため,このようにしています。

例えば入力信号の1つをLEDの1つのビットに割り当てておきます。その入力端子に取り付けたリード線のもう一方の端子を電源に接触させると対応するLEDが点灯,GNDに接触させたときはLEDは消灯します。リード線の電圧を切り替えてもLEDの点灯状態が変化しない場合はクロックが入力されていないことがわかります。

トップレベル記述のコード全体

最後にARRAY_MIC.vhdのコード全体を示します。

USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
USE ieee.std_logic_unsigned.all;
USE work.ARRAY_PKG.all;

-- TOP Level Hardware 
ENTITY recording_mic IS
	PORT(
			clk_60MHz	: in 		STD_LOGIC; -- Clock from FT232H USB interface
			mic_clk		: buffer 	STD_LOGIC; -- Clock for MEMS Mic
			mic_in		: in 		SIG_ARRAY; -- Connect to Mic Output
			                        -- SIG_ARRAYの寸法はARRAY_PKG.vhdで設定
			RESET_SW   	: in  		STD_LOGIC; -- Connect to tact switch
			START_SW	: in  		STD_LOGIC; -- Connect to tact switch2
			word_data  	: out 		STD_LOGIC_VECTOR(N_BYTE*8-1 downto 0);
			                        -- send to USB UART/FIFO IC (FT232H)
		--  Controll Signals
			WR			: out		STD_LOGIC; 
			TXE			: in		STD_LOGIC;
			RD			: out		STD_LOGIC;
			RXF			: in		STD_LOGIC;
			LED 		: out 		STD_LOGIC_VECTOR(7 downto 0)
	     );
END recording_mic;

ARCHITECTURE behavior OF recording_mic IS 
	signal clk_3MHz	: STD_LOGIC; -- Clock
	signal mclk_D 	: STD_LOGIC; -- Clock delayed 1 clock cycle 
	signal mclk 	: STD_LOGIC; -- 確認用
	signal reset 	: STD_LOGIC;
    signal Ch_cnt	: STD_LOGIC_VECTOR(7 downto 0); -- マイクのカウント用
	signal Ch_n		: STD_LOGIC_VECTOR(7 downto 0); -- チャンネル数設定用
	signal WR_busy	: STD_LOGIC;  --書き込み状態で1
	signal Samp_cnt	: STD_LOGIC_VECTOR(15 downto 0) := "0000000000000000"; 
	                  -- 時間サンプル数のカウント用
	signal Samp_n	: STD_LOGIC_VECTOR(15 downto 0); --時間サンプル数の上限設定 
	signal data_cnt	: STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
	signal START_D 	: STD_LOGIC;  -- START生成用
	signal START 	: STD_LOGIC; -- START状態を示す
	signal cnt_3M 	: STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
	signal RUN 		: STD_LOGIC := '0'; -- 動作状態のとき1
	signal in_data_buffer : SIG_ARRAY; -- 入力バッファ
	signal mic_data	: STD_LOGIC_VECTOR(N_BYTE*8-1 downto 0);
	
COMPONENT FT232H_FIFO_write
	PORT(
			--write_word 	:  	out	STD_LOGIC_VECTOR(N_BYTE*8-1 downto 0);
			write_word 	:  	out	SIG;
			in_data   	:  	in 	SIG_ARRAY;
			reset     	:  	in 	STD_LOGIC;
			Ch_cnt		:  	in 	STD_LOGIC_VECTOR(7 downto 0);
			clk			:  	in 	STD_LOGIC;
			clk_60MHz  	:  	in 	STD_LOGIC;
			--WR			:	in	STD_LOGIC;
			WR_busy	   	:  	in  STD_LOGIC;
			TXE			:	in	STD_LOGIC 
		 );		
END COMPONENT;

BEGIN
			
USB:FT232H_FIFO_write
	PORT MAP(
				write_word	=> word_data, 
				in_data 	=> in_data_buffer,
				clk 		=> mclk, 
				clk_60MHz 	=> clk_60MHz,
				Ch_cnt 		=> Ch_cnt, 
				reset 		=> reset,	  
				WR_busy		=> WR_busy,
				TXE			=> TXE 
			 );
			 
mic_clk	&lt;= clk_3MHz;
mclk 	&lt;= clk_3MHz;

PROCESS(mclk) -- MEMS Mic 1bit output (Synchronized with mclk)
BEGIN
	IF(mclk'EVENT AND mclk='1') THEN
		in_data_buffer &lt;= mic_in;
	END IF;
END PROCESS;

reset 	&lt;= RESET_SW;
Ch_n	&lt;= CONV_std_logic_vector(N_CH,8);
Samp_n 	&lt;= CONV_std_logic_vector(8192,16);

PROCESS(clk_60MHz,reset) --60MHz → 3MHz 
BEGIN
	IF(reset ='0')THEN
		cnt_3M  &lt;= (others => '0');
		clk_3MHz &lt;='0';
	ELSIF(clk_60MHz'EVENT AND clk_60MHz='1')THEN
		IF(cnt_3M &lt; 19)THEN
			cnt_3M &lt;= cnt_3M + 1;
			IF(cnt_3M &lt; 10)THEN
				clk_3MHz &lt;= '0';
			ELSE
				clk_3MHz &lt;= '1';
			END IF;
		ELSE
			cnt_3M  &lt;= (others => '0');
		END IF;
	END IF;
END PROCESS;

PROCESS(clk_60MHz,reset) --Ch counter
BEGIN
	IF(reset='0')THEN
		Ch_cnt &lt;= (others => '0');
	ELSIF(clk_60MHz'EVENT AND clk_60MHz='1' AND WR_busy ='1')THEN
		IF(Ch_cnt = Ch_n-1)THEN
			Ch_cnt &lt;= (others => '0');
		ELSE
			Ch_cnt &lt;= Ch_cnt + 1;
		END IF;
	END IF;
END PROCESS;

PROCESS(clk_60MHz, reset) -- サンプル回数のカウンタ
BEGIN
	IF(reset='0')THEN
		Samp_cnt &lt;= (others => '0');
	ELSIF(clk_60MHz'EVENT AND clk_60MHz='1' AND Ch_cnt =Ch_n-1)THEN
	    IF(Samp_cnt = Samp_n)THEN
		    Samp_cnt &lt;= Samp_n;
	    ELSE
			Samp_cnt &lt;= Samp_cnt + 1;
		END IF;
	END IF;
END PROCESS;

PROCESS(clk_60MHz, reset) -- mclkからSTART信号生成
BEGIN
	IF(reset='0')THEN
		mclk_D &lt;= '0';
	ELSIF(clk_60MHz'EVENT AND clk_60MHz='1')THEN
		mclk_D &lt;= mclk;
	END IF;
END PROCESS;

START &lt;= NOT(mclk AND NOT(mclk_D) AND RUN);

PROCESS(START_SW, reset) -- 外部スイッチで動作開始
BEGIN
	IF(reset='0')THEN
		RUN &lt;= '0';
	ELSE
		IF(START_SW = '0')THEN
			RUN &lt;= '1';
		END IF;
		--IF(Samp_cnt = Samp_n)THEN
		--    RUN &lt;= '0';
	    --END IF;
	END IF;
END PROCESS;
  
PROCESS(clk_60MHz,reset) --State transition for WR_busy
BEGIN
	IF(reset='0')THEN
		WR_busy &lt;= '0';
	ELSIF(clk_60MHz'EVENT AND clk_60MHz='1')THEN
		CASE WR_busy is
			WHEN '0' =>
				IF(START = '0' AND TXE = '0')THEN
					WR_busy &lt;= '1';
				ELSE
					WR_busy &lt;= '0';
				END IF;
			WHEN '1' => 
				IF(Ch_cnt = Ch_n-1)THEN
					WR_busy &lt;= '0';
				ELSE
					WR_busy &lt;= '1';
				END IF;
			WHEN OTHERS =>
					WR_busy &lt;='0';
		END CASE;
	END IF; 
END PROCESS; 

-- WR control
WR &lt;=	'1' 	WHEN (TXE = '1' OR WR_busy ='0') ELSE
		'0' 	WHEN (TXE = '0' AND WR_busy = '1');
		
-- Clock Check
PROCESS(mic_clk)
BEGIN
	IF(mic_clk'EVENT AND mic_clk = '1')THEN
		--LED &lt;= mic_in(0);
		LED &lt;= (others =>'1');
		LED(7) &lt;= NOT(RUN);
	END IF;
END PROCESS;
		
END behavior;

FT232H_FIFOwrite.vhdの記述

上に示してきたトップレベルの回路記述の中で使われる部品(component)であるFT232H_FIFOwrite.vhdについて解説します。

-- FT232H FIFO mode FPGA -> PC (Write)

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
USE ieee.std_logic_unsigned.all;
USE work.ARRAY_PKG.all;

ENTITY FT232H_FIFO_write IS
	PORT(
			write_word 	:  	out	SIG;
			in_data   	:  	in 	SIG_ARRAY;
			reset     	:  	in 	STD_LOGIC;
			Ch_cnt		:  	in 	STD_LOGIC_VECTOR(7 downto 0);
			clk			:  	in 	STD_LOGIC;
			clk_60MHz  	:	in 	STD_LOGIC;
			WR_busy		:	in 	STD_LOGIC;
			TXE			:	in	STD_LOGIC
		 );
END FT232H_FIFO_write;

ARCHITECTURE behavior OF FT232H_FIFO_write IS
	signal count : STD_LOGIC_VECTOR(1 downto 0);

BEGIN

PROCESS(Ch_cnt,in_data,clk_60MHz) 
BEGIN
	IF(clk_60MHz'EVENT AND clk_60MHz ='1')THEN
		write_word &lt;= in_data(conv_integer(Ch_cnt));
	END IF;
END PROCESS;

END behavior;

部品としての入出力ポートです。この部品は,8チャンネルの入力信号の中からCh_cntで指定されたチャンネルのデータを選んでwrite_wordとして出力します。

  • write_word : out SIG;
    SIG型の出力信号
  • in_data : in SIG_ARRAY;
    SIG型の信号の配列であるSIG_ARRAY型。
  • write_word <= in_data(conv_integer(Ch_cnt));
    配列in_dataの中からCh_cntでインデックスを指定されたデータをwrite_wordに割り当てる。

開発当初は,このFT232H_FIFO_writeに制御信号WRの生成なども含めて記述する予定でした。しかし,トップレベルの記述で動くことが確認できたため,そのまま進めてしまいした。そのため結果としてFT232H_FIFO_writeは非常に少ない記述になっています。トップレベルの記述からFT232H_FIFO_writeの方に記述を移動した方が可読性・メンテナンス性ともに良くなるのはわかっているのですが,そのままにしてあります。

コメント