video: reintegrate dvisampler from mixxeo (DVI/HDMI interfaces are common in today's SoCs)
This commit is contained in:
parent
93b80b2f1c
commit
1b7f8d0439
|
@ -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"}
|
|
@ -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)
|
||||
)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -0,0 +1,2 @@
|
|||
control_tokens = [0b1101010100, 0b0010101011, 0b0101010100, 0b1010101011]
|
||||
channel_layout = [("d", 8), ("c", 2), ("de", 1)]
|
|
@ -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)
|
|
@ -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)
|
||||
]
|
|
@ -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)
|
|
@ -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()
|
|
@ -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")))
|
|
@ -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))
|
Loading…
Reference in New Issue