HDMI显示实验
在vivado中文件的层次机构如下
其中VGA_shift为VGA模块生成的888+2RGB数据和Hs,Vs行核场同步信号
encode为8bits转10bits译码器
parallel_to_serial为10bits转1bit的并行转串行差分输出转换器
HDMI_CLK输出VGA并行时钟与HDMI串行时钟
HDMI.V代码如下:
module HDMI(
input wire rst_n,
input wire clk,
output wire hdmi_oen,
output wire clk_742_5_n,
output wire clk_742_5_p,
output wire D0_n,
output wire D0_p,
output wire D1_n,
output wire D1_p,
output wire D2_n,
output wire D2_p
);
parameter CNT_MAX = 1000;
reg [10:0] cnt ;
wire [7:0] rgb_r ;//输出图像值
wire [7:0] rgb_g ;//输出图像值
wire [7:0] rgb_b ;
wire vpg_vs;
wire vpg_hs;
wire clk_148_5;
wire clk_742_5;
wire vpg_de;
wire [9:0] R_10;
wire [9:0] G_10;
wire [9:0] B_10;
/* assign hdmi_oen=1; */
/* wire D0_n,D0_p;//红色HDMI输出
wire D1_n,D1_p;//绿色HDMI输出
wire D2_n,D2_p;//蓝色HDMI输出 */
/* hdmi_oen为HDMI输出使能。设置在1000个时钟周期后输出 */
reg hdmi_oen_r;
assign hdmi_oen = hdmi_oen_r;
always @(posedge clk_148_5) begin
if (rst_n==0) begin
cnt <= 'd0;
end
else if(cnt < CNT_MAX)begin
cnt <= cnt + 1'b1;
end
else begin
cnt <= cnt;
end
end
always @(posedge clk_148_5) begin
if (rst_n==0) begin
hdmi_oen_r <= 1'b0;
end
else if(cnt == CNT_MAX)begin
hdmi_oen_r <= 1'b1;
end
end
HDMI_CLK HDMI_CLK_inst
(
// Clock out ports
.clk_148_5(clk_148_5), // output clk_148_5
.clk_742_5(clk_742_5), // output clk_742_5
// Status and control signals
.reset(~rst_n), // input reset
.locked(), // output locked
// Clock in ports
.sys_clk(clk)); // input sys_clk
/* 例化VGA模块,输出8*8*8的RGB数据 */
vga_shift vga_shift_inst(
// .clk(),// input wire clk ,//clock 50M
.rst(~rst_n),// input wire rst_n ,//复位
.vpg_pclk(clk_148_5),// output wire vpg_pclk ,//像素时钟
.vpg_de(vpg_de),// output wire vpg_de ,//输出数据有效信号
.vpg_hs(vpg_hs),// output reg vpg_hs ,//行同步信号
.vpg_vs(vpg_vs),// output reg vpg_vs ,//场同步信号
.rgb_r(rgb_r),//
.rgb_g(rgb_g),
.rgb_b(rgb_b)
);
/* 红色8bits转换成10bits的数据 */
encode VGA_R(
.clkin(clk_148_5),// input clkin, // pixel clock input
.rstin(~rst_n),// input rstin, // async. reset input (active high)
.din(rgb_r),// input [7:0] din, // data inputs: expect registered
.c0(1'b0),// input c0, // c0 input
.c1(1'b0),// input c1, // c1 input
.de(vpg_de),// input de, // de input
.dout(R_10)// output reg [9:0] dout // data outputs
);
/* 绿色8bits转换成10bits的数据 */
encode VGA_G(
.clkin(clk_148_5),// input clkin, // pixel clock input
.rstin(~rst_n),// input rstin, // async. reset input (active high)
.din(rgb_g),// input [7:0] din, // data inputs: expect registered
.c0(1'b0),// input c0, // c0 input
.c1(1'b0),// input c1, // c1 input
.de(vpg_de),// input de, // de input
.dout(G_10)// output reg [9:0] dout // data outputs
);
/* 蓝色8bits转换成10bits的数据 */
encode VGA_B(
.clkin(clk_148_5),// input clkin, // pixel clock input
.rstin(~rst_n),// input rstin, // async. reset input (active high)
.din(rgb_b),// input [7:0] din, // data inputs: expect registered
.c0(vpg_hs),// input c0, // c0 input
.c1(vpg_vs),// input c1, // c1 input
.de(vpg_de),// input de, // de input
.dout(B_10)// output reg [9:0] dout // data outputs
);
/* 10位R并行改串行的数据 */
parallel_to_serial HDMI_R(
.clk1x(clk_148_5),// input wire clk1x ,
.clk5x(clk_742_5),// input wire clk5x ,
.rst(~rst_n),// input wire rst ,
.din(R_10),// input wire [9:0] din ,
.dout_p(D0_p),// output wire dout_p ,
.dout_n(D0_n)// output wire dout_n
);
/* 10位G并行改串行的数据 */
parallel_to_serial HDMI_G(
.clk1x(clk_148_5),// input wire clk1x ,
.clk5x(clk_742_5),// input wire clk5x ,
.rst(~rst_n),// input wire rst ,
.din(G_10),// input wire [9:0] din ,
.dout_p(D1_p),// output wire dout_p ,
.dout_n(D1_n)// output wire dout_n
);
/* 10位B并行改串行的数据 */
parallel_to_serial HDMI_B(
.clk1x(clk_148_5),// input wire clk1x ,
.clk5x(clk_742_5),// input wire clk5x ,
.rst(~rst_n),// input wire rst ,
.din(B_10),// input wire [9:0] din ,
.dout_p(D2_p),// output wire dout_p ,
.dout_n(D2_n)// output wire dout_n
);
/* 742.5MHZ的时钟差分输出 */
parallel_to_serial HDMI_CLK_742_5
(
.clk1x (clk_148_5),
.clk5x (clk_742_5),
.rst (~rst_n),
.din (10'b11111_00000),
.dout_p (clk_742_5_p),
.dout_n (clk_742_5_n));
endmodule
其中encode为官方下载文件代码如下
//
//
// Xilinx, Inc. 2008 www.xilinx.com
//
//
//
// File name : encode.v
//
// Description : TMDS encoder
//
// Date - revision : Jan. 2008 - v 1.0
//
// Author : Bob Feng
//
// Disclaimer: LIMITED WARRANTY AND DISCLAMER. These designs are
// provided to you "as is". Xilinx and its licensors make and you
// receive no warranties or conditions, express, implied,
// statutory or otherwise, and Xilinx specifically disclaims any
// implied warranties of merchantability, non-infringement,or
// fitness for a particular purpose. Xilinx does not warrant that
// the functions contained in these designs will meet your
// requirements, or that the operation of these designs will be
// uninterrupted or error free, or that defects in the Designs
// will be corrected. Furthermore, Xilinx does not warrantor
// make any representations regarding use or the results of the
// use of the designs in terms of correctness, accuracy,
// reliability, or otherwise.
//
// LIMITATION OF LIABILITY. In no event will Xilinx or its
// licensors be liable for any loss of data, lost profits,cost
// or procurement of substitute goods or services, or for any
// special, incidental, consequential, or indirect damages
// arising from the use or operation of the designs or
// accompanying documentation, however caused and on any theory
// of liability. This limitation will apply even if Xilinx
// has been advised of the possibility of such damage. This
// limitation shall apply not-withstanding the failure of the
// essential purpose of any limited remedies herein.
//
// Copyright ?2006 Xilinx, Inc.
// All rights reserved
//
//
`timescale 1 ps / 1ps
module encode (
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;
always @ (posedge clkin) begin
n1d <= din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7];
din_q <= 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));
/*
reg [8:0] q_m;
always @ (posedge clkin) begin
q_m[0] <=#1 din_q[0];
q_m[1] <=#1 (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]);
q_m[2] <=#1 (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]);
q_m[3] <=#1 (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]);
q_m[4] <=#1 (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]);
q_m[5] <=#1 (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]);
q_m[6] <=#1 (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]);
q_m[7] <=#1 (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]);
q_m[8] <=#1 (decision1) ? 1'b0 : 1'b1;
end
*/
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 <= 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 <= 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 <= de;
de_reg <= de_q;
c0_q <= c0;
c0_reg <= c0_q;
c1_q <= c1;
c1_reg <= c1_q;
q_m_reg <= 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] <= ~q_m_reg[8];
dout[8] <= q_m_reg[8];
dout[7:0] <= (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'b1;
dout[8] <= q_m_reg[8];
dout[7:0] <= ~q_m_reg[7:0];
cnt <=#1 cnt + {q_m_reg[8], 1'b0} + (n0q_m - n1q_m);
end else begin
dout[9] <= 1'b0;
dout[8] <= q_m_reg[8];
dout[7:0] <= q_m_reg[7:0];
cnt <= cnt - {~q_m_reg[8], 1'b0} + (n1q_m - n0q_m);
end
end
end else begin
case ({c1_reg, c0_reg})
2'b00: dout <= CTRLTOKEN0;
2'b01: dout <= CTRLTOKEN1;
2'b10: dout <= CTRLTOKEN2;
default: dout <= CTRLTOKEN3;
endcase
cnt <= 5'h0;
end
end
end
endmodule
parallel_to_serial为10bits转1bit的并行转串行差分输出转换器是将2个OSERDESE2组合作为10位输入1位差分输出具体代码如下
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// --------------------------------------------------------------------
module parallel_to_serial(
input wire clk1x ,
input wire clk5x ,
input wire rst ,
input wire [9:0] din ,
output wire dout_p ,
output wire dout_n
);
wire dout ;
wire shift_in1 ;
wire shift_in2 ;
OBUFDS #(
.IOSTANDARD("DEFAULT"), // Specify the output I/O standard
.SLEW("SLOW") // Specify the output slew rate
) OBUFDS_inst (
.O(dout_p), // Diff_p output (connect directly to top-level port)
.OB(dout_n), // Diff_n output (connect directly to top-level port)
.I(dout) // Buffer input
);
OSERDESE2 #(
.DATA_RATE_OQ("DDR"), // DDR, SDR
.DATA_RATE_TQ("SDR"), // DDR, BUF, SDR
.DATA_WIDTH(10), // Parallel data width (2-8,10,14)
.INIT_OQ(1'b0), // Initial value of OQ output (1'b0,1'b1)
.INIT_TQ(1'b0), // Initial value of TQ output (1'b0,1'b1)
.SERDES_MODE("MASTER"), // MASTER, SLAVE
.SRVAL_OQ(1'b0), // OQ output value when SR is used (1'b0,1'b1)
.SRVAL_TQ(1'b0), // TQ output value when SR is used (1'b0,1'b1)
.TBYTE_CTL("FALSE"), // Enable tristate byte operation (FALSE, TRUE)
.TBYTE_SRC("FALSE"), // Tristate byte source (FALSE, TRUE)
.TRISTATE_WIDTH(1) // 3-state converter width (1,4)
)
OSERDESE2_inst_master (
.OFB(), // 1-bit output: Feedback path for data
.OQ(dout), // 1-bit output: Data path output
// SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
.SHIFTOUT1(),
.SHIFTOUT2(),
.TBYTEOUT(), // 1-bit output: Byte group tristate
.TFB(), // 1-bit output: 3-state control
.TQ(), // 1-bit output: 3-state control
.CLK(clk5x), // 1-bit input: High speed clock
.CLKDIV(clk1x), // 1-bit input: Divided clock
// D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
.D1(din[0]),
.D2(din[1]),
.D3(din[2]),
.D4(din[3]),
.D5(din[4]),
.D6(din[5]),
.D7(din[6]),
.D8(din[7]),
.OCE(1'b1), // 1-bit input: Output data clock enable
.RST(rst), // 1-bit input: Reset
// SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
.SHIFTIN1(shift_in1),
.SHIFTIN2(shift_in2),
// T1 - T4: 1-bit (each) input: Parallel 3-state inputs
.T1(1'b0),
.T2(1'b0),
.T3(1'b0),
.T4(1'b0),
.TBYTEIN(1'b0), // 1-bit input: Byte group tristate
.TCE(1'b0) // 1-bit input: 3-state clock enable
);
OSERDESE2 #(
.DATA_RATE_OQ("DDR"), // DDR, SDR
.DATA_RATE_TQ("SDR"), // DDR, BUF, SDR
.DATA_WIDTH(10), // Parallel data width (2-8,10,14)
.INIT_OQ(1'b0), // Initial value of OQ output (1'b0,1'b1)
.INIT_TQ(1'b0), // Initial value of TQ output (1'b0,1'b1)
.SERDES_MODE("SLAVE"), // MASTER, SLAVE
.SRVAL_OQ(1'b0), // OQ output value when SR is used (1'b0,1'b1)
.SRVAL_TQ(1'b0), // TQ output value when SR is used (1'b0,1'b1)
.TBYTE_CTL("FALSE"), // Enable tristate byte operation (FALSE, TRUE)
.TBYTE_SRC("FALSE"), // Tristate byte source (FALSE, TRUE)
.TRISTATE_WIDTH(1) // 3-state converter width (1,4)
)
OSERDESE2_inst_slave (
.OFB(), // 1-bit output: Feedback path for data
.OQ(), // 1-bit output: Data path output
// SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
.SHIFTOUT1(shift_in1),
.SHIFTOUT2(shift_in2),
.TBYTEOUT(), // 1-bit output: Byte group tristate
.TFB(), // 1-bit output: 3-state control
.TQ(), // 1-bit output: 3-state control
.CLK(clk5x), // 1-bit input: High speed clock
.CLKDIV(clk1x), // 1-bit input: Divided clock
// D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
.D1(),
.D2(),
.D3(din[8]),
.D4(din[9]),
.D5(),
.D6(),
.D7(),
.D8(),
.OCE(1'b1), // 1-bit input: Output data clock enable
.RST(rst), // 1-bit input: Reset
// SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
.SHIFTIN1(),
.SHIFTIN2(),
// T1 - T4: 1-bit (each) input: Parallel 3-state inputs
.T1(1'b0),
.T2(1'b0),
.T3(1'b0),
.T4(1'b0),
.TBYTEIN(1'b0), // 1-bit input: Byte group tristate
.TCE(1'b0) // 1-bit input: 3-state clock enable
);
endmodule