HDMI 2.0 passthrough at high (non-integer) framerates

Hi everybody.

I’ve gotten the zcu104 board and are trying to establish a hdmi passthrough at 1920x1080@~120Hz using the base overlay on pynq 3.0.1. This have shown to be more difficult than I thought it would be - I have not succeeded (yet) and hope to hear some of your thoughts on the problems I am facing here.

I am using a ASUS PG27AQDP 480 Hz OLED monitor ( ASUS ROG Swift OLED PG27AQDP Review - RTINGS.com ). Before connecting it to the HDMI out port of the zcu104 - I’ve connected the monitor directly to my ubuntu PC. On the PC I have set the display settings on the monitor to be the 1920x1080 @119.88Hz option. I’ve then run the command “xvidtune -show” to get the timing information:

“1920x1080“ 296.70 1920 2008 2052 2200 1080 1084 1089 1125 +hsync +vsync

With a pixel clock of 296.70 MHz it should be well under the 300 Mhz with 2 ppc that the zcu104 has capabilities to handle. I’ve used wxEDID to make a EDID block that sets this resolution as the preferred resolution.

I have tested both HDMI cables for the passthrough setup between the PC and the monitor running this resolution. Now I setup the monitor, the zcu104, and the PC in a passthrough setup where I use the one HDMI cables to connect from the PC to the zcu104 HDMI-in port and the other to connect the zcu104 HDMI-out port to the monitor HDMI in.

I then use the following python/pynq code to setup the hdmi passthrough:

from pynq.overlays.base import BaseOverlay
from pynq.lib.video import *

base = BaseOverlay("self_base.bit")
hdmi_in = base.video.hdmi_in
hdmi_out = base.video.hdmi_out

import time

# EDID that I created used xvidtune and wxEDID
EDID_HEX = """00FFFFFFFFFFFF0006B34027FFFFFFFF
30220103803B21782A5420AD523FAF25
0C5054254A00814081808100A940D1C0
010101010101E673801871382D40582C
45004B4A2100001A000000FD0A28E11E
FFFC000A202020202020000000FC0050
473237415144500A20202020000000FF
0053424C4D52533034303938390A0154
020367F1E27802550413101F61607675
3F402221205F5E5D0312021101230907
07830100006D030C0020003844200060
0302016DD85DC40178806B0268E0C364
1DE20FF0E200EAE305C301E606050197
4C03741A0000030328F0F08497034C04
E00100000000006FC200A0A0A0555030
2035004B4A2100001A00000000000000"""

edid_data = bytes.fromhex(''.join(EDID_HEX.split()))  # removes whitespace/newlines

if not (128 <= len(edid_data) <= 256):
    raise ValueError("EDID must be between 128 and 256 bytes long")

# Load EDID into the rx submodule
hdmi_in.frontend.load_edid(edid_data)

hdmi_in.configure()
print(hdmi_in.mode)
hdmi_out.configure(hdmi_in.mode)
print(hdmi_out.mode)

hdmi_out.cacheable_frames = False
hdmi_in.cacheable_frames = False
hdmi_out.start()
hdmi_in.start()
hdmi_in.tie(hdmi_out)

try:
    hdmi_out._hdmi.report()
except Exception as e:
    print("hdmi_out.report() raised:", e)
    
try:
    hdmi_in._hdmi.report()
except Exception as e:
    print("hdmi_in.report() raised:", e)

Running this will yield the following output to the terminal:

Successfully loaded edid.

Successfully loaded edid.

VideoMode: width=1920 height=1080 bpp=24 fps=120
VideoMode: width=1920 height=1080 bpp=24 fps=120
------------
HDMI TX SubSystem
------------

  ->HDMI TX Subsystem Cores
    : HDMI TX 
    : VTC Core 
  HDMI TX version : 03.00 (0400)
  VTC version     : 06.02 (000c)

