FPGAでHDMI出力(Full-HD):その①のつづきです。
TMDSエンコーダ
エンコードの方法については、資料を参考にしており、これを回路化しました。
エンコーダをR、G、B各色にマルチインスタンスします。
このとき、同期信号はBlueのチャンネルにのみ入力します。
エンコーダを通すと8bitデータが10bitに変換されます。、。
HDMI_TX_ENCODE.vhd
----------------------------------------------------------------
--TMDSエンコーダモジュール
-- 参考文献↓
-- http://www.hdmi-navi.com/tmds/
-----------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity HDMI_TX_ENCODE is
port(
i_pclk :in std_logic ;--ピクセルクロック
i_v_sync :in std_logic ;--垂直同期信号
i_h_sync :in std_logic ;--水平同期信号
i_ena :in std_logic ;--データイネーブル
i_dat :in std_logic_vector(7 downto 0) ;--画素値
o_dat :out std_logic_vector(9 downto 0) );--エンコード出力
end entity;
architecture top of HDMI_TX_ENCODE is
type typ_2BIT_ARY
is array(integer range <>) of std_logic_vector(1 downto 0);
---------------------------------------------
--SIGNAL
---------------------------------------------
signal sync :typ_2BIT_ARY(1 to 3) :=(others=>(others=>'0')) ;
signal ena :std_logic_vector(1 to 3) :=(others=>'0') ;
signal dat1 :std_logic_vector(7 downto 0) :=(others=>'0') ;
signal bit_num :std_logic_vector(3 downto 0) :=(others=>'0') ;
signal enc_xnor :std_logic_vector(8 downto 0) ;
signal enc_xor :std_logic_vector(8 downto 0) ;
signal dat2_tm :std_logic_vector(8 downto 0) :=(others=>'0') ;
signal dat3 :std_logic_vector(8 downto 0) :=(others=>'0') ;
signal n1 :integer range 0 to 8 :=0 ;
signal dc :integer range -8 to 8 :=0 ;
signal disparity :integer range -16 to 15 :=0 ;
signal enc :std_logic_vector(9 downto 0) :=(others=>'0') ;
begin
--===================================================
--同期信号とデータイネーブルをパイプラインレイテンシに
--合わせて遅延
--===================================================
process(i_pclk)begin
if(rising_edge(i_pclk))then
--Stage1
sync(1)(0) <=i_h_sync ;
sync(1)(1) <=i_v_sync ;
ena(1) <=i_ena ;
--Stage2
sync(2) <=sync(1) ;
ena(2) <=ena(1) ;
--Stage3
sync(3) <=sync(2) ;
ena(3) <=ena(2) ;
end if;
end process;
--===================================================
--Transition Minimized :遷移回数削減処理
-- 8bit => 9bit変換
--===================================================
--Stage1----------------------------------------
process(i_pclk)
variable tmp :std_logic_vector(3 downto 0);
begin
if(rising_edge(i_pclk))then
--タイミング合わせFF
dat1 <=i_dat;
--bitの立っている数を数える
tmp:=(others=>'0');
for i in 0 to 7 loop
if(i_dat(i)='1')then
tmp :=tmp +1;
end if;
end loop;
bit_num <=tmp;
end if;
end process;
--Stage2----------------------------------------
--XNORプロセス:隣のbitと不一致なら0
enc_xnor(0) <=dat1(0);
enc_xnor(1) <=dat1(1) xnor enc_xnor(0);
enc_xnor(2) <=dat1(2) xnor enc_xnor(1);
enc_xnor(3) <=dat1(3) xnor enc_xnor(2);
enc_xnor(4) <=dat1(4) xnor enc_xnor(3);
enc_xnor(5) <=dat1(5) xnor enc_xnor(4);
enc_xnor(6) <=dat1(6) xnor enc_xnor(5);
enc_xnor(7) <=dat1(7) xnor enc_xnor(6);
enc_xnor(8) <='0';
--XORプロセス:隣のbitと不一致なら1
enc_xor(0) <=dat1(0);
enc_xor(1) <=dat1(1) xor enc_xor(0);
enc_xor(2) <=dat1(2) xor enc_xor(1);
enc_xor(3) <=dat1(3) xor enc_xor(2);
enc_xor(4) <=dat1(4) xor enc_xor(3);
enc_xor(5) <=dat1(5) xor enc_xor(4);
enc_xor(6) <=dat1(6) xor enc_xor(5);
enc_xor(7) <=dat1(7) xor enc_xor(6);
enc_xor(8) <='1';
process(i_pclk)begin
if(rising_edge(i_pclk))then
--TMデータ(Transmition Minimized)
if (bit_num >x"4") or --5bit以上たっている。
( (bit_num=x"4")and(dat1(0)='0') )then --4bit立っててかつbit0が0
dat2_tm <=enc_xnor;
else
dat2_tm <=enc_xor;
end if;
end if;
end process;
--===================================================
--DC-Balancing:0と1の割合を50%に近付ける
-- 9bit⇒10bit変換
--===================================================
--Stage3----------------------------------------
process(i_pclk)
variable tmp_n1 :integer range 0 to 8;
variable tmp_dc :integer range -8 to 8;
begin
if(rising_edge(i_pclk))then
--タイミング合わせFF
dat3 <=dat2_tm;
--TMデータ下位8not中に含まれる1の数を数える
tmp_n1:=0;
for i in 0 to 7 loop
if(dat2_tm(i)='1')then
tmp_n1 :=tmp_n1 +1;
end if;
end loop;
n1 <=tmp_n1;
--TMデータ下位8not中DC成分(1の数-0の数)
tmp_dc:=0;
for i in 0 to 7 loop
if(dat2_tm(i)='0')then
tmp_dc :=tmp_dc -1;
else
tmp_dc :=tmp_dc +1;
end if;
end loop;
dc <=tmp_dc;
end if;
end process;
--Stage4----------------------------------------
process(i_pclk)begin
if(rising_edge(i_pclk)) then
if(ena(3)='0') then
---------------------
--ブランク期間
---------------------
--データ:2b10b変換
case(sync(3))is
when "00" => enc <= "1101010100";
when "01" => enc <= "0010101011";
when "10" => enc <= "0101010100";
when "11" => enc <= "1010101011";
when others => null;
end case;
--ディスパリティ:ゼロクリア
disparity <= 0;
else
---------------------
--有効データ期間
---------------------
--詳しくは:https://eewiki.net/pages/viewpage.action?pageId=36569119
if(disparity=0)or(n1=4) then
if(dat3(8)='0') then
enc(9) <='1';
enc(8) <='0';
enc(7 downto 0) <=not dat3(7 downto 0);
disparity <=disparity - dc;
else
enc(9) <='0';
enc(8) <='1';
enc(7 downto 0) <=dat3(7 downto 0);
disparity <=disparity + dc;
end if;
else
if((disparity>0)and(n1>4))
or((disparity<0)and(n1<4)) then
if(dat3(8)='0') then
enc(9) <='1';
enc(8) <='0';
enc(7 downto 0) <=not dat3(7 downto 0);
disparity <=disparity - dc;
else
enc(9) <='1';
enc(8) <='1';
enc(7 downto 0) <=not dat3(7 downto 0);
disparity <=disparity - dc + 2;
end if;
else
if(dat3(8) = '0') then
enc(9) <='0';
enc(8) <='0';
enc(7 downto 0) <=dat3(7 downto 0);
disparity <=disparity + dc - 2;
else
enc(9) <='0';
enc(8) <='1';
enc(7 downto 0) <=dat3(7 downto 0);
disparity <=disparity + dc;
end if;
end if;
end if;
end if;
end if;
end process;
o_dat <=enc;
end architecture;
#パラシリ変換
パラシリ変換部は10bitにエンコードされたデータをシリアルにして出力します。
-
シリアルデータ出力使用
-
LSBファースト
-
差動信号に変換し出力
-
R,G,B,クロックの4ペア
-
クロックペアは、ピクセルクロック周波数(148.5MHz)と同じレート
-
データペアは、ピクセルクロックの10倍のレート(1.485Gbps)
-
Spartan6に実装する上でのポイント
- データペアが1.485Gで動作するため、通常のロジック記述では性能が出ないため、SERDESEを使用する。
- SERDESEは8:1のシリアライズまでしか対応していないため、一度5bitで×2倍のレートにし、5:1のSERDESEを使用する。
n対1 SERDESE
n対1にシリアライズするSERDESE部分です。
これを、各ペア分(4つ)マルチインスタンスします。
HDMI_TX_SERDESE.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library UNISIM;
use UNISIM.Vcomponents.all;
entity HDMI_TX_SERDESE is
generic ( SF : integer := 0 );
port (
ioclk :in std_logic ;
gclk :in std_logic ;
rst :in std_logic ;
serdesstrobe_i :in std_logic ;
data_i :in std_logic_vector(SF-1 downto 0) ;
data_o :out std_logic );
end entity;
architecture rtl of HDMI_TX_SERDESE is
signal cascade_di : std_logic;
signal cascade_do : std_logic;
signal cascade_ti : std_logic;
signal cascade_to : std_logic;
signal mdatain : std_logic_vector(8 downto 0);
begin
datir: for I in 0 to SF-1 generate
mdatain(I) <= data_i(I);
end generate;
dati0: for I in SF to 8 generate
mdatain(I) <= '0';
end generate;
oserdes_m: OSERDES2
generic map
(DATA_WIDTH =>SF -- SERDES word width. This should match the setting is BUFPLL
,DATA_RATE_OQ =>"SDR" -- <SDR>, DDR
,DATA_RATE_OT =>"SDR" -- <SDR>, DDR
,SERDES_MODE =>"MASTER" -- <DEFAULT>, MASTER, SLAVE
,OUTPUT_MODE =>"DIFFERENTIAL"
)
port map
(OQ =>data_o
,OCE =>'1'
,CLK0 =>ioclk
,CLK1 =>'0'
,IOCE =>serdesstrobe_i
,RST =>rst
,CLKDIV =>gclk
,D4 =>mdatain(7)
,D3 =>mdatain(6)
,D2 =>mdatain(5)
,D1 =>mdatain(4)
,TQ =>open
,T1 =>'0'
,T2 =>'0'
,T3 =>'0'
,T4 =>'0'
,TRAIN =>'0'
,TCE =>'1'
,SHIFTIN1 =>'1' -- Dummy input in Master
,SHIFTIN2 =>'1' -- Dummy input in Master
,SHIFTIN3 =>cascade_do -- Cascade output D data from slave
,SHIFTIN4 =>cascade_to -- Cascade output T data from slave
,SHIFTOUT1 =>cascade_di -- Cascade input D data to slave
,SHIFTOUT2 =>cascade_ti -- Cascade input T data to slave
,SHIFTOUT3 =>open -- Dummy output in Master
,SHIFTOUT4 =>open -- Dummy output in Master
);
oserdes_s: OSERDES2
generic map
(DATA_WIDTH =>SF -- SERDES word width. This should match the setting is BUFPLL
,DATA_RATE_OT =>"SDR" -- <SDR>, DDR
,DATA_RATE_OQ =>"SDR" -- <SDR>, DDR
,SERDES_MODE =>"SLAVE" -- <DEFAULT>, MASTER, SLAVE
,OUTPUT_MODE =>"DIFFERENTIAL"
)
port map
(OQ =>open
,OCE =>'1'
,CLK0 =>ioclk
,CLK1 =>'0'
,IOCE =>serdesstrobe_i
,RST =>rst
,CLKDIV =>gclk
,D4 =>mdatain(3)
,D3 =>mdatain(2)
,D2 =>mdatain(1)
,D1 =>mdatain(0)
,TQ =>open
,T1 =>'0'
,T2 =>'0'
,T3 =>'0'
,T4 =>'0'
,TRAIN =>'0'
,TCE =>'1'
,SHIFTIN1 =>cascade_di -- Cascade input D from Master
,SHIFTIN2 =>cascade_ti -- Cascade input T from Master
,SHIFTIN4 =>'1' -- Dummy input in Slave
,SHIFTIN3 =>'1' -- Dummy input in Slave
,SHIFTOUT1 =>open -- Dummy output in Slave
,SHIFTOUT2 =>open -- Dummy output in Slave
,SHIFTOUT3 =>cascade_do -- Cascade output D data to Master
,SHIFTOUT4 =>cascade_to -- Cascade output T data to Master
);
end architecture;
SERDESE エンコーダ ラッパー
エンコーダ×3 と SERDESE×4をインスタンスするラッパーモジュールです。
SERDESE用の×10クロックと、5bit化する際の ×2クロックの生成も行います。
HDMI_TX.vhd
library ieee;
use ieee.std_logic_1164.all;
library UNISIM;
use UNISIM.Vcomponents.all;
entity HDMI_TX is
port(
i_clk :in std_logic ;--Pixl Clock
i_rst :in std_logic ;--Reset High active
i_img_vs :in std_logic ;--Vertical sync
i_img_hs :in std_logic ;--Horizontal sync
i_img_ena :in std_logic ;--Image data enable
i_img_red :in std_logic_vector(7 downto 0) ;--Image data RED
i_img_grn :in std_logic_vector(7 downto 0) ;--Image data GREEN
i_img_blu :in std_logic_vector(7 downto 0) ;--Image data BLUE
o_hdmi_clk_p:out std_logic ;--HDMI clock+
o_hdmi_clk_n:out std_logic ;--HDMI clock-
o_hdmi_red_p:out std_logic ;--Red+
o_hdmi_red_n:out std_logic ;--Red-
o_hdmi_grn_p:out std_logic ;--Green+
o_hdmi_grn_n:out std_logic ;--Green-
o_hdmi_blu_p:out std_logic ;--Blue+
o_hdmi_blu_n:out std_logic );--Blue-
end entity;
architecture top of HDMI_TX is
constant PLLX :integer :=10 ;--PLL逓倍比
constant PLLD :integer :=1 ;--PLL分周比
constant SER_FACT :integer :=5 ;--シリアライズ係数 1..8
constant CLKIN_PERIOD:real :=13.33 ;--入力クロック周期(ns)
type typ_ARY5BIT is array(integer range <>) of std_logic_vector(4 downto 0);
signal pll_lock :std_logic ;--
signal feedback :std_logic ;--
signal pll_clk :std_logic_vector(0 to 1) ;--
signal pclk_x2 :std_logic ;--
signal pclk_x10 :std_logic ;--
signal ser_stb :std_logic ;--
signal enc_r :std_logic_vector(9 downto 0) ;--
signal enc_g :std_logic_vector(9 downto 0) ;--
signal enc_b :std_logic_vector(9 downto 0) ;--
signal flg :std_logic ;--
signal enc_buf :typ_ARY5BIT(0 to 3) ;--
signal sirial_dt :std_logic_vector(0 to 3) ;--シリアルデータ(clk+RGB)
begin
---------------------------------------------
--10倍クロック生成
---------------------------------------------
u_HDMI_PLL:PLL_ADV
generic map
(BANDWIDTH =>"OPTIMIZED" -- "high", "low" or "optimized"
,CLKFBOUT_MULT =>PLLX -- 逓倍比(1~64)
,CLKFBOUT_PHASE =>0.0 -- phase shift (degrees) of all output clocks
,CLKIN1_PERIOD =>CLKIN_PERIOD -- clock period (ns) of input clock on clkin1
,CLKIN2_PERIOD =>CLKIN_PERIOD -- clock period (ns) of input clock on clkin2
,CLKOUT0_DUTY_CYCLE =>0.5 --Duty cycle for clkout0 (0.01 to 0.99)
,CLKOUT1_DUTY_CYCLE =>0.5 --
,CLKOUT2_DUTY_CYCLE =>0.5 --
,CLKOUT3_DUTY_CYCLE =>0.5 --
,CLKOUT4_DUTY_CYCLE =>0.5 --
,CLKOUT5_DUTY_CYCLE =>0.5 --
,CLKOUT0_PHASE =>90.0 --phase shift (degrees) for clkout0 (0.0 to 360.0)
,CLKOUT1_PHASE =>0.0 --位相は90°が良かった。(SIMの話だが・・・)
,CLKOUT2_PHASE =>0.0 --
,CLKOUT3_PHASE =>0.0 --
,CLKOUT4_PHASE =>0.0 --
,CLKOUT5_PHASE =>0.0 --
,CLKOUT0_DIVIDE =>5 --分周比(1 to 128)
,CLKOUT1_DIVIDE =>1 --
,CLKOUT2_DIVIDE =>10 --
,CLKOUT3_DIVIDE =>1 --
,CLKOUT4_DIVIDE =>1 --
,CLKOUT5_DIVIDE =>1 --
,COMPENSATION =>"INTERNAL" -- "SYSTEM_SYNCHRONOUS", "SOURCE_SYNCHRONOUS", "INTERNAL", "EXTERNAL", "DCM2PLL", "PLL2DCM"
,DIVCLK_DIVIDE =>PLLD -- 分周比 (1 to 52)
,REF_JITTER =>0.100 -- input reference jitter (0.000 to 0.999 ui%)
)
port map
(CLKFBDCM =>open --DCMを駆動する場合のフィードバック
,CLKFBOUT =>feedback --general output feedback signal
,CLKIN1 =>i_clk --primary clock input
,CLKIN2 =>'0' --セカンダリクロック(未使用)
,CLKOUT0 =>pll_clk(0) --PLLクロック
,CLKOUT1 =>pll_clk(1) --
,CLKOUT2 =>open --
,CLKOUT3 =>open --
,CLKOUT4 =>open --
,CLKOUT5 =>open --
,CLKOUTDCM0 =>open -- DCMに接続できるクロック
,CLKOUTDCM1 =>open --
,CLKOUTDCM2 =>open --
,CLKOUTDCM3 =>open --
,CLKOUTDCM4 =>open --
,CLKOUTDCM5 =>open --
,DO =>open -- dynamic reconfig data output (16-bits)
,DRDY =>open -- dynamic reconfig ready output
,LOCKED =>pll_lock -- PLLロックステータス
,CLKFBIN =>feedback -- clock feedback input
,CLKINSEL =>'1' -- selects '1' = clkin1, '0' = clkin2
,DADDR =>"00000" -- dynamic reconfig address input (5-bits)
,DCLK =>'0' -- dynamic reconfig clock input
,DEN =>'0' -- dynamic reconfig enable input
,DI =>x"0000" -- dynamic reconfig data input (16-bits)
,DWE =>'0' -- dynamic reconfig write enable input
,RST =>i_rst -- asynchronous pll reset
,REL =>'0' -- used to force the state of the PFD outputs (test only)
);
---------------------------------------------
--グローバルバッファ(x2クロック)
---------------------------------------------
u_BUFG_PCLK_X2:BUFG
port map
(I =>pll_clk(0)
,O =>pclk_x2
);
---------------------------------------------
--SERDESE用x10クロック生成
---------------------------------------------
u_BUFPLL:BUFPLL
generic map
(DIVIDE =>SER_FACT --シリパラ比(1 to 8)
)
port map
(PLLIN =>pll_clk(1) -- PLL Clock input
,GCLK =>pclk_x2 -- Global Clock input
,LOCKED =>pll_lock -- Clock0 locked input
,IOCLK =>pclk_x10 -- Output PLL Clock
,LOCK =>open -- PLLロックステータス(本当は見た方が良い)
,SERDESSTROBE =>ser_stb -- Output SERDES strobe
);
--エンコード:RED
u_HDMI_TX_ENCODE_RED:entity work.HDMI_TX_ENCODE
port map
(i_pclk =>i_clk
,i_v_sync =>'0'
,i_h_sync =>'0'
,i_ena =>i_img_ena
,i_dat =>i_img_red
,o_dat =>enc_r
);
--エンコード:GREEN
u_HDMI_TX_ENCODE_GRN:entity work.HDMI_TX_ENCODE
port map
(i_pclk =>i_clk
,i_v_sync =>'0'
,i_h_sync =>'0'
,i_ena =>i_img_ena
,i_dat =>i_img_grn
,o_dat =>enc_g
);
--エンコード:BLUE
u_HDMI_TX_ENCODE_BLU:entity work.HDMI_TX_ENCODE
port map
(i_pclk =>i_clk
,i_v_sync =>i_img_vs
,i_h_sync =>i_img_hs
,i_ena =>i_img_ena
,i_dat =>i_img_blu
,o_dat =>enc_b
);
--10bit⇒5bit×2サイクル
-- ここでPixlClockで来てるデータをpclk_x2で叩いている。
-- 一応動作はしているけど、本当は非同期渡しをきっちりやったほうが良い。
process(pclk_x2,i_rst)begin
if(i_rst='1')then
flg <='1';
enc_buf <=(others=>(others=>'0'));
elsif(Rising_Edge(pclk_x2))then
flg <=not flg;
if(flg='1')then
enc_buf(0) <="11111";
enc_buf(1) <=enc_r(4 downto 0);
enc_buf(2) <=enc_g(4 downto 0);
enc_buf(3) <=enc_b(4 downto 0);
else
enc_buf(0) <="00000";
enc_buf(1) <=enc_r(9 downto 5);
enc_buf(2) <=enc_g(9 downto 5);
enc_buf(3) <=enc_b(9 downto 5);
end if;
end if;
end process;
--シリアライザ 5to1
GEN_SERIALIZER:for i in 0 to 3 generate
u_HDMI_TX_SERDESE: entity work.HDMI_TX_SERDESE
generic map
(SF => 5)
port map
(ioclk =>pclk_x10
,gclk =>pclk_x2
,rst =>i_rst
,serdesstrobe_i =>ser_stb
,data_i =>enc_buf(i)
,data_o =>sirial_dt(i)
);
end generate;
--出力差動バッファ
OBUFDS_CLK:OBUFDS port map(I =>sirial_dt(0) , O =>o_hdmi_clk_p , OB =>o_hdmi_clk_n);
OBUFDS_RED:OBUFDS port map(I =>sirial_dt(1) , O =>o_hdmi_red_p , OB =>o_hdmi_red_n);
OBUFDS_GRN:OBUFDS port map(I =>sirial_dt(2) , O =>o_hdmi_grn_p , OB =>o_hdmi_grn_n);
OBUFDS_BLU:OBUFDS port map(I =>sirial_dt(3) , O =>o_hdmi_blu_p , OB =>o_hdmi_blu_n);
end architecture;
その③へつづく