def __init__(self, pads, clock_domain="sys", pn_swap=[]): self.sink = sink = stream.Endpoint(video_data_layout) # # # # Always ack Sink, no backpressure. self.comb += sink.ready.eq(1) # Clocking + Pseudo Differential Signaling. self.specials += DDROutput(i1=1, i2=0, o=pads.clk_p, clk=ClockSignal(clock_domain)) # Encode/Serialize Datas. for color in ["r", "g", "b"]: # TMDS Encoding. encoder = ClockDomainsRenamer(clock_domain)(TMDSEncoder()) setattr(self.submodules, f"{color}_encoder", 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:1 Serialization + Pseudo Differential Signaling. c2d = {"r": 0, "g": 1, "b": 2} data = encoder.out if color not in pn_swap else ~encoder.out serializer = VideoHDMI10to1Serializer( data_i = data, data_o = getattr(pads, f"data{c2d[color]}_p"), clock_domain = clock_domain, ) setattr(self.submodules, f"{color}_serializer", serializer)
def __init__(self, pads, clock_domain="sys"): self.sink = sink = stream.Endpoint(video_data_layout) # # # # 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) # 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:1 Serialization + Differential Signaling. pad_o = Signal() serializer = VideoS7HDMI10to1Serializer( data_i = encoder.out, data_o = pad_o, clock_domain = clock_domain, ) self.submodules += serializer c2d = {"r": 0, "g": 1, "b": 2} pad_p = getattr(pads, f"data{c2d[color]}_p") pad_n = getattr(pads, f"data{c2d[color]}_n") self.specials += Instance("OBUFDS", i_I=pad_o, o_O=pad_p, o_OB=pad_n)
def __init__(self, pads, clock_domain="sys", pn_swap=[]): self.sink = sink = stream.Endpoint(video_data_layout) # # # # Determine driven polarities: # - p only for True/Pseudo Differential. # - p and n for Fake Differential. drive_pols = [] for pol in ["p", "n"]: if hasattr(pads, f"clk_{pol}"): drive_pols.append(pol) # Always ack Sink, no backpressure. self.comb += sink.ready.eq(1) # Clocking + Pseudo Differential Signaling. for pol in drive_pols: self.specials += DDROutput( i1={ "p": 1, "n": 0 }[pol], i2={ "p": 0, "n": 1 }[pol], o=getattr(pads, f"clk_{pol}"), clk=ClockSignal(clock_domain), ) # Encode/Serialize Datas. for pol in drive_pols: for color in ["r", "g", "b"]: # TMDS Encoding. encoder = ClockDomainsRenamer(clock_domain)(TMDSEncoder()) setattr(self.submodules, f"{color}_encoder", 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:1 Serialization + Pseudo Differential Signaling. c2d = {"r": 0, "g": 1, "b": 2} data_i = encoder.out if color not in pn_swap else ~encoder.out data_o = getattr(pads, f"data{c2d[color]}_{pol}") serializer = VideoHDMI10to1Serializer( data_i={ "p": data_i, "n": ~data_i }[pol], data_o=data_o, clock_domain=clock_domain, ) setattr(self.submodules, f"{color}_serializer", serializer)
def __init__(self, pads, sys_clk_freq, clock_domain="sys", clk_freq=148.5e6, refclk=None): assert sys_clk_freq >= clk_freq 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. if refclk is None: # No RefClk provided, use the Video Clk as GTP RefClk. refclk = ClockSignal(clock_domain) elif isinstance(refclk, Record): # Differential RefCLk provided, add an IBUFDS_GTE2. refclk_se = Signal() self.specials += Instance("IBUFDS_GTE2", i_CEB = 0, i_I = refclk.p, i_IB = refclk.n, o_O = refclk_se ) refclk = refclk_se self.submodules.pll = pll = GTPQuadPLL(refclk, clk_freq, 1.485e9) # 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)