HDMI TX timing
------------
HDMI TX Mode - HDMI 
HDMI Video Mask is Disabled

        Color Format:             RGB
        Color Depth:              8
        Pixels Per Clock:         2
        Mode:                     Progressive
        DSC Status:               Uncompressed
        Frame Rate:               120Hz
        Resolution:               1920x1080@120Hz
        Pixel Clock:              297000 kHz

        HSYNC Timing: hav=1920, hfp=88, hsw=44(hsp=1), hbp=148, htot=2200 
        VSYNC Timing: vav=1080, vfp=04, vsw=05(vsp=1), vbp=036, vtot=1125
Scrambled: 0
Sample rate: 1

Audio
---------
Format   : L-PCM
Channels : 0
DRM info frame
--------------
No DRM info
------------
HDMI RX SubSystem
------------

  ->HDMI RX Subsystem Cores
    : HDMI RX 
  HDMI RX version : 03.00 (0460)

HDMI RX Mode - HDMI
------------
HDMI RX timing
------------
        Color Format:             RGB
        Color Depth:              8
        Pixels Per Clock:         2
        Mode:                     Progressive
        DSC Status:               Uncompressed
        Frame Rate:               120Hz
        Resolution:               1920x1080 [Custom Mode]
        Pixel Clock:              297000 kHz

        HSYNC Timing: hav=1920, hfp=88, hsw=44(hsp=1), hbp=148, htot=2200 
        VSYNC Timing: vav=1080, vfp=04, vsw=05(vsp=0), vbp=036, vtot=1125
VIC: 0
Scrambled: 0
Link quality
---------
Link quality channel 0 : bad (65535)
Link quality channel 1 : bad (65535)
Link quality channel 2 : bad (65535)
Audio
---------
Format   : L-PCM
Channels : 2
ACR CTS  : 329666
ACR N    : 6272
DRM info frame
--------------
eotf: 0
DRM IF info:
desc id: 0
display primaries x0, y0, x1, y1, x2, y2: 0 0 0 0 0 0
white point x, y: 0 0
min/max display mastering luminance: 0 0
Max_CLL: 0
max_fall: 0
Infoframe
---------
RX header: 271a0187

But the monitor will just remain black…

One thing that I noted is that it appears that the frame rate is interpreted as an integer 119.88Hz != 120 Hz. This then appears to result in a wrong pixelclock 296700 kHz != 297000 kHz. Could this be the reason? Looking through some of the source code of libxhdmi it looks like framerates are consistently treated as integers ( https://github.com/Xilinx/PYNQ/blob/master/pynq/lib/\_pynq/\_xhdmi/hdmi_functions.c and the embeddedsw) - how can this be? Can this be circumvented somehow? or are we locked to resolutions with fixed integer framerates? Or am I overseeing something obvious here?

Hi @realDa9e

Do the HDMI base overlay notebooks work?

When testing the v3.1 images recently I was able to pass an HDMI image, from a PC HDMI output to a monitor HDMI input, routed through the PL no problem using the opencv_face_detect_hdmi.ipynb.

Hi @joshgoldsmith

Yes, the python snippet I used is a modification of the hdmi_introduction notebook. It works but only when the refresh rate is exactly 60 Hz - not e.g. 59.92 Hz or something similar. Did you test anything else than straight out 60 Hz refresh rate resolutions? As far as I can see the HdmiRx component loads in a default EDID with standard resolutions and then transfers the mode to the HdmiTx component, which then uses fps (which is an int) to find a similar resolution/refresh rate in a “lookup table” ( https://github.com/Xilinx/PYNQ/blob/master/pynq/lib/\_pynq/\_xhdmi/hdmi_functions.c#L266 ). This is a problem if you want to output signals that one knows a specific monitor will be compatible with..

I only checked the functionality of the notebook. The HDMI driver is not something I’ve looked at properly before, unfortunately.

We do have a bit more information on PYNQ’s video capabilities in the documentation, but I’m sure you’ve seen this already.