soc/cores/video: Add VideoS7GTPHDMIPHY (7-Series HDMI PHY over GTPs).
Validated on Decklink Mini 4K Monitor at 1080p60 (should allow UHD/4K), still some fixed things, but should provide a good basis to go further...
This commit is contained in:
parent
d6084cd1f9
commit
bfb90f5625
|
@ -857,6 +857,65 @@ class VideoS7HDMIPHY(Module):
|
||||||
self.specials += Instance("OBUFDS", i_I=pad_o, o_O=pad_p, o_OB=pad_n)
|
self.specials += Instance("OBUFDS", i_I=pad_o, o_O=pad_p, o_OB=pad_n)
|
||||||
|
|
||||||
|
|
||||||
|
class VideoS7GTPHDMIPHY(Module):
|
||||||
|
def __init__(self, pads, sys_clk_freq, clock_domain="sys"):
|
||||||
|
self.sink = sink = stream.Endpoint(video_data_layout)
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
from liteiclink.serdes.gtp_7series import GTPQuadPLL, GTP
|
||||||
|
|
||||||
|
# Always ack Sink, no backpressure.
|
||||||
|
self.comb += sink.ready.eq(1)
|
||||||
|
|
||||||
|
# Clocking + Differential Signaling.
|
||||||
|
pads_clk = Signal()
|
||||||
|
self.specials += DDROutput(i1=1, i2=0, o=pads_clk, clk=ClockSignal(clock_domain))
|
||||||
|
self.specials += Instance("OBUFDS", i_I=pads_clk, o_O=pads.clk_p, o_OB=pads.clk_n)
|
||||||
|
|
||||||
|
# GTP Quad PLL.
|
||||||
|
self.submodules.pll = pll = GTPQuadPLL(ClockSignal(clock_domain), 148.5e6, 1.485e9) # FIXME: 1080P60, allow other Video timings.
|
||||||
|
|
||||||
|
# Encode/Serialize Datas.
|
||||||
|
for color in ["r", "g", "b"]:
|
||||||
|
# TMDS Encoding.
|
||||||
|
encoder = ClockDomainsRenamer(clock_domain)(TMDSEncoder())
|
||||||
|
self.submodules += encoder
|
||||||
|
self.comb += encoder.d.eq(getattr(sink, color))
|
||||||
|
self.comb += encoder.c.eq(Cat(sink.hsync, sink.vsync) if color == "r" else 0)
|
||||||
|
self.comb += encoder.de.eq(sink.de)
|
||||||
|
|
||||||
|
# 10:20 (SerDes has a minimal 20:1 Serialization ratio).
|
||||||
|
converter = ClockDomainsRenamer(clock_domain)(stream.Converter(10, 20))
|
||||||
|
self.submodules += converter
|
||||||
|
self.comb += converter.sink.valid.eq(1)
|
||||||
|
self.comb += converter.sink.data.eq(encoder.out)
|
||||||
|
|
||||||
|
# Clock Domain Crossing (video_clk --> gtp_tx)
|
||||||
|
cdc = stream.ClockDomainCrossing([("data", 20)], cd_from=clock_domain, cd_to=f"gtp{color}_tx")
|
||||||
|
self.submodules += cdc
|
||||||
|
self.comb += converter.source.connect(cdc.sink)
|
||||||
|
self.comb += cdc.source.ready.eq(1) # No backpressure.
|
||||||
|
|
||||||
|
# 20:1 Serialization + Differential Signaling.
|
||||||
|
c2d = {"r": 2, "g": 1, "b": 0}
|
||||||
|
class GTPPads:
|
||||||
|
def __init__(self, p, n):
|
||||||
|
self.p = p
|
||||||
|
self.n = n
|
||||||
|
tx_pads = GTPPads(p=getattr(pads, f"data{c2d[color]}_p"), n=getattr(pads, f"data{c2d[color]}_n"))
|
||||||
|
# FIXME: Find a way to avoid RX pads.
|
||||||
|
rx_pads = GTPPads(p=getattr(pads, f"rx{c2d[color]}_p"), n=getattr(pads, f"rx{c2d[color]}_n"))
|
||||||
|
gtp = GTP(pll, tx_pads, rx_pads=rx_pads, sys_clk_freq=sys_clk_freq,
|
||||||
|
tx_polarity = 1, # FIXME: Specific to Decklink Mini 4K, make it configurable.
|
||||||
|
tx_buffer_enable = True,
|
||||||
|
rx_buffer_enable = True,
|
||||||
|
clock_aligner = False
|
||||||
|
)
|
||||||
|
setattr(self.submodules, f"gtp{color}", gtp)
|
||||||
|
self.comb += gtp.tx_produce_pattern.eq(1)
|
||||||
|
self.comb += gtp.tx_pattern.eq(cdc.source.data)
|
||||||
|
|
||||||
# HDMI (Lattice ECP5).
|
# HDMI (Lattice ECP5).
|
||||||
|
|
||||||
class VideoECP5HDMIPHY(Module):
|
class VideoECP5HDMIPHY(Module):
|
||||||
|
|
Loading…
Reference in New Issue