diff --git a/milkymist/dvisampler/__init__.py b/milkymist/dvisampler/__init__.py index 7858edc5f..16ab0715b 100644 --- a/milkymist/dvisampler/__init__.py +++ b/milkymist/dvisampler/__init__.py @@ -1,8 +1,6 @@ from migen.fhdl.structure import * from migen.fhdl.module import Module -from migen.bank.description import * -from migen.genlib.fifo import AsyncFIFO -from migen.actorlib import structuring, dma_asmi, spi +from migen.bank.description import AutoCSR from milkymist.dvisampler.edid import EDID from milkymist.dvisampler.clocking import Clocking @@ -10,11 +8,11 @@ from milkymist.dvisampler.datacapture import DataCapture from milkymist.dvisampler.charsync import CharSync from milkymist.dvisampler.decoding import Decoding from milkymist.dvisampler.chansync import ChanSync -from milkymist.dvisampler.syncpol import SyncPolarity -from milkymist.dvisampler.resdetection import ResolutionDetection +from milkymist.dvisampler.analysis import SyncPolarity, ResolutionDetection, FrameExtraction +from milkymist.dvisampler.dma import DMA class DVISampler(Module, AutoCSR): - def __init__(self, pads): + def __init__(self, pads, asmiport): self.submodules.edid = EDID(pads) self.submodules.clocking = Clocking(pads) @@ -54,6 +52,8 @@ class DVISampler(Module, AutoCSR): self.chansync.data_in2.eq(self.data2_decod.output), ] + ### + self.submodules.syncpol = SyncPolarity() self.comb += [ self.syncpol.valid_i.eq(self.chansync.chan_synced), @@ -64,42 +64,20 @@ class DVISampler(Module, AutoCSR): self.submodules.resdetection = ResolutionDetection() self.comb += [ + self.resdetection.valid_i.eq(self.syncpol.valid_o), self.resdetection.de.eq(self.syncpol.de), self.resdetection.vsync.eq(self.syncpol.vsync) ] -class RawDVISampler(Module, AutoCSR): - def __init__(self, pads, asmiport): - self.submodules.edid = EDID(pads) - self.submodules.clocking = Clocking(pads) - - invert = False - try: - s = getattr(pads, "data0") - except AttributeError: - s = getattr(pads, "data0_n") - invert = True - self.submodules.data0_cap = DataCapture(8, invert) + self.submodules.frame = FrameExtraction() self.comb += [ - self.data0_cap.pad.eq(s), - self.data0_cap.serdesstrobe.eq(self.clocking.serdesstrobe) + self.frame.valid_i.eq(self.syncpol.valid_o), + self.frame.de.eq(self.syncpol.de), + self.frame.vsync.eq(self.syncpol.vsync), + self.frame.r.eq(self.syncpol.r), + self.frame.g.eq(self.syncpol.g), + self.frame.b.eq(self.syncpol.b) ] - fifo = AsyncFIFO(10, 1024) - self.add_submodule(fifo, {"write": "pix", "read": "sys"}) - self.comb += [ - fifo.din.eq(self.data0_cap.d), - fifo.we.eq(1) - ] - - pack_factor = asmiport.hub.dw//16 - self.submodules.packer = structuring.Pack([("word", 10), ("pad", 6)], pack_factor) - self.submodules.cast = structuring.Cast(self.packer.source.payload.layout, asmiport.hub.dw) - self.submodules.dma = spi.DMAWriteController(dma_asmi.Writer(asmiport), spi.MODE_SINGLE_SHOT) - self.comb += [ - self.packer.sink.stb.eq(fifo.readable), - fifo.re.eq(self.packer.sink.ack), - self.packer.sink.payload.word.eq(fifo.dout), - self.packer.source.connect(self.cast.sink, match_by_position=True), - self.cast.source.connect(self.dma.data, match_by_position=True) - ] + self.submodules.dma = DMA(asmiport) + self.comb += self.frame.frame.connect(self.dma.frame) diff --git a/milkymist/dvisampler/analysis.py b/milkymist/dvisampler/analysis.py new file mode 100644 index 000000000..4361f0abe --- /dev/null +++ b/milkymist/dvisampler/analysis.py @@ -0,0 +1,148 @@ +from migen.fhdl.structure import * +from migen.fhdl.module import Module +from migen.genlib.cdc import MultiReg +from migen.genlib.fifo import AsyncFIFO +from migen.genlib.record import Record +from migen.bank.description import * +from migen.flow.actor import * + +from milkymist.dvisampler.common import channel_layout, frame_layout + +class SyncPolarity(Module): + def __init__(self): + self.valid_i = Signal() + self.data_in0 = Record(channel_layout) + self.data_in1 = Record(channel_layout) + self.data_in2 = Record(channel_layout) + + self.valid_o = Signal() + self.de = Signal() + self.hsync = Signal() + self.vsync = Signal() + self.r = Signal(8) + self.g = Signal(8) + self.b = Signal(8) + + ### + + de = self.data_in0.de + de_r = Signal() + c = self.data_in0.c + c_polarity = Signal(2) + c_out = Signal(2) + + self.comb += [ + self.de.eq(de_r), + self.hsync.eq(c_out[0]), + self.vsync.eq(c_out[1]) + ] + + self.sync.pix += [ + self.valid_o.eq(self.valid_i), + self.r.eq(self.data_in2.d), + self.g.eq(self.data_in1.d), + self.b.eq(self.data_in0.d), + + de_r.eq(de), + If(de_r & ~de, + c_polarity.eq(c), + c_out.eq(0) + ).Else( + c_out.eq(c ^ c_polarity) + ) + ] + +class ResolutionDetection(Module, AutoCSR): + def __init__(self, nbits=11): + self.valid_i = Signal() + self.vsync = Signal() + self.de = Signal() + + self._hres = CSRStatus(nbits) + self._vres = CSRStatus(nbits) + + ### + + # Detect DE transitions + de_r = Signal() + pn_de = Signal() + self.sync.pix += de_r.eq(self.de) + self.comb += pn_de.eq(~self.de & de_r) + + # HRES + hcounter = Signal(nbits) + self.sync.pix += If(self.valid_i & self.de, + hcounter.eq(hcounter + 1) + ).Else( + hcounter.eq(0) + ) + + hcounter_st = Signal(nbits) + self.sync.pix += If(self.valid_i, + If(pn_de, hcounter_st.eq(hcounter)) + ).Else( + hcounter_st.eq(0) + ) + self.specials += MultiReg(hcounter_st, self._hres.status) + + # VRES + vsync_r = Signal() + p_vsync = Signal() + self.sync.pix += vsync_r.eq(self.vsync), + self.comb += p_vsync.eq(self.vsync & ~vsync_r) + + vcounter = Signal(nbits) + self.sync.pix += If(self.valid_i & p_vsync, + vcounter.eq(0) + ).Elif(pn_de, + vcounter.eq(vcounter + 1) + ) + + vcounter_st = Signal(nbits) + self.sync.pix += If(self.valid_i, + If(p_vsync, vcounter_st.eq(vcounter)) + ).Else( + vcounter_st.eq(0) + ) + self.specials += MultiReg(vcounter_st, self._vres.status) + +class FrameExtraction(Module): + def __init__(self): + # in pix clock domain + self.valid_i = Signal() + self.vsync = Signal() + self.de = Signal() + self.r = Signal(8) + self.g = Signal(8) + self.b = Signal(8) + + # in sys clock domain + self.frame = Source(frame_layout) + self.busy = Signal() + + ### + + fifo_stb = Signal() + fifo_in = Record(frame_layout) + self.comb += [ + fifo_stb.eq(self.valid_i & self.de), + fifo_in.r.eq(self.r), + fifo_in.g.eq(self.g), + fifo_in.b.eq(self.b), + ] + vsync_r = Signal() + self.sync.pix += [ + If(self.vsync & ~vsync_r, fifo_in.parity.eq(~fifo_in.parity)), + vsync_r.eq(self.vsync) + ] + + fifo = AsyncFIFO(layout_len(frame_layout), 256) + self.add_submodule(fifo, {"write": "pix", "read": "sys"}) + self.comb += [ + fifo.we.eq(fifo_stb), + fifo.din.eq(fifo_in.raw_bits()), + self.frame.stb.eq(fifo.readable), + self.frame.payload.raw_bits().eq(fifo.dout), + fifo.re.eq(self.frame.ack), + self.busy.eq(0) + ] diff --git a/milkymist/dvisampler/common.py b/milkymist/dvisampler/common.py index 7fb9a420a..f053237f3 100644 --- a/milkymist/dvisampler/common.py +++ b/milkymist/dvisampler/common.py @@ -1,2 +1,3 @@ control_tokens = [0b1101010100, 0b0010101011, 0b0101010100, 0b1010101011] channel_layout = [("d", 8), ("c", 2), ("de", 1)] +frame_layout = [("parity", 1), ("r", 8), ("g", 8), ("b", 8)] diff --git a/milkymist/dvisampler/debug.py b/milkymist/dvisampler/debug.py new file mode 100644 index 000000000..4693e07a2 --- /dev/null +++ b/milkymist/dvisampler/debug.py @@ -0,0 +1,46 @@ +from migen.fhdl.structure import * +from migen.fhdl.module import Module +from migen.genlib.fifo import AsyncFIFO +from migen.genlib.record import layout_len +from migen.bank.description import AutoCSR +from migen.actorlib import structuring, dma_asmi, spi + +from milkymist.dvisampler.edid import EDID +from milkymist.dvisampler.clocking import Clocking +from milkymist.dvisampler.datacapture import DataCapture + +class RawDVISampler(Module, AutoCSR): + def __init__(self, pads, asmiport): + self.submodules.edid = EDID(pads) + self.submodules.clocking = Clocking(pads) + + invert = False + try: + s = getattr(pads, "data0") + except AttributeError: + s = getattr(pads, "data0_n") + invert = True + self.submodules.data0_cap = DataCapture(8, invert) + self.comb += [ + self.data0_cap.pad.eq(s), + self.data0_cap.serdesstrobe.eq(self.clocking.serdesstrobe) + ] + + fifo = AsyncFIFO(10, 256) + self.add_submodule(fifo, {"write": "pix", "read": "sys"}) + self.comb += [ + fifo.din.eq(self.data0_cap.d), + fifo.we.eq(1) + ] + + pack_factor = asmiport.hub.dw//16 + self.submodules.packer = structuring.Pack([("word", 10), ("pad", 6)], pack_factor) + self.submodules.cast = structuring.Cast(self.packer.source.payload.layout, asmiport.hub.dw) + self.submodules.dma = spi.DMAWriteController(dma_asmi.Writer(asmiport), spi.MODE_SINGLE_SHOT, free_flow=True) + self.comb += [ + self.packer.sink.stb.eq(fifo.readable), + fifo.re.eq(self.packer.sink.ack), + self.packer.sink.payload.word.eq(fifo.dout), + self.packer.source.connect(self.cast.sink, match_by_position=True), + self.cast.source.connect(self.dma.data, match_by_position=True) + ] diff --git a/milkymist/dvisampler/dma.py b/milkymist/dvisampler/dma.py new file mode 100644 index 000000000..823872860 --- /dev/null +++ b/milkymist/dvisampler/dma.py @@ -0,0 +1,48 @@ +from migen.fhdl.structure import * +from migen.fhdl.module import Module +from migen.bank.description import * +from migen.flow.actor import * +from migen.actorlib import structuring, dma_asmi, spi + +from milkymist.dvisampler.common import frame_layout + +class DMA(Module): + def __init__(self, asmiport): + self.frame = Sink(frame_layout) + self.shoot = CSR() + + ### + + sof = Signal() + parity_r = Signal() + self.comb += sof.eq(self.frame.stb & (parity_r ^ self.frame.payload.parity)) + self.sync += If(self.frame.stb & self.frame.ack, parity_r.eq(self.frame.payload.parity)) + + pending = Signal() + frame_en = Signal() + self.sync += [ + If(sof, + frame_en.eq(0), + If(pending, frame_en.eq(1)), + pending.eq(0) + ), + If(self.shoot.re, pending.eq(1)) + ] + + pack_factor = asmiport.hub.dw//32 + self.submodules.packer = structuring.Pack(list(reversed([("pad", 2), ("r", 10), ("g", 10), ("b", 10)])), pack_factor) + self.submodules.cast = structuring.Cast(self.packer.source.payload.layout, asmiport.hub.dw, reverse_from=False) + self.submodules.dma = spi.DMAWriteController(dma_asmi.Writer(asmiport), spi.MODE_EXTERNAL) + self.comb += [ + self.dma.generator.trigger.eq(self.shoot.re), + self.packer.sink.stb.eq(self.frame.stb & frame_en), + self.frame.ack.eq(self.packer.sink.ack | (~frame_en & ~(pending & sof))), + self.packer.sink.payload.r.eq(self.frame.payload.r << 2), + self.packer.sink.payload.g.eq(self.frame.payload.g << 2), + self.packer.sink.payload.b.eq(self.frame.payload.b << 2), + self.packer.source.connect(self.cast.sink, match_by_position=True), + self.cast.source.connect(self.dma.data, match_by_position=True) + ] + + def get_csrs(self): + return [self.shoot] + self.dma.get_csrs() diff --git a/milkymist/dvisampler/resdetection.py b/milkymist/dvisampler/resdetection.py deleted file mode 100644 index c345be381..000000000 --- a/milkymist/dvisampler/resdetection.py +++ /dev/null @@ -1,49 +0,0 @@ -from migen.fhdl.structure import * -from migen.fhdl.module import Module -from migen.genlib.cdc import MultiReg -from migen.bank.description import * - -class ResolutionDetection(Module, AutoCSR): - def __init__(self, nbits=11): - self.vsync = Signal() - self.de = Signal() - - self._hres = CSRStatus(nbits) - self._vres = CSRStatus(nbits) - - ### - - # Detect DE transitions - de_r = Signal() - pn_de = Signal() - self.sync.pix += de_r.eq(self.de) - self.comb += pn_de.eq(~self.de & de_r) - - # HRES - hcounter = Signal(nbits) - self.sync.pix += If(self.de, - hcounter.eq(hcounter + 1) - ).Else( - hcounter.eq(0) - ) - - hcounter_st = Signal(nbits) - self.sync.pix += If(pn_de, hcounter_st.eq(hcounter)) - self.specials += MultiReg(hcounter_st, self._hres.status) - - # VRES - vsync_r = Signal() - p_vsync = Signal() - self.sync.pix += vsync_r.eq(self.vsync), - self.comb += p_vsync.eq(self.vsync & ~vsync_r) - - vcounter = Signal(nbits) - self.sync.pix += If(p_vsync, - vcounter.eq(0) - ).Elif(pn_de, - vcounter.eq(vcounter + 1) - ) - - vcounter_st = Signal(nbits) - self.sync.pix += If(p_vsync, vcounter_st.eq(vcounter)) - self.specials += MultiReg(vcounter_st, self._vres.status) diff --git a/milkymist/dvisampler/syncpol.py b/milkymist/dvisampler/syncpol.py deleted file mode 100644 index 2ef752a89..000000000 --- a/milkymist/dvisampler/syncpol.py +++ /dev/null @@ -1,49 +0,0 @@ -from migen.fhdl.structure import * -from migen.fhdl.module import Module -from migen.genlib.record import Record - -from milkymist.dvisampler.common import channel_layout - -class SyncPolarity(Module): - def __init__(self): - self.valid_i = Signal() - self.data_in0 = Record(channel_layout) - self.data_in1 = Record(channel_layout) - self.data_in2 = Record(channel_layout) - - self.valid_o = Signal() - self.de = Signal() - self.hsync = Signal() - self.vsync = Signal() - self.r = Signal(8) - self.g = Signal(8) - self.b = Signal(8) - - ### - - de = self.data_in0.de - de_r = Signal() - c = self.data_in0.c - c_polarity = Signal(2) - c_out = Signal(2) - - self.comb += [ - self.de.eq(de_r), - self.hsync.eq(c_out[0]), - self.vsync.eq(c_out[1]) - ] - - self.sync.pix += [ - self.valid_o.eq(self.valid_i), - self.r.eq(self.data_in2.d), - self.g.eq(self.data_in1.d), - self.b.eq(self.data_in0.d), - - de_r.eq(de), - If(de_r & ~de, - c_polarity.eq(c), - c_out.eq(0) - ).Else( - c_out.eq(c ^ c_polarity) - ) - ] diff --git a/top.py b/top.py index 6a41c507c..f3ed0b94d 100644 --- a/top.py +++ b/top.py @@ -92,6 +92,7 @@ class SoC(Module): self.submodules.asmicon = asmicon.ASMIcon(sdram_phy, sdram_geom, sdram_timing) asmiport_wb = self.asmicon.hub.get_port() asmiport_fb = self.asmicon.hub.get_port(3) + asmiport_dvi0 = self.asmicon.hub.get_port(2) self.asmicon.finalize() # @@ -141,8 +142,8 @@ class SoC(Module): self.submodules.timer0 = timer.Timer() self.submodules.fb = framebuffer.Framebuffer(platform.request("vga"), asmiport_fb) self.submodules.asmiprobe = asmiprobe.ASMIprobe(self.asmicon.hub) - self.submodules.dvisampler0 = dvisampler.DVISampler(platform.request("dvi_in", 0)) - self.submodules.dvisampler1 = dvisampler.DVISampler(platform.request("dvi_in", 1)) + self.submodules.dvisampler0 = dvisampler.DVISampler(platform.request("dvi_in", 0), asmiport_dvi0) + #self.submodules.dvisampler1 = dvisampler.DVISampler(platform.request("dvi_in", 1)) self.submodules.csrbankarray = csrgen.BankArray(self, lambda name, memory: self.csr_map[name if memory is None else name + "_" + memory.name_override])