1.HDMI是新一代的多媒體接口標準。
2.HDMI向下兼容DVI
3.TMDS: 最小化傳輸差分信號
4.TMDS編碼算法
5.HDMI模塊框圖
6.HDMI引腳定義
7.程序設計
下面是程序框圖
1.VGA生成模塊
2.TMDS和CTRL編碼模塊(需要例化三對)
3.並串轉換模塊(需要例化四對)
4.單端轉差分模塊(需要例化四對)
5.頂層模塊
1.VGA時序生成模塊
module video_driver(
input pixel_clk,
input sys_rst_n,
//RGB接口
output video_hs, //行同步信號
output video_vs, //場同步信號
output video_de, //數據使能
output [23:0] video_rgb, //RGB888顏色數據
input [23:0] pixel_data, //像素點數據
output [10:0] pixel_xpos, //像素點橫座標
output [10:0] pixel_ypos //像素點縱座標
);
//parameter define
//1280*720 分辨率時序參數
parameter H_SYNC = 11'd40; //行同步
parameter H_BACK = 11'd220; //行顯示後沿
parameter H_DISP = 11'd1280; //行有效數據
parameter H_FRONT = 11'd110; //行顯示前沿
parameter H_TOTAL = 11'd1650; //行掃描週期
parameter V_SYNC = 11'd5; //場同步
parameter V_BACK = 11'd20; //場顯示後沿
parameter V_DISP = 11'd720; //場有效數據
parameter V_FRONT = 11'd5; //場顯示前沿
parameter V_TOTAL = 11'd750; //場掃描週期
//reg define
reg [10:0] cnt_h;
reg [10:0] cnt_v;
//wire define
wire video_en;
wire data_req;
//*****************************************************
//** main code
//*****************************************************
assign video_de = video_en;
assign video_hs = ( cnt_h < H_SYNC ) ? 1'b0 : 1'b1; //行同步信號賦值
assign video_vs = ( cnt_v < V_SYNC ) ? 1'b0 : 1'b1; //場同步信號賦值
//使能RGB數據輸出
assign video_en = (((cnt_h >= H_SYNC+H_BACK) && (cnt_h < H_SYNC+H_BACK+H_DISP))
&&((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
? 1'b1 : 1'b0;
//RGB888數據輸出
assign video_rgb = video_en ? pixel_data : 24'd0;
//請求像素點顏色數據輸入
assign data_req = (((cnt_h >= H_SYNC+H_BACK-1'b1) &&
(cnt_h < H_SYNC+H_BACK+H_DISP-1'b1))
&& ((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
? 1'b1 : 1'b0;
//像素點座標
assign pixel_xpos = data_req ? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 11'd0;
assign pixel_ypos = data_req ? (cnt_v - (V_SYNC + V_BACK - 1'b1)) : 11'd0;
//行計數器對像素時鐘計數
always @(posedge pixel_clk ) begin
if (!sys_rst_n)
cnt_h <= 11'd0;
else begin
if(cnt_h < H_TOTAL - 1'b1)
cnt_h <= cnt_h + 1'b1;
else
cnt_h <= 11'd0;
end
end
//場計數器對行計數
always @(posedge pixel_clk ) begin
if (!sys_rst_n)
cnt_v <= 11'd0;
else if(cnt_h == H_TOTAL - 1'b1) begin
if(cnt_v < V_TOTAL - 1'b1)
cnt_v <= cnt_v + 1'b1;
else
cnt_v <= 11'd0;
end
end
endmodule
2.TMDS和CTRL編碼模塊(需要例化三對)
dvi_encoder encoder_b (
.clkin (pclk),
.rstin (reset),
.din (video_din[7:0]),
.c0 (video_hsync),
.c1 (video_vsync),
.de (video_de),
.dout (blue_10bit)
) ;
dvi_encoder encoder_g (
.clkin (pclk),
.rstin (reset),
.din (video_din[15:8]),
.c0 (1'b0),
.c1 (1'b0),
.de (video_de),
.dout (green_10bit)
) ;
dvi_encoder encoder_r (
.clkin (pclk),
.rstin (reset),
.din (video_din[23:16]),
.c0 (1'b0),
.c1 (1'b0),
.de (video_de),
.dout (red_10bit)
) ;
3.並串轉換模塊(需要例化四對)
//對編碼後的數據進行並串轉換
serializer_10_to_1 serializer_b(
.reset (reset), // 復位,高有效
.paralell_clk (pclk), // 輸入並行數據時鐘
.serial_clk_5x (pclk_x5), // 輸入串行數據時鐘
.paralell_data (blue_10bit), // 輸入並行數據
.serial_data_out (tmds_data_serial[0]) // 輸出串行數據
);
serializer_10_to_1 serializer_g(
.reset (reset),
.paralell_clk (pclk),
.serial_clk_5x (pclk_x5),
.paralell_data (green_10bit),
.serial_data_out (tmds_data_serial[1])
);
serializer_10_to_1 serializer_r(
.reset (reset),
.paralell_clk (pclk),
.serial_clk_5x (pclk_x5),
.paralell_data (red_10bit),
.serial_data_out (tmds_data_serial[2])
);
serializer_10_to_1 serializer_clk(
.reset (reset),
.paralell_clk (pclk),
.serial_clk_5x (pclk_x5),
.paralell_data (clk_10bit),
.serial_data_out (tmds_clk_serial)
);
4.單端轉差分模塊(需要例化四對)
//轉換差分信號
OBUFDS #(
.IOSTANDARD ("TMDS_33") // I/O電平標準爲TMDS
) TMDS0 (
.I (tmds_data_serial[0]),
.O (tmds_data_p[0]),
.OB (tmds_data_n[0])
);
OBUFDS #(
.IOSTANDARD ("TMDS_33") // I/O電平標準爲TMDS
) TMDS1 (
.I (tmds_data_serial[1]),
.O (tmds_data_p[1]),
.OB (tmds_data_n[1])
);
OBUFDS #(
.IOSTANDARD ("TMDS_33") // I/O電平標準爲TMDS
) TMDS2 (
.I (tmds_data_serial[2]),
.O (tmds_data_p[2]),
.OB (tmds_data_n[2])
);
OBUFDS #(
.IOSTANDARD ("TMDS_33") // I/O電平標準爲TMDS
) TMDS3 (
.I (tmds_clk_serial),
.O (tmds_clk_p),
.OB (tmds_clk_n)
);
5.頂層模塊
module hdmi_colorbar_top(
input sys_clk,
input sys_rst_n,
output tmds_clk_p, // TMDS 時鐘通道
output tmds_clk_n,
output [2:0] tmds_data_p, // TMDS 數據通道
output [2:0] tmds_data_n,
output tmds_oen // TMDS 輸出使能,0:表示是HDMI輸入 1:表示是HDMI輸出
);
附錄(TMDS編碼算法):
此算法可以自己研究。
module dvi_encoder (
input clkin, // pixel clock input
input rstin, // async. reset input (active high)
input [7:0] din, // data inputs: expect registered
input c0, // c0 input
input c1, // c1 input
input de, // de input
output reg [9:0] dout // data outputs
);
////////////////////////////////////////////////////////////
// Counting number of 1s and 0s for each incoming pixel
// component. Pipe line the result.
// Register Data Input so it matches the pipe lined adder
// output
////////////////////////////////////////////////////////////
reg [3:0] n1d; //number of 1s in din
reg [7:0] din_q;
//計算像素數據中“1”的個數
always @ (posedge clkin) begin
n1d <=#1 din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7];
din_q <=#1 din;
end
///////////////////////////////////////////////////////
// Stage 1: 8 bit -> 9 bit
// Refer to DVI 1.0 Specification, page 29, Figure 3-5
///////////////////////////////////////////////////////
wire decision1;
assign decision1 = (n1d > 4'h4) | ((n1d == 4'h4) & (din_q[0] == 1'b0));
wire [8:0] q_m;
assign q_m[0] = din_q[0];
assign q_m[1] = (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]);
assign q_m[2] = (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]);
assign q_m[3] = (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]);
assign q_m[4] = (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]);
assign q_m[5] = (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]);
assign q_m[6] = (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]);
assign q_m[7] = (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]);
assign q_m[8] = (decision1) ? 1'b0 : 1'b1;
/////////////////////////////////////////////////////////
// Stage 2: 9 bit -> 10 bit
// Refer to DVI 1.0 Specification, page 29, Figure 3-5
/////////////////////////////////////////////////////////
reg [3:0] n1q_m, n0q_m; // number of 1s and 0s for q_m
always @ (posedge clkin) begin
n1q_m <=#1 q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7];
n0q_m <=#1 4'h8 - (q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]);
end
parameter CTRLTOKEN0 = 10'b1101010100;
parameter CTRLTOKEN1 = 10'b0010101011;
parameter CTRLTOKEN2 = 10'b0101010100;
parameter CTRLTOKEN3 = 10'b1010101011;
reg [4:0] cnt; //disparity counter, MSB is the sign bit
wire decision2, decision3;
assign decision2 = (cnt == 5'h0) | (n1q_m == n0q_m);
/////////////////////////////////////////////////////////////////////////
// [(cnt > 0) and (N1q_m > N0q_m)] or [(cnt < 0) and (N0q_m > N1q_m)]
/////////////////////////////////////////////////////////////////////////
assign decision3 = (~cnt[4] & (n1q_m > n0q_m)) | (cnt[4] & (n0q_m > n1q_m));
////////////////////////////////////
// pipe line alignment
////////////////////////////////////
reg de_q, de_reg;
reg c0_q, c1_q;
reg c0_reg, c1_reg;
reg [8:0] q_m_reg;
always @ (posedge clkin) begin
de_q <=#1 de;
de_reg <=#1 de_q;
c0_q <=#1 c0;
c0_reg <=#1 c0_q;
c1_q <=#1 c1;
c1_reg <=#1 c1_q;
q_m_reg <=#1 q_m;
end
///////////////////////////////
// 10-bit out
// disparity counter
///////////////////////////////
always @ (posedge clkin or posedge rstin) begin
if(rstin) begin
dout <= 10'h0;
cnt <= 5'h0;
end else begin
if (de_reg) begin
if(decision2) begin
dout[9] <=#1 ~q_m_reg[8];
dout[8] <=#1 q_m_reg[8];
dout[7:0] <=#1 (q_m_reg[8]) ? q_m_reg[7:0] : ~q_m_reg[7:0];
cnt <=#1 (~q_m_reg[8]) ? (cnt + n0q_m - n1q_m) : (cnt + n1q_m - n0q_m);
end else begin
if(decision3) begin
dout[9] <=#1 1'b1;
dout[8] <=#1 q_m_reg[8];
dout[7:0] <=#1 ~q_m_reg[7:0];
cnt <=#1 cnt + {q_m_reg[8], 1'b0} + (n0q_m - n1q_m);
end else begin
dout[9] <=#1 1'b0;
dout[8] <=#1 q_m_reg[8];
dout[7:0] <=#1 q_m_reg[7:0];
cnt <=#1 cnt - {~q_m_reg[8], 1'b0} + (n1q_m - n0q_m);
end
end
end else begin
case ({c1_reg, c0_reg})
2'b00: dout <=#1 CTRLTOKEN0;
2'b01: dout <=#1 CTRLTOKEN1;
2'b10: dout <=#1 CTRLTOKEN2;
default: dout <=#1 CTRLTOKEN3;
endcase
cnt <=#1 5'h0;
end
end
end
endmodule