27
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

FPGAでHDMI出力(Full-HD):その①同期信号/画素データ生成

Last updated at Posted at 2017-10-11

概要

FPGAを使用した、HDMI画像表示回路を作成方法についてまとめました。

動作仕様&使用機材

動作仕様

項目 仕様
表示サイズ Full-HD(1920x1080)
捜査 プログレッシブ
フレームレート 60fps
画像処理クロック 148.5MHz

使用機材

項目 機材
FPGA Spartan6(XC6SLX45-3CSG324)
FPGAボード ATLYS (DIGILENT社製)

回路ブロック

ブロック Name 概要
同期信号生成部 IMG_SYNC_GEN 同期信号を生成します
画像重畳部 IMG_BRENDER 同期信号に画像データを付加します
エンコーダー HDMI_TX_ENCODER 8B10B変換を行うエンコーダー
パラシリ変換 HDMI_TX_SERIALIZER 10:1のシリアライザ

HDMI_1.jpg

  • HS・・・水平同期信号
  • VS・・・垂直同期信号
  • DE・・・データイネーブル(有効画素フラグ)
  • R・・・赤色データ(8bit)
  • G・・・緑色データ(8bit)
  • B・・・青色データ(8bit)

同期信号生成部

動作仕様

同期信号生成部は、画像同期信号を生成します。
画像同期信号の考え方自体は、VGA(アナログビデオ信号)のときと同様に考えてください。(ここでの説明は省略します)

Full-HDサイズ画像同期信号タイミングを下図に示します。
(FPGA内部の信号なのでHigh優位に統一しています)

HDMI_2.jpg

ソースコード

  • トリガのタイミング決定する"水平/垂直設定"はモジュール外からの入力とし、変更可能にする
  • "水平/垂直設定"に矛盾した設定が入力された場合は動作保障外とする
IMAGE_SYNC.vhd
	library	ieee;
	use		ieee.std_logic_1164.all;
	use		ieee.std_logic_unsigned.all;
	use		ieee.std_logic_arith.all;
	
	entity IMAGE_SYNC is
		port(
		i_pclk		:in		std_logic						;--ピクセルクロック
		i_ena		:in		std_logic						;--同期信号出力イネーブル
		i_h_cyc		:in		std_logic_vector(11 downto 0)	;--水平設定:周期
		i_h_set		:in		std_logic_vector(11 downto 0)	;--水平設定:同期信号アサート
		i_h_clr		:in		std_logic_vector(11 downto 0)	;--水平設定:同期信号ネゲート
		i_h_act		:in		std_logic_vector(11 downto 0)	;--水平設定:有効期間開始
		i_h_dis		:in		std_logic_vector(11 downto 0)	;--水平設定:有効期間終了
		i_v_cyc		:in		std_logic_vector(11 downto 0)	;--垂直設定:周期
		i_v_set		:in		std_logic_vector(11 downto 0)	;--垂直設定:同期信号アサート
		i_v_clr		:in		std_logic_vector(11 downto 0)	;--垂直設定:同期信号ネゲート
		i_v_act		:in		std_logic_vector(11 downto 0)	;--垂直設定:有効期間開始
		i_v_dis		:in		std_logic_vector(11 downto 0)	;--垂直設定:有効期間終了
		o_vs		:out	std_logic						;--垂直同期信号
		o_hs		:out	std_logic						;--水平同期信号
		o_de		:out	std_logic						);--有効画素フラグ
	end	entity;
	
architecture top of IMAGE_SYNC is

	--=====================================================
	--TYPE
	--=====================================================
	--同期信号セットクリアタイミング
	type typ_VIDEO_TRIGER is record
		ena		:std_logic		;--カウンタイネーブル
		stt		:std_logic		;--動作開始
		fin		:std_logic		;--1周期終了
		inc		:std_logic		;--カウンタインクリメント
		set		:std_logic		;--同期信号アサート
		clr		:std_logic		;--同期信号ネゲート
		act		:std_logic		;--有効期間開始
		dis		:std_logic		;--有効期間終了
	end record;
	
	--=====================================================
	--SIGNAL
	--=====================================================
	signal	ena		:std_logic_vector(0 to 1)		:="00"			;
	signal	ena_rise:std_logic										;
	signal	h_cyc	:std_logic_vector(11 downto 0)	:=(others=>'1')	;
	signal	h_set	:std_logic_vector(11 downto 0)	:=(others=>'1')	;
	signal	h_clr	:std_logic_vector(11 downto 0)	:=(others=>'1')	;
	signal	h_act	:std_logic_vector(11 downto 0)	:=(others=>'1')	;
	signal	h_dis	:std_logic_vector(11 downto 0)	:=(others=>'1')	;
	signal	v_cyc	:std_logic_vector(11 downto 0)	:=(others=>'1')	;
	signal	v_set	:std_logic_vector(11 downto 0)	:=(others=>'1')	;
	signal	v_clr	:std_logic_vector(11 downto 0)	:=(others=>'1')	;
	signal	v_act	:std_logic_vector(11 downto 0)	:=(others=>'1')	;
	signal	v_dis	:std_logic_vector(11 downto 0)	:=(others=>'1')	;
	signal	h_cnt	:std_logic_vector(11 downto 0)	:=(others=>'0')	;
	signal	v_cnt	:std_logic_vector(11 downto 0)	:=(others=>'0')	;
	signal	h		:typ_VIDEO_TRIGER								;
	signal	v		:typ_VIDEO_TRIGER								;
	signal	vs		:std_logic						:='0'			;
	signal	hs		:std_logic						:='0'			;
	signal	ve		:std_logic						:='0'			;
	signal	de		:std_logic						:='0'			;
begin

	--回路イネーブル微分FF
	process(i_pclk)begin
		if(Rising_edge(i_pclk))then
			ena(0)	<=i_ena;
			ena(1)	<=ena(0);
		end if;
	end process;
	
	--回路イネーブル立上り
	ena_rise	<=ena(0) and (not ena(1));
	
	--同期信号タイミング設定値ラッチ
	process(i_pclk)begin
		if(Rising_edge(i_pclk))then
			if(ena_rise='1')then
				h_cyc	<=i_h_cyc;
				h_set	<=i_h_set;
				h_clr	<=i_h_clr;
				h_act	<=i_h_act;
				h_dis	<=i_h_dis;
				v_cyc	<=i_v_cyc;
				v_set	<=i_v_set;
				v_clr	<=i_v_clr;
				v_act	<=i_v_act;
				v_dis	<=i_v_dis;
			end if;
		end if;
	end process;
	
	--カウンタ
	process(i_pclk)begin
		if(Rising_edge(i_pclk))then
			--水平カウンタ
			if(h.stt='1')then
				h_cnt	<=conv_std_logic_vector(1,h_cnt'length);
			elsif(h.ena='1')then
				if(h.fin='1')then
					h_cnt	<=conv_std_logic_vector(1,h_cnt'length);
				elsif(h.inc='1')then
					h_cnt	<=h_cnt+1;
				else
					h_cnt	<=h_cnt;
				end if;
			else
				h_cnt	<=(others=>'0');
			end if;
			--垂直カウンタ
			if(v.stt='1')then
				v_cnt	<=conv_std_logic_vector(1,v_cnt'length);
			elsif(v.ena='1')then
				if(v.fin='1')then
					v_cnt	<=conv_std_logic_vector(1,v_cnt'length);
				elsif(v.inc='1')then
					v_cnt	<=v_cnt+1;
				else
					v_cnt	<=v_cnt;
				end if;
			else
				v_cnt	<=(others=>'0');
			end if;
		end if;
	end process;
	
	--同期信号セットクリアタイミング
	h.ena	<=ena(1)						;
	h.stt	<=ena_rise						;
	h.fin	<='1' when(h_cnt=h_cyc)else '0'	;
	h.inc	<=ena(1)						;
	h.set	<='1' when(h_cnt=h_set)else '0'	;
	h.clr	<='1' when(h_cnt=h_clr)else '0'	;
	h.act	<='1' when(h_cnt=h_act)else '0'	;
	h.dis	<='1' when(h_cnt=h_dis)else '0'	;
	
	v.ena	<=ena(1)						;
	v.stt	<=ena_rise						;
	v.fin	<='1' when(v_cnt=v_cyc)else '0'	;
	v.inc	<=h.fin							;
	v.set	<='1' when(v_cnt=v_set)else '0'	;
	v.clr	<='1' when(v_cnt=v_clr)else '0'	;
	v.act	<='1' when(v_cnt=v_act)else '0'	;
	v.dis	<='1' when(v_cnt=v_dis)else '0'	;
	
	--同期信号
	process(i_pclk)begin
		if(Rising_edge(i_pclk))then
			--垂直同期信号
			if(v.set='1')then
				vs	<='1';
			elsif(v.clr='1')and(v.inc='1')then
				vs	<='0';
			end if;
			--垂直イネーブル
			if(v.act='1')then
				ve	<='1';
			elsif(v.dis='1')and(v.inc='1')then
				ve	<='0';
			end if;
			
			--水平同期信号
			if(h.set='1')then
				hs	<='1';
			elsif(h.clr='1')then
				hs	<='0';
			end if;
			--水平イネーブル(有効画素イネーブル)
			if(h.act='1')then
				de	<=ve;
			elsif(h.dis='1')then
				de	<='0';
			end if;
		end if;
	end process;
	
	--出力ポート
	o_vs	<=vs;
	o_hs	<=hs;
	o_de	<=de;
	
end architecture;

画像重畳部

画像重畳部はDEを検出して、水平/垂直画素カウントを数えています。
このカウントを利用してRGBの画素値に割り当てて、グラデーションを描いています。
(テスト用に適当に作ったものなので、詳しい説明は省略します。)

IMAGE_GEN.vhd
	library	ieee;
	use		ieee.std_logic_1164.all;
	use		ieee.std_logic_unsigned.all;
	use		ieee.std_logic_arith.all;
	
	entity IMAGE_GEN is
		port(
		i_sync_rst	:in		std_logic						;
		i_pclk		:in		std_logic						;
		i_vs		:in		std_logic						;
		i_hs		:in		std_logic						;
		i_de		:in		std_logic						;
		o_img_vs	:out	std_logic						;
		o_img_hs	:out	std_logic						;
		o_img_de	:out	std_logic						;
		o_img_red	:out	std_logic_vector(7 downto 0)	;
		o_img_grn	:out	std_logic_vector(7 downto 0)	;
		o_img_blu	:out	std_logic_vector(7 downto 0)	);
	end	entity;
	
architecture top of IMAGE_GEN is
	signal	de		:std_logic						:='0'			;
	signal	vs		:std_logic						:='0'			;
	signal	line_end:std_logic										;
	signal	frm_stt	:std_logic										;
	signal	v_cnt	:std_logic_vector(11 downto 0)	:=(others=>'0')	;
	signal	h_cnt	:std_logic_vector(11 downto 0)	:=(others=>'0')	;
	signal	red		:std_logic_vector(7 downto 0)	:=(others=>'0')	;
	signal	grn		:std_logic_vector(7 downto 0)	:=(others=>'0')	;
	signal	blu		:std_logic_vector(7 downto 0)	:=(others=>'0')	;
	
	signal	v_ptr	:std_logic_vector(11 downto 0)	:=(others=>'0')	;
	signal	h_ptr	:std_logic_vector(11 downto 0)	:=(others=>'0')	;
	signal	v_dir	:std_logic						:='0'			;
	signal	h_dir	:std_logic						:='0'			;
	--*********************************************
	begin
	--*********************************************
	
	--ラインの終わり
	line_end	<=de and (not i_de);
	--フレームの始まり
	frm_stt		<=(not vs) and i_vs;
	-----------------------------------
	--画素カウンタ
	-----------------------------------
	process(i_pclk)begin
		if(rising_edge(i_pclk))then
			--
			de	<=i_de;
			vs	<=i_vs;
			--水平カウンタ
			if(i_de='1')then
				h_cnt	<=h_cnt +1;
			else
				h_cnt	<=(others=>'0');
			end if;
			
			--垂直カウンタ
			if(i_vs='1')then
				v_cnt	<=(others=>'0');
			else
				if(line_end='1')then
					v_cnt	<=v_cnt +1;
				else
					v_cnt	<=v_cnt;
				end if;
			end if;
		end if;
	end process;
	
	-----------------------------------
	--十字ポインタ
	-----------------------------------
	process(i_pclk)begin
		if(rising_edge(i_pclk))then
			if(frm_stt='1')then
				if(v_dir='0')then
					v_ptr	<=v_ptr +1;
				else
					v_ptr	<=v_ptr -1;
				end if;
				
				if(h_dir='0')then
					h_ptr	<=h_ptr +1;
				else
					h_ptr	<=h_ptr -1;
				end if;
				
				if(v_ptr=conv_std_logic_vector(1079,12))then
					v_dir	<='1';
				elsif(v_ptr=conv_std_logic_vector(0,12))then
					v_dir	<='0';
				end if;
				
				if(h_ptr=conv_std_logic_vector(1919,12))then
					h_dir	<='1';
				elsif(h_ptr=conv_std_logic_vector(0,12))then
					h_dir	<='0';
				end if;
			end if;
		end if;
	end process;
	
	-----------------------------------
	--ダミー画像生成
	-----------------------------------
	process(i_pclk)begin
		if(rising_edge(i_pclk))then
			if(i_de='1')then
				if(h_cnt=h_ptr)or(v_cnt=v_ptr)then
					red	<=x"00";
					grn	<=x"00";
					blu	<=x"00";
				else
					case(h_cnt(9 downto 8))is
						when "00"=>
							red	<=h_cnt(7 downto 0);
							grn	<=v_cnt(7 downto 0);
							blu	<=x"00";
						when "01"=>
							red	<=h_cnt(7 downto 0);
							grn	<=x"00";
							blu	<=v_cnt(7 downto 0);
						when "10"=>
							red	<=x"00";
							grn	<=v_cnt(7 downto 0);
							blu	<=h_cnt(7 downto 0);
						when others=>
							red	<=h_cnt(7 downto 0);
							grn	<=v_cnt(7 downto 0);
							blu	<=h_cnt(7 downto 0);
					end case;
				end if;
			else
				red	<=x"00";
				grn	<=x"00";
				blu	<=x"00";
			end if;
		end if;
	end process;
	

	o_img_red	<=red;
	o_img_grn	<=grn;
	o_img_blu	<=blu;

	process(i_pclk,i_sync_rst)begin
		if(i_sync_rst='1')then
			o_img_vs	<='0';
			o_img_hs	<='0';
			o_img_de	<='0';
		elsif(rising_edge(i_pclk))then
			o_img_vs	<=i_vs;
			o_img_hs	<=i_hs;
			o_img_de	<=i_de;
		end if;
	end process;
	
end architecture;

その②へつづく

27
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?