video: reintegrate dvisampler from mixxeo (DVI/HDMI interfaces are common in today's SoCs)

This commit is contained in:
Florent Kermarrec 2015-03-01 10:01:23 +01:00
parent 93b80b2f1c
commit 1b7f8d0439
12 changed files with 1191 additions and 0 deletions

View File

@ -0,0 +1,79 @@
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"}

View File

@ -0,0 +1,205 @@
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)
)

View File

@ -0,0 +1,129 @@
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)

View File

@ -0,0 +1,53 @@
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)

View File

@ -0,0 +1,79 @@
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)

View File

@ -0,0 +1,2 @@
control_tokens = [0b1101010100, 0b0010101011, 0b0101010100, 0b1010101011]
channel_layout = [("d", 8), ("c", 2), ("de", 1)]

View File

@ -0,0 +1,186 @@
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)

View File

@ -0,0 +1,46 @@
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)
]

View File

@ -0,0 +1,24 @@
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)

View File

@ -0,0 +1,140 @@
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()

View File

@ -0,0 +1,189 @@
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")))

View File

@ -0,0 +1,59 @@
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))