--- /dev/null
+from migen.fhdl.std import *
+from migen.bank.description import AutoCSR
+
+from misoclib.video.dvisampler.edid import EDID
+from misoclib.video.dvisampler.clocking import Clocking
+from misoclib.video.dvisampler.datacapture import DataCapture
+from misoclib.video.dvisampler.charsync import CharSync
+from misoclib.video.dvisampler.wer import WER
+from misoclib.video.dvisampler.decoding import Decoding
+from misoclib.video.dvisampler.chansync import ChanSync
+from misoclib.video.dvisampler.analysis import SyncPolarity, ResolutionDetection, FrameExtraction
+from misoclib.video.dvisampler.dma import DMA
+
+class DVISampler(Module, AutoCSR):
+ def __init__(self, pads, lasmim, n_dma_slots=2):
+ self.submodules.edid = EDID(pads)
+ self.submodules.clocking = Clocking(pads)
+
+ for datan in range(3):
+ name = "data" + str(datan)
+
+ cap = DataCapture(getattr(pads, name + "_p"), getattr(pads, name + "_n"), 8)
+ setattr(self.submodules, name + "_cap", cap)
+ self.comb += cap.serdesstrobe.eq(self.clocking.serdesstrobe)
+
+ charsync = CharSync()
+ setattr(self.submodules, name + "_charsync", charsync)
+ self.comb += charsync.raw_data.eq(cap.d)
+
+ wer = WER()
+ setattr(self.submodules, name + "_wer", wer)
+ self.comb += wer.data.eq(charsync.data)
+
+ decoding = Decoding()
+ setattr(self.submodules, name + "_decod", decoding)
+ self.comb += [
+ decoding.valid_i.eq(charsync.synced),
+ decoding.input.eq(charsync.data)
+ ]
+
+ self.submodules.chansync = ChanSync()
+ self.comb += [
+ self.chansync.valid_i.eq(self.data0_decod.valid_o & \
+ self.data1_decod.valid_o & self.data2_decod.valid_o),
+ self.chansync.data_in0.eq(self.data0_decod.output),
+ self.chansync.data_in1.eq(self.data1_decod.output),
+ self.chansync.data_in2.eq(self.data2_decod.output),
+ ]
+
+ self.submodules.syncpol = SyncPolarity()
+ self.comb += [
+ self.syncpol.valid_i.eq(self.chansync.chan_synced),
+ self.syncpol.data_in0.eq(self.chansync.data_out0),
+ self.syncpol.data_in1.eq(self.chansync.data_out1),
+ self.syncpol.data_in2.eq(self.chansync.data_out2)
+ ]
+
+ 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)
+ ]
+
+ self.submodules.frame = FrameExtraction(24*lasmim.dw//32)
+ self.comb += [
+ 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)
+ ]
+
+ self.submodules.dma = DMA(lasmim, n_dma_slots)
+ self.comb += self.frame.frame.connect(self.dma.frame)
+ self.ev = self.dma.ev
+
+ autocsr_exclude = {"ev"}
--- /dev/null
+from migen.fhdl.std import *
+from migen.genlib.cdc import MultiReg, PulseSynchronizer
+from migen.genlib.fifo import AsyncFIFO
+from migen.genlib.record import Record
+from migen.bank.description import *
+from migen.flow.actor import *
+
+from misoclib.video.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)
+ )
+ ]
+
+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, AutoCSR):
+ def __init__(self, word_width):
+ # 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
+ word_layout = [("sof", 1), ("pixels", word_width)]
+ self.frame = Source(word_layout)
+ self.busy = Signal()
+
+ self._r_overflow = CSR()
+
+ ###
+
+ # start of frame detection
+ vsync_r = Signal()
+ new_frame = Signal()
+ self.comb += new_frame.eq(self.vsync & ~vsync_r)
+ self.sync.pix += vsync_r.eq(self.vsync)
+
+ # pack pixels into words
+ cur_word = Signal(word_width)
+ cur_word_valid = Signal()
+ encoded_pixel = Signal(24)
+ self.comb += encoded_pixel.eq(Cat(self.b, self.g, self.r))
+ pack_factor = word_width//24
+ assert(pack_factor & (pack_factor - 1) == 0) # only support powers of 2
+ pack_counter = Signal(max=pack_factor)
+ self.sync.pix += [
+ cur_word_valid.eq(0),
+ If(new_frame,
+ cur_word_valid.eq(pack_counter == (pack_factor - 1)),
+ pack_counter.eq(0),
+ ).Elif(self.valid_i & self.de,
+ [If(pack_counter == (pack_factor-i-1),
+ cur_word[24*i:24*(i+1)].eq(encoded_pixel)) for i in range(pack_factor)],
+ cur_word_valid.eq(pack_counter == (pack_factor - 1)),
+ pack_counter.eq(pack_counter + 1)
+ )
+ ]
+
+ # FIFO
+ fifo = RenameClockDomains(AsyncFIFO(word_layout, 512),
+ {"write": "pix", "read": "sys"})
+ self.submodules += fifo
+ self.comb += [
+ fifo.din.pixels.eq(cur_word),
+ fifo.we.eq(cur_word_valid)
+ ]
+ self.sync.pix += \
+ If(new_frame,
+ fifo.din.sof.eq(1)
+ ).Elif(cur_word_valid,
+ fifo.din.sof.eq(0)
+ )
+ self.comb += [
+ self.frame.stb.eq(fifo.readable),
+ self.frame.payload.eq(fifo.dout),
+ fifo.re.eq(self.frame.ack),
+ self.busy.eq(0)
+ ]
+
+ # overflow detection
+ pix_overflow = Signal()
+ pix_overflow_reset = Signal()
+ self.sync.pix += [
+ If(fifo.we & ~fifo.writable,
+ pix_overflow.eq(1)
+ ).Elif(pix_overflow_reset,
+ pix_overflow.eq(0)
+ )
+ ]
+
+ sys_overflow = Signal()
+ self.specials += MultiReg(pix_overflow, sys_overflow)
+ self.submodules.overflow_reset = PulseSynchronizer("sys", "pix")
+ self.submodules.overflow_reset_ack = PulseSynchronizer("pix", "sys")
+ self.comb += [
+ pix_overflow_reset.eq(self.overflow_reset.o),
+ self.overflow_reset_ack.i.eq(pix_overflow_reset)
+ ]
+
+ overflow_mask = Signal()
+ self.comb += [
+ self._r_overflow.w.eq(sys_overflow & ~overflow_mask),
+ self.overflow_reset.i.eq(self._r_overflow.re)
+ ]
+ self.sync += \
+ If(self._r_overflow.re,
+ overflow_mask.eq(1)
+ ).Elif(self.overflow_reset_ack.o,
+ overflow_mask.eq(0)
+ )
--- /dev/null
+from migen.fhdl.std import *
+from migen.genlib.cdc import MultiReg
+from migen.genlib.fifo import _inc
+from migen.genlib.record import Record, layout_len
+from migen.genlib.misc import optree
+from migen.bank.description import *
+
+from misoclib.video.dvisampler.common import channel_layout
+
+class _SyncBuffer(Module):
+ def __init__(self, width, depth):
+ self.din = Signal(width)
+ self.dout = Signal(width)
+ self.re = Signal()
+
+ ###
+
+ produce = Signal(max=depth)
+ consume = Signal(max=depth)
+ storage = Memory(width, depth)
+ self.specials += storage
+
+ wrport = storage.get_port(write_capable=True)
+ self.specials += wrport
+ self.comb += [
+ wrport.adr.eq(produce),
+ wrport.dat_w.eq(self.din),
+ wrport.we.eq(1)
+ ]
+ self.sync += _inc(produce, depth)
+
+ rdport = storage.get_port(async_read=True)
+ self.specials += rdport
+ self.comb += [
+ rdport.adr.eq(consume),
+ self.dout.eq(rdport.dat_r)
+ ]
+ self.sync += If(self.re, _inc(consume, depth))
+
+class ChanSync(Module, AutoCSR):
+ def __init__(self, nchan=3, depth=8):
+ self.valid_i = Signal()
+ self.chan_synced = Signal()
+
+ self._r_channels_synced = CSRStatus()
+
+ lst_control = []
+ all_control = Signal()
+ for i in range(nchan):
+ name = "data_in" + str(i)
+ data_in = Record(channel_layout, name=name)
+ setattr(self, name, data_in)
+ name = "data_out" + str(i)
+ data_out = Record(channel_layout, name=name)
+ setattr(self, name, data_out)
+
+ ###
+
+ syncbuffer = RenameClockDomains(_SyncBuffer(layout_len(channel_layout), depth), "pix")
+ self.submodules += syncbuffer
+ self.comb += [
+ syncbuffer.din.eq(data_in.raw_bits()),
+ data_out.raw_bits().eq(syncbuffer.dout)
+ ]
+ is_control = Signal()
+ self.comb += [
+ is_control.eq(~data_out.de),
+ syncbuffer.re.eq(~is_control | all_control)
+ ]
+ lst_control.append(is_control)
+
+ some_control = Signal()
+ self.comb += [
+ all_control.eq(optree("&", lst_control)),
+ some_control.eq(optree("|", lst_control))
+ ]
+ self.sync.pix += If(~self.valid_i,
+ self.chan_synced.eq(0)
+ ).Else(
+ If(some_control,
+ If(all_control,
+ self.chan_synced.eq(1)
+ ).Else(
+ self.chan_synced.eq(0)
+ )
+ )
+ )
+ self.specials += MultiReg(self.chan_synced, self._r_channels_synced.status)
+
+class _TB(Module):
+ def __init__(self, test_seq_it):
+ self.test_seq_it = test_seq_it
+
+ self.submodules.chansync = RenameClockDomains(ChanSync(), {"pix": "sys"})
+ self.comb += self.chansync.valid_i.eq(1)
+
+ def do_simulation(self, selfp):
+ try:
+ de0, de1, de2 = next(self.test_seq_it)
+ except StopIteration:
+ raise StopSimulation
+
+ selfp.chansync.data_in0.de = de0
+ selfp.chansync.data_in1.de = de1
+ selfp.chansync.data_in2.de = de2
+ selfp.chansync.data_in0.d = selfp.simulator.cycle_counter
+ selfp.chansync.data_in1.d = selfp.simulator.cycle_counter
+ selfp.chansync.data_in2.d = selfp.simulator.cycle_counter
+
+ out0 = selfp.chansync.data_out0.d
+ out1 = selfp.chansync.data_out1.d
+ out2 = selfp.chansync.data_out2.d
+
+ print("{0:5} {1:5} {2:5}".format(out0, out1, out2))
+
+if __name__ == "__main__":
+ from migen.sim.generic import run_simulation
+
+ test_seq = [
+ (1, 1, 1),
+ (1, 1, 0),
+ (0, 0, 0),
+ (0, 0, 0),
+ (0, 0, 1),
+ (1, 1, 1),
+ (1, 1, 1),
+ ]
+ tb = _TB(iter(test_seq*2))
+ run_simulation(tb)
--- /dev/null
+from migen.fhdl.std import *
+from migen.genlib.cdc import MultiReg
+from migen.genlib.misc import optree
+from migen.bank.description import *
+
+from misoclib.video.dvisampler.common import control_tokens
+
+class CharSync(Module, AutoCSR):
+ def __init__(self, required_controls=8):
+ self.raw_data = Signal(10)
+ self.synced = Signal()
+ self.data = Signal(10)
+
+ self._r_char_synced = CSRStatus()
+ self._r_ctl_pos = CSRStatus(bits_for(9))
+
+ ###
+
+ raw_data1 = Signal(10)
+ self.sync.pix += raw_data1.eq(self.raw_data)
+ raw = Signal(20)
+ self.comb += raw.eq(Cat(raw_data1, self.raw_data))
+
+ found_control = Signal()
+ control_position = Signal(max=10)
+ self.sync.pix += found_control.eq(0)
+ for i in range(10):
+ self.sync.pix += If(optree("|", [raw[i:i+10] == t for t in control_tokens]),
+ found_control.eq(1),
+ control_position.eq(i)
+ )
+
+ control_counter = Signal(max=required_controls)
+ previous_control_position = Signal(max=10)
+ word_sel = Signal(max=10)
+ self.sync.pix += [
+ If(found_control & (control_position == previous_control_position),
+ If(control_counter == (required_controls - 1),
+ control_counter.eq(0),
+ self.synced.eq(1),
+ word_sel.eq(control_position)
+ ).Else(
+ control_counter.eq(control_counter + 1)
+ )
+ ).Else(
+ control_counter.eq(0)
+ ),
+ previous_control_position.eq(control_position)
+ ]
+ self.specials += MultiReg(self.synced, self._r_char_synced.status)
+ self.specials += MultiReg(word_sel, self._r_ctl_pos.status)
+
+ self.sync.pix += self.data.eq(raw >> word_sel)
--- /dev/null
+from migen.fhdl.std import *
+from migen.genlib.cdc import MultiReg
+from migen.bank.description import *
+
+class Clocking(Module, AutoCSR):
+ def __init__(self, pads):
+ self._r_pll_reset = CSRStorage(reset=1)
+ self._r_locked = CSRStatus()
+
+ # DRP
+ self._r_pll_adr = CSRStorage(5)
+ self._r_pll_dat_r = CSRStatus(16)
+ self._r_pll_dat_w = CSRStorage(16)
+ self._r_pll_read = CSR()
+ self._r_pll_write = CSR()
+ self._r_pll_drdy = CSRStatus()
+
+ self.locked = Signal()
+ self.serdesstrobe = Signal()
+ self.clock_domains._cd_pix = ClockDomain()
+ self.clock_domains._cd_pix2x = ClockDomain()
+ self.clock_domains._cd_pix10x = ClockDomain(reset_less=True)
+
+ ###
+
+ clk_se = Signal()
+ self.specials += Instance("IBUFDS", i_I=pads.clk_p, i_IB=pads.clk_n, o_O=clk_se)
+
+ clkfbout = Signal()
+ pll_locked = Signal()
+ pll_clk0 = Signal()
+ pll_clk1 = Signal()
+ pll_clk2 = Signal()
+ pll_drdy = Signal()
+ self.sync += If(self._r_pll_read.re | self._r_pll_write.re,
+ self._r_pll_drdy.status.eq(0)
+ ).Elif(pll_drdy,
+ self._r_pll_drdy.status.eq(1)
+ )
+ self.specials += Instance("PLL_ADV",
+ p_CLKFBOUT_MULT=10,
+ p_CLKOUT0_DIVIDE=1, # pix10x
+ p_CLKOUT1_DIVIDE=5, # pix2x
+ p_CLKOUT2_DIVIDE=10, # pix
+ p_COMPENSATION="INTERNAL",
+
+ i_CLKINSEL=1,
+ i_CLKIN1=clk_se,
+ o_CLKOUT0=pll_clk0, o_CLKOUT1=pll_clk1, o_CLKOUT2=pll_clk2,
+ o_CLKFBOUT=clkfbout, i_CLKFBIN=clkfbout,
+ o_LOCKED=pll_locked, i_RST=self._r_pll_reset.storage,
+
+ i_DADDR=self._r_pll_adr.storage,
+ o_DO=self._r_pll_dat_r.status,
+ i_DI=self._r_pll_dat_w.storage,
+ i_DEN=self._r_pll_read.re | self._r_pll_write.re,
+ i_DWE=self._r_pll_write.re,
+ o_DRDY=pll_drdy,
+ i_DCLK=ClockSignal())
+
+ locked_async = Signal()
+ self.specials += [
+ Instance("BUFPLL", p_DIVIDE=5,
+ i_PLLIN=pll_clk0, i_GCLK=ClockSignal("pix2x"), i_LOCKED=pll_locked,
+ o_IOCLK=self._cd_pix10x.clk, o_LOCK=locked_async, o_SERDESSTROBE=self.serdesstrobe),
+ Instance("BUFG", i_I=pll_clk1, o_O=self._cd_pix2x.clk),
+ Instance("BUFG", i_I=pll_clk2, o_O=self._cd_pix.clk),
+ MultiReg(locked_async, self.locked, "sys")
+ ]
+ self.comb += self._r_locked.status.eq(self.locked)
+
+ # sychronize pix+pix2x reset
+ pix_rst_n = 1
+ for i in range(2):
+ new_pix_rst_n = Signal()
+ self.specials += Instance("FDCE", i_D=pix_rst_n, i_CE=1, i_C=ClockSignal("pix"),
+ i_CLR=~locked_async, o_Q=new_pix_rst_n)
+ pix_rst_n = new_pix_rst_n
+ self.comb += self._cd_pix.rst.eq(~pix_rst_n), self._cd_pix2x.rst.eq(~pix_rst_n)
--- /dev/null
+control_tokens = [0b1101010100, 0b0010101011, 0b0101010100, 0b1010101011]
+channel_layout = [("d", 8), ("c", 2), ("de", 1)]
--- /dev/null
+from migen.fhdl.std import *
+from migen.genlib.cdc import MultiReg, PulseSynchronizer
+from migen.bank.description import *
+
+class DataCapture(Module, AutoCSR):
+ def __init__(self, pad_p, pad_n, ntbits):
+ self.serdesstrobe = Signal()
+ self.d = Signal(10)
+
+ self._r_dly_ctl = CSR(6)
+ self._r_dly_busy = CSRStatus(2)
+ self._r_phase = CSRStatus(2)
+ self._r_phase_reset = CSR()
+
+ ###
+
+ # IO
+ pad_se = Signal()
+ self.specials += Instance("IBUFDS", i_I=pad_p, i_IB=pad_n, o_O=pad_se)
+
+ pad_delayed_master = Signal()
+ pad_delayed_slave = Signal()
+ delay_inc = Signal()
+ delay_ce = Signal()
+ delay_master_cal = Signal()
+ delay_master_rst = Signal()
+ delay_master_busy = Signal()
+ delay_slave_cal = Signal()
+ delay_slave_rst = Signal()
+ delay_slave_busy = Signal()
+ self.specials += Instance("IODELAY2",
+ p_SERDES_MODE="MASTER",
+ p_DELAY_SRC="IDATAIN", p_IDELAY_TYPE="DIFF_PHASE_DETECTOR",
+ p_COUNTER_WRAPAROUND="STAY_AT_LIMIT", p_DATA_RATE="SDR",
+
+ i_IDATAIN=pad_se, o_DATAOUT=pad_delayed_master,
+ i_CLK=ClockSignal("pix2x"), i_IOCLK0=ClockSignal("pix10x"),
+
+ i_INC=delay_inc, i_CE=delay_ce,
+ i_CAL=delay_master_cal, i_RST=delay_master_rst, o_BUSY=delay_master_busy,
+ i_T=1)
+ self.specials += Instance("IODELAY2",
+ p_SERDES_MODE="SLAVE",
+ p_DELAY_SRC="IDATAIN", p_IDELAY_TYPE="DIFF_PHASE_DETECTOR",
+ p_COUNTER_WRAPAROUND="WRAPAROUND", p_DATA_RATE="SDR",
+
+ i_IDATAIN=pad_se, o_DATAOUT=pad_delayed_slave,
+ i_CLK=ClockSignal("pix2x"), i_IOCLK0=ClockSignal("pix10x"),
+
+ i_INC=delay_inc, i_CE=delay_ce,
+ i_CAL=delay_slave_cal, i_RST=delay_slave_rst, o_BUSY=delay_slave_busy,
+ i_T=1)
+
+ dsr2 = Signal(5)
+ pd_valid = Signal()
+ pd_incdec = Signal()
+ pd_edge = Signal()
+ pd_cascade = Signal()
+ self.specials += Instance("ISERDES2",
+ p_SERDES_MODE="MASTER",
+ p_BITSLIP_ENABLE="FALSE", p_DATA_RATE="SDR", p_DATA_WIDTH=5,
+ p_INTERFACE_TYPE="RETIMED",
+
+ i_D=pad_delayed_master,
+ o_Q4=dsr2[4], o_Q3=dsr2[3], o_Q2=dsr2[2], o_Q1=dsr2[1],
+
+ i_BITSLIP=0, i_CE0=1, i_RST=0,
+ i_CLK0=ClockSignal("pix10x"), i_CLKDIV=ClockSignal("pix2x"),
+ i_IOCE=self.serdesstrobe,
+
+ o_VALID=pd_valid, o_INCDEC=pd_incdec,
+ i_SHIFTIN=pd_edge, o_SHIFTOUT=pd_cascade)
+ self.specials += Instance("ISERDES2",
+ p_SERDES_MODE="SLAVE",
+ p_BITSLIP_ENABLE="FALSE", p_DATA_RATE="SDR", p_DATA_WIDTH=5,
+ p_INTERFACE_TYPE="RETIMED",
+
+ i_D=pad_delayed_slave,
+ o_Q4=dsr2[0],
+
+ i_BITSLIP=0, i_CE0=1, i_RST=0,
+ i_CLK0=ClockSignal("pix10x"), i_CLKDIV=ClockSignal("pix2x"),
+ i_IOCE=self.serdesstrobe,
+
+ i_SHIFTIN=pd_cascade, o_SHIFTOUT=pd_edge)
+
+ # Phase error accumulator
+ lateness = Signal(ntbits, reset=2**(ntbits - 1))
+ too_late = Signal()
+ too_early = Signal()
+ reset_lateness = Signal()
+ self.comb += [
+ too_late.eq(lateness == (2**ntbits - 1)),
+ too_early.eq(lateness == 0)
+ ]
+ self.sync.pix2x += [
+ If(reset_lateness,
+ lateness.eq(2**(ntbits - 1))
+ ).Elif(~delay_master_busy & ~delay_slave_busy & ~too_late & ~too_early,
+ If(pd_valid & pd_incdec, lateness.eq(lateness - 1)),
+ If(pd_valid & ~pd_incdec, lateness.eq(lateness + 1))
+ )
+ ]
+
+ # Delay control
+ self.submodules.delay_master_done = PulseSynchronizer("pix2x", "sys")
+ delay_master_pending = Signal()
+ self.sync.pix2x += [
+ self.delay_master_done.i.eq(0),
+ If(~delay_master_pending,
+ If(delay_master_cal | delay_ce, delay_master_pending.eq(1))
+ ).Else(
+ If(~delay_master_busy,
+ self.delay_master_done.i.eq(1),
+ delay_master_pending.eq(0)
+ )
+ )
+ ]
+ self.submodules.delay_slave_done = PulseSynchronizer("pix2x", "sys")
+ delay_slave_pending = Signal()
+ self.sync.pix2x += [
+ self.delay_slave_done.i.eq(0),
+ If(~delay_slave_pending,
+ If(delay_slave_cal | delay_ce, delay_slave_pending.eq(1))
+ ).Else(
+ If(~delay_slave_busy,
+ self.delay_slave_done.i.eq(1),
+ delay_slave_pending.eq(0)
+ )
+ )
+ ]
+
+ self.submodules.do_delay_master_cal = PulseSynchronizer("sys", "pix2x")
+ self.submodules.do_delay_master_rst = PulseSynchronizer("sys", "pix2x")
+ self.submodules.do_delay_slave_cal = PulseSynchronizer("sys", "pix2x")
+ self.submodules.do_delay_slave_rst = PulseSynchronizer("sys", "pix2x")
+ self.submodules.do_delay_inc = PulseSynchronizer("sys", "pix2x")
+ self.submodules.do_delay_dec = PulseSynchronizer("sys", "pix2x")
+ self.comb += [
+ delay_master_cal.eq(self.do_delay_master_cal.o),
+ delay_master_rst.eq(self.do_delay_master_rst.o),
+ delay_slave_cal.eq(self.do_delay_slave_cal.o),
+ delay_slave_rst.eq(self.do_delay_slave_rst.o),
+ delay_inc.eq(self.do_delay_inc.o),
+ delay_ce.eq(self.do_delay_inc.o | self.do_delay_dec.o),
+ ]
+
+ sys_delay_master_pending = Signal()
+ self.sync += [
+ If(self.do_delay_master_cal.i | self.do_delay_inc.i | self.do_delay_dec.i,
+ sys_delay_master_pending.eq(1)
+ ).Elif(self.delay_master_done.o,
+ sys_delay_master_pending.eq(0)
+ )
+ ]
+ sys_delay_slave_pending = Signal()
+ self.sync += [
+ If(self.do_delay_slave_cal.i | self.do_delay_inc.i | self.do_delay_dec.i,
+ sys_delay_slave_pending.eq(1)
+ ).Elif(self.delay_slave_done.o,
+ sys_delay_slave_pending.eq(0)
+ )
+ ]
+
+ self.comb += [
+ self.do_delay_master_cal.i.eq(self._r_dly_ctl.re & self._r_dly_ctl.r[0]),
+ self.do_delay_master_rst.i.eq(self._r_dly_ctl.re & self._r_dly_ctl.r[1]),
+ self.do_delay_slave_cal.i.eq(self._r_dly_ctl.re & self._r_dly_ctl.r[2]),
+ self.do_delay_slave_rst.i.eq(self._r_dly_ctl.re & self._r_dly_ctl.r[3]),
+ self.do_delay_inc.i.eq(self._r_dly_ctl.re & self._r_dly_ctl.r[4]),
+ self.do_delay_dec.i.eq(self._r_dly_ctl.re & self._r_dly_ctl.r[5]),
+ self._r_dly_busy.status.eq(Cat(sys_delay_master_pending, sys_delay_slave_pending))
+ ]
+
+ # Phase detector control
+ self.specials += MultiReg(Cat(too_late, too_early), self._r_phase.status)
+ self.submodules.do_reset_lateness = PulseSynchronizer("sys", "pix2x")
+ self.comb += [
+ reset_lateness.eq(self.do_reset_lateness.o),
+ self.do_reset_lateness.i.eq(self._r_phase_reset.re)
+ ]
+
+ # 5:10 deserialization
+ dsr = Signal(10)
+ self.sync.pix2x += dsr.eq(Cat(dsr[5:], dsr2))
+ self.sync.pix += self.d.eq(dsr)
--- /dev/null
+from migen.fhdl.std import *
+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_lasmi, spi
+
+from misoclib.video.dvisampler.edid import EDID
+from misoclib.video.dvisampler.clocking import Clocking
+from misoclib.video.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 = RenameClockDomains(AsyncFIFO(10, 256),
+ {"write": "pix", "read": "sys"})
+ self.submodules += fifo
+ 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_lasmi.Writer(lasmim), spi.MODE_SINGLE_SHOT)
+ self.comb += [
+ self.packer.sink.stb.eq(fifo.readable),
+ fifo.re.eq(self.packer.sink.ack),
+ self.packer.sink.word.eq(fifo.dout),
+ self.packer.source.connect_flat(self.cast.sink),
+ self.cast.source.connect_flat(self.dma.data)
+ ]
--- /dev/null
+from migen.fhdl.std import *
+from migen.genlib.record import Record
+
+from misoclib.video.dvisampler.common import control_tokens, channel_layout
+
+class Decoding(Module):
+ def __init__(self):
+ self.valid_i = Signal()
+ self.input = Signal(10)
+ self.valid_o = Signal()
+ self.output = Record(channel_layout)
+
+ ###
+
+ self.sync.pix += self.output.de.eq(1)
+ for i, t in enumerate(control_tokens):
+ self.sync.pix += If(self.input == t,
+ self.output.de.eq(0),
+ self.output.c.eq(i)
+ )
+ self.sync.pix += self.output.d[0].eq(self.input[0] ^ self.input[9])
+ for i in range(1, 8):
+ self.sync.pix += self.output.d[i].eq(self.input[i] ^ self.input[i-1] ^ ~self.input[8])
+ self.sync.pix += self.valid_o.eq(self.valid_i)
--- /dev/null
+from migen.fhdl.std import *
+from migen.genlib.fsm import FSM, NextState
+from migen.bank.description import *
+from migen.bank.eventmanager import *
+from migen.flow.actor import *
+from migen.actorlib import dma_lasmi
+
+# Slot status: EMPTY=0 LOADED=1 PENDING=2
+class _Slot(Module, AutoCSR):
+ def __init__(self, addr_bits, alignment_bits):
+ self.ev_source = EventSourceLevel()
+ self.address = Signal(addr_bits)
+ self.address_reached = Signal(addr_bits)
+ self.address_valid = Signal()
+ self.address_done = Signal()
+
+ self._r_status = CSRStorage(2, write_from_dev=True)
+ self._r_address = CSRStorage(addr_bits + alignment_bits, alignment_bits=alignment_bits, write_from_dev=True)
+
+ ###
+
+ self.comb += [
+ self.address.eq(self._r_address.storage),
+ self.address_valid.eq(self._r_status.storage[0]),
+ self._r_status.dat_w.eq(2),
+ self._r_status.we.eq(self.address_done),
+ self._r_address.dat_w.eq(self.address_reached),
+ self._r_address.we.eq(self.address_done),
+ self.ev_source.trigger.eq(self._r_status.storage[1])
+ ]
+
+class _SlotArray(Module, AutoCSR):
+ def __init__(self, nslots, addr_bits, alignment_bits):
+ self.submodules.ev = EventManager()
+ self.address = Signal(addr_bits)
+ self.address_reached = Signal(addr_bits)
+ self.address_valid = Signal()
+ self.address_done = Signal()
+
+ ###
+
+ slots = [_Slot(addr_bits, alignment_bits) for i in range(nslots)]
+ for n, slot in enumerate(slots):
+ setattr(self.submodules, "slot"+str(n), slot)
+ setattr(self.ev, "slot"+str(n), slot.ev_source)
+ self.ev.finalize()
+
+ change_slot = Signal()
+ current_slot = Signal(max=nslots)
+ self.sync += If(change_slot, [If(slot.address_valid, current_slot.eq(n)) for n, slot in reversed(list(enumerate(slots)))])
+ self.comb += change_slot.eq(~self.address_valid | self.address_done)
+
+ self.comb += [
+ self.address.eq(Array(slot.address for slot in slots)[current_slot]),
+ self.address_valid.eq(Array(slot.address_valid for slot in slots)[current_slot])
+ ]
+ self.comb += [slot.address_reached.eq(self.address_reached) for slot in slots]
+ self.comb += [slot.address_done.eq(self.address_done & (current_slot == n)) for n, slot in enumerate(slots)]
+
+class DMA(Module):
+ def __init__(self, lasmim, nslots):
+ bus_aw = lasmim.aw
+ bus_dw = lasmim.dw
+ alignment_bits = bits_for(bus_dw//8) - 1
+
+ fifo_word_width = 24*bus_dw//32
+ self.frame = Sink([("sof", 1), ("pixels", fifo_word_width)])
+ self._r_frame_size = CSRStorage(bus_aw + alignment_bits, alignment_bits=alignment_bits)
+ self.submodules._slot_array = _SlotArray(nslots, bus_aw, alignment_bits)
+ self.ev = self._slot_array.ev
+
+ ###
+
+ # address generator + maximum memory word count to prevent DMA buffer overrun
+ reset_words = Signal()
+ count_word = Signal()
+ last_word = Signal()
+ current_address = Signal(bus_aw)
+ mwords_remaining = Signal(bus_aw)
+ self.comb += [
+ self._slot_array.address_reached.eq(current_address),
+ last_word.eq(mwords_remaining == 1)
+ ]
+ self.sync += [
+ If(reset_words,
+ current_address.eq(self._slot_array.address),
+ mwords_remaining.eq(self._r_frame_size.storage)
+ ).Elif(count_word,
+ current_address.eq(current_address + 1),
+ mwords_remaining.eq(mwords_remaining - 1)
+ )
+ ]
+
+ # 24bpp -> 32bpp
+ memory_word = Signal(bus_dw)
+ pixbits = []
+ for i in range(bus_dw//32):
+ for j in range(3):
+ b = (i*3+j)*8
+ pixbits.append(self.frame.pixels[b+6:b+8])
+ pixbits.append(self.frame.pixels[b:b+8])
+ pixbits.append(0)
+ pixbits.append(0)
+ self.comb += memory_word.eq(Cat(*pixbits))
+
+ # bus accessor
+ self.submodules._bus_accessor = dma_lasmi.Writer(lasmim)
+ self.comb += [
+ self._bus_accessor.address_data.a.eq(current_address),
+ self._bus_accessor.address_data.d.eq(memory_word)
+ ]
+
+ # control FSM
+ fsm = FSM()
+ self.submodules += fsm
+
+ fsm.act("WAIT_SOF",
+ reset_words.eq(1),
+ self.frame.ack.eq(~self._slot_array.address_valid | ~self.frame.sof),
+ If(self._slot_array.address_valid & self.frame.sof & self.frame.stb, NextState("TRANSFER_PIXELS"))
+ )
+ fsm.act("TRANSFER_PIXELS",
+ self.frame.ack.eq(self._bus_accessor.address_data.ack),
+ If(self.frame.stb,
+ self._bus_accessor.address_data.stb.eq(1),
+ If(self._bus_accessor.address_data.ack,
+ count_word.eq(1),
+ If(last_word, NextState("EOF"))
+ )
+ )
+ )
+ fsm.act("EOF",
+ If(~self._bus_accessor.busy,
+ self._slot_array.address_done.eq(1),
+ NextState("WAIT_SOF")
+ )
+ )
+
+ def get_csrs(self):
+ return [self._r_frame_size] + self._slot_array.get_csrs()
--- /dev/null
+from migen.fhdl.std import *
+from migen.fhdl.specials import Tristate
+from migen.genlib.cdc import MultiReg
+from migen.genlib.fsm import FSM, NextState
+from migen.genlib.misc import chooser
+from migen.bank.description import CSRStorage, CSRStatus, AutoCSR
+
+_default_edid = [
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3D, 0x17, 0x32, 0x12, 0x2A, 0x6A, 0xBF, 0x00,
+ 0x05, 0x17, 0x01, 0x03, 0x80, 0x28, 0x1E, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xB2, 0x0C, 0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x18, 0x88,
+ 0x36, 0x00, 0x28, 0x1E, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x4D, 0x31, 0x20,
+ 0x44, 0x56, 0x49, 0x20, 0x6D, 0x69, 0x78, 0x65, 0x72, 0x0A, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34,
+]
+
+class EDID(Module, AutoCSR):
+ def __init__(self, pads, default=_default_edid):
+ self._r_hpd_notif = CSRStatus()
+ self._r_hpd_en = CSRStorage()
+ self.specials.mem = Memory(8, 128, init=default)
+
+ ###
+
+ # HPD
+ if hasattr(pads, "hpd_notif"):
+ self.specials += MultiReg(pads.hpd_notif, self._r_hpd_notif.status)
+ else:
+ self.comb += self._r_hpd_notif.status.eq(1)
+ if hasattr(pads, "hpd_en"):
+ self.comb += pads.hpd_en.eq(self._r_hpd_en.storage)
+
+ # EDID
+ scl_raw = Signal()
+ sda_i = Signal()
+ sda_drv = Signal()
+ _sda_drv_reg = Signal()
+ _sda_i_async = Signal()
+ self.sync += _sda_drv_reg.eq(sda_drv)
+ self.specials += [
+ MultiReg(pads.scl, scl_raw),
+ Tristate(pads.sda, 0, _sda_drv_reg, _sda_i_async),
+ MultiReg(_sda_i_async, sda_i)
+ ]
+
+ scl_i = Signal()
+ samp_count = Signal(6)
+ samp_carry = Signal()
+ self.sync += [
+ Cat(samp_count, samp_carry).eq(samp_count + 1),
+ If(samp_carry, scl_i.eq(scl_raw))
+ ]
+
+ scl_r = Signal()
+ sda_r = Signal()
+ scl_rising = Signal()
+ sda_rising = Signal()
+ sda_falling = Signal()
+ self.sync += [
+ scl_r.eq(scl_i),
+ sda_r.eq(sda_i)
+ ]
+ self.comb += [
+ scl_rising.eq(scl_i & ~scl_r),
+ sda_rising.eq(sda_i & ~sda_r),
+ sda_falling.eq(~sda_i & sda_r)
+ ]
+
+ start = Signal()
+ self.comb += start.eq(scl_i & sda_falling)
+
+ din = Signal(8)
+ counter = Signal(max=9)
+ self.sync += [
+ If(start, counter.eq(0)),
+ If(scl_rising,
+ If(counter == 8,
+ counter.eq(0)
+ ).Else(
+ counter.eq(counter + 1),
+ din.eq(Cat(sda_i, din[:7]))
+ )
+ )
+ ]
+
+ is_read = Signal()
+ update_is_read = Signal()
+ self.sync += If(update_is_read, is_read.eq(din[0]))
+
+ offset_counter = Signal(max=128)
+ oc_load = Signal()
+ oc_inc = Signal()
+ self.sync += [
+ If(oc_load,
+ offset_counter.eq(din)
+ ).Elif(oc_inc,
+ offset_counter.eq(offset_counter + 1)
+ )
+ ]
+ rdport = self.mem.get_port()
+ self.specials += rdport
+ self.comb += rdport.adr.eq(offset_counter)
+ data_bit = Signal()
+
+ zero_drv = Signal()
+ data_drv = Signal()
+ self.comb += If(zero_drv, sda_drv.eq(1)).Elif(data_drv, sda_drv.eq(~data_bit))
+
+ data_drv_en = Signal()
+ data_drv_stop = Signal()
+ self.sync += If(data_drv_en, data_drv.eq(1)).Elif(data_drv_stop, data_drv.eq(0))
+ self.sync += If(data_drv_en, chooser(rdport.dat_r, counter, data_bit, 8, reverse=True))
+
+ fsm = FSM()
+ self.submodules += fsm
+
+ fsm.act("WAIT_START")
+ fsm.act("RCV_ADDRESS",
+ If(counter == 8,
+ If(din[1:] == 0x50,
+ update_is_read.eq(1),
+ NextState("ACK_ADDRESS0")
+ ).Else(
+ NextState("WAIT_START")
+ )
+ )
+ )
+ fsm.act("ACK_ADDRESS0",
+ If(~scl_i, NextState("ACK_ADDRESS1"))
+ )
+ fsm.act("ACK_ADDRESS1",
+ zero_drv.eq(1),
+ If(scl_i, NextState("ACK_ADDRESS2"))
+ )
+ fsm.act("ACK_ADDRESS2",
+ zero_drv.eq(1),
+ If(~scl_i,
+ If(is_read,
+ NextState("READ")
+ ).Else(
+ NextState("RCV_OFFSET")
+ )
+ )
+ )
+
+ fsm.act("RCV_OFFSET",
+ If(counter == 8,
+ oc_load.eq(1),
+ NextState("ACK_OFFSET0")
+ )
+ )
+ fsm.act("ACK_OFFSET0",
+ If(~scl_i, NextState("ACK_OFFSET1"))
+ )
+ fsm.act("ACK_OFFSET1",
+ zero_drv.eq(1),
+ If(scl_i, NextState("ACK_OFFSET2"))
+ )
+ fsm.act("ACK_OFFSET2",
+ zero_drv.eq(1),
+ If(~scl_i, NextState("RCV_ADDRESS"))
+ )
+
+ fsm.act("READ",
+ If(~scl_i,
+ If(counter == 8,
+ data_drv_stop.eq(1),
+ NextState("ACK_READ")
+ ).Else(
+ data_drv_en.eq(1)
+ )
+ )
+ )
+ fsm.act("ACK_READ",
+ If(scl_rising,
+ oc_inc.eq(1),
+ If(sda_i,
+ NextState("WAIT_START")
+ ).Else(
+ NextState("READ")
+ )
+ )
+ )
+
+ for state in fsm.actions.keys():
+ fsm.act(state, If(start, NextState("RCV_ADDRESS")))
+ fsm.act(state, If(~self._r_hpd_en.storage, NextState("WAIT_START")))
--- /dev/null
+from migen.fhdl.std import *
+from migen.bank.description import *
+from migen.genlib.misc import optree
+from migen.genlib.cdc import PulseSynchronizer
+
+from misoclib.video.dvisampler.common import control_tokens
+
+class WER(Module, AutoCSR):
+ def __init__(self, period_bits=24):
+ self.data = Signal(10)
+ self._r_update = CSR()
+ self._r_value = CSRStatus(period_bits)
+
+ ###
+
+ # pipeline stage 1
+ # we ignore the 10th (inversion) bit, as it is independent of the transition minimization
+ data_r = Signal(9)
+ self.sync.pix += data_r.eq(self.data[:9])
+
+ # pipeline stage 2
+ transitions = Signal(8)
+ self.comb += [transitions[i].eq(data_r[i] ^ data_r[i+1]) for i in range(8)]
+ transition_count = Signal(max=9)
+ self.sync.pix += transition_count.eq(optree("+", [transitions[i] for i in range(8)]))
+
+ is_control = Signal()
+ self.sync.pix += is_control.eq(optree("|", [data_r == ct for ct in control_tokens]))
+
+ # pipeline stage 3
+ is_error = Signal()
+ self.sync.pix += is_error.eq((transition_count > 4) & ~is_control)
+
+ # counter
+ period_counter = Signal(period_bits)
+ period_done = Signal()
+ self.sync.pix += Cat(period_counter, period_done).eq(period_counter + 1)
+
+ wer_counter = Signal(period_bits)
+ wer_counter_r = Signal(period_bits)
+ wer_counter_r_updated = Signal()
+ self.sync.pix += [
+ wer_counter_r_updated.eq(period_done),
+ If(period_done,
+ wer_counter_r.eq(wer_counter),
+ wer_counter.eq(0)
+ ).Elif(is_error,
+ wer_counter.eq(wer_counter + 1)
+ )
+ ]
+
+ # sync to system clock domain
+ wer_counter_sys = Signal(period_bits)
+ self.submodules.ps_counter = PulseSynchronizer("pix", "sys")
+ self.comb += self.ps_counter.i.eq(wer_counter_r_updated)
+ self.sync += If(self.ps_counter.o, wer_counter_sys.eq(wer_counter_r))
+
+ # register interface
+ self.sync += If(self._r_update.re, self._r_value.status.eq(wer_counter_sys))