I want to implement my own HDMI-Passthrough on Nexys-Video Board equipped with Artix-7 FPGA and HDMI sink/source ports. My setup is: A PC HDMI port is connected to the sink port while an LED monitor is connected to the source HDMI port.
Since there is no TDMS encoder/decoder on the board, I will also need to implement them next (I don't want to just grab one of the closed source implementations readily available on the internet). But for now, I just need to connect sink/source ports over the FPGA so I can get the video shown on the monitor. However, I could not succeed yet. No picture is shown and the monitor says 'No Signal'. I am a little bit worried about mis-using FGPA ports which could result in permanent damage to the board. Therefore, I did not try everything came to my mind. I am expecting advices to correct/complete my code.
I connected HDMI signals as in the following code and schematic:
module HDMI_Top(RSTN, CLK, BTN, SW, LED,
HDMIR_TXEN, HDMIR_HPA, HDMIT_HPD,
HDMIR_SCL, HDMIR_SDA, HDMIT_SCL, HDMIT_SDA,
HDMIR_CLK_P, HDMIR_CLK_N, HDMIR_DATA_P, HDMIR_DATA_N,
HDMIT_CLK_P, HDMIT_CLK_N, HDMIT_DATA_P, HDMIT_DATA_N);
input RSTN;
input CLK;
input [4:0] BTN;
input [7:0] SW;
output [7:0] LED;
output HDMIR_TXEN;
output HDMIR_HPA;
input HDMIT_HPD;
inout HDMIR_SCL;
inout HDMIR_SDA;
inout HDMIT_SCL;
inout HDMIT_SDA;
input HDMIR_CLK_P;
input HDMIR_CLK_N;
input [2:0] HDMIR_DATA_P;
input [2:0] HDMIR_DATA_N;
output HDMIT_CLK_P;
output HDMIT_CLK_N;
output [2:0] HDMIT_DATA_P;
output [2:0] HDMIT_DATA_N;
wire [2:0] HDMI_DATA;
wire HDMI_CLK;
wire w0, w1, w2;
assign LED = SW;
//assign HDMIR_HPA = HDMIT_HPD;
assign HDMIR_TXEN = 1'b1;
assign HDMIT_SCL = HDMIR_SCL;
assign HDMIT_SDA = HDMIR_SDA;
// IBUFDS: Differential Input Buffer
IBUFDS #(
.DIFF_TERM("FALSE"), // Differential Termination
.IOSTANDARD("DEFAULT") // Specify the input I/O standard
) IBUFDS_hdmir_clk (
.O(HDMI_CLK), // Buffer output
.I(HDMIR_CLK_P), // Diff_p buffer input (connect directly to top-level port)
.IB(HDMIR_CLK_N) // Diff_n buffer input (connect directly to top-level port)
);
OBUFDS #(
.IOSTANDARD("DEFAULT") // Specify the output I/O standard
) OBUFDS_hdmit_clk (
.O(HDMIT_CLK_P), // Diff_p output (connect directly to top-level port)
.OB(HDMIT_CLK_N), // Diff_n output (connect directly to top-level port)
.I(HDMI_CLK) // Buffer input
);
// IBUFDS: Differential Input Buffer
IBUFDS #(
.DIFF_TERM("FALSE"), // Differential Termination
.IOSTANDARD("DEFAULT") // Specify the input I/O standard
) IBUFDS_hdmir_data [2:0] (
.O(HDMI_DATA), // Buffer output
.I(HDMIR_DATA_P), // Diff_p buffer input (connect directly to top-level port)
.IB(HDMIR_DATA_N) // Diff_n buffer input (connect directly to top-level port)
);
OBUFDS #(
.IOSTANDARD("DEFAULT") // Specify the output I/O standard
) OBUFDS_hdmit_data [2:0] (
.O(HDMIT_DATA_P), // Diff_p output (connect directly to top-level port)
.OB(HDMIT_DATA_N), // Diff_n output (connect directly to top-level port)
.I(HDMI_DATA) // Buffer input
);endmodule
Here is the schematic corresponding to the code.
Thanks;
I got it working finally. Now my Nexys Video card passes through a Full HD video. Here are the details:
assign HDMIR_HPA = ~HDMIT_HPD;
I found a VHDL implementation of EDID ROM from here. It emulates a 1024 x 768 monitor, however I changed it to 1920 x 1080.
This is the revised code for the top module:
module HDMI_Top(
input RSTN,
input CLK,
input [4:0] BTN,
input [7:0] SW,
output [7:0] LED,
output HDMIR_TXEN,
output HDMIR_HPA,
input HDMIT_HPD,
input HDMIR_SCL,
inout HDMIR_SDA,
output HDMIT_SCL,
inout HDMIT_SDA,
input HDMIR_CLK_P,
input HDMIR_CLK_N,
input [2:0] HDMIR_DATA_P,
input [2:0] HDMIR_DATA_N,
output HDMIT_CLK_P,
output HDMIT_CLK_N,
output [2:0] HDMIT_DATA_P,
output [2:0] HDMIT_DATA_N
);
wire HDMI_CLK;
wire [2:0] HDMI_DATA;
assign LED = SW;
// Whenever a sink is ready and wishes to announce its presence, it connects the 5V0 supply pin to the HPD pin. On
// the Nexys Video, this is done by driving the HPA (Hot Plug Assert) signal high. Note: this should only be done
// after a DDC channel slave has been implemented in the FPGA and is ready to transmit display data.
// FPGA lets the HDMI source (e.g., a PC) connected to its sink port know its presence by setting HPA signal to '1'.
// A monitor connected to the source port sets HPD signal to '0'.
// assign HDMIR_HPA = 1'b1;
assign HDMIR_HPA = ~HDMIT_HPD;
// A pull-down resistor on the TXEN signal makes sure the sink buffer's transmitter facing the FPGA is disabled by default.
// An FPGA design using the sink port needs to actively drive this pin high for the buffer to pass data through.
assign HDMIR_TXEN = 1'b1;
// The Display Data Channel, or DDC, is a collection of protocols that enable communication between the display
// (sink) and graphics adapter (source). The DDC2B variant is based on I2C, the bus master being the source and the
// bus slave the sink. When a source detects high level on the HPD pin, it queries the sink over the DDC bus for video
// capabilities. It determines whether the sink is DVI or HDMI-capable and what resolutions are supported. Only
// afterwards will video transmission begin. Refer to VESA E-DDC specifications for more information.
edid_rom edid_rom_rx0 (.clk(CLK), .sclk_raw(HDMIR_SCL), .sdat_raw(HDMIR_SDA));
// IBUFDS: Differential Input Buffer
IBUFDS #(
.DIFF_TERM("FALSE"), // Differential Termination
.IOSTANDARD("DEFAULT") // Specify the input I/O standard
) IBUFDS_hdmir_clk (
.O(HDMI_CLK), // Buffer output
.I(HDMIR_CLK_P), // Diff_p buffer input (connect directly to top-level port)
.IB(HDMIR_CLK_N) // Diff_n buffer input (connect directly to top-level port)
);
OBUFDS #(
.IOSTANDARD("DEFAULT") // Specify the output I/O standard
) OBUFDS_hdmit_clk (
.O(HDMIT_CLK_P), // Diff_p output (connect directly to top-level port)
.OB(HDMIT_CLK_N), // Diff_n output (connect directly to top-level port)
.I(HDMI_CLK) // Buffer input
);
// IBUFDS: Differential Input Buffer
IBUFDS #(
.DIFF_TERM("FALSE"), // Differential Termination
.IOSTANDARD("DEFAULT") // Specify the input I/O standard
) IBUFDS_hdmir_data [2:0] (
.O(HDMI_DATA), // Buffer output
.I(HDMIR_DATA_P), // Diff_p buffer input (connect directly to top-level port)
.IB(HDMIR_DATA_N) // Diff_n buffer input (connect directly to top-level port)
);
OBUFDS #(
.IOSTANDARD("DEFAULT") // Specify the output I/O standard
) OBUFDS_hdmit_data [2:0] (
.O(HDMIT_DATA_P), // Diff_p output (connect directly to top-level port)
.OB(HDMIT_DATA_N), // Diff_n output (connect directly to top-level port)
.I(HDMI_DATA) // Buffer input
); endmodule
You can find the whole source code here.
My next step is adding serializer/deserializer and tmds encoder/decoder to the project. For those who may wish to do the same thing, here is the most recent version of my (working) source code including serializer/deserializer and tmds encoder/decoder.