def __init__(self): cd_sys_name = "sys" cd_phy_name = "phy" # # # self.submodules.tfg = tfg = ClockDomainsRenamer(cd_phy_name)( TestFrameGenerator(data_width=32)) self.submodules.cdc_s2p = cdc_s2p = stream.ClockDomainCrossing( tfg.sink_ctrl.description, cd_from=cd_sys_name, cd_to=cd_phy_name, depth=4) self.submodules.cdc_p2s = cdc_p2s = stream.ClockDomainCrossing( tfg.source.description, cd_from=cd_phy_name, cd_to=cd_sys_name, depth=16) self.comb += [ cdc_s2p.source.connect(tfg.sink_ctrl), tfg.source.connect(cdc_p2s.sink) ] self.sink_ctrl = cdc_s2p.sink self.source = cdc_p2s.source
def __init__(self, dram_port, hres=800, vres=600, base=0x00000000, fifo_depth=65536, clock_domain="sys", clock_faster_than_sys=False): self.vtg_sink = vtg_sink = stream.Endpoint(video_timing_layout) self.source = source = stream.Endpoint(video_data_layout) self.underflow = Signal() # # # # Video DMA. from litedram.frontend.dma import LiteDRAMDMAReader self.submodules.dma = LiteDRAMDMAReader(dram_port, fifo_depth=fifo_depth//(dram_port.data_width//8), fifo_buffered=True) self.dma.add_csr( default_base = base, default_length = hres*vres*32//8, # 32-bit RGB-444 default_enable = 0, default_loop = 1 ) # If DRAM Data Width > 32-bit and Video clock is faster than sys_clk: if (dram_port.data_width > 32) and clock_faster_than_sys: # Do Clock Domain Crossing first... self.submodules.cdc = stream.ClockDomainCrossing([("data", dram_port.data_width)], cd_from="sys", cd_to=clock_domain) self.comb += self.dma.source.connect(self.cdc.sink) # ... and then Data-Width Conversion. self.submodules.conv = stream.Converter(dram_port.data_width, 32) self.comb += self.cdc.source.connect(self.conv.sink) video_pipe_source = self.conv.source # Elsif DRAM Data Widt < 32-bit or Video clock is slower than sys_clk: else: # Do Data-Width Conversion first... self.submodules.conv = stream.Converter(dram_port.data_width, 32) self.comb += self.dma.source.connect(self.conv.sink) # ... and then Clock Domain Crossing. self.submodules.cdc = stream.ClockDomainCrossing([("data", 32)], cd_from="sys", cd_to=clock_domain) self.comb += self.conv.source.connect(self.cdc.sink) self.comb += If(dram_port.data_width < 32, # FIXME. self.cdc.sink.data[ 0: 8].eq(self.conv.source.data[16:24]), self.cdc.sink.data[16:24].eq(self.conv.source.data[ 0: 8]), ) video_pipe_source = self.cdc.source # Video Generation. self.comb += [ vtg_sink.ready.eq(1), If(vtg_sink.valid & vtg_sink.de, video_pipe_source.connect(source, keep={"valid", "ready"}), vtg_sink.ready.eq(source.valid & source.ready), ), vtg_sink.connect(source, keep={"de", "hsync", "vsync"}), source.r.eq(video_pipe_source.data[16:24]), source.g.eq(video_pipe_source.data[ 8:16]), source.b.eq(video_pipe_source.data[ 0: 8]), ] # Underflow. self.comb += self.underflow.eq(~source.valid)
def __init__(self, phy, clk_freq, cd="sys"): if cd == "sys": self.submodules.phy = phy Stream2Wishbone.__init__(self, self.phy, clk_freq=clk_freq) else: self.submodules.phy = ClockDomainsRenamer(cd)(phy) self.submodules.tx_cdc = stream.ClockDomainCrossing([("data", 8)], cd_from="sys", cd_to=cd) self.submodules.rx_cdc = stream.ClockDomainCrossing([("data", 8)], cd_from=cd, cd_to="sys") self.comb += self.phy.source.connect(self.rx_cdc.sink) self.comb += self.tx_cdc.source.connect(self.phy.sink) Stream2Wishbone.__init__(self, clk_freq=clk_freq) self.comb += self.rx_cdc.source.connect(self.sink) self.comb += self.source.connect(self.tx_cdc.sink)
def get_port(self, udp_port, dw=8, cd="sys"): if udp_port in self.users.keys(): raise ValueError("Port {0:#x} already assigned".format(udp_port)) user_port = LiteEthUDPUserPort(dw) internal_port = LiteEthUDPUserPort(self.dw) # TX # --- # CDC. self.submodules.tx_cdc = tx_cdc = stream.ClockDomainCrossing( layout=eth_udp_user_description(user_port.dw), cd_from=cd, cd_to="sys") self.comb += user_port.sink.connect(tx_cdc.sink) # Data-Width Conversion. self.submodules.tx_converter = tx_converter = stream.StrideConverter( description_from=eth_udp_user_description(user_port.dw), description_to=eth_udp_user_description(self.dw)) self.comb += tx_cdc.source.connect(tx_converter.sink) # Interface. self.comb += tx_converter.source.connect(internal_port.sink) # RX # -- # Data-Width Conversion. self.submodules.rx_converter = rx_converter = stream.StrideConverter( description_from=eth_udp_user_description(self.dw), description_to=eth_udp_user_description(user_port.dw)) self.comb += internal_port.source.connect(rx_converter.sink) # CDC. self.submodules.rx_cdc = rx_cdc = stream.ClockDomainCrossing( layout=eth_udp_user_description(user_port.dw), cd_from="sys", cd_to=cd) self.comb += rx_converter.source.connect(rx_cdc.sink) # Interface. self.comb += rx_cdc.source.connect(user_port.source) # Expose/Return User Port. # ------------------------ self.users[udp_port] = internal_port return user_port
def __init__(self, port_from, port_to, cmd_depth=4, wdata_depth=16, rdata_depth=16): assert port_from.address_width == port_to.address_width assert port_from.data_width == port_to.data_width assert port_from.mode == port_to.mode address_width = port_from.address_width data_width = port_from.data_width mode = port_from.mode # # # cmd_cdc = stream.ClockDomainCrossing(layout=[("we", 1), ("addr", address_width)], cd_from=port_from.clock_domain, cd_to=port_to.clock_domain, depth=cmd_depth) self.submodules += cmd_cdc self.submodules += stream.Pipeline(port_from.cmd, cmd_cdc, port_to.cmd) if mode in ["write", "both"]: wdata_cdc = stream.ClockDomainCrossing( layout=[("data", data_width), ("we", data_width // 8)], cd_from=port_from.clock_domain, cd_to=port_to.clock_domain, depth=wdata_depth) self.submodules += wdata_cdc self.submodules += stream.Pipeline(port_from.wdata, wdata_cdc, port_to.wdata) if mode in ["read", "both"]: rdata_cdc = stream.ClockDomainCrossing( layout=[("data", data_width)], cd_from=port_to.clock_domain, cd_to=port_from.clock_domain, depth=rdata_depth) self.submodules += rdata_cdc self.submodules += stream.Pipeline(port_to.rdata, rdata_cdc, port_from.rdata)
def __init__(self, master, slave, cd_from="sys", cd_to="sys"): # Same Clock Domain, direct connection. if cd_from == cd_to: self.comb += [ # Write. master.aw.connect(slave.aw), master.w.connect(slave.w), slave.b.connect(master.b), # Read. master.ar.connect(slave.ar), slave.r.connect(master.r), ] # Clock Domain Crossing. else: # Write. aw_cdc = stream.ClockDomainCrossing(master.aw.description, cd_from, cd_to) w_cdc = stream.ClockDomainCrossing(master.w.description, cd_from, cd_to) b_cdc = stream.ClockDomainCrossing(master.b.description, cd_to, cd_from) self.submodules += aw_cdc, w_cdc, b_cdc self.comb += [ master.aw.connect(aw_cdc.sink), aw_cdc.source.connect(slave.aw), master.w.connect(w_cdc.sink), w_cdc.source.connect(slave.w), slave.b.connect(b_cdc.sink), b_cdc.source.connect(master.b), ] # Read. ar_cdc = stream.ClockDomainCrossing(master.ar.description, cd_from, cd_to) r_cdc = stream.ClockDomainCrossing(master.r.description, cd_to, cd_from) self.submodules += ar_cdc, r_cdc self.comb += [ master.ar.connect(ar_cdc.sink), ar_cdc.source.connect(slave.ar), slave.r.connect(r_cdc.sink), r_cdc.source.connect(master.r), ]
def __init__(self, dram_port, hres=800, vres=600, base=0x00000000, clock_domain="sys"): self.vtg_sink = vtg_sink = stream.Endpoint(video_timing_layout) self.source = source = stream.Endpoint(video_data_layout) # # # # Video DMA. from litedram.frontend.dma import LiteDRAMDMAReader self.submodules.dma = LiteDRAMDMAReader( dram_port, fifo_depth=2048, fifo_buffered=True) # FIXME: Adjust/Expose. self.dma.add_csr( default_base=base, default_length=hres * vres * 32 // 8, # 32-bit RGB-444 default_enable=0, default_loop=1) # FIXME: Make sure it will work for all DRAM's data-width/all Video resolutions. # Video Data-Width Converter. self.submodules.conv = stream.Converter(dram_port.data_width, 32) self.comb += self.dma.source.connect(self.conv.sink) # Video CDC. self.submodules.cdc = stream.ClockDomainCrossing([("data", 32)], cd_from="sys", cd_to=clock_domain) self.comb += self.conv.source.connect(self.cdc.sink) # Video Generation. self.comb += [ vtg_sink.ready.eq(1), If( vtg_sink.valid & vtg_sink.de, self.cdc.source.connect(source, keep={"valid", "ready"}), vtg_sink.ready.eq(source.valid & source.ready), ), vtg_sink.connect(source, keep={"de", "hsync", "vsync"}), source.r.eq(self.cdc.source.data[16:24]), source.g.eq(self.cdc.source.data[8:16]), source.b.eq(self.cdc.source.data[0:8]), ]
def __init__(self, data_i, data_o, clock_domain): # Clock Domain Crossing. self.submodules.cdc = stream.ClockDomainCrossing([("data", 10)], cd_from=clock_domain, cd_to=clock_domain + "5x") self.comb += self.cdc.sink.valid.eq(1) self.comb += self.cdc.sink.data.eq(data_i) # 10:2 Gearbox. self.submodules.gearbox = ClockDomainsRenamer(clock_domain + "5x")(stream.Gearbox(i_dw=10, o_dw=2, msb_first=False)) self.comb += self.cdc.source.connect(self.gearbox.sink) # 2:1 Output DDR. self.comb += self.gearbox.source.ready.eq(1) self.specials += DDROutput( clk = ClockSignal(clock_domain + "5x"), i1 = self.gearbox.source.data[0], i2 = self.gearbox.source.data[1], o = data_o, )
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)
def __init__(self, dram_port, hres=800, vres=600, base=0x00000000, fifo_depth=65536, clock_domain="sys", clock_faster_than_sys=False, format="rgb888"): self.vtg_sink = vtg_sink = stream.Endpoint(video_timing_layout) self.source = source = stream.Endpoint(video_data_layout) self.underflow = Signal() self.depth = depth = { "rgb888" : 32, "rgb565" : 16 }[format] # # # # Video DMA. from litedram.frontend.dma import LiteDRAMDMAReader self.submodules.dma = LiteDRAMDMAReader(dram_port, fifo_depth=fifo_depth//(dram_port.data_width//8), fifo_buffered=True) self.dma.add_csr( default_base = base, default_length = hres*vres*depth//8, # 32-bit RGB-888 or 16-bit RGB-565 default_enable = 0, default_loop = 1 ) # If DRAM Data Width > depth and Video clock is faster than sys_clk: if (dram_port.data_width > depth) and clock_faster_than_sys: # Do Clock Domain Crossing first... self.submodules.cdc = stream.ClockDomainCrossing([("data", dram_port.data_width)], cd_from="sys", cd_to=clock_domain) self.comb += self.dma.source.connect(self.cdc.sink) # ... and then Data-Width Conversion. self.submodules.conv = ClockDomainsRenamer(clock_domain)(stream.Converter(dram_port.data_width, depth)) self.comb += self.cdc.source.connect(self.conv.sink) video_pipe_source = self.conv.source # Elsif DRAM Data Width <= depth or Video clock is slower than sys_clk: else: # Do Data-Width Conversion first... self.submodules.conv = stream.Converter(dram_port.data_width, depth) self.comb += self.dma.source.connect(self.conv.sink) # ... and then Clock Domain Crossing. self.submodules.cdc = stream.ClockDomainCrossing([("data", depth)], cd_from="sys", cd_to=clock_domain) self.comb += self.conv.source.connect(self.cdc.sink) if (dram_port.data_width < depth) and (depth == 32): # FIXME. self.comb += [ self.cdc.sink.data[ 0: 8].eq(self.conv.source.data[16:24]), self.cdc.sink.data[16:24].eq(self.conv.source.data[ 0: 8]), ] video_pipe_source = self.cdc.source # Video Generation. self.comb += [ vtg_sink.ready.eq(1), If(vtg_sink.valid & vtg_sink.de, video_pipe_source.connect(source, keep={"valid", "ready"}), vtg_sink.ready.eq(source.valid & source.ready), ), vtg_sink.connect(source, keep={"de", "hsync", "vsync"}), ] if (depth == 32): self.comb += [ source.r.eq(video_pipe_source.data[16:24]), source.g.eq(video_pipe_source.data[ 8:16]), source.b.eq(video_pipe_source.data[ 0: 8]), ] else: # depth == 16 self.comb += [ source.r.eq(Cat(Signal(3, reset = 0), video_pipe_source.data[ 0: 5])), source.g.eq(Cat(Signal(2, reset = 0), video_pipe_source.data[ 5:11])), source.b.eq(Cat(Signal(3, reset = 0), video_pipe_source.data[11:16])), ] # Underflow. self.comb += self.underflow.eq(~source.valid)
def __init__(self, variant="minimal", with_sampler=False, with_analyzer=False, host_ip="192.168.1.100", host_udp_port=2000): platform = arty.Platform() # BenchSoC --------------------------------------------------------------------------------- bench_kwargs = { "minimal": dict(cpu_variant="minimal", integrated_main_ram_size=0x1000), "standard": dict(), }[variant] BaseSoC.__init__(self, sys_clk_freq=int(100e6), integrated_rom_size=0x10000, integrated_rom_mode="rw", **bench_kwargs) # SDCard on PMODD with Digilent's Pmod MicroSD --------------------------------------------- self.platform.add_extension(arty._sdcard_pmod_io) self.add_sdcard("sdcard") if with_sampler or with_analyzer: # Etherbone ---------------------------------------------------------------------------- from liteeth.phy.mii import LiteEthPHYMII self.submodules.ethphy = LiteEthPHYMII( clock_pads=self.platform.request("eth_clocks"), pads=self.platform.request("eth")) self.add_csr("ethphy") self.add_etherbone(phy=self.ethphy) if with_sampler: # PMODB Sampler (connected to PmodTPH2 with Pmode Cable Kit) --------------------------- _la_pmod_ios = [("la_pmod", 0, Pins("pmoda:0 pmoda:1 pmoda:2 pmoda:3", "pmoda:4 pmoda:5 pmoda:6 pmoda:7"), IOStandard("LVCMOS33"))] self.platform.add_extension(_la_pmod_ios) self.submodules.sampler = Sampler(self.platform.request("la_pmod")) self.add_csr("sampler") # DRAMFIFO ----------------------------------------------------------------------------- from litedram.frontend.fifo import LiteDRAMFIFO self.submodules.fifo = LiteDRAMFIFO( data_width=8, base=0x00000000, depth=0x01000000, # 16MB write_port=self.sdram.crossbar.get_port(mode="write", data_width=8), read_port=self.sdram.crossbar.get_port(mode="read", data_width=8), ) # UDPStreamer -------------------------------------------------------------------------- from liteeth.common import convert_ip from liteeth.frontend.stream import LiteEthStream2UDPTX udp_port = self.ethcore.udp.crossbar.get_port(host_udp_port, dw=8) udp_streamer = LiteEthStream2UDPTX(ip_address=convert_ip(host_ip), udp_port=host_udp_port, fifo_depth=1024) udp_streamer = ClockDomainsRenamer("eth_tx")(udp_streamer) self.submodules += udp_streamer udp_cdc = stream.ClockDomainCrossing([("data", 8)], "sys", "eth_tx") self.submodules += udp_cdc # Sampler/FIFO/UDPStreamer flow ------------------------------------------------------------- self.comb += self.sampler.source.connect(self.fifo.sink) self.comb += self.fifo.source.connect(udp_cdc.sink) self.comb += udp_cdc.source.connect(udp_streamer.sink) self.comb += udp_streamer.source.connect(udp_port.sink) if with_analyzer: from litescope import LiteScopeAnalyzer analyzer_signals = [ self.sdphy.sdpads, self.sdphy.cmdw.sink, self.sdphy.cmdr.sink, self.sdphy.cmdr.source, self.sdphy.dataw.sink, self.sdphy.dataw.stop, self.sdphy.dataw.crc.source, self.sdphy.dataw.status.status, self.sdphy.datar.sink, self.sdphy.datar.source, self.sdphy.clocker.ce, self.sdphy.clocker.stop, ] self.submodules.analyzer = LiteScopeAnalyzer( analyzer_signals, depth=2048, clock_domain="sys", csr_csv="analyzer.csv") self.add_csr("analyzer")
def __init__(self, pads, clk_freq, fifo_depth = 64, read_time = 128, write_time = 128): self.dw = dw = len(pads.data) self.pads = pads self.sink = stream.Endpoint(phy_description(dw)) self.source = stream.Endpoint(phy_description(dw)) # # # # Pads Reset. # ----------- pads.oe_n.reset = 1 pads.rd_n.reset = 1 pads.wr_n.reset = 1 # Read CDC/FIFO (FTDI --> SoC). # ----------------------------- self.submodules.read_cdc = stream.ClockDomainCrossing(phy_description(dw), cd_from = "usb", cd_to = "sys", with_common_rst = True ) self.submodules.read_fifo = stream.SyncFIFO(phy_description(dw), fifo_depth) self.comb += self.read_cdc.source.connect(self.read_fifo.sink) self.comb += self.read_fifo.source.connect(self.source) read_fifo_almost_full = (self.read_fifo.level > (fifo_depth - 4)) read_fifo_almost_full_usb = Signal() self.specials += MultiReg(read_fifo_almost_full, read_fifo_almost_full_usb) # Write FIFO/CDC (SoC --> FTDI). # ------------------------------ self.submodules.write_fifo = stream.SyncFIFO(phy_description(dw), fifo_depth) self.submodules.write_cdc = stream.ClockDomainCrossing(phy_description(dw), cd_from = "sys", cd_to = "usb", with_common_rst = True ) self.comb += self.sink.connect(self.write_fifo.sink) self.comb += self.write_fifo.source.connect(self.write_cdc.sink) # Read / Write Anti-Starvation. # ----------------------------- read_time_en, max_read_time = anti_starvation(self, read_time) write_time_en, max_write_time = anti_starvation(self, write_time) # Read / Write Detection. # ----------------------- self.wants_write = wants_write = Signal() self.wants_read = wants_read = Signal() self.comb += [ wants_write.eq(~pads.txe_n & self.write_cdc.source.valid), wants_read.eq( ~pads.rxf_n & (self.read_cdc.sink.ready & ~read_fifo_almost_full_usb)), ] # Data Bus Tristate. # ------------------ self.data_w = data_w = Signal(dw) self.data_r = data_r = Signal(dw) self.data_oe = data_oe = Signal() for i in range(dw): self.specials += SDRTristate( io = pads.data[i], o = data_w[i], oe = data_oe, i = data_r[i], clk = ClockSignal("usb") ) if hasattr(pads, "be"): for i in range(dw//8): self.specials += SDRTristate( io = pads.be[i], o = Signal(reset=0b1), oe = data_oe, i = Signal(), clk = ClockSignal("usb") ) # Read / Write FSM. # ----------------- fsm = FSM(reset_state="READ") fsm = ClockDomainsRenamer("usb")(fsm) self.submodules.fsm = fsm fsm.act("READ", # Arbitration. read_time_en.eq(1), If(wants_write, If(~wants_read | max_read_time, NextState("READ-TO-WRITE") ) ), # Control/Data-Path. data_oe.eq(0), NextValue(pads.oe_n, ~wants_read), NextValue(pads.rd_n, pads.oe_n | ~wants_read), NextValue(pads.wr_n, 1), ) self.comb += self.read_cdc.sink.data.eq(data_r) self.sync.usb += self.read_cdc.sink.valid.eq(~pads.rd_n & ~pads.rxf_n) fsm.act("READ-TO-WRITE", NextState("WRITE") ) fsm.act("WRITE", # Arbitration. write_time_en.eq(1), If(wants_read, If(~wants_write | max_write_time, NextState("WRITE-TO-READ") ) ), # Control/Data-Path. data_oe.eq(1), NextValue(pads.oe_n, 1), NextValue(pads.rd_n, 1), NextValue(pads.wr_n, ~wants_write), #data_w.eq(write_fifo.source.data), NextValue(data_w, self.write_cdc.source.data), # FIXME: Add 1 cycle delay. self.write_cdc.source.ready.eq(wants_write), ) fsm.act("WRITE-TO-READ", NextState("READ") )
def __init__(self, dram_port, upd_clut_fifo=None, hres=800, vres=600, base=0x00000000, fifo_depth=65536, clock_domain="sys", clock_faster_than_sys=False, hwcursor=False, upd_overlay_fifo=False, upd_omap_fifo=False): clut = Array( Array(Signal(8, reset=(255 - i)) for i in range(0, 256)) for j in range(0, 3)) upd_clut_fifo_dout = Record(cmap_layout) self.comb += upd_clut_fifo_dout.raw_bits().eq(upd_clut_fifo.dout) if (hwcursor): upd_omap_fifo_dout = Record(omap_layout) self.comb += upd_omap_fifo_dout.raw_bits().eq(upd_omap_fifo.dout) print( f"FRAMEBUFFER: dram_port.data_width = {dram_port.data_width}, {hres}x{vres}, 0x{base:x}, in {clock_domain}, clock_faster_than_sys={clock_faster_than_sys}" ) vga_sync = getattr(self.sync, clock_domain) vga_sync += [ If( upd_clut_fifo.readable, upd_clut_fifo.re.eq(1), clut[upd_clut_fifo_dout.color][upd_clut_fifo_dout.address].eq( upd_clut_fifo_dout.data), ).Else(upd_clut_fifo.re.eq(0), ) ] if (hwcursor): self.vtg_sink = vtg_sink = stream.Endpoint( video_timing_hwcursor_layout) overlay = Array( Array( Array(Signal(1) for x in range(0, 32)) for y in range(0, 32)) for i in range(0, 2)) omap = Array( Array(Signal(8, reset=(255 - i)) for i in range(0, 4)) for j in range(0, 3)) vga_sync += [ If( upd_overlay_fifo.readable, upd_overlay_fifo.re.eq(1), [ overlay[upd_overlay_fifo.dout[0]][ upd_overlay_fifo.dout[1:6]][x].eq( upd_overlay_fifo.dout[6 + x]) for x in range(0, 32) ], ).Else(upd_overlay_fifo.re.eq(0), ) ] vga_sync += [ If( upd_omap_fifo.readable, upd_omap_fifo.re.eq(1), omap[upd_omap_fifo_dout.color] [upd_omap_fifo_dout.address].eq(upd_omap_fifo_dout.data), ).Else(upd_omap_fifo.re.eq(0), ) ] else: self.vtg_sink = vtg_sink = stream.Endpoint(video_timing_layout) self.source = source = stream.Endpoint(video_data_layout) self.underflow = Signal() #source_buf_ready = Signal() source_buf_valid = Signal() source_buf_de = Signal() source_buf_hsync = Signal() source_buf_vsync = Signal() data_buf = Signal(8) if (hwcursor): hwcursor_buf = Signal() hwcursorx_buf = Signal(5) hwcursory_buf = Signal(5) #source_out_ready = Signal() source_out_valid = Signal() source_out_de = Signal() source_out_hsync = Signal() source_out_vsync = Signal() source_out_r = Signal(8) source_out_g = Signal(8) source_out_b = Signal(8) # # # # Video DMA. from litedram.frontend.dma import LiteDRAMDMAReader self.submodules.dma = LiteDRAMDMAReader(dram_port, fifo_depth=fifo_depth // (dram_port.data_width // 8), fifo_buffered=True) self.dma.add_csr( default_base=base, default_length=hres * vres * 8 // 8, # 8-bit PseudoColor default_enable=0, default_loop=1) # If DRAM Data Width > 8-bit and Video clock is faster than sys_clk: if (dram_port.data_width > 8) and clock_faster_than_sys: # Do Clock Domain Crossing first... self.submodules.cdc = stream.ClockDomainCrossing( [("data", dram_port.data_width)], cd_from="sys", cd_to=clock_domain) self.comb += self.dma.source.connect(self.cdc.sink) # ... and then Data-Width Conversion. self.submodules.conv = ClockDomainsRenamer({"sys": clock_domain})( stream.Converter(dram_port.data_width, 8)) self.comb += self.cdc.source.connect(self.conv.sink) video_pipe_source = self.conv.source # Elsif DRAM Data Widt < 8-bit or Video clock is slower than sys_clk: else: # Do Data-Width Conversion first... self.submodules.conv = stream.Converter(dram_port.data_width, 8) self.comb += self.dma.source.connect(self.conv.sink) # ... and then Clock Domain Crossing. self.submodules.cdc = stream.ClockDomainCrossing( [("data", 8)], cd_from="sys", cd_to=clock_domain) self.comb += self.conv.source.connect(self.cdc.sink) video_pipe_source = self.cdc.source # Video Generation. self.comb += [ vtg_sink.ready.eq(1), If( vtg_sink.valid & vtg_sink.de, source_buf_valid.eq(video_pipe_source.valid), #video_pipe_source.ready.eq(source_buf_ready),# ready flow the other way video_pipe_source.connect( source, keep={"ready"} ), # source.ready is set to 1 by the sink anyway, bypass the cycle delay #vtg_sink.ready.eq(source_buf_valid & source_buf_ready), vtg_sink.ready.eq(source_buf_valid & source.ready), ), source_buf_de.eq(vtg_sink.de), source_buf_hsync.eq(vtg_sink.hsync), source_buf_vsync.eq(vtg_sink.vsync), data_buf.eq(video_pipe_source.data), ] if (hwcursor): self.comb += [ hwcursor_buf.eq(vtg_sink.hwcursor), hwcursorx_buf.eq(vtg_sink.hwcursorx), hwcursory_buf.eq(vtg_sink.hwcursory), ] if (hwcursor): source_mid_valid = Signal() source_mid_de = Signal() source_mid_hsync = Signal() source_mid_vsync = Signal() data_mid = Signal(8) hwcursor_color_idx = Signal(2) # first cycle, buffer everything and look up the cursor overlay color vga_sync += [ source_mid_de.eq(source_buf_de), source_mid_hsync.eq(source_buf_hsync), source_mid_vsync.eq(source_buf_vsync), source_mid_valid.eq(source_buf_valid), data_mid.eq(data_buf), If( hwcursor_buf, hwcursor_color_idx.eq( Cat(overlay[0][hwcursory_buf][hwcursorx_buf], overlay[1][hwcursory_buf][hwcursorx_buf])), ).Else(hwcursor_color_idx.eq(0), ) ] #second cycle, produce the pixel by doing CLUT lookup vga_sync += [ source_out_de.eq(source_mid_de), source_out_hsync.eq(source_mid_hsync), source_out_vsync.eq(source_mid_vsync), source_out_valid.eq(source_mid_valid), #source_buf_ready.eq(source_out_ready), # ready flow the other way If( hwcursor_color_idx != 0, source_out_r.eq(omap[0][hwcursor_color_idx]), source_out_g.eq(omap[1][hwcursor_color_idx]), source_out_b.eq(omap[2][hwcursor_color_idx]), ).Elif(source_mid_de, source_out_r.eq(clut[0][data_mid]), source_out_g.eq(clut[1][data_mid]), source_out_b.eq(clut[2][data_mid])).Else( source_out_r.eq(0), source_out_g.eq(0), source_out_b.eq(0)) ] else: vga_sync += [ source_out_de.eq(source_buf_de), source_out_hsync.eq(source_buf_hsync), source_out_vsync.eq(source_buf_vsync), source_out_valid.eq(source_buf_valid), #source_buf_ready.eq(source_out_ready), # ready flow the other way If(source_buf_de, source_out_r.eq(clut[0][data_buf]), source_out_g.eq(clut[1][data_buf]), source_out_b.eq(clut[2][data_buf])).Else( source_out_r.eq(0), source_out_g.eq(0), source_out_b.eq(0)) ] self.comb += [ source.de.eq(source_out_de), source.hsync.eq(source_out_hsync), source.vsync.eq(source_out_vsync), source.valid.eq(source_out_valid), #source_out_ready.eq(source.ready), # ready flow the other way source.r.eq(source_out_r), source.g.eq(source_out_g), source.b.eq(source_out_b), ] # Underflow. self.comb += self.underflow.eq(~source.valid)