【MEMSマイクアレイ】 データ収集システム FIFO内蔵型に変更

FPGA

制御信号の発生回路

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

PROCESS(START_SW, reset) -- Start with tact switch
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(START_SW, reset) — 外部スイッチで動作開始
・・・
このプロセス文はRUN信号発生回路を記述している。RUN信号はreset=0で0となる。START_SWが0になると,RUN=0となり,reset=0とならない限り0を保ち続ける。
信号START_SWはFPGA外部のタクトスイッチを使って発生する。タクトスイッチは,2つの端子が通常は開放つまり切れている状態であり,ボタンを押すと短絡される。スイッチを押すことでSTART_SWは0になる。(PCからのコマンドによりリセットやスタートができるように改修をする予定。)

FT232H書込み制御信号WRの発生

FPGAはFT232Hにデータを渡している間はFT232Hへの書き込み制御信号WRを0に保持します。信号WRはFT232Hの内部FIFOの状態を表すフラグ信号TXFの値も考慮して生成しなければなりません。以前の回路とは異なり,組み合わせ論理を記述する「条件つき代入文」でWRを作っています。

-- WR control for FT232H
WR <=	'1' 	WHEN (TXE = '1' OR RdEn ='0') ELSE
		'0' 	WHEN (TXE = '0' AND RdEn = '1');

WR <= ‘1’ WHEN (TXE = ‘1’ OR WR_busy =’0′) ELSE
‘0’ WHEN (TXE = ‘0’ AND WR_busy = ‘1’);
この2行の記述は,WHENを使っていますがプロセス文ではなく,「条件付き代入文」と呼ばれる同時処理文になる。TXE = ‘1’ (FT232HのFIFOが満杯で書き込みできない状態)か,RdEn=’0’(FPGA内蔵のFIFOから読み出しできない状態)のときはWRを1にします。一方,TX=0かつRdEn=’1’のときはWR=’0’にしてFT232Hにデータを書き込むよう指示します。

FIFOの制御信号の発生

FPGAに内蔵するFIFOであるFIFO64to8は以下のような制御信号があります。

書き込みと読み出しを許可する制御信号:

  • WrEn:’1’のとき,WrClockの立上りに同期してFIFOにデータが書き込まれる
  • RdEn:’1’のとき,RdClockの立上りに同期してFIFOからデータが読み出される

また,FIFOの使用状況を示すフラグ信号が出力されています。

  • AlmostEmpty:FIFO内のデータの量が閾値AmEmptyThreshで設定した値以下になると“1”になる
  • AlmostFull:FIFO内のデータの量が閾値AmFullThreshで設定した値以上のとき“1”になる
  • Full:データ量がFIFO容量の上限に達すると“1”になる
  • Empty:データ量が0のとき“1”になる

以下に書き込み許可信号WrEnと読み出し許可信号RdEnを生成するコードを示します。

WrEn &lt;= RUN AND NOT(Full);

PROCESS(clk_60MHz,reset) --State transition for RdEn
BEGIN
	IF(reset='0')THEN
		RdEn &lt;= '0';
	ELSIF(clk_60MHz'EVENT AND clk_60MHz='1')THEN
		CASE RdEn is
			WHEN '0' =>
				IF(AmFull = '1')THEN
					RdEn &lt;= '1';
				ELSE
					RdEn &lt;= '0';
				END IF;
			WHEN '1' => 
				IF(AmEmpty = '1')THEN
					RdEn &lt;= '0';
				ELSE
					RdEn &lt;= '1';
				END IF;
			WHEN OTHERS =>
					RdEn &lt;='0';
		END CASE;
	END IF; 
END PROCESS; 
  • WrEn <= RUN AND NOT(Full);
    動作状態(RUN=’1’)かつFIFOバッファがFullでないときに書き込み許可する。
  • PROCESS(clk_60MHz,reset) –State transition for RdEn
    BEGIN
    ・・・
    RdEnを生成するステートマシンの記述である。リセットされた状態ではRdEn=’0’になっており,FIFOがAlmostFullになるまで読み出し許可はしない。AlmostFullまでデータが蓄積されると読み出し許可(RdEn=’1’)にする。一旦読み出し許可をすると,AlmostEmptyに達するまではRdEn=’1’を保持する。このように制御する理由は,FT232Hに渡すデータがなるべく途切れないようにするためである。しかし,この方法が動作の安定化に効果があるかどうかは未確認である。また,FIFOに2000サンプル分のデータが蓄積されるのを待ってからUSB転送を開始するので,遅延(レイテンシ)が大きくなってしまうことに注意が必要です。

その他

動作確認用の記述です。

PROCESS(mic_clk)
BEGIN
IF(mic_clk'EVENT AND mic_clk = '1')THEN
	for n in 0 to N_CH-1 loop
		temp &lt;= temp AND mic_in(n);
	END loop;
	for n in 0 to 7 loop 
		dum_out &lt;= dum_out AND temp(n);
		END loop;
	END IF;
END PROCESS;
		
-- Display signals with LED 
PROCESS(mic_clk)
BEGIN
	IF(mic_clk'EVENT AND mic_clk = '1')THEN
		LED &lt;= (others =>'1');
		LED(7) &lt;= NOT(RUN);
		LED(0) &lt;= dum_out;
	END IF;
END PROCESS;
  • PROCESS(mic_clk)
    BEGIN
    IF(mic_clk’EVENT AND mic_clk = ‘1’)THEN
    ・・・
    このプロセス文は最終的な動作には関係のないものです。MEMSマイクからの信号mic_inを使ってダミー出力dum_outを作っています。
    デバッグのため内部で生成したテスト信号をMEMSマイクの出力と置き換えるようにコードを変更すると,Lattice Diamondの処理系がMEMSマイクからの入力信号を無効として取り扱うため信号のピン割り当てが変化してしまいます。処理系への指示でピン配置が変化しないようにする方法があると思うのですが,調べる時間がありませんでした。
    MEMSマイクの信号を内部で使うだけでは効果がなく,出力信号に影響を与えるようにしないといけないことがわかったので,無理やりMEMSマイクの信号をダミー信号の計算に使い,それをMachXO2評価基板上のLEDに出力するようにして対応しています。
  • — Display signals with LED
    PROCESS(mic_clk)
    BEGIN
    ・・・
    LED(0) <= dum_out;
    ・・・
    LED(0)にdum_outを出力するようにしています。

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

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

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;

-- 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
			            -- size of SIG_ARRAY is defined in ARRAY_PKG.vhd
			RESET_SW   	: in  		STD_LOGIC; -- Connect to tact switch
			START_SW	: in  		STD_LOGIC; -- Connect to tact switch2
			word_out  	: 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 monitor
			LED 		: out 		STD_LOGIC_VECTOR(7 downto 0)
	     );
END recording_mic;

ARCHITECTURE behavior OF recording_mic IS 
	signal clk_3MHz	: STD_LOGIC; -- Clock
	signal mic_clk_d: STD_LOGIC;
	signal reset 	: STD_LOGIC;
    signal Ch_cnt	: STD_LOGIC_VECTOR(7 downto 0); -- Counter for mic-Channnel
	signal Ch_n		: STD_LOGIC_VECTOR(7 downto 0); -- number of ch-counter
	signal Samp_cnt	: STD_LOGIC_VECTOR(15 downto 0) := "0000000000000000"; 
	                  -- conter for number of time samples
	signal Samp_n	: STD_LOGIC_VECTOR(15 downto 0); --upper limit of Samp_cnt 
	signal cnt_3M 	: STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
	signal RUN 		: STD_LOGIC := '0'; -- Flag indicating running
	signal in_data_buffer : SIG_ARRAY; -- buffer for mic signal
	signal in_data_dummy  : SIG_ARRAY;
	signal mic_inG 	: SIG_ARRAY; -- Internally generated mic signal for test
	signal mic_data	: STD_LOGIC_VECTOR(N_BYTE*8-1 downto 0);
	-- signals for FIFO64to8
	signal FIFO_in 	: STD_LOGIC_VECTOR(63 downto 0); -- to FIFO64to8
	signal WrEn 	: STD_LOGIC; -- Write Enable
	signal RdEn 	: STD_LOGIC; -- Read Enable
	signal Empty 	: STD_LOGIC;
	signal Full 	: STD_LOGIC;
	signal AmEmpty 	: STD_LOGIC; -- Almost Empty
	signal AmFull  	: STD_LOGIC; -- Almost Full
	signal FIFO_reset  : STD_LOGIC;
	signal RPReset 	   : STD_LOGIC;-- Read Pointer Reset signal
	signal AmEmptyThresh : STD_LOGIC_VECTOR(13 downto 0); 
	signal AmFullThresh  : STD_LOGIC_VECTOR(10 downto 0); 
	signal temp 	: STD_LOGIC_VECTOR(7 downto 0);
	signal dum_out 	: STD_LOGIC;

COMPONENT FIFO64to8
    port (
        Data: in  std_logic_vector(63 downto 0); 
        WrClock: in  std_logic; 
        RdClock: in  std_logic; 
        WrEn: in  std_logic; 
        RdEn: in  std_logic; 
        Reset: in  std_logic; 
        RPReset: in  std_logic; 
        AmEmptyThresh: in  std_logic_vector(13 downto 0); 
        AmFullThresh: in  std_logic_vector(10 downto 0); 
        Q: out  std_logic_vector(7 downto 0); 
        Empty: out  std_logic; 
        Full: out  std_logic; 
        AlmostEmpty: out  std_logic; 
        AlmostFull: out  std_logic);
end COMPONENT;

BEGIN
FIFO:FIFO64to8 
	PORT MAP(
			Data 		=> FIFO_in,
			WrClock 	=> mic_clk_d,
			RdClock 	=> clk_60MHz,
			WrEn 		=> WrEn,
			RdEn 		=> RdEn,
			Reset 		=> FIFO_reset,
			RPReset 	=> RPReset,
			AmEmptyThresh => AmEmptyThresh,
			AmFullThresh => AmFullThresh,
			Q 			=> word_out,
			Empty 		=> Empty,
			Full 		=> Full,
			AlmostEmpty	=> AmEmpty,
			AlmostFull	=> AmFull
			);
					 
mic_clk	&lt;= clk_3MHz;
PROCESS(clk_60MHz)
BEGIN
	IF(clk_60MHz'EVENT AND clk_60MHz = '1')THEN
		mic_clk_d &lt;= mic_clk;
	END IF;
END PROCESS;

AmEmptyThresh &lt;= CONV_std_logic_vector(128,14); 
AmFullThresh &lt;= CONV_std_logic_vector(2000,11); 

PROCESS(clk_60MHz,mic_inG,reset) -- MEMS Mic 1bit output (Synchronized with mic_clk)
BEGIN
	IF(reset = '0')THEN
		in_data_buffer &lt;= mic_inG;
	ELSIF(clk_60MHz'EVENT AND clk_60MHz = '1')THEN
		IF(cnt_3M = 19)THEN
			IF(Samp_cnt &lt;4)THEN
				in_data_buffer &lt;= mic_inG;
			ELSE
				in_data_buffer &lt;= mic_in;
			END IF;
		END IF;
	END IF;
END PROCESS;

mic_G: for n in 0 to 7 generate -- Generate mic signal for test
	mic_inG(n) &lt;= CONV_STD_LOGIC_VECTOR(7-n,8);
END generate;
--mic_inG(0) &lt;= CONV_STD_LOGIC_VECTOR(1,8);
--mic_inG(1) &lt;= CONV_STD_LOGIC_VECTOR(1,8);
--mic_inG(2) &lt;= CONV_STD_LOGIC_VECTOR(0,8);
--mic_inG(3) &lt;= CONV_STD_LOGIC_VECTOR(0,8);
--mic_inG(4) &lt;= CONV_STD_LOGIC_VECTOR(0,8);
--mic_inG(5) &lt;= CONV_STD_LOGIC_VECTOR(0,8);
--mic_inG(6) &lt;= CONV_STD_LOGIC_VECTOR(0,8);
--mic_inG(7) &lt;= CONV_STD_LOGIC_VECTOR(0,8);

reset 	&lt;= RESET_SW; -- generated by tact switch
FIFO_reset &lt;= NOT(reset);
RPReset &lt;= NOT(reset);

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) -- input signal to FIFO64to8
BEGIN
IF(reset = '0')THEN
	FIFO_in &lt;= (others => '0'); 
	ELSIF(clk_60MHz'EVENT AND clk_60MHz='1')THEN
		IF(cnt_3M = 19)THEN
			FIFO_in(7 downto 0) &lt;= mic_in(0);
			for I in 1 to N_CH-1 loop
				FIFO_in((I+1)*N_BYTE*8-1 downto I*N_BYTE*8)&lt;=mic_in(I);
			end loop;
		END IF;
	END IF;
END PROCESS;

PROCESS(mic_clk, RUN, reset) -- counter for time samples
BEGIN
	IF(reset='0')THEN
		Samp_cnt &lt;= (others => '0');
	ELSIF(mic_clk'EVENT AND mic_clk='1' AND RUN = '1')THEN
	    IF(Samp_cnt = Samp_n-1)THEN
		    Samp_cnt &lt;= (others => '0');
	    ELSE
			Samp_cnt &lt;= Samp_cnt + 1;
		END IF;
	END IF;
END PROCESS;

PROCESS(START_SW, reset) -- Start with tact switch
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;

-- WR control for FT232H
WR &lt;=	'1' 	WHEN (TXE = '1' OR RdEn ='0') ELSE
		'0' 	WHEN (TXE = '0' AND RdEn = '1');

WrEn &lt;= RUN AND NOT(Full);

PROCESS(clk_60MHz,reset) --State transition for RdEn
BEGIN
	IF(reset='0')THEN
		RdEn &lt;= '0';
	ELSIF(clk_60MHz'EVENT AND clk_60MHz='1')THEN
		CASE RdEn is
			WHEN '0' =>
				IF(AmFull = '1')THEN
					RdEn &lt;= '1';
				ELSE
					RdEn &lt;= '0';
				END IF;
			WHEN '1' => 
				IF(AmEmpty = '1')THEN
					RdEn &lt;= '0';
				ELSE
					RdEn &lt;= '1';
				END IF;
			WHEN OTHERS =>
					RdEn &lt;='0';
		END CASE;
	END IF; 
END PROCESS; 

PROCESS(mic_clk)
BEGIN
IF(mic_clk'EVENT AND mic_clk = '1')THEN
	for n in 0 to N_CH-1 loop
		temp &lt;= temp AND mic_in(n);
	END loop;
	for n in 0 to 7 loop 
		dum_out &lt;= dum_out AND temp(n);
		END loop;
	END IF;
END PROCESS;
		
-- Display signals with LED 
PROCESS(mic_clk)
BEGIN
	IF(mic_clk'EVENT AND mic_clk = '1')THEN
		LED &lt;= (others =>'1');
		LED(7) &lt;= NOT(RUN);
		LED(0) &lt;= dum_out;
	END IF;
END PROCESS;
		
END behavior;

コメント