From ccc891699560fc2c54fa1b54c78aa325f819e4e0 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Wed, 3 Mar 2021 19:58:11 +0100 Subject: [PATCH] soc/cores/video: Add initial (and simple) VideoFrameBuffer core. --- litex/soc/cores/video.py | 49 +++++++++++++++++++++++++++++++++++- litex/soc/integration/soc.py | 27 ++++++++++++++++++-- 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/litex/soc/cores/video.py b/litex/soc/cores/video.py index d352d19b0..f35aa780c 100644 --- a/litex/soc/cores/video.py +++ b/litex/soc/cores/video.py @@ -13,6 +13,8 @@ from migen.genlib.cdc import MultiReg from litex.soc.interconnect.csr import * from litex.soc.interconnect import stream +from litedram.frontend.dma import LiteDRAMDMAReader + # Video Constants ---------------------------------------------------------------------------------- hbits = 12 @@ -229,7 +231,7 @@ class VideoTimingGenerator(Module, AutoCSR): ) ) -# Patterns ----------------------------------------------------------------------------------------- +# Video Patterns ----------------------------------------------------------------------------------- class ColorBarsPattern(Module): """Color Bars Pattern""" @@ -551,6 +553,51 @@ class VideoTerminal(Module): source.b.eq(0x00) ) +# Video FrameBuffer -------------------------------------------------------------------------------- + +class VideoFrameBuffer(Module, AutoCSR): + """Video FrameBuffer""" + def __init__(self, dram_port, hres=640, vres=480, 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. + 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_start = 1, + 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, + source.valid.eq(self.cdc.source.valid), + vtg_sink.ready.eq(source.ready), + self.cdc.source.ready.eq(source.ready) + ), + source.de.eq(vtg_sink.de), + source.hsync.eq(vtg_sink.hsync), + source.vsync.eq(vtg_sink.vsync), + source.r.eq(self.cdc.source.data[ 0: 8]), + source.g.eq(self.cdc.source.data[ 8:16]), + source.b.eq(self.cdc.source.data[16:24]), + ] + # Video PHYs --------------------------------------------------------------------------------------- class VideoDVIPHY(Module): diff --git a/litex/soc/integration/soc.py b/litex/soc/integration/soc.py index 8f261c87e..8cb7edd69 100644 --- a/litex/soc/integration/soc.py +++ b/litex/soc/integration/soc.py @@ -18,7 +18,7 @@ from litex.soc.cores.identifier import Identifier from litex.soc.cores.timer import Timer from litex.soc.cores.spi_flash import SpiFlash from litex.soc.cores.spi import SPIMaster -from litex.soc.cores.video import VideoTimingGenerator, VideoTerminal +from litex.soc.cores.video import VideoTimingGenerator, VideoTerminal, VideoFrameBuffer from litex.soc.interconnect.csr import * from litex.soc.interconnect.csr_eventmanager import * @@ -1639,7 +1639,7 @@ class LiteXSoC(SoC): vres = int(timings.split("@")[0].split("x")[1]), ) vt = ClockDomainsRenamer(clock_domain)(vt) - self.submodules.video_terminal_vt = vt + self.submodules.video_terminal = vt # Connect Video Timing Generator to Video Terminal. self.comb += vtg.source.connect(vt.vtg_sink) @@ -1655,3 +1655,26 @@ class LiteXSoC(SoC): # Connect Video Terminal to Video PHY. self.comb += vt.source.connect(phy.sink) + + # Add Video Framebuffer ------------------------------------------------------------------------ + def add_video_framebuffer(self, name="video_framebuffer", phy=None, timings="800x600@60Hz", clock_domain="sys"): + # Video Timing Generator. + vtg = VideoTimingGenerator(default_video_timings=timings) + vtg = ClockDomainsRenamer(clock_domain)(vtg) + self.submodules.video_framebuffer_vtg = vtg + self.add_csr("video_framebuffer_vtg") + + # Video FrameBuffer. + vfb = VideoFrameBuffer(self.sdram.crossbar.get_port(), + hres = int(timings.split("@")[0].split("x")[0]), + vres = int(timings.split("@")[0].split("x")[1]), + clock_domain = "vga" + ) + self.submodules.video_framebuffer = vfb + self.add_csr("video_framebuffer") + + # Connect Video Timing Generator to Video FrameBuffer. + self.comb += vtg.source.connect(vfb.vtg_sink) + + # Connect Video FrameBuffer to Video PHY. + self.comb += vfb.source.connect(phy.sink)