制御信号の発生回路
このシステムでは,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 <= '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 <= '1';
ELSE
WR_busy <= '0';
END IF;
WHEN '1' =>
IF(Ch_cnt = Ch_n-1)THEN
WR_busy <= '0';
ELSE
WR_busy <= '1';
END IF;
WHEN OTHERS =>
WR_busy <='0';
END CASE;
END IF;
END PROCESS;
-- WR control
WR <= '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 <= mic_in(0);
LED <= (others =>'1');
LED(7) <= 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 <= clk_3MHz;
mclk <= clk_3MHz;
PROCESS(mclk) -- MEMS Mic 1bit output (Synchronized with mclk)
BEGIN
IF(mclk'EVENT AND mclk='1') THEN
in_data_buffer <= mic_in;
END IF;
END PROCESS;
reset <= RESET_SW;
Ch_n <= CONV_std_logic_vector(N_CH,8);
Samp_n <= CONV_std_logic_vector(8192,16);
PROCESS(clk_60MHz,reset) --60MHz → 3MHz
BEGIN
IF(reset ='0')THEN
cnt_3M <= (others => '0');
clk_3MHz <='0';
ELSIF(clk_60MHz'EVENT AND clk_60MHz='1')THEN
IF(cnt_3M < 19)THEN
cnt_3M <= cnt_3M + 1;
IF(cnt_3M < 10)THEN
clk_3MHz <= '0';
ELSE
clk_3MHz <= '1';
END IF;
ELSE
cnt_3M <= (others => '0');
END IF;
END IF;
END PROCESS;
PROCESS(clk_60MHz,reset) --Ch counter
BEGIN
IF(reset='0')THEN
Ch_cnt <= (others => '0');
ELSIF(clk_60MHz'EVENT AND clk_60MHz='1' AND WR_busy ='1')THEN
IF(Ch_cnt = Ch_n-1)THEN
Ch_cnt <= (others => '0');
ELSE
Ch_cnt <= Ch_cnt + 1;
END IF;
END IF;
END PROCESS;
PROCESS(clk_60MHz, reset) -- サンプル回数のカウンタ
BEGIN
IF(reset='0')THEN
Samp_cnt <= (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 <= Samp_n;
ELSE
Samp_cnt <= Samp_cnt + 1;
END IF;
END IF;
END PROCESS;
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) --State transition for WR_busy
BEGIN
IF(reset='0')THEN
WR_busy <= '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 <= '1';
ELSE
WR_busy <= '0';
END IF;
WHEN '1' =>
IF(Ch_cnt = Ch_n-1)THEN
WR_busy <= '0';
ELSE
WR_busy <= '1';
END IF;
WHEN OTHERS =>
WR_busy <='0';
END CASE;
END IF;
END PROCESS;
-- WR control
WR <= '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 <= mic_in(0);
LED <= (others =>'1');
LED(7) <= 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 <= 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の方に記述を移動した方が可読性・メンテナンス性ともに良くなるのはわかっているのですが,そのままにしてあります。
コメント