FPGAによるハードウェア開発

開発の例

それでは,具体的な開発過程の例を示していきます。ここでは,あらかじめ作成したVHDLコードを使うことにします。

以下の3つのコードを使います。(このコードは卒業研究用のコードなので,チュートリアル用のコードを作ったら,そちらに切り替える予定です。

  • 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
			                        -- 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);
			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
-- 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;
  • ARRAY_PKG.vhd
LIBRARY ieee;
USE ieee.std_logic_1164.all;
package ARRAY_PKG is
  constant N_CH : integer := 8;
  constant N_BYTE : integer := 1;
  subtype SIG is std_logic_vector (N_BYTE*8-1 downto 0);
  type SIG_ARRAY is array (0 to N_CH-1) of SIG; 
       --std_logic_vector (N_BYTE*8-1 downto 0);
end ARRAY_PKG;

このコードをダウンロードしたら,どこか好きなフォルダに格納しておきます。

Lattice Diamondの起動

開発ツールのLattice Diamondを立ち上げます。このツールは,回路記述の入力,シミュレーション,合成,ピン配置,FPGAの書き込み(プログラム)などの機能を備えています。

Lattice Semiconductor社のFPGA製品であれば,全ての作業をこのツールで行うことができます。論理シミュレーションのModelSimのLattice Diamond版がバンドルされています。ですから,FPGAボードを持っていなくてもハードウェア記述言語や論理回路の自学習することもできます。

ここからは,印刷版の資料の手順に沿って説明していきます。PDF版の資料を閲覧するか,印刷して読めば,以下の解説は読まなくても大丈夫です。

1)起動

Windows の Start menu > Lattice Diamond x.xx > Lattice Diamond
で起動し,Lattice Diamondのウインドウが開きます。

起動時のウインドウは幾つかのページと区画(ペイン,pane)さらに上部の複数のツールバーで構成されています。

  • 左ペイン(Left Pane)
    ファイルリスト,合成プロセス,階層構造を表示します。タブで表示内容を切り替えます。
  • 中央のページ
    様々な機能を持つページを追加し,タブで切り替えが可能です。起動時にはStart Pageが表示されています。Start Pageは左側にプロジェクトを操作するためのProject Paneがあります。
  • 下側のペイン
    操作の結果を表示します。コードの文法や合成プロセスのエラーや警告は,ここに表示されます。
  • ツールバー
    様々な操作をするツールのアイコンが配置されています。ツールバーのアイコンは小さくて見づらいですね

なお,デフォルトの起動時に表示されStart Pageの右側のペインには,Users GuidesやModelSimのマニュアルなど多数の文書へのリンクが配置されています。全て英語表記なのですが,ユーザガイドなどは,ここのが一番わかりやすいし詳しいと思います。

2)新規プロジェクト作成の場合

  • ウィザードの開始
    Start Pageを選ぶ(①)
     中央のStart Pageタブを選択します。Start Pageが閉じている場合は,最上段のメニューバーから,
     View > Start Page
    を選ぶことで,表示されます。
    Project Paneで New…をクリック(②)
     Project:と表示されている項目の中の“New…”をクリックします。これで新規プロジェクトを作成するウィザードが起動されます。

Dialog Boxが開きます。このダイアログボックスは「これからプロジェクトを開きますが進めてよいですか?」という確認のためのものなので,続ける場合は,“Next”をクリックします(①)。

プロジェクトの名前と作成する場所などを設定するダイアログボックスが開きます。
テキストボックスName:にプロジェクト名,Location:にプロジェクトを作るフォルダのパスを入力します(②)。

Implementation:のところで実装(implementation)関連のフォルダ名とパスを設定します。何もしなくても,Locate:でフォルダ名を設定していれば,実装関連のフォルダ名 は impl1,パスはプロジェクトのパスの下に設定されます。特にユーザの設定をしない場合は,そのままでNextボタンを押します。

  • ソースファイルの指定
    Add Sourceと書かれたダイアログボックスが表示されます。コードをDiamond付属のエディタで手打ちで入力する場合は,Add Source…はクリックせず,そのままNextをクリックします。(このチュートリアルでは,そのようにします。)
    ここでソースコードを入力する場合は,Add Source…をクリックします(①)。Import Fileダイアログボックスが開くのでソースコードを格納していたフォルダを指定して必要なsourceファイル(VHDLコードなど)を指定します。複数個のファイルを指定可能です。

ソースコード(複数可)を追加し終わったら,Nextボタンをクリック。

デバイス設定のダイアログボックスが表示されます。Add Sourceダイアログボックスでソースコードを設定せずにNextをクリックした場合は,すぐにこのボックスが表示されます。

  • デバイスの指定
    Select Deviceダイアログボックスが開くので,デバイスを指定。
    MachXO2の場合,
    Family:ペインでMachXO2 ,右隣のDevice:ペインで LCMXO2-7000HEを選択
    Performance Grade: 4を選択
    Operating Conditions:Industrialを選択
     → Parts Names: LCMXO2-7000HE-4TG144I と表示されます。
     また,デバイスに関する情報が右側のDevice Information:ペインに表示されます。
    (以上は,私の購入したFPGA基板の場合です,)
    確認したら,Nextをクリックします。
  • 合成ツールの選択
    Select Synthesis ToolダイアログボックスでLattice ISEを指定し,Nextをクリック
    Project Informationを確認し,Finishをクリック。

以上で新規プロジェクトの作成ができました。

この状態で,ウィンドウズのファイルエクスプローラで確認すると,プロジェクトに指定したパスの位置に,プロジェクト名のフォルダが新しくできています。このフォルダの直下に,impl1という実装関連フォルダがあり,そこを開くとsourceというフォルダがあります。ソースファイルの指定を行わずにこの段階まで進んできた場合は,このフォルダの中身は空になっているはずです。

チュートリアルを進める場合は,このsourceフォルダの中にチュートリアル用のソースコードをコピー&ペーストで格納してください。

3)コンパイル(回路合成)

  • Exportファイルの指定
    Exportファイルというのは合成プロセスで作って外部に取り出すファイルのことです。ここではハードウェアの論理回路としての構成の情報をまとめたJedecファイルを作ることにします。
    ウインドウのProcessタグを選択します(①)。論理合成の過程がツリー方式で示されます。
    ツリーの下部,Export Filesの中から目的とするファイルを選びます。シミュレーションをしない場合は,Jedecファイルだけにチェックマークを入れるだけでよいです(②)。

exportしたいファイル名をダブルクリックすると,合成のプロセスが進行し,指定したファイル名のところに緑色のチェックマークが表示されれば,合成は終了です。impl1フォルダの下に,XXXXX_impl1.jed(XXXXXはプロジェクト名)というJEDECファイルが作られています。

また,Diamondアプリケーションの下部のoutputペインには,長々と合成プロセスの途中経過が表示されます。最後に,“Done: completed successfully”と表示されれば,無事に合成が終了したことになります。

慣れるまでは,途中経過をよく見た方がよいでしょう。特に,オレンジ色で表示される警告(Warning)は注意します。合成はできていても,シビアなタイミングの仕様は満たしていない可能性があるからです。

4)ピンの指定

ピン割り当てはLattice Diamondにお任せでよいという人は,このプロセスは飛ばしてもかまいません。

VHDLなどのハードウェア記述言語(HDL)は,FPGAの論理回路としての構成をプログラムできます。合成プロセスでは,HDLの記述から論理回路としての構成を生成し,さらに,この構成を,ターゲットとするFPGAの内部に書き込むためのデータに変換しJEDECファイルとして出力します。

VHDLで設定した入力信号と出力信号を,ターゲットデバイスのピンに割り当てる作業がピン割り当てです。この作業は,FPGAの書き込み(プログラム)の前の段階で実行しておく必要があります。ピン割り当てが異なれば,書き込みデータも異なってくるからです。

HDL自体には,対象とするFPGAのピンへの信号の割り当てを記述する機能はありません。このため,ピン割り当ては開発用ソフトであるLattice Diamondで別途実行することになります。

コメント