【MEMSマイクアレイ】 データ収集システム 同期信号の追加

FPGA

VHDLコード

FPGA内部の回路を記述するVHDLコードについて解説します。

ファイルの構成

コードは全部で5つのファイルで構成されています。

  • ARRAY_PKG.vhd
    自作のパッケージファイル。今回,若干の変更を加えた。
  • MEMS_IN.vhd
    MEMSマイクのL/R切り替え機能を利用して入力ピン数の2倍の個数のマイクを接続するためのコンポーネント。
  • CHANNEL_SELECTOR.vhd
    複数チャンネルの信号から1チャンネルを選択してFIFOに入力するためのコンポーネント。
  • FIFO8to8.vhd
    IPExpressで生成した8ビット入力8ビット出力FIFO。コードは非常に長い。
  • ARRAY_MIC_FIFO_P.vhd
    トップレベルの回路を記述するファイル。

コードの解説

自作パッケージARRAY_PKG.vhd

-- Package for Array signal processing
LIBRARY ieee;
USE ieee.std_logic_1164.all;
package ARRAY_PKG is
  constant N_CH : integer := 12; -- Maximum number of channels,最大チャンネル数
        -- Actual number of mic-channel must be less than or equal to N_CH
		
constant N_BYTE : integer := 1; -- Bytes of data, 扱うデータのバイト数
  subtype SIG is std_logic_vector (N_BYTE*8-1 downto 0);-- one unit of data
  
  -- Signal for microphone input
  type SIG_ARRAY is array (0 to N_CH-1) of SIG;
  
  -- Buffer signal with additional channel for frame synchronization 
  type BUF_ARRAY is array (-1 to N_CH-1) of SIG;
  
  -- Multiplexed microphone signal with L/R switching 
  subtype MEMSIG is std_logic_vector (N_BYTE*4-1 downto 0); 
  type MEMSIG_ARRAY is array(0 to N_CH-1) of MEMSIG;
  
end ARRAY_PKG;

ここでは,以下のような変数型を定義しています。

  • constant N_CH : integer := 12;
    接続するマイクロフォンの“チャンネル数の上限”を表す定数。“チャンネル”というのは,このシステムで扱うデータの単位のことで,現在,8ビットにしている。FPGAとUSBインタフェースのFT232Hも,このチャンネルを単位としてデータをやり取りする。N_CHは定数として12という値に固定されている。
  • constant N_BYTE : integer := 1;
    N_BYTEはチャンネルを構成するバイト数を表す定数。N_BYTEバイトで構成されるチャンネルをN_CH個使って,1回の時間サンプルで得られるデータを表す。つまり,N_CH×N_Byte×8ビットがFT232Hの入力ポートに入力される。
  • subtype SIG is std_logic_vector (N_BYTE*8-1 downto 0);
    サブタイプSIGを定義している。N_BYTE=1の場合は,std_logic_vector(7 downto 0)という8ビット幅のstd_logic_vector型になる。
  • type SIG_ARRAY is array (0 to N_CH-1) of SIG;
    FPGAから出力される信号を,N_BYTE*8ビットのstd_logic_vector型データの配列で表する。配列は0からN_CH-1までの昇順のインデックスで指定される。N_CH = 8,N_BYTE = 1なので,SIG_ARRAY型は,std_logic_vector(7 downto 0)型のデータの寸法N_CHの配列になる。
  • type BUF_ARRAY is array (-1 to N_CH-1) of SIG;
    チャンネルセレクタへの入力。N_CH+1チャンネルを確保する。インデックスの範囲が-1から始まっていることに注意。インデックス-1のチャンネルは同期用の信号に割り当てている。
  • subtype MEMSIG is std_logic_vector (N_BYTE*4-1 downto 0);
    type MEMSIG_ARRAY is array(0 to N_CH-1) of MEMSIG;
    MEMマイクのL/R切り替え機能を利用する際に使うデータ型を定義する。

MEMSマイク入力用コンポーネント MEMS_IN.vhd

MEMS_INの構成を下の図に示します。

MEMS_INの構成

コードは以下のようになっています。内部の詳しい動作については,L/R切り替え機能を利用してマイクロフォンの入力数を2倍に増やす仕組みを参考にしてください。

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 MEMS_IN IS
	PORT(
			memsin 		:  	in 	MEMSIG_ARRAY;
			mic   		:  	out SIG_ARRAY;
			reset     	:  	in 	STD_LOGIC;
			clk			:  	in 	STD_LOGIC
		 );
END MEMS_IN;

ARCHITECTURE behavior OF MEMS_IN IS
	signal regN : MEMSIG_ARRAY;
BEGIN
PROCESS(clk,reset) 
BEGIN
	IF(reset = '0')THEN
		regN <= (others => (others =>'0'));
		mic <= (others => (others =>'0'));
		ELSIF(clk'EVENT AND clk = '1')THEN
			for n in 0 to N_CH -1 loop
				for m in N_BYTE*4 -1 downto 0 loop
					mic(n)(2*m)   <= regN(n)(m);
					mic(n)(2*m+1) <= memsin(n)(m);
				end loop;
			end loop;
			ELSIF(clk'EVENT AND clk = '0')THEN
				for n in 0 to N_CH -1 loop 
					regN(n) <= memsin(n);
				end loop; 
	END IF;		
END PROCESS;
END behavior;

チャンネルセレクタ CHANNEL_SELECTOR.vhd

CH_SELECTORの構成

複数の入力の中から,4ビットの入力Ch_cntで指定されたチャンネルを出力するだけの回路です。Ch_chtは60MHzのクロックに同期して切り替えられます。わざわざコンポーネントにするまでもないのですが,複雑なタイミングのインタフェースも組み込む予定だったコンポーネントを基にしているので,このようになっています。

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 CHANNEL_SELECTOR IS
	PORT(
			out_data	:  	out	SIG;
			in_data   	:  	in 	BUF_ARRAY;
			Ch_cnt		:  	in 	SIGNED(4 downto 0);
			clk_60MHz  	:	in 	STD_LOGIC
		 );
END CHANNEL_SELECTOR;

ARCHITECTURE behavior OF CHANNEL_SELECTOR 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
		out_data <= in_data(conv_integer(Ch_cnt));
	END IF;
END PROCESS;

END behavior;
  • Ch_cnt : in SIGNED(4 downto 0);
    選択するチャンネルのインデックスを指定する信号Ch_cntは符号付5ビットとしている。
  • out_data <= in_data(conv_integer(Ch_cnt));
    Ch_cntでインデックスを指定されたチャンネルがout_dataとして出力される。

FIFO8to8.vhd

8ビット入力8ビット出力のFIFOのブロック線図を以下に示します。LatticeDiamondのIPExpressで自動生成したものです。

FIFO8to8

入力,出力は以下のようになっています。

入力
 Data:FIFOへの入力データ,8ビット幅
 WrClock:FIFOへの書き込みクロック,60MHzクロックを入力
 RdClock:FIFOからの読み出しクロック,60MHzクロックを入力
 WrEn:書込み許可信号,’1’のときWrClockの立上りに同期して書き込み
 RdEn:読み出し許可信号,’1’のときRdClockの立上りに同期して読み出し
 Reset:FIFOのリセット信号,FIFO内部メモリへのポインタをリセット
 RPReset:FIFOのリードポインタのリセット信号
 AmEmptyThresh:Almost Emptyフラグの閾値,14ビットで指定
  FIFO内部の書き込みポインタがこの値以下のとき,AlmostEmptyが’1’となる
 AmFullThresh:Almost Fullフラグの閾値
  FIFO内部の書き込みポインタがこの値以上のとき,AlmostFullが’1’となる

出力
 Q:FIFOの出力
 Empty:FIFOが空になったときに’1’になるフラグ信号
 Full:FIFOがフルになったとき’1’になるフラグ信号
 AlmostEmpty:FIFOの消費メモリの残りが設定値以下になると’1’になる
 AlmostFull:FIFOの消費メモリが設定値以上になると’1’になる

AlmostEmptyとAlmostFullは,FIFO8to8が空にもフルにもならないように制御するために使用できる。今回の実装では,AlmostFullが’1’になるまでFIFO8to8からの読み出しをしないようにしてある。

FIFO8to8.vhdは自動生成されたコードで非常に長いので,entity宣言のところまでのリストのみ示します。

-- VHDL netlist generated by SCUBA Diamond (64-bit) 3.12.1.454
-- Module  Version: 5.8
--D:\lscc\diamond.12\ispfpga\bin\nt64\scuba.exe -w -n FIFO8to8 -lang vhdl -synth lse -bus_exp 7 -bb -arch xo2c00 -type ebfifo -depth 16384 -width 8 -rwidth 8 -no_enable -pe 0 -pf 0 

-- Mon Oct 24 11:22:45 2022

library IEEE;
use IEEE.std_logic_1164.all;
-- synopsys translate_off
library MACHXO2;
use MACHXO2.components.all;
-- synopsys translate_on

entity FIFO8to8 is
    port (
        Data: in  std_logic_vector(7 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(13 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 FIFO8to8;

トップレベルの回路記述

トップレベルの回路記述,ARRAY_MIC_FIFO_P.vhdを見ていきましょう。

ライブラリ宣言

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
USE ieee.std_logic_unsigned.all;
USE ieee.std_logic_textio.all;
LIBRARY STD;
USE std.textio.all;
USE work.ARRAY_PKG.all;

VHDLコードで使うライブラリとパッケージを指定します。

  • USE ieee.std_logic_arith.all;
    算術演算を使うためのパッケージ。
  • USE ieee.std_logic_unsigned.all;
    符号なし整数を使うためのパッケージ。
  • USE std.textio.all;
    テキストデータの入出力用のライブラリ。シミュレーションに使う予定で宣言しているが,未使用。
  • USE work.ARRAY_PKG.all;
    自作のパッケージ。チャンネル数や,信号を使うための変数型を宣言している。Lattice Diamondの処理系ではユーザ作成のパッケージは,workというパッケージライブラリの中に作られるので,ライブラリ宣言ではwork.ARRAY_PKG.allのように指定する。

トップレベル回路のEntity宣言

-- 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
			memsin		: in 		MEMSIG_ARRAY; -- Connect to MEMS Mic
			                        -- SIG_ARRAY dimension is set in ARRAY_PKG.vhd
			RESET_SW   	: in  		STD_LOGIC; -- Connect to tact switch1
			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(Flag signals with FT232H)
			WR			: out		STD_LOGIC; 
			TXE			: in		STD_LOGIC;
			RD			: out		STD_LOGIC;
			RXF			: in		STD_LOGIC;
		--  Signals for  monitoring internal state
			LED 		: out 		STD_LOGIC_VECTOR(7 downto 0)
	     );
END recording_mic; 

この宣言で,recording_micという名前の回路の入出力が宣言されます。主な入出力信号は以下のようになります。

  • clk_60MHz : in STD_LOGIC;
    FT232Hに内蔵された発振器から出力される公称値60MHzのクロック信号。FPGAは,このクロック信号に同期して動作する。clk_60MHz はFT232Hの電源ONの直後には出力されない。FT232Hを制御するC++コードなどで同期FIFOの動作モードが設定された後で出力される。
  • mic_clk : buffer STD_LOGIC;
    MEMSマイクに供給される周波数3MHzのサンプリングクロック。入出力の属性である“buffer”は,出力として取り出した信号をFPGA内部で他の信号の生成に使う場合に宣言する。
  • memsin:in MESIG_ARRAY;
    最大96個のMEMSマイクを接続可能な全部で48ピンの入力ポート。
  • RESET_SW,START_SW
    FPGAのリセットと動作開始をさせるためのタクトスイッチからの信号。将来的には,自動的なリセットや動作開始信号に置き換える予定。
  • word_out : out STD_LOGIC_VECTOR(N_BYTE*8-1 downto 0);
    FT232Hへの出力。FT232Hは1バイト単位での入出力になるので,N_BYTE=1である。2バイト,4バイトなどで入出力をするデバイスを将来使用する場合は,N_BYTEの値を変更すればよい。
  • WR : out STD_LOGIC;
    FPGAからFT232Hに送信する書き込み制御信号。WR = ‘0’のとき,FT232HはFPGAの出力を内部バッファに書き込む。本システムでは,FPGAからFT232Hにデータを送信するので,このWR信号を使う。
  • TXE : in STD_LOGIC;
    FT232Hの内部FIFOの状態を表すフラグ信号。TXEが0のときはFIFOに空きがありデータを書き込むことができる。
  • RD : buffer STD_LOGIC;
    FPGAからFT232Hに送信する読み込み制御信号。RD = ‘0’のとき,FT232Hはデータを出力する。本システムでは使用しない。
  • RXF : in STD_LOGIC;
    Read Buffer Full。FT232HのFIFOバッファにデータが残っていることを示すフラグ。0の時データが残っていることを示す。FPGAはRXF = ‘0’を確認してRD = ‘0’として,データを読み込む。
    本システムでは使用しない。
  • LED : out STD_LOGIC_VECTOR(7 downto 0)
    デバッグ用のLED出力。FPGA内部の信号を目視でモニタする場合に使う。VHDLコードを書き換えて目的とする信号を割り当てる。

Architecture宣言(信号と部品の宣言)

Entity宣言では「外側から見た回路」を定義しました。Architecture宣言では,回路の中身の仕組みは動作を記述していきます。

ARCHITECTURE behavioral OF recording_mic IS
    signal mic_in  	: SIG_ARRAY; -- Mic slgnal from MEMS_IN
	signal clk_3MHz	: STD_LOGIC; -- Clock
	signal mclk_D 	: STD_LOGIC; -- Clock delayed 1 clock cycle 
	signal mclk 	: STD_LOGIC; -- Confirmation output 確認用出力
	signal reset 	: STD_LOGIC;
    signal Ch_cnt	: signed(4 downto 0); -- Channel Counter
	signal Ch_n		: signed(4 downto 0):= "01000"; -- Number of channels
	signal WR_busy	: STD_LOGIC;  -- 1 in write status 書込み状態で1
	signal Samp_cnt	: STD_LOGIC_VECTOR(15 downto 0) := "0000000000000000"; 
	                  -- Time Sample Counter 時間サンプル数のカウント用
	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;  -- For START signal generation START生成用
	signal START 	: STD_LOGIC; -- Signal indicating START status
	signal cnt_3M 	: STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
	signal RUN 		: STD_LOGIC := '0'; --  1 in RUN status,動作状態のとき1
	signal in_data_buffer : BUF_ARRAY; -- Input Buffer, size = Ch_n +1
	signal in_data_dummy  : SIG_ARRAY; -- 
	signal mic_data	: STD_LOGIC_VECTOR(N_BYTE*8-1 downto 0);
	-- Built-in FIFO-related signals,FIFO用信号
	signal FIFO_out	: STD_LOGIC_VECTOR(7 downto 0);
	signal FIFO_in 	: STD_LOGIC_VECTOR(7 downto 0); -- to FIFO8to8
	signal out_data	: STD_LOGIC_VECTOR(7 downto 0);
	signal WrEn 	: STD_LOGIC;
	signal WrEnPre 	: STD_LOGIC;
	signal WrEn_D 	: STD_LOGIC;
	signal WrEn1_D 	: STD_LOGIC;
	signal RdEn 	: STD_LOGIC;
	signal RdEn_D 	: STD_LOGIC;
	signal Empty 	: STD_LOGIC;
	signal Full 	: STD_LOGIC;
	signal AmEmpty 	: STD_LOGIC;
	signal AmFull  	: STD_LOGIC;
	signal FIFO_reset  : STD_LOGIC;
	signal RPReset 	   : STD_LOGIC; -- for Read Pointer Reset
	signal AmEmptyThresh : STD_LOGIC_VECTOR(13 downto 0); 
	signal AmFullThresh  : STD_LOGIC_VECTOR(13 downto 0); 
	signal dummy         : BUF_ARRAY;

COMPONENT MEMS_IN IS
	port(
			memsin		:	in	MEMSIG_ARRAY;
			mic			:	out SIG_ARRAY;
			reset		:	in  STD_LOGIC;
			clk			:	in  STD_LOGIC
		);
END COMPONENT;	
	
COMPONENT CHANNEL_SELECTOR
	PORT(
			out_data 	:  	out	SIG; -- Output of selector
			in_data   	:  	in 	BUF_ARRAY;-- Input of selector
			Ch_cnt		:  	in 	SIGNED(4 downto 0);
			clk_60MHz  	:  	in 	STD_LOGIC
		 );		
END COMPONENT;

COMPONENT FIFO8to8
    port (
        Data: in  std_logic_vector(7 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(13 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;

ARCHITECTURE behavior OF recording_mic IS
から回路recording_micの仕組み(architecture)の記述を開始します。

architecture宣言のはじめの部分で,回路内部で使う信号をsignal宣言で定義します。また,あらかじめ構造を定義した部品(component)も,ここで宣言します。

  • constant Ch_n : STD_LOGIC_VECTOR(3 downto 0):= “01000”;
    Ch_nを設定している。Ch_nはARRAY_PKG.vhdの中で定義しているN_CH以下にする必要がある。ここでは,Ch_n を1000つまり8としている。
  • COMPONENT FIFO8to8
    port (
    Data: in std_logic_vector(7 downto 0);
    ・・・
    ・・・
    IPExpressで生成したFIFO8to8を部品として登録している。
  • signal Ch_cnt   : signed(4 downto 0); — Channel Counter
    signal Ch_n     : signed(4 downto 0):= “01000”; — Number of channels
    CHANNEL_SELECTORを制御する信号Ch_cntとチャンネル数を与える信号Ch_nは符号付整数として定義している。チャンネルのインデックスに負数が含まれることに対応している。

Architecture宣言(中身の仕組み・動作を記述)

signal宣言とcomponent宣言の後のBEGINから最終行のEND behavior;までで,Entity宣言で定義された入出力信号,signal宣言で定義された信号と部品を使って,回路の動作や構造を記述していきます。以下のコードは,BEGINの直後を示しています。

BEGIN	

Mic_signal: MEMS_IN
	PORT MAP(
			memsin		=> memsin,
			mic			=> mic_in,
			reset		=> reset,
			clk			=> clk_3MHz
			);
			
ChSelector: CHANNEL_SELECTOR
	PORT MAP(
				out_data	=> FIFO_in,
				in_data 	=> in_data_buffer,
				clk_60MHz 	=> clk_60MHz,
				Ch_cnt 		=> Ch_cnt   
			 );
			 
FIFO:FIFO8to8
	PORT MAP(
			Data 		=> FIFO_in,
			WrClock 	=> clk_60MHz,
			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;
mclk 	&lt;= clk_3MHz;
-- FIFO8to8のフラグの閾値を設定
AmEmptyThresh &lt;= CONV_std_logic_vector(128,14); 
AmFullThresh &lt;= CONV_std_logic_vector(2048,14); 
WrEnPre &lt;= RUN AND Wr_busy AND NOT(Full) ;	
  • ChSelector: CHANNEL_SELECTOR
    PORT MAP(
    ・・・・
    Ch_cnt      => Ch_cnt;
    );
    このコードブロックは,Component宣言で定義したCHANNNEL_SELECTORという部品を“ChSelector”という名前をつけて生成する「コンポーネントインスタンス文」になる。
  • FIFO:FIFO8to8
    PORT MAP(
    ・・・・ 
    AlmostFull => AmFull
    );
    FIFO8to8をFIFOという名前をつけて生成している。
  • mic_clk <= clk_3MHz;
    mclk    <= clk_3MHz;
    コードの後の方のブロックで60MHzのクロック信号clk_60MHzを分周して3MHzのクロック信号clk_3MHzを生成している。この3MHzクロック信号をmic_clkとして使う。(mic_clkとmclkという同じ働きの信号が2つあるのは無駄なので,いずれ1つに絞りたい。)
  • AmEmptyThresh <= CONV_std_logic_vector(128,14);
    AmFullThresh <= CONV_std_logic_vector(2048,14);
    FIFO8to8のAlmostEmpty(“ほとんど空”)が1になる閾値AmEmptyThreshの値を定めている。AmEmptyThreshは14ビットの信号で,128に設定。
    AlmostFull(“ほとんど満杯”)が1になる閾値AmFullThreshも14ビットの信号なので最大値は16384になる。この閾値を2048に設定にしている。FIFOからの読み出しは,AlmostFullフラグが1になってから開始される。
  • WrEnPre <= RUN AND Wr_busy AND NOT(Full) ;
    FIFO8to8への書き込み許可信号WrEnを生成するための信号。RUN=’1’(FPGAが動作状態),Wr_busy=’1’(マイク信号の書き込み状態),Full=’0’(FIFO8to8が満杯でない)という3つの条件が成立するとき,この信号WrEnPreを1とする。実際の書き込み許可信号WrEnは,WrEnPreを1クロック遅延させたものになっている。
    書き込み制御信号 Wr_busyを生成するコードは243行目以降に記述してある。
PROCESS(clk_60MHz) -- WrEnPreを1クロック遅延した信号を生成
BEGIN
	IF(clk_60MHz'EVENT AND clk_60MHz = '1')THEN
		WrEn &lt;= WrEnPre;
	END IF;
END PROCESS;

チャンネルセレクタ用のバッファ信号を生成

マイクのデータは12チャンネルありますが,チャンネルセレクタには同期用の信号1チャンネルが追加されます。そのためにin_data_bufferという13チャンネルのデータを作ります。

12チャンネルのmic_in信号に同期信号を付加

-- Connect microphone input to buffer
buffer_gen : for I in 0 to conv_integer(N_CH) - 1 generate
                in_data_buffer(I) &lt;= mic_in(I);
			 end generate;

-- Assign frame sync signal to Ch_n channel, フレーム同期用データを設定
in_data_buffer(conv_integer(-1)) &lt;= "11110000";
  • buffer_gen : for I in 0 to conv_integer(N_CH) – 1 generate
    in_data_buffer(I) <= mic_in(I);
    end generate;
    for generate構文を使って,チャンネル0からチャンネルN_CH-1までのN_CHチャンネルをバッファ信号in_data_bufferの0からチャンネルN_CH-1に接続する。
  • in_data_buffer(conv_integer(-1)) <= “11110000”;
    in_data_bufferのインデックス-1で指定されるチャンネルに同期用信号を入力する。同期信号は”11110000″(10進数で-16)としてある。

分周によりサンプリングクロックを生成

PROCESS(clk_60MHz,reset) -- 3MHz clock generation
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;

SyncFIFOモードのFT232Hが出力する60MHzのクロック信号を分周してMEMSマイクに供給する3MHzのクロック信号を生成する部分です。また,このクロック信号に同期してMEMSマイクの出力をFPGAに取り込みます。

チャンネルカウンタ

PROCESS(clk_60MHz,START, WR_busy,reset)  -- Ch counter
BEGIN                     -- Count from 0 to Ch_n  
	IF(reset='0' OR START = '0')THEN
		Ch_cnt &lt;= "11111";
	ELSIF(clk_60MHz'EVENT AND clk_60MHz='1' AND WR_busy ='1')THEN
		IF(Ch_cnt = Ch_n-1)THEN
			Ch_cnt &lt;= "11111";
		ELSE
			Ch_cnt &lt;= Ch_cnt + 1;
		END IF;
	END IF; 
END PROCESS;

チャンネルセレクタのチャンネルを指定するための信号を生成します。カウンタ信号Ch_cntはリセットされると-1となり,クロック毎に1だけインクリメントしていき,ある値に達したら再び-1にしています。

  • IF(Ch_cnt = Ch_n-1)THEN
    Ch_cnt <= “11111”;
    5ビット符号付整数では”11111″は-1を表す。したがってCh_cntの値は,-1からCh_n-1までの範囲で変化することになる。

FIFOへの書き込みスタート信号の生成

PROCESS(clk_60MHz, reset)-- Generates a active low write start signal
BEGIN                    --  mclkからLアクティブの書き込みスタート信号を生成
	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;
-- Generate a negative pulse at every rising edge of mclk
-- mclkの立上りエッジ毎に負パルスを生成
START &lt;= NOT(mclk AND NOT(mclk_D) AND RUN);
  • IF(reset=’0′)THEN
    mclk_D <= ‘0’;
    ELSIF(clk_60MHz’EVENT AND clk_60MHz=’1′)THEN
    mclk_D <= mclk;
    ここで,3MHzのクロック信号mclkを60MHzのクロック1周期分だけ遅らせた信号mclk_Dを生成している。
  • START <= NOT(mclk AND NOT(mclk_D) AND RUN);
    mxlkの立上りエッジに同期する1→0に遷移するパルス信号を生成している。これをFIFOへの書き込みをスタートさせるトリガにしている。(この回路のことを,個人的には「論理微分回路」と呼んでいる。74シリーズの集積回路を使っていた頃からよく使っている。)

「論理微分回路」 入力Inの立上りに同期した負方向のパルスを生成している

RUN信号の生成

スタートスイッチを押すことで,FPGAを動作状態RUNに移行させる。

PROCESS(START_SW, reset) -- Generate RUN signal with external switch
BEGIN                     
	IF(reset='0')THEN
		RUN &lt;= '0';
	ELSE
		IF(START_SW = '0')THEN
			RUN &lt;= '1';  -- 一度RUN=1になったらリセットまで1
		END IF;
	END IF;
END PROCESS;

FIFO8to8への書き込み制御信号WR_busyの生成

PROCESS(clk_60MHz,reset) --State transition for WR_busy
BEGIN -- FIFO8to8への書き込み許可信号を生成するステートマシン
	IF(reset='0')THEN  -- 書込みビジー状態を表す信号Wr_busyを作る
		WR_busy &lt;= '0';
	ELSIF(clk_60MHz'EVENT AND clk_60MHz='1')THEN
		CASE WR_busy is
		WHEN '0' => -- FIFO8to8がFULLでなければSTART=0で書き込み状態に移行
				IF(START = '0' AND Full = '0')THEN
					WR_busy &lt;= '1';
				ELSE
					WR_busy &lt;= '0';
				END IF;
			WHEN '1' =>  -- Ch_cnt = Ch_n-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; 

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

FPGAはFT232Hにデータを渡している間はFT232Hへの書き込み制御信号WRを0に保持します。信号WRはFT232Hの内部FIFOの状態を表すフラグ信号TXFの値も考慮して生成しなければなりません。

-- WR control FT232H内蔵のFIFOへの書き込み制御信号WRを生成
-- FT232Hの空きバッファフラグTXE=0かつFIFO8to8の読み出し可能信号RdEn=1のとき0
WR &lt;=	'1' 	WHEN (TXE = '1' OR RdEn ='0') ELSE
		'0' 	WHEN (TXE = '0' AND RdEn = '1');

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

FIFO8to8からのデータ読み出し許可信号RdEnの生成

FIFO8to8からのデータ読み出しを許可する信号RdEnを生成する。データ読み込みを続けてFIFOが空になると,読み出したデータは無効になるので読み出し許可信号を0にする。また,FIFO内にある程度データが蓄積されてから読み出しを開始するようにしている。

PROCESS(clk_60MHz,reset) --State transition for RdEn
-- FIFO8to8からのデータ読み出し許可信号RdEnを生成
-- FIFO8to8がFullにならないように制御したい ・・・
BEGIN
	IF(reset='0')THEN
		RdEn &lt;= '0';
	ELSIF(clk_60MHz'EVENT AND clk_60MHz='1')THEN
		CASE RdEn is
			WHEN '0' =>  -- FIFO8to8がAlmostFullになってからRdEn=1とする
				IF(AmFull = '1')THEN
					RdEn &lt;= '1';
				ELSE
					RdEn &lt;= '0';
				END IF;
			WHEN '1' =>  -- FIFO8to8がAlmostEmptyになったらRdEn=0とする
				IF(AmEmpty = '1')THEN
					RdEn &lt;= '0';
				ELSE
					RdEn &lt;= '1';
				END IF;
			WHEN OTHERS =>
					RdEn &lt;='0';
		END CASE;
	END IF; 
END PROCESS; 

なお,読み出しによってFIFOが空になるのは問題が無いが,FIFOがフルになった場合は,サンプルしたマイク出力の書き込みをFIFOに空きができるまで待たせる必要がある。この待機時間が3MHzのサンプリング周期を越えてしまうと,データが欠落してしまう。このようなデータ欠落を防ぐように制御する必要があるが,現時点でのコードで,それができるかどうかは確認する必要がある。

コメント