From 213cb43ae54175e8cdd0848435a6d88863861789 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 3 Aug 2014 12:30:15 +0800 Subject: [PATCH] Keep only basic SoC designs in MiSoC --- .gitignore | 5 - misoclib/counteradc/__init__.py | 61 ----- misoclib/dvisampler/__init__.py | 79 ------ misoclib/dvisampler/analysis.py | 205 --------------- misoclib/dvisampler/chansync.py | 129 ---------- misoclib/dvisampler/charsync.py | 53 ---- misoclib/dvisampler/clocking.py | 79 ------ misoclib/dvisampler/common.py | 2 - misoclib/dvisampler/datacapture.py | 186 -------------- misoclib/dvisampler/debug.py | 46 ---- misoclib/dvisampler/decoding.py | 24 -- misoclib/dvisampler/dma.py | 140 ---------- misoclib/dvisampler/edid.py | 189 -------------- misoclib/dvisampler/wer.py | 59 ----- misoclib/framebuffer/__init__.py | 81 +----- misoclib/videostream/downscaler.py | 386 ---------------------------- misoclib/videostream/testpic_in.jpg | Bin 50391 -> 0 bytes software/videomixer/Makefile | 80 ------ software/videomixer/ci.c | 83 ------ software/videomixer/ci.h | 6 - software/videomixer/config.c | 91 ------- software/videomixer/config.h | 21 -- software/videomixer/dvisamplerX.c | 363 -------------------------- software/videomixer/dvisamplerX.h | 17 -- software/videomixer/edid.c | 248 ------------------ software/videomixer/edid.h | 29 --- software/videomixer/isr.c | 21 -- software/videomixer/main.c | 107 -------- software/videomixer/pll.c | 107 -------- software/videomixer/pll.h | 7 - software/videomixer/processor.c | 283 -------------------- software/videomixer/processor.h | 11 - targets/mlabs_video.py | 89 +++---- targets/{simple.py => ppro.py} | 4 +- 34 files changed, 43 insertions(+), 3248 deletions(-) delete mode 100644 misoclib/counteradc/__init__.py delete mode 100644 misoclib/dvisampler/__init__.py delete mode 100644 misoclib/dvisampler/analysis.py delete mode 100644 misoclib/dvisampler/chansync.py delete mode 100644 misoclib/dvisampler/charsync.py delete mode 100644 misoclib/dvisampler/clocking.py delete mode 100644 misoclib/dvisampler/common.py delete mode 100644 misoclib/dvisampler/datacapture.py delete mode 100644 misoclib/dvisampler/debug.py delete mode 100644 misoclib/dvisampler/decoding.py delete mode 100644 misoclib/dvisampler/dma.py delete mode 100644 misoclib/dvisampler/edid.py delete mode 100644 misoclib/dvisampler/wer.py delete mode 100644 misoclib/videostream/downscaler.py delete mode 100644 misoclib/videostream/testpic_in.jpg delete mode 100644 software/videomixer/Makefile delete mode 100644 software/videomixer/ci.c delete mode 100644 software/videomixer/ci.h delete mode 100644 software/videomixer/config.c delete mode 100644 software/videomixer/config.h delete mode 100644 software/videomixer/dvisamplerX.c delete mode 100644 software/videomixer/dvisamplerX.h delete mode 100644 software/videomixer/edid.c delete mode 100644 software/videomixer/edid.h delete mode 100644 software/videomixer/isr.c delete mode 100644 software/videomixer/main.c delete mode 100644 software/videomixer/pll.c delete mode 100644 software/videomixer/pll.h delete mode 100644 software/videomixer/processor.c delete mode 100644 software/videomixer/processor.h rename targets/{simple.py => ppro.py} (98%) diff --git a/.gitignore b/.gitignore index 0ce6c634..36ca43f7 100644 --- a/.gitignore +++ b/.gitignore @@ -7,12 +7,7 @@ build/* *.bin *.fbi tools/flterm -tools/mkmscimg tools/byteswap software/include/generated/*.h software/include/generated/*.ld software/include/generated/*.mak -software/videomixer/dvisampler0.c -software/videomixer/dvisampler0.h -software/videomixer/dvisampler1.c -software/videomixer/dvisampler1.h diff --git a/misoclib/counteradc/__init__.py b/misoclib/counteradc/__init__.py deleted file mode 100644 index 9df83c35..00000000 --- a/misoclib/counteradc/__init__.py +++ /dev/null @@ -1,61 +0,0 @@ -import collections - -from migen.fhdl.std import * -from migen.bank.description import * -from migen.genlib.misc import optree -from migen.genlib.cdc import MultiReg - -class CounterADC(Module, AutoCSR): - def __init__(self, charge, sense, width=24): - if not isinstance(sense, collections.Iterable): - sense = [sense] - - channels = len(sense) - - self._start_busy = CSR() - self._overflow = CSRStatus(channels) - self._polarity = CSRStorage() - - count = Signal(width) - busy = Signal(channels) - - res = [] - for i in range(channels): - res.append(CSRStatus(width, name="res"+str(i))) - setattr(self, "_res"+str(i), res[-1]) - - any_busy = Signal() - self.comb += [ - any_busy.eq(optree("|", - [busy[i] for i in range(channels)])), - self._start_busy.w.eq(any_busy) - ] - - carry = Signal() - - self.sync += [ - If(self._start_busy.re, - count.eq(0), - busy.eq((1 << channels)-1), - self._overflow.status.eq(0), - charge.eq(~self._polarity.storage) - ).Elif(any_busy, - Cat(count, carry).eq(count + 1), - If(carry, - self._overflow.status.eq(busy), - busy.eq(0) - ) - ).Else( - charge.eq(self._polarity.storage) - ) - ] - - for i in range(channels): - sense_synced = Signal() - self.specials += MultiReg(sense[i], sense_synced) - self.sync += If(busy[i], - If(sense_synced != self._polarity.storage, - res[i].status.eq(count), - busy[i].eq(0) - ) - ) diff --git a/misoclib/dvisampler/__init__.py b/misoclib/dvisampler/__init__.py deleted file mode 100644 index 612e9141..00000000 --- a/misoclib/dvisampler/__init__.py +++ /dev/null @@ -1,79 +0,0 @@ -from migen.fhdl.std import * -from migen.bank.description import AutoCSR - -from misoclib.dvisampler.edid import EDID -from misoclib.dvisampler.clocking import Clocking -from misoclib.dvisampler.datacapture import DataCapture -from misoclib.dvisampler.charsync import CharSync -from misoclib.dvisampler.wer import WER -from misoclib.dvisampler.decoding import Decoding -from misoclib.dvisampler.chansync import ChanSync -from misoclib.dvisampler.analysis import SyncPolarity, ResolutionDetection, FrameExtraction -from misoclib.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"} diff --git a/misoclib/dvisampler/analysis.py b/misoclib/dvisampler/analysis.py deleted file mode 100644 index 05d26880..00000000 --- a/misoclib/dvisampler/analysis.py +++ /dev/null @@ -1,205 +0,0 @@ -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.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) - ) diff --git a/misoclib/dvisampler/chansync.py b/misoclib/dvisampler/chansync.py deleted file mode 100644 index c8475608..00000000 --- a/misoclib/dvisampler/chansync.py +++ /dev/null @@ -1,129 +0,0 @@ -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.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) diff --git a/misoclib/dvisampler/charsync.py b/misoclib/dvisampler/charsync.py deleted file mode 100644 index 99ea9aaa..00000000 --- a/misoclib/dvisampler/charsync.py +++ /dev/null @@ -1,53 +0,0 @@ -from migen.fhdl.std import * -from migen.genlib.cdc import MultiReg -from migen.genlib.misc import optree -from migen.bank.description import * - -from misoclib.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) diff --git a/misoclib/dvisampler/clocking.py b/misoclib/dvisampler/clocking.py deleted file mode 100644 index 4f2babdf..00000000 --- a/misoclib/dvisampler/clocking.py +++ /dev/null @@ -1,79 +0,0 @@ -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) diff --git a/misoclib/dvisampler/common.py b/misoclib/dvisampler/common.py deleted file mode 100644 index 7fb9a420..00000000 --- a/misoclib/dvisampler/common.py +++ /dev/null @@ -1,2 +0,0 @@ -control_tokens = [0b1101010100, 0b0010101011, 0b0101010100, 0b1010101011] -channel_layout = [("d", 8), ("c", 2), ("de", 1)] diff --git a/misoclib/dvisampler/datacapture.py b/misoclib/dvisampler/datacapture.py deleted file mode 100644 index d7888433..00000000 --- a/misoclib/dvisampler/datacapture.py +++ /dev/null @@ -1,186 +0,0 @@ -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) diff --git a/misoclib/dvisampler/debug.py b/misoclib/dvisampler/debug.py deleted file mode 100644 index 555d3eea..00000000 --- a/misoclib/dvisampler/debug.py +++ /dev/null @@ -1,46 +0,0 @@ -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.dvisampler.edid import EDID -from misoclib.dvisampler.clocking import Clocking -from misoclib.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.payload.word.eq(fifo.dout), - self.packer.source.connect_flat(self.cast.sink), - self.cast.source.connect_flat(self.dma.data) - ] diff --git a/misoclib/dvisampler/decoding.py b/misoclib/dvisampler/decoding.py deleted file mode 100644 index 73a6718a..00000000 --- a/misoclib/dvisampler/decoding.py +++ /dev/null @@ -1,24 +0,0 @@ -from migen.fhdl.std import * -from migen.genlib.record import Record - -from misoclib.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) diff --git a/misoclib/dvisampler/dma.py b/misoclib/dvisampler/dma.py deleted file mode 100644 index 114a5397..00000000 --- a/misoclib/dvisampler/dma.py +++ /dev/null @@ -1,140 +0,0 @@ -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.payload.pixels[b+6:b+8]) - pixbits.append(self.frame.payload.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.payload.a.eq(current_address), - self._bus_accessor.address_data.payload.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.payload.sof), - If(self._slot_array.address_valid & self.frame.payload.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() diff --git a/misoclib/dvisampler/edid.py b/misoclib/dvisampler/edid.py deleted file mode 100644 index b4afb4eb..00000000 --- a/misoclib/dvisampler/edid.py +++ /dev/null @@ -1,189 +0,0 @@ -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"))) diff --git a/misoclib/dvisampler/wer.py b/misoclib/dvisampler/wer.py deleted file mode 100644 index 6be35abf..00000000 --- a/misoclib/dvisampler/wer.py +++ /dev/null @@ -1,59 +0,0 @@ -from migen.fhdl.std import * -from migen.bank.description import * -from migen.genlib.misc import optree -from migen.genlib.cdc import PulseSynchronizer - -from misoclib.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)) diff --git a/misoclib/framebuffer/__init__.py b/misoclib/framebuffer/__init__.py index 7b8015f1..a3e17ab9 100644 --- a/misoclib/framebuffer/__init__.py +++ b/misoclib/framebuffer/__init__.py @@ -1,15 +1,14 @@ from migen.fhdl.std import * -from migen.flow.actor import * from migen.flow.network import * from migen.flow import plumbing -from migen.bank.description import CSRStorage, AutoCSR -from migen.actorlib import dma_lasmi, structuring, sim, misc +from migen.bank.description import AutoCSR +from migen.actorlib import dma_lasmi, structuring, misc from misoclib.framebuffer.format import bpp, pixel_layout, FrameInitiator, VTG from misoclib.framebuffer.phy import Driver class Framebuffer(Module, AutoCSR): - def __init__(self, pads_vga, pads_dvi, lasmim, simulation=False): + def __init__(self, pads_vga, pads_dvi, lasmim): pack_factor = lasmim.dw//bpp g = DataFlowGraph() @@ -30,77 +29,3 @@ class Framebuffer(Module, AutoCSR): g.add_connection(cast, vtg, sink_ep="pixels") g.add_connection(vtg, self.driver) self.submodules += CompositeActor(g) - -class Blender(PipelinedActor, AutoCSR): - def __init__(self, nimages, pack_factor, latency): - epixel_layout = pixel_layout(pack_factor) - sink_layout = [("i"+str(i), epixel_layout) for i in range(nimages)] - self.sink = Sink(sink_layout) - self.source = Source(epixel_layout) - factors = [] - for i in range(nimages): - name = "f"+str(i) - csr = CSRStorage(8, name=name) - setattr(self, name, csr) - factors.append(csr.storage) - PipelinedActor.__init__(self, latency) - - ### - - sink_registered = Record(sink_layout) - self.sync += If(self.pipe_ce, sink_registered.eq(self.sink.payload)) - - imgs = [getattr(sink_registered, "i"+str(i)) for i in range(nimages)] - outval = Record(epixel_layout) - for e in epixel_layout: - name = e[0] - inpixs = [getattr(img, name) for img in imgs] - outpix = getattr(outval, name) - for component in ["r", "g", "b"]: - incomps = [getattr(pix, component) for pix in inpixs] - outcomp = getattr(outpix, component) - outcomp_full = Signal(19) - self.comb += [ - outcomp_full.eq(sum(incomp*factor for incomp, factor in zip(incomps, factors))), - If(outcomp_full[18], - outcomp.eq(2**10 - 1) # saturate on overflow - ).Else( - outcomp.eq(outcomp_full[8:18]) - ) - ] - - pipe_stmts = [] - for i in range(latency-1): - new_outval = Record(epixel_layout) - pipe_stmts.append(new_outval.eq(outval)) - outval = new_outval - self.sync += If(self.pipe_ce, pipe_stmts) - self.comb += self.source.payload.eq(outval) - -class MixFramebuffer(Module, AutoCSR): - def __init__(self, pads_vga, pads_dvi, *lasmims, blender_latency=5): - assert(all(lasmim.aw == lasmims[0].aw and lasmim.dw == lasmims[0].dw - for lasmim in lasmims)) - pack_factor = lasmims[0].dw//bpp - - self.fi = FrameInitiator(lasmims[0].aw, pack_factor, len(lasmims)) - self.blender = Blender(len(lasmims), pack_factor, blender_latency) - self.driver = Driver(pack_factor, pads_vga, pads_dvi) - - g = DataFlowGraph() - epixel_layout = pixel_layout(pack_factor) - for n, lasmim in enumerate(lasmims): - intseq = misc.IntSequence(lasmim.aw, lasmim.aw) - dma_out = AbstractActor(plumbing.Buffer) - g.add_connection(self.fi, intseq, source_subr=self.fi.dma_subr(n)) - g.add_pipeline(intseq, AbstractActor(plumbing.Buffer), dma_lasmi.Reader(lasmim), dma_out) - - cast = structuring.Cast(lasmim.dw, epixel_layout, reverse_to=True) - g.add_connection(dma_out, cast) - g.add_connection(cast, self.blender, sink_subr=["i"+str(n)]) - - vtg = VTG(pack_factor) - g.add_connection(self.fi, vtg, source_subr=self.fi.timing_subr, sink_ep="timing") - g.add_connection(self.blender, vtg, sink_ep="pixels") - g.add_connection(vtg, self.driver) - self.submodules += CompositeActor(g) diff --git a/misoclib/videostream/downscaler.py b/misoclib/videostream/downscaler.py deleted file mode 100644 index 7744f2d6..00000000 --- a/misoclib/videostream/downscaler.py +++ /dev/null @@ -1,386 +0,0 @@ -from migen.fhdl.std import * -from migen.genlib.fsm import * -from migen.genlib.record import Record -from migen.genlib.misc import optree -from migen.sim.generic import run_simulation - -class Chopper(Module): - def __init__(self, frac_bits): - self.p = Signal(frac_bits) - self.q = Signal(frac_bits) - self.chopper = Signal(reset=1) - - ### - - acc = Signal(frac_bits) - self.sync += If(acc + self.p >= Cat(self.q, 0), # FIXME - acc.eq(acc + self.p - self.q), - self.chopper.eq(1) - ).Else( - acc.eq(acc + self.p), - self.chopper.eq(0) - ) - -class _ChopperTB(Module): - def __init__(self): - self.submodules.dut = Chopper(16) - self.comb += self.dut.p.eq(320), self.dut.q.eq(681) - - def gen_simulation(self, selfp): - ones = 0 - niter = 681 - for i in range(niter): - ones += selfp.dut.chopper - yield - print("Ones: {} (expected: {})".format(ones, selfp.dut.p*niter//selfp.dut.q)) - -class MultiChopper(Module): - def __init__(self, N, frac_bits): - self.init = Signal() - self.ready = Signal() - self.next = Signal() - self.p = Signal(frac_bits) - self.q = Signal(frac_bits) - self.chopper = Signal(N) - - ### - - # initialization counter - ic = Signal(frac_bits) - ic_overflow = Signal() - ic_inc = Signal() - self.sync += \ - If(self.init, - ic.eq(0), - ic_overflow.eq(1) - ).Elif(ic_inc, - If(ic + self.p >= self.q, - ic.eq(ic + self.p - self.q), - ic_overflow.eq(1) - ).Else( - ic.eq(ic + self.p), - ic_overflow.eq(0) - ) - ) - - # computed N*p mod q - Np = Signal(frac_bits) - load_np = Signal() - self.sync += If(load_np, Np.eq(ic)) - - fsm = FSM() - self.submodules += fsm - fsm.act("IDLE", - self.ready.eq(1), - If(self.init, NextState(0)) - ) - - prev_acc_r = Signal(frac_bits) - prev_acc = prev_acc_r - for i in range(N): - acc = Signal(frac_bits) - - # pipeline stage 1: update accumulators - load_init_acc = Signal() - self.sync += \ - If(load_init_acc, - acc.eq(ic) - ).Elif(self.next, - If(acc + Np >= Cat(self.q, 0), # FIXME: workaround for imbecilic Verilog extension rules, needs to be put in Migen backend - acc.eq(acc + Np - self.q), - ).Else( - acc.eq(acc + Np) - ) - ) - - # pipeline stage 2: detect overflows and generate chopper signal - load_init_chopper = Signal() - self.sync += \ - If(load_init_chopper, - self.chopper[i].eq(ic_overflow) - ).Elif(self.next, - self.chopper[i].eq(prev_acc >= acc) - ) - if i == N-1: - self.sync += \ - If(load_init_chopper, - prev_acc_r.eq(ic) - ).Elif(self.next, - prev_acc_r.eq(acc) - ) - prev_acc = acc - - # initialize stage 2 - fsm.act(i, - load_init_chopper.eq(1), - ic_inc.eq(1), - NextState(i + 1) - ) - # initialize stage 1 - fsm.act(N + i, - load_init_acc.eq(1), - ic_inc.eq(1), - NextState(N + i + 1) if i < N-1 else NextState("IDLE") - ) - # initialize Np - fsm.act(N, load_np.eq(1)) - -def _count_ones(n): - r = 0 - while n: - if n & 1: - r += 1 - n >>= 1 - return r - -class _MultiChopperTB(Module): - def __init__(self): - self.submodules.dut = MultiChopper(4, 16) - - def gen_simulation(self, selfp): - dut = selfp.dut - - print("initializing chopper...") - dut.init = 1 - dut.p = 320 - dut.q = 681 - yield - dut.init = 0 - yield - while not dut.ready: - yield - print("done") - - dut.next = 1 - yield - ones = 0 - niter = 681 - for i in range(niter): - #print("{:04b}".format(dut.chopper)) - ones += _count_ones(dut.chopper) - yield - print("Ones: {} (expected: {})".format(ones, dut.p*niter*4//dut.q)) - -class Compacter(Module): - def __init__(self, base_layout, N): - self.i = Record([("w"+str(i), base_layout) for i in range(N)]) - self.sel = Signal(N) - - self.o = Record([("w"+str(i), base_layout) for i in range(N)]) - self.count = Signal(max=N+1) - - ### - - def set_word(wn, selstart): - if wn >= N or selstart >= N: - return - r = None - for i in reversed(range(selstart, N)): - r = If(self.sel[i], - getattr(self.o, "w"+str(wn)).eq(getattr(self.i, "w"+str(i))), - set_word(wn+1, i+1) - ).Else(r) - return r - self.sync += set_word(0, 0) - self.sync += self.count.eq(optree("+", [self.sel[i] for i in range(N)])) - -class Packer(Module): - def __init__(self, base_layout, N): - assert(N & (N - 1) == 0) # only support powers of 2 - - self.i = Record([("w"+str(i), base_layout) for i in range(N)]) - self.count = Signal(max=N+1) - - self.o = Record([("w"+str(i), base_layout) for i in range(N)]) - self.stb = Signal() - - ### - - buf = Record([("w"+str(i), base_layout) for i in range(2*N)]) - - wrp = Signal(max=2*N) - wrp_next = Signal(max=2*N) - self.comb += wrp_next.eq(wrp + self.count) - self.sync += [ - wrp.eq(wrp_next), self.stb.eq(wrp_next[-1] ^ wrp[-1]), - Case(wrp, {i: [getattr(buf, "w"+str(j + i & 2*N - 1)).eq(getattr(self.i, "w"+str(j))) for j in range(N)] for i in range(2*N)}) - ] - - rdp = Signal() - self.sync += If(self.stb, rdp.eq(~rdp)) - self.comb += If(rdp, - [getattr(self.o, "w"+str(i)).eq(getattr(buf, "w"+str(i+N))) for i in range(N)] - ).Else( - [getattr(self.o, "w"+str(i)).eq(getattr(buf, "w"+str(i))) for i in range(N)] - ) - -class _CompacterPackerTB(Module): - def __init__(self): - self.test_seq = [ - (42, 0), (32, 1), ( 4, 1), (21, 0), - (43, 1), (11, 1), ( 5, 1), (18, 0), - (71, 0), (70, 1), (30, 1), (12, 1), - ( 3, 1), (12, 1), (21, 1), (10, 0), - ( 1, 1), (87, 0), (72, 0), (12, 0) - ] - self.input_it = iter(self.test_seq) - self.output = [] - self.end_cycle = -1 - - self.submodules.compacter = Compacter(16, 4) - self.submodules.packer = Packer(16, 4) - self.comb += self.packer.i.eq(self.compacter.o), self.packer.count.eq(self.compacter.count) - - def do_simulation(self, selfp): - if selfp.simulator.cycle_counter == self.end_cycle: - print("got: " + str(self.output)) - print("expected: " + str([value for value, keep in self.test_seq if keep])) - raise StopSimulation - - # push values - sel = 0 - for i in range(4): - try: - value, keep = next(self.input_it) - except StopIteration: - value, keep = 0, 0 - if self.end_cycle == -1: - self.end_cycle = selfp.simulator.cycle_counter + 3 - sel |= int(keep) << i - setattr(selfp.compacter.i, "w"+str(i), value) - selfp.compacter.sel = sel - - # pull values - if selfp.packer.stb: - for i in range(4): - self.output.append(getattr(selfp.packer.o, "w"+str(i))) - -class DownscalerCore(Module): - def __init__(self, base_layout, N, res_bits): - self.init = Signal() - self.ready = Signal() - self.ce = Signal() - - self.hres_in = Signal(res_bits) - self.vres_in = Signal(res_bits) - self.i = Record([("w"+str(i), base_layout) for i in range(N)]) - - self.hres_out = Signal(res_bits) - self.vres_out = Signal(res_bits) - self.o = Record([("w"+str(i), base_layout) for i in range(N)]) - self.stb = Signal() - - ### - - packbits = log2_int(N) - hcounter = Signal(res_bits-packbits) - self.sync += If(self.init, - hcounter.eq(self.hres_in[packbits:] - 1) - ).Elif(self.ce, - If(hcounter == 0, - hcounter.eq(self.hres_in[packbits:] - 1) - ).Else( - hcounter.eq(hcounter - 1) - ) - ) - self.submodules.vselector = InsertReset(InsertCE(Chopper(res_bits))) - self.comb += [ - self.vselector.reset.eq(self.init), - self.vselector.ce.eq(self.ce & (hcounter == 0)), - self.vselector.p.eq(self.vres_out), - self.vselector.q.eq(self.vres_in) - ] - - self.submodules.hselector = MultiChopper(N, res_bits) - self.comb += [ - self.hselector.init.eq(self.init), - self.ready.eq(self.hselector.ready), - self.hselector.next.eq(self.ce), - self.hselector.p.eq(self.hres_out), - self.hselector.q.eq(self.hres_in) - ] - - self.submodules.compacter = InsertReset(InsertCE(Compacter(base_layout, N))) - self.submodules.packer = InsertReset(InsertCE(Packer(base_layout, N))) - self.comb += [ - self.compacter.reset.eq(self.init), - self.packer.reset.eq(self.init), - self.compacter.ce.eq(self.ce), - self.packer.ce.eq(self.ce), - - self.compacter.i.eq(self.i), - self.compacter.sel.eq(self.hselector.chopper & Replicate(self.vselector.chopper, N)), - self.packer.i.eq(self.compacter.o), - self.packer.count.eq(self.compacter.count), - self.o.eq(self.packer.o), - self.stb.eq(self.packer.stb) - ] - -def _img_iter(img): - for y in range(img.size[1]): - for x in range(img.size[0]): - newpix = yield img.getpixel((x, y)) - if newpix is not None: - img.putpixel((x, y), newpix) - -class _DownscalerCoreTB(Module): - def __init__(self): - layout = [("r", 8), ("g", 8), ("b", 8)] - self.submodules.dut = DownscalerCore(layout, 4, 11) - - def gen_simulation(self, selfp): - from PIL import Image - import subprocess - dut = selfp.dut - im_in = Image.open("testpic_in.jpg") - im_out = Image.new("RGB", (320, 240)) - - print("initializing downscaler...") - dut.init = 1 - dut.hres_in, dut.vres_in = im_in.size - dut.hres_out, dut.vres_out = im_out.size - yield - dut.init = 0 - yield - while not dut.ready: - yield - print("done") - - dut.ce = 1 - it_in, it_out = _img_iter(im_in), _img_iter(im_out) - it_out.send(None) - while True: - try: - for i in range(4): - w = getattr(dut.i, "w"+str(i)) - w.r, w.g, w.b = next(it_in) - except StopIteration: - pass - if dut.stb: - try: - for i in range(4): - w = getattr(dut.o, "w"+str(i)) - it_out.send((w.r, w.g, w.b)) - except StopIteration: - break - yield - - im_out.save("testpic_out.png") - try: - subprocess.call(["tycat", "testpic_out.png"]) - except OSError: - print("Image saved as testpic_out.png, but could not be displayed.") - pass - -if __name__ == "__main__": - print("*** Testing chopper ***") - run_simulation(_ChopperTB()) - - print("*** Testing multichopper ***") - run_simulation(_MultiChopperTB()) - - print("*** Testing compacter and packer ***") - run_simulation(_CompacterPackerTB()) - - print("*** Testing downscaler core ***") - run_simulation(_DownscalerCoreTB()) diff --git a/misoclib/videostream/testpic_in.jpg b/misoclib/videostream/testpic_in.jpg deleted file mode 100644 index 49e65a8c093d358917ffa42ed6883c963c53675d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50391 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 diff --git a/software/videomixer/Makefile b/software/videomixer/Makefile deleted file mode 100644 index 86be97fe..00000000 --- a/software/videomixer/Makefile +++ /dev/null @@ -1,80 +0,0 @@ -MSCDIR=../.. -include $(MSCDIR)/software/common.mak - -OBJECTS=isr.o processor.o dvisampler0.o dvisampler1.o edid.o pll.o ci.o config.o main.o - -all: videomixer.bin videomixer.fbi - -# pull in dependency info for *existing* .o files --include $(OBJECTS:.o=.d) - -%.bin: %.elf - $(OBJCOPY) -O binary $< $@ - chmod -x $@ - -%.fbi: %.bin - $(MSCDIR)/mkmscimg.py -f -o $@ $< - -videomixer.elf: $(OBJECTS) libs - -%.elf: - $(LD) $(LDFLAGS) \ - -T $(MSCDIR)/software/libbase/linker-sdram.ld \ - -N -o $@ \ - $(MSCDIR)/software/libbase/crt0-$(CPU).o \ - $(OBJECTS) \ - -L$(MSCDIR)/software/libbase \ - -L$(MSCDIR)/software/libcompiler-rt \ - -lbase -lcompiler-rt - chmod -x $@ - -main.o: main.c - $(compile-dep) - -%.o: %.c - $(compile-dep) - -%.o: %.S - $(assemble) - -define gen0 -@echo " GEN " $@ -@sed -e "s/dvisamplerX/dvisampler0/g;s/DVISAMPLERX/DVISAMPLER0/g;s/fb_fi_baseX/fb_fi_base0/g" $< > $@ -endef - -define gen1 -@echo " GEN " $@ -@sed -e "s/dvisamplerX/dvisampler1/g;s/DVISAMPLERX/DVISAMPLER1/g;s/fb_fi_baseX/fb_fi_base1/g" $< > $@ -endef - -dvisampler0.c: dvisamplerX.c - $(gen0) -dvisampler0.h: dvisamplerX.h - $(gen0) -dvisampler1.c: dvisamplerX.c - $(gen1) -dvisampler1.h: dvisamplerX.h - $(gen1) - -isr.o: dvisampler0.h dvisampler1.h -main.o: dvisampler0.h dvisampler1.h -dvisampler0.o: dvisampler0.h -dvisampler1.o: dvisampler1.h - -libs: - $(MAKE) -C $(MSCDIR)/software/libcompiler-rt - $(MAKE) -C $(MSCDIR)/software/libbase - -load: videomixer.bin - $(MAKE) -C $(MSCDIR)/tools - $(MSCDIR)/tools/flterm --port /dev/ttyUSB0 --kernel videomixer.bin - -flash: videomixer.fbi - $(MSCDIR)/flash_extra.py mixxeo videomixer.fbi 0x001a0000 - -clean: - $(RM) $(OBJECTS) $(OBJECTS:.o=.d) videomixer.elf videomixer.bin videomixer.fbi - $(RM) .*~ *~ - $(RM) dvisampler0.h dvisampler0.c dvisampler1.h dvisampler1.c - -.PHONY: all main.o clean libs load diff --git a/software/videomixer/ci.c b/software/videomixer/ci.c deleted file mode 100644 index 2f91e380..00000000 --- a/software/videomixer/ci.c +++ /dev/null @@ -1,83 +0,0 @@ -#include - -#include -#include - -#include "config.h" -#include "dvisampler0.h" -#include "dvisampler1.h" -#include "processor.h" -#include "pll.h" -#include "ci.h" - -static void print_mem_bandwidth(void) -{ - unsigned long long int nr, nw; - unsigned long long int f; - unsigned int rdb, wrb; - - lasmicon_bandwidth_update_write(1); - nr = lasmicon_bandwidth_nreads_read(); - nw = lasmicon_bandwidth_nwrites_read(); - f = identifier_frequency_read(); - rdb = (nr*f >> (24 - 7))/1000000ULL; - wrb = (nw*f >> (24 - 7))/1000000ULL; - printf("read:%5dMbps write:%5dMbps all:%5dMbps\n", rdb, wrb, rdb + wrb); -} - -static void list_video_modes(void) -{ - char mode_descriptors[PROCESSOR_MODE_COUNT*PROCESSOR_MODE_DESCLEN]; - int i; - - processor_list_modes(mode_descriptors); - printf("==== Available video modes ====\n"); - for(i=0;i= '0') && (c <= '9')) { - int m; - - m = c - '0'; - if(m < PROCESSOR_MODE_COUNT) { - config_set(CONFIG_KEY_RESOLUTION, m); - processor_start(m); - } - } - switch(c) { - case 'l': - list_video_modes(); - break; - case 'D': - dvisampler0_debug = dvisampler1_debug = 1; - printf("DVI sampler debug is ON\n"); - break; - case 'd': - dvisampler0_debug = dvisampler1_debug = 0; - printf("DVI sampler debug is OFF\n"); - break; - case 'F': - fb_fi_enable_write(1); - printf("framebuffer is ON\n"); - break; - case 'f': - fb_fi_enable_write(0); - printf("framebuffer is OFF\n"); - break; - case 'm': - print_mem_bandwidth(); - break; - case 'p': - pll_dump(); - break; - } - } -} diff --git a/software/videomixer/ci.h b/software/videomixer/ci.h deleted file mode 100644 index 63400feb..00000000 --- a/software/videomixer/ci.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __CI_H -#define __CI_H - -void ci_service(void); - -#endif \ No newline at end of file diff --git a/software/videomixer/config.c b/software/videomixer/config.c deleted file mode 100644 index b1292efa..00000000 --- a/software/videomixer/config.c +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include -#include - -#include "config.h" - -#define FLASH_BLOCK_SIZE (128*1024) -#define FLASH_OFFSET_CONFIG (FLASH_BOOT_ADDRESS + FLASH_BLOCK_SIZE) - -static volatile unsigned short *flash_config = (unsigned short *)(0x80000000 | FLASH_OFFSET_CONFIG); - -static void wait_program(void) -{ - while(!(*flash_config & 0x0080)); /* Read status register */ - *flash_config = 0x0050; /* Clear status register */ - *flash_config = 0x00ff; /* Go to Read Array mode */ -} - -static void config_erase_block(void) -{ - *flash_config = 0x0020; /* Setup Erase */ - *flash_config = 0x00d0; /* Confirm Erase */ - wait_program(); -} - -static void config_write(int offset, unsigned short data) -{ - flash_config[offset] = 0x0040; /* Word Program */ - flash_config[offset] = data; - wait_program(); -} - -static const unsigned char config_defaults[CONFIG_KEY_COUNT] = CONFIG_DEFAULTS; -static int config_record_count; -static unsigned char config_values[CONFIG_KEY_COUNT]; - -static int config_process_record(unsigned char key, unsigned char value) -{ - if(key >= CONFIG_KEY_COUNT) - return 0; - config_record_count++; - config_values[key] = value; - return 1; -} - -void config_init(void) -{ - volatile unsigned int *flash_config32 = (unsigned int *)flash_config; - int i; - unsigned int flash_word; - - memcpy(config_values, config_defaults, CONFIG_KEY_COUNT); - - for(i=0;i> 24) & 0xff, (flash_word >> 16) & 0xff)) - break; - if(!config_process_record((flash_word >> 8) & 0xff, flash_word & 0xff)) - break; - } -} - -void config_write_all(void) -{ - int i; - - config_erase_block(); - config_record_count = 0; - for(i=0;i -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "dvisamplerX.h" - -int dvisamplerX_debug; - -#define FRAMEBUFFER_COUNT 4 -#define FRAMEBUFFER_MASK (FRAMEBUFFER_COUNT - 1) - -static unsigned int dvisamplerX_framebuffers[FRAMEBUFFER_COUNT][1280*720] __attribute__((aligned(16))); -static int dvisamplerX_fb_slot_indexes[2]; -static int dvisamplerX_next_fb_index; -static int dvisamplerX_hres, dvisamplerX_vres; - -void dvisamplerX_isr(void) -{ - int fb_index = -1; - int length; - int expected_length; - unsigned int address_min, address_max; - - address_min = (unsigned int)dvisamplerX_framebuffers & 0x0fffffff; - address_max = address_min + sizeof(dvisamplerX_framebuffers); - if((dvisamplerX_dma_slot0_status_read() == DVISAMPLER_SLOT_PENDING) - && ((dvisamplerX_dma_slot0_address_read() < address_min) || (dvisamplerX_dma_slot0_address_read() > address_max))) - printf("dvisamplerX: slot0: stray DMA\n"); - if((dvisamplerX_dma_slot1_status_read() == DVISAMPLER_SLOT_PENDING) - && ((dvisamplerX_dma_slot1_address_read() < address_min) || (dvisamplerX_dma_slot1_address_read() > address_max))) - printf("dvisamplerX: slot1: stray DMA\n"); - - if((dvisamplerX_resdetection_hres_read() != dvisamplerX_hres) - || (dvisamplerX_resdetection_vres_read() != dvisamplerX_vres)) { - /* Dump frames until we get the expected resolution */ - if(dvisamplerX_dma_slot0_status_read() == DVISAMPLER_SLOT_PENDING) { - dvisamplerX_dma_slot0_address_write((unsigned int)dvisamplerX_framebuffers[dvisamplerX_fb_slot_indexes[0]]); - dvisamplerX_dma_slot0_status_write(DVISAMPLER_SLOT_LOADED); - } - if(dvisamplerX_dma_slot1_status_read() == DVISAMPLER_SLOT_PENDING) { - dvisamplerX_dma_slot1_address_write((unsigned int)dvisamplerX_framebuffers[dvisamplerX_fb_slot_indexes[1]]); - dvisamplerX_dma_slot1_status_write(DVISAMPLER_SLOT_LOADED); - } - return; - } - - expected_length = dvisamplerX_hres*dvisamplerX_vres*4; - if(dvisamplerX_dma_slot0_status_read() == DVISAMPLER_SLOT_PENDING) { - length = dvisamplerX_dma_slot0_address_read() - ((unsigned int)dvisamplerX_framebuffers[dvisamplerX_fb_slot_indexes[0]] & 0x0fffffff); - if(length == expected_length) { - fb_index = dvisamplerX_fb_slot_indexes[0]; - dvisamplerX_fb_slot_indexes[0] = dvisamplerX_next_fb_index; - dvisamplerX_next_fb_index = (dvisamplerX_next_fb_index + 1) & FRAMEBUFFER_MASK; - } else - printf("dvisamplerX: slot0: unexpected frame length: %d\n", length); - dvisamplerX_dma_slot0_address_write((unsigned int)dvisamplerX_framebuffers[dvisamplerX_fb_slot_indexes[0]]); - dvisamplerX_dma_slot0_status_write(DVISAMPLER_SLOT_LOADED); - } - if(dvisamplerX_dma_slot1_status_read() == DVISAMPLER_SLOT_PENDING) { - length = dvisamplerX_dma_slot1_address_read() - ((unsigned int)dvisamplerX_framebuffers[dvisamplerX_fb_slot_indexes[1]] & 0x0fffffff); - if(length == expected_length) { - fb_index = dvisamplerX_fb_slot_indexes[1]; - dvisamplerX_fb_slot_indexes[1] = dvisamplerX_next_fb_index; - dvisamplerX_next_fb_index = (dvisamplerX_next_fb_index + 1) & FRAMEBUFFER_MASK; - } else - printf("dvisamplerX: slot1: unexpected frame length: %d\n", length); - dvisamplerX_dma_slot1_address_write((unsigned int)dvisamplerX_framebuffers[dvisamplerX_fb_slot_indexes[1]]); - dvisamplerX_dma_slot1_status_write(DVISAMPLER_SLOT_LOADED); - } - - if(fb_index != -1) - fb_fi_baseX_write((unsigned int)dvisamplerX_framebuffers[fb_index]); -} - -static int dvisamplerX_connected; -static int dvisamplerX_locked; - -void dvisamplerX_init_video(int hres, int vres) -{ - unsigned int mask; - - dvisamplerX_clocking_pll_reset_write(1); - dvisamplerX_connected = dvisamplerX_locked = 0; - dvisamplerX_hres = hres; dvisamplerX_vres = vres; - - dvisamplerX_dma_frame_size_write(hres*vres*4); - dvisamplerX_fb_slot_indexes[0] = 0; - dvisamplerX_dma_slot0_address_write((unsigned int)dvisamplerX_framebuffers[0]); - dvisamplerX_dma_slot0_status_write(DVISAMPLER_SLOT_LOADED); - dvisamplerX_fb_slot_indexes[1] = 1; - dvisamplerX_dma_slot1_address_write((unsigned int)dvisamplerX_framebuffers[1]); - dvisamplerX_dma_slot1_status_write(DVISAMPLER_SLOT_LOADED); - dvisamplerX_next_fb_index = 2; - - dvisamplerX_dma_ev_pending_write(dvisamplerX_dma_ev_pending_read()); - dvisamplerX_dma_ev_enable_write(0x3); - mask = irq_getmask(); - mask |= 1 << DVISAMPLERX_INTERRUPT; - irq_setmask(mask); - - fb_fi_baseX_write((unsigned int)dvisamplerX_framebuffers[3]); -} - -void dvisamplerX_disable(void) -{ - unsigned int mask; - - mask = irq_getmask(); - mask &= ~(1 << DVISAMPLERX_INTERRUPT); - irq_setmask(mask); - - dvisamplerX_dma_slot0_status_write(DVISAMPLER_SLOT_EMPTY); - dvisamplerX_dma_slot1_status_write(DVISAMPLER_SLOT_EMPTY); - dvisamplerX_clocking_pll_reset_write(1); -} - -void dvisamplerX_clear_framebuffers(void) -{ - memset(&dvisamplerX_framebuffers, 0, sizeof(dvisamplerX_framebuffers)); - flush_l2_cache(); -} - -static int dvisamplerX_d0, dvisamplerX_d1, dvisamplerX_d2; - -void dvisamplerX_print_status(void) -{ - dvisamplerX_data0_wer_update_write(1); - dvisamplerX_data1_wer_update_write(1); - dvisamplerX_data2_wer_update_write(1); - printf("dvisamplerX: ph:%4d %4d %4d // charsync:%d%d%d [%d %d %d] // WER:%3d %3d %3d // chansync:%d // res:%dx%d\n", - dvisamplerX_d0, dvisamplerX_d1, dvisamplerX_d2, - dvisamplerX_data0_charsync_char_synced_read(), - dvisamplerX_data1_charsync_char_synced_read(), - dvisamplerX_data2_charsync_char_synced_read(), - dvisamplerX_data0_charsync_ctl_pos_read(), - dvisamplerX_data1_charsync_ctl_pos_read(), - dvisamplerX_data2_charsync_ctl_pos_read(), - dvisamplerX_data0_wer_value_read(), - dvisamplerX_data1_wer_value_read(), - dvisamplerX_data2_wer_value_read(), - dvisamplerX_chansync_channels_synced_read(), - dvisamplerX_resdetection_hres_read(), - dvisamplerX_resdetection_vres_read()); -} - -static int wait_idelays(void) -{ - int ev; - - ev = 0; - elapsed(&ev, 1); - while(dvisamplerX_data0_cap_dly_busy_read() - || dvisamplerX_data1_cap_dly_busy_read() - || dvisamplerX_data2_cap_dly_busy_read()) { - if(elapsed(&ev, identifier_frequency_read() >> 6) == 0) { - printf("dvisamplerX: IDELAY busy timeout\n"); - return 0; - } - } - return 1; -} - -int dvisamplerX_calibrate_delays(void) -{ - dvisamplerX_data0_cap_dly_ctl_write(DVISAMPLER_DELAY_MASTER_CAL|DVISAMPLER_DELAY_SLAVE_CAL); - dvisamplerX_data1_cap_dly_ctl_write(DVISAMPLER_DELAY_MASTER_CAL|DVISAMPLER_DELAY_SLAVE_CAL); - dvisamplerX_data2_cap_dly_ctl_write(DVISAMPLER_DELAY_MASTER_CAL|DVISAMPLER_DELAY_SLAVE_CAL); - if(!wait_idelays()) - return 0; - dvisamplerX_data0_cap_dly_ctl_write(DVISAMPLER_DELAY_MASTER_RST|DVISAMPLER_DELAY_SLAVE_RST); - dvisamplerX_data1_cap_dly_ctl_write(DVISAMPLER_DELAY_MASTER_RST|DVISAMPLER_DELAY_SLAVE_RST); - dvisamplerX_data2_cap_dly_ctl_write(DVISAMPLER_DELAY_MASTER_RST|DVISAMPLER_DELAY_SLAVE_RST); - dvisamplerX_data0_cap_phase_reset_write(1); - dvisamplerX_data1_cap_phase_reset_write(1); - dvisamplerX_data2_cap_phase_reset_write(1); - dvisamplerX_d0 = dvisamplerX_d1 = dvisamplerX_d2 = 0; - return 1; -} - -int dvisamplerX_adjust_phase(void) -{ - switch(dvisamplerX_data0_cap_phase_read()) { - case DVISAMPLER_TOO_LATE: - dvisamplerX_data0_cap_dly_ctl_write(DVISAMPLER_DELAY_DEC); - if(!wait_idelays()) - return 0; - dvisamplerX_d0--; - dvisamplerX_data0_cap_phase_reset_write(1); - break; - case DVISAMPLER_TOO_EARLY: - dvisamplerX_data0_cap_dly_ctl_write(DVISAMPLER_DELAY_INC); - if(!wait_idelays()) - return 0; - dvisamplerX_d0++; - dvisamplerX_data0_cap_phase_reset_write(1); - break; - } - switch(dvisamplerX_data1_cap_phase_read()) { - case DVISAMPLER_TOO_LATE: - dvisamplerX_data1_cap_dly_ctl_write(DVISAMPLER_DELAY_DEC); - if(!wait_idelays()) - return 0; - dvisamplerX_d1--; - dvisamplerX_data1_cap_phase_reset_write(1); - break; - case DVISAMPLER_TOO_EARLY: - dvisamplerX_data1_cap_dly_ctl_write(DVISAMPLER_DELAY_INC); - if(!wait_idelays()) - return 0; - dvisamplerX_d1++; - dvisamplerX_data1_cap_phase_reset_write(1); - break; - } - switch(dvisamplerX_data2_cap_phase_read()) { - case DVISAMPLER_TOO_LATE: - dvisamplerX_data2_cap_dly_ctl_write(DVISAMPLER_DELAY_DEC); - if(!wait_idelays()) - return 0; - dvisamplerX_d2--; - dvisamplerX_data2_cap_phase_reset_write(1); - break; - case DVISAMPLER_TOO_EARLY: - dvisamplerX_data2_cap_dly_ctl_write(DVISAMPLER_DELAY_INC); - if(!wait_idelays()) - return 0; - dvisamplerX_d2++; - dvisamplerX_data2_cap_phase_reset_write(1); - break; - } - return 1; -} - -int dvisamplerX_init_phase(void) -{ - int o_d0, o_d1, o_d2; - int i, j; - - for(i=0;i<100;i++) { - o_d0 = dvisamplerX_d0; - o_d1 = dvisamplerX_d1; - o_d2 = dvisamplerX_d2; - for(j=0;j<1000;j++) { - if(!dvisamplerX_adjust_phase()) - return 0; - } - if((abs(dvisamplerX_d0 - o_d0) < 4) && (abs(dvisamplerX_d1 - o_d1) < 4) && (abs(dvisamplerX_d2 - o_d2) < 4)) - return 1; - } - return 0; -} - -int dvisamplerX_phase_startup(void) -{ - int ret; - int attempts; - - attempts = 0; - while(1) { - attempts++; - dvisamplerX_calibrate_delays(); - if(dvisamplerX_debug) - printf("dvisamplerX: delays calibrated\n"); - ret = dvisamplerX_init_phase(); - if(ret) { - if(dvisamplerX_debug) - printf("dvisamplerX: phase init OK\n"); - return 1; - } else { - printf("dvisamplerX: phase init failed\n"); - if(attempts > 3) { - printf("dvisamplerX: giving up\n"); - dvisamplerX_calibrate_delays(); - return 0; - } - } - } -} - -static void dvisamplerX_check_overflow(void) -{ - if(dvisamplerX_frame_overflow_read()) { - printf("dvisamplerX: FIFO overflow\n"); - dvisamplerX_frame_overflow_write(1); - } -} - -static int dvisamplerX_clocking_locked_filtered(void) -{ - static int lock_start_time; - static int lock_status; - - if(dvisamplerX_clocking_locked_read()) { - switch(lock_status) { - case 0: - elapsed(&lock_start_time, -1); - lock_status = 1; - break; - case 1: - if(elapsed(&lock_start_time, identifier_frequency_read()/4)) - lock_status = 2; - break; - case 2: - return 1; - } - } else - lock_status = 0; - return 0; -} - -void dvisamplerX_service(void) -{ - static int last_event; - - if(dvisamplerX_connected) { - if(!dvisamplerX_edid_hpd_notif_read()) { - if(dvisamplerX_debug) - printf("dvisamplerX: disconnected\n"); - dvisamplerX_connected = 0; - dvisamplerX_locked = 0; - dvisamplerX_clocking_pll_reset_write(1); - dvisamplerX_clear_framebuffers(); - } else { - if(dvisamplerX_locked) { - if(dvisamplerX_clocking_locked_filtered()) { - if(elapsed(&last_event, identifier_frequency_read()/2)) { - dvisamplerX_adjust_phase(); - if(dvisamplerX_debug) - dvisamplerX_print_status(); - } - } else { - if(dvisamplerX_debug) - printf("dvisamplerX: lost PLL lock\n"); - dvisamplerX_locked = 0; - dvisamplerX_clear_framebuffers(); - } - } else { - if(dvisamplerX_clocking_locked_filtered()) { - if(dvisamplerX_debug) - printf("dvisamplerX: PLL locked\n"); - dvisamplerX_phase_startup(); - if(dvisamplerX_debug) - dvisamplerX_print_status(); - dvisamplerX_locked = 1; - } - } - } - } else { - if(dvisamplerX_edid_hpd_notif_read()) { - if(dvisamplerX_debug) - printf("dvisamplerX: connected\n"); - dvisamplerX_connected = 1; - dvisamplerX_clocking_pll_reset_write(0); - } - } - dvisamplerX_check_overflow(); -} diff --git a/software/videomixer/dvisamplerX.h b/software/videomixer/dvisamplerX.h deleted file mode 100644 index 13422dce..00000000 --- a/software/videomixer/dvisamplerX.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef __DVISAMPLERX_H -#define __DVISAMPLERX_H - -extern int dvisamplerX_debug; - -void dvisamplerX_isr(void); -void dvisamplerX_init_video(int hres, int vres); -void dvisamplerX_disable(void); -void dvisamplerX_clear_framebuffers(void); -void dvisamplerX_print_status(void); -int dvisamplerX_calibrate_delays(void); -int dvisamplerX_adjust_phase(void); -int dvisamplerX_init_phase(void); -int dvisamplerX_phase_startup(void); -void dvisamplerX_service(void); - -#endif diff --git a/software/videomixer/edid.c b/software/videomixer/edid.c deleted file mode 100644 index 10f9931e..00000000 --- a/software/videomixer/edid.c +++ /dev/null @@ -1,248 +0,0 @@ -#include -#include - -#include "edid.h" - -struct edid { - uint8_t header[8]; - - uint8_t manufacturer[2]; - uint8_t product_code[2]; - uint8_t serial_number[4]; - uint8_t manufacture_week; - uint8_t manufacture_year; - - uint8_t edid_version; - uint8_t edid_revision; - - uint8_t video_input; - uint8_t h_image_size; - uint8_t v_image_size; - uint8_t gamma; - uint8_t feature_support; - - uint8_t cc_rg_l; - uint8_t cc_bw_l; - uint8_t cc_rx_h; - uint8_t cc_ry_h; - uint8_t cc_gx_h; - uint8_t cc_gy_h; - uint8_t cc_bx_h; - uint8_t cc_by_h; - uint8_t cc_wx_h; - uint8_t cc_wy_h; - - uint8_t est_timings_1; - uint8_t est_timings_2; - uint8_t rsv_timings; - - uint8_t timings_std[16]; - - uint8_t data_blocks[4][18]; - - uint8_t ext_block_count; - - uint8_t checksum; -} __attribute__((packed)); - -struct edid_timing { - uint8_t pixel_clock[2]; - - uint8_t h_active_l; - uint8_t h_blanking_l; - uint8_t h_active_blanking_h; - - uint8_t v_active_l; - uint8_t v_blanking_l; - uint8_t v_active_blanking_h; - - uint8_t h_sync_offset_l; - uint8_t h_sync_width_l; - uint8_t v_sync_offset_width_l; - uint8_t hv_sync_offset_width_h; - - uint8_t h_image_size_l; - uint8_t v_image_size_l; - uint8_t hv_image_size_h; - - uint8_t h_border; - uint8_t v_border; - - uint8_t flags; -} __attribute__((packed)); - -struct edid_descriptor { - uint8_t flag0; - uint8_t flag1; - uint8_t flag2; - uint8_t data_type; - uint8_t flag3; - uint8_t data[13]; -} __attribute__((packed)); - -static const char correct_header[8] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}; - -static uint8_t compute_checksum(struct edid *e) -{ - uint8_t *p = (uint8_t *)e; - uint8_t sum; - int i; - - sum = 0; - for(i=0;i<127;i++) - sum += p[i]; - return -sum; -} - -int validate_edid(const void *buf) -{ - struct edid *e = (struct edid *)buf; - - if(memcmp(e->header, correct_header, 8) != 0) - return 0; - if(compute_checksum(e) != e->checksum) - return 0; - return 1; -} - -void get_monitor_name(const void *buf, char *name) -{ - struct edid *e = (struct edid *)buf; - int i; - uint8_t *data_block; - char *c; - - name[0] = 0; - - data_block = NULL; - for(i=0;i<4;i++) - if((e->data_blocks[i][0] == 0x00) - && (e->data_blocks[i][1] == 0x00) - && (e->data_blocks[i][2] == 0x00) - && (e->data_blocks[i][3] == 0xfc)) { - data_block = e->data_blocks[i]; - break; - } - if(!data_block) - return; - - name[MAX_MONITOR_NAME_LEN] = 0; - memcpy(name, &data_block[5], MAX_MONITOR_NAME_LEN); - c = strchr(name, '\n'); - if(c) - *c = 0; -} - -static void generate_edid_timing(uint8_t *data_block, const struct video_timing *timing) -{ - struct edid_timing *t = (struct edid_timing *)data_block; - unsigned int h_image_size, v_image_size; - - t->pixel_clock[0] = timing->pixel_clock & 0xff; - t->pixel_clock[1] = timing->pixel_clock >> 8; - - t->h_active_l = timing->h_active & 0xff; - t->h_blanking_l = timing->h_blanking & 0xff; - t->h_active_blanking_h = ((timing->h_active >> 8) << 4) | (timing->h_blanking >> 8); - - t->v_active_l = timing->v_active & 0xff; - t->v_blanking_l = timing->v_blanking & 0xff; - t->v_active_blanking_h = ((timing->v_active >> 8) << 4) | (timing->v_blanking >> 8); - - t->h_sync_offset_l = timing->h_sync_offset & 0xff; - t->h_sync_width_l = timing->h_sync_width & 0xff; - t->v_sync_offset_width_l = timing->v_sync_offset & 0xff; - t->hv_sync_offset_width_h = ((timing->h_sync_offset >> 8) << 6) | ((timing->h_sync_width >> 8) << 4) - | ((timing->v_sync_offset >> 8) << 2) | (timing->v_sync_width >> 8); - - h_image_size = 10*timing->h_active/64; - v_image_size = 10*timing->v_active/64; - t->h_image_size_l = h_image_size & 0xff; - t->v_image_size_l = v_image_size & 0xff; - t->hv_image_size_h = ((h_image_size >> 8) << 4) | (v_image_size >> 8); - - t->h_border = 0; - t->v_border = 0; - - t->flags = 0x1e; -} - -static void generate_monitor_name(uint8_t *data_block, const char *name) -{ - struct edid_descriptor *d = (struct edid_descriptor *)data_block; - int i; - - d->flag0 = d->flag1 = d->flag2 = d->flag3 = 0; - d->data_type = 0xfc; - for(i=0;i<12;i++) { - if(!name[i]) - break; - d->data[i] = name[i]; - } - d->data[i++] = 0x0a; - for(;i<13;i++) - d->data[i] = 0x20; -} - -static void generate_unused(uint8_t *data_block) -{ - struct edid_descriptor *d = (struct edid_descriptor *)data_block; - - memset(d, 0, sizeof(struct edid_descriptor)); - d->data_type = 0x10; -} - -void generate_edid(void *out, - const char mfg_name[3], const char product_code[2], int year, - const char *name, - const struct video_timing *timing) -{ - struct edid *e = (struct edid *)out; - int i, j, k; - - memcpy(e->header, correct_header, 8); - - i = mfg_name[0] - 'A' + 1; - j = mfg_name[1] - 'A' + 1; - k = mfg_name[2] - 'A' + 1; - e->manufacturer[0] = (i << 2) | (j >> 3); - e->manufacturer[1] = ((j & 0x07) << 5) | k; - e->product_code[0] = product_code[0]; e->product_code[1] = product_code[1]; - e->serial_number[0] = e->serial_number[1] = e->serial_number[2] = e->serial_number[3] = 0; - e->manufacture_week = 0; - e->manufacture_year = year - 1990; - - e->edid_version = 1; - e->edid_revision = 3; - - e->video_input = 0x80; /* digital */ - e->h_image_size = timing->h_active/64; - e->v_image_size = timing->v_active/64; - e->gamma = 0xff; - e->feature_support = 0x06; - - e->cc_rg_l = 0; - e->cc_bw_l = 0; - e->cc_rx_h = 0; - e->cc_ry_h = 0; - e->cc_gx_h = 0; - e->cc_gy_h = 0; - e->cc_bx_h = 0; - e->cc_by_h = 0; - e->cc_wx_h = 0; - e->cc_wy_h = 0; - - e->est_timings_1 = timing->established_timing >> 8; - e->est_timings_2 = timing->established_timing & 0xff; - e->rsv_timings = 0; - memset(e->timings_std, 0x01, 16); - - generate_edid_timing(e->data_blocks[0], timing); - generate_monitor_name(e->data_blocks[1], name); - generate_unused(e->data_blocks[2]); - generate_unused(e->data_blocks[3]); - - e->ext_block_count = 0; - - e->checksum = compute_checksum(e); -} diff --git a/software/videomixer/edid.h b/software/videomixer/edid.h deleted file mode 100644 index 975d9c0a..00000000 --- a/software/videomixer/edid.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef __EDID_H -#define __EDID_H - -#define MAX_MONITOR_NAME_LEN 13 - -struct video_timing { - unsigned int pixel_clock; /* in tens of kHz */ - - unsigned int h_active; - unsigned int h_blanking; - unsigned int h_sync_offset; - unsigned int h_sync_width; - - unsigned int v_active; - unsigned int v_blanking; - unsigned int v_sync_offset; - unsigned int v_sync_width; - - unsigned int established_timing; -}; - -int validate_edid(const void *buf); -void get_monitor_name(const void *buf, char *name); -void generate_edid(void *out, - const char mfg_name[3], const char product_code[2], int year, - const char *name, - const struct video_timing *timing); - -#endif diff --git a/software/videomixer/isr.c b/software/videomixer/isr.c deleted file mode 100644 index db878a39..00000000 --- a/software/videomixer/isr.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include -#include - -#include "dvisampler0.h" -#include "dvisampler1.h" - -void isr(void); -void isr(void) -{ - unsigned int irqs; - - irqs = irq_pending() & irq_getmask(); - - if(irqs & (1 << UART_INTERRUPT)) - uart_isr(); - if(irqs & (1 << DVISAMPLER0_INTERRUPT)) - dvisampler0_isr(); - if(irqs & (1 << DVISAMPLER1_INTERRUPT)) - dvisampler1_isr(); -} diff --git a/software/videomixer/main.c b/software/videomixer/main.c deleted file mode 100644 index 375a1a00..00000000 --- a/software/videomixer/main.c +++ /dev/null @@ -1,107 +0,0 @@ -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "config.h" -#include "ci.h" -#include "processor.h" - -#ifdef POTS_BASE -static int scale_pot(int raw, int range) -{ - int pot_min = 64000; - int pot_max = 103000; - int scaled; - - scaled = range*(raw - pot_min)/(pot_max - pot_min); - if(scaled < 0) - scaled = 0; - if(scaled > range) - scaled = range; - return scaled; -} - -static void regular_blend(int p0, int p1) -{ - int blackout; - int crossfade; - - blackout = scale_pot(p0, 256); - crossfade = scale_pot(p1, 255); - - fb_blender_f0_write(crossfade*blackout >> 8); - fb_blender_f1_write((255-crossfade)*blackout >> 8); -} - -static void additive_blend(int p0, int p1) -{ - fb_blender_f0_write(scale_pot(p0, 255)); - fb_blender_f1_write(scale_pot(p1, 255)); -} - -static void ui_service(void) -{ - static int last_event; - static int additive_blend_enabled; - static int old_btn; - int btn; - int p0, p1; - - if(elapsed(&last_event, identifier_frequency_read()/32)) { - btn = buttons_in_read() & 0x1; - if(btn && !old_btn) { - additive_blend_enabled = !additive_blend_enabled; - if(additive_blend_enabled) - leds_out_write(leds_out_read() | 0x1); - else - leds_out_write(leds_out_read() & ~0x1); - } - old_btn = btn; - - pots_start_busy_write(1); - while(pots_start_busy_read()); - p0 = pots_res0_read(); - p1 = pots_res1_read(); - if(!additive_blend_enabled) - regular_blend(p0, p1); - else - additive_blend(p0, p1); - } -} - -#else - -static void ui_service(void) -{ - fb_blender_f0_write(0xff); - fb_blender_f1_write(0xff); -} - -#endif - -int main(void) -{ - irq_setmask(0); - irq_setie(1); - uart_init(); - - printf("Mixxeo software rev. %08x built "__DATE__" "__TIME__"\n\n", MSC_GIT_ID); - - config_init(); - time_init(); - processor_start(config_get(CONFIG_KEY_RESOLUTION)); - - while(1) { - processor_service(); - ui_service(); - ci_service(); - } - - return 0; -} diff --git a/software/videomixer/pll.c b/software/videomixer/pll.c deleted file mode 100644 index 843a0276..00000000 --- a/software/videomixer/pll.c +++ /dev/null @@ -1,107 +0,0 @@ -#include -#include - -#include "pll.h" - -/* - * Despite varying pixel clocks, we must keep the PLL VCO operating - * in the specified range of 400MHz - 1000MHz. - * This code can program two sets of DRP data: - * 1. with VCO operating at 20x the pixel clock (for 20MHz - 50MHz pixel clock) - * 2. with VCO operating at 10x the pixel clock (for 40MHz - 100MHz pixel clock) - */ - -static const unsigned short int pll_config_20x[32] = { - 0x0006, 0x0008, 0x0000, 0x4400, 0x1708, 0x0097, 0x0501, 0x8288, - 0x4201, 0x0d90, 0x00a1, 0x0111, 0x1004, 0x2028, 0x0802, 0x2800, - 0x0288, 0x8058, 0x020c, 0x0200, 0x1210, 0x400b, 0xfc21, 0x0b21, - 0x7f5f, 0xc0eb, 0x472a, 0xc02a, 0x20b6, 0x0e96, 0x1002, 0xd6ce -}; - -static const unsigned short int pll_config_10x[32] = { - 0x0006, 0x0008, 0x0000, 0x4400, 0x1708, 0x0097, 0x0901, 0x8118, - 0x4181, 0x0d60, 0x00a1, 0x0111, 0x1004, 0x2028, 0x0802, 0x0608, - 0x0148, 0x8018, 0x020c, 0x0200, 0x1210, 0x400b, 0xfc21, 0x0b22, - 0x5fdf, 0x40eb, 0x472b, 0xc02a, 0x20b6, 0x0e96, 0x1002, 0xd6ce -}; - -static void program_data(const unsigned short *data) -{ - int i; - - /* - * Some bits of words 4 and 5 appear to depend on PLL location, - * so we start at word 6. - * PLLs also seem to dislike any write to the last words. - */ - for(i=6;i<32-5;i++) { - fb_driver_clocking_pll_adr_write(i); - fb_driver_clocking_pll_dat_w_write(data[i]); - fb_driver_clocking_pll_write_write(1); - while(!fb_driver_clocking_pll_drdy_read()); - } - for(i=6;i<32-5;i++) { - dvisampler0_clocking_pll_adr_write(i); - dvisampler0_clocking_pll_dat_w_write(data[i]); - dvisampler0_clocking_pll_write_write(1); - while(!dvisampler0_clocking_pll_drdy_read()); - } - for(i=6;i<32-5;i++) { - dvisampler1_clocking_pll_adr_write(i); - dvisampler1_clocking_pll_dat_w_write(data[i]); - dvisampler1_clocking_pll_write_write(1); - while(!dvisampler1_clocking_pll_drdy_read()); - } -} - -void pll_config_for_clock(int freq) -{ - /* - * FIXME: - * 10x configuration causes random IDELAY lockups (at high frequencies it seems) - * 20x configuration seems to always work, even with overclocked VCO - * Reproducible both with DRP and initial reconfiguration. - * Until this spartan6 weirdness is sorted out, just stick to 20x. - */ - program_data(pll_config_20x); -#ifdef XILINX_SPARTAN6_WORKS_AMAZINGLY_WELL - if(freq < 2000) - printf("Frequency too low for PLLs\n"); - else if(freq < 4500) - program_data(pll_config_20x); - else if(freq < 10000) - program_data(pll_config_10x); - else - printf("Frequency too high for PLLs\n"); -#endif -} - -void pll_dump(void) -{ - int i; - - printf("framebuffer PLL:\n"); - for(i=0;i<32;i++) { - fb_driver_clocking_pll_adr_write(i); - fb_driver_clocking_pll_read_write(1); - while(!fb_driver_clocking_pll_drdy_read()); - printf("%04x ", fb_driver_clocking_pll_dat_r_read()); - } - printf("\n"); - printf("dvisampler0 PLL:\n"); - for(i=0;i<32;i++) { - dvisampler0_clocking_pll_adr_write(i); - dvisampler0_clocking_pll_read_write(1); - while(!dvisampler0_clocking_pll_drdy_read()); - printf("%04x ", dvisampler0_clocking_pll_dat_r_read()); - } - printf("\n"); - printf("dvisampler1 PLL:\n"); - for(i=0;i<32;i++) { - dvisampler1_clocking_pll_adr_write(i); - dvisampler1_clocking_pll_read_write(1); - while(!dvisampler1_clocking_pll_drdy_read()); - printf("%04x ", dvisampler1_clocking_pll_dat_r_read()); - } - printf("\n"); -} diff --git a/software/videomixer/pll.h b/software/videomixer/pll.h deleted file mode 100644 index 3c4a626b..00000000 --- a/software/videomixer/pll.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef __PLL_H -#define __PLL_H - -void pll_config_for_clock(int freq); -void pll_dump(void); - -#endif diff --git a/software/videomixer/processor.c b/software/videomixer/processor.c deleted file mode 100644 index 07e73a38..00000000 --- a/software/videomixer/processor.c +++ /dev/null @@ -1,283 +0,0 @@ -#include -#include - -#include -#include - -#include "dvisampler0.h" -#include "dvisampler1.h" -#include "edid.h" -#include "pll.h" -#include "processor.h" - -/* reference: http://martin.hinner.info/vga/timing.html */ -static const struct video_timing video_modes[PROCESSOR_MODE_COUNT] = { - { - .pixel_clock = 3150, - - .h_active = 640, - .h_blanking = 192, - .h_sync_offset = 24, - .h_sync_width = 40, - - .v_active = 480, - .v_blanking = 40, - .v_sync_offset = 9, - .v_sync_width = 3, - - .established_timing = 0x0800 - }, - { - .pixel_clock = 3150, - - .h_active = 640, - .h_blanking = 200, - .h_sync_offset = 16, - .h_sync_width = 64, - - .v_active = 480, - .v_blanking = 20, - .v_sync_offset = 1, - .v_sync_width = 3, - - .established_timing = 0x0400 - }, - { - .pixel_clock = 3600, - - .h_active = 800, - .h_blanking = 224, - .h_sync_offset = 24, - .h_sync_width = 72, - - .v_active = 600, - .v_blanking = 25, - .v_sync_offset = 1, - .v_sync_width = 2, - - .established_timing = 0x0200 - }, - { - .pixel_clock = 4000, - - .h_active = 800, - .h_blanking = 256, - .h_sync_offset = 40, - .h_sync_width = 128, - - .v_active = 600, - .v_blanking = 28, - .v_sync_offset = 1, - .v_sync_width = 4, - - .established_timing = 0x0100 - }, - { - .pixel_clock = 5000, - - .h_active = 800, - .h_blanking = 240, - .h_sync_offset = 56, - .h_sync_width = 120, - - .v_active = 600, - .v_blanking = 66, - .v_sync_offset = 37, - .v_sync_width = 6, - - .established_timing = 0x0080 - }, - { - .pixel_clock = 4950, - - .h_active = 800, - .h_blanking = 256, - .h_sync_offset = 16, - .h_sync_width = 80, - - .v_active = 600, - .v_blanking = 25, - .v_sync_offset = 1, - .v_sync_width = 3, - - .established_timing = 0x0040 - }, - { - .pixel_clock = 6500, - - .h_active = 1024, - .h_blanking = 320, - .h_sync_offset = 24, - .h_sync_width = 136, - - .v_active = 768, - .v_blanking = 38, - .v_sync_offset = 3, - .v_sync_width = 6, - - .established_timing = 0x0008 - }, - { - .pixel_clock = 7500, - - .h_active = 1024, - .h_blanking = 304, - .h_sync_offset = 24, - .h_sync_width = 136, - - .v_active = 768, - .v_blanking = 38, - .v_sync_offset = 3, - .v_sync_width = 6, - - .established_timing = 0x0004 - }, - { - .pixel_clock = 7880, - - .h_active = 1024, - .h_blanking = 288, - .h_sync_offset = 16, - .h_sync_width = 96, - - .v_active = 768, - .v_blanking = 32, - .v_sync_offset = 1, - .v_sync_width = 3, - - .established_timing = 0x0002 - }, - { - .pixel_clock = 7425, - - .h_active = 1280, - .h_blanking = 370, - .h_sync_offset = 220, - .h_sync_width = 40, - - .v_active = 720, - .v_blanking = 30, - .v_sync_offset = 20, - .v_sync_width = 5 - } -}; - -void processor_list_modes(char *mode_descriptors) -{ - int i; - unsigned int refresh_span; - unsigned int refresh_rate; - - for(i=0;ipixel_clock, &clock_m, &clock_d); - - fb_fi_hres_write(mode->h_active); - fb_fi_hsync_start_write(mode->h_active + mode->h_sync_offset); - fb_fi_hsync_end_write(mode->h_active + mode->h_sync_offset + mode->h_sync_width); - fb_fi_hscan_write(mode->h_active + mode->h_blanking); - fb_fi_vres_write(mode->v_active); - fb_fi_vsync_start_write(mode->v_active + mode->v_sync_offset); - fb_fi_vsync_end_write(mode->v_active + mode->v_sync_offset + mode->v_sync_width); - fb_fi_vscan_write(mode->v_active + mode->v_blanking); - - fb_fi_length_write(mode->h_active*mode->v_active*4); - - fb_clkgen_write(0x1, clock_d-1); - fb_clkgen_write(0x3, clock_m-1); - fb_driver_clocking_send_go_write(1); - while(!(fb_driver_clocking_status_read() & CLKGEN_STATUS_PROGDONE)); - while(!(fb_driver_clocking_status_read() & CLKGEN_STATUS_LOCKED)); -} - -static void edid_set_mode(const struct video_timing *mode) -{ - unsigned char edid[128]; - int i; - - generate_edid(&edid, "OHW", "MX", 2013, "Mixxeo ch.A", mode); - for(i=0;ipixel_clock); - fb_set_mode(m); - edid_set_mode(m); - dvisampler0_init_video(m->h_active, m->v_active); - dvisampler1_init_video(m->h_active, m->v_active); - - fb_driver_clocking_pll_reset_write(0); - fb_fi_enable_write(1); - dvisampler0_edid_hpd_en_write(1); - dvisampler1_edid_hpd_en_write(1); -} - -void processor_service(void) -{ - dvisampler0_service(); - dvisampler1_service(); -} diff --git a/software/videomixer/processor.h b/software/videomixer/processor.h deleted file mode 100644 index cd121b19..00000000 --- a/software/videomixer/processor.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __PROCESSOR_H -#define __PROCESSOR_H - -#define PROCESSOR_MODE_COUNT 10 -#define PROCESSOR_MODE_DESCLEN 32 - -void processor_list_modes(char *mode_descriptors); -void processor_start(int mode); -void processor_service(void); - -#endif /* __VIDEOMODE_H */ diff --git a/targets/mlabs_video.py b/targets/mlabs_video.py index 1b7a1806..79ad4ae3 100644 --- a/targets/mlabs_video.py +++ b/targets/mlabs_video.py @@ -4,7 +4,7 @@ from fractions import Fraction from migen.fhdl.std import * from mibuild.generic_platform import ConstraintError -from misoclib import lasmicon, mxcrg, norflash16, minimac3, framebuffer, dvisampler, gpio +from misoclib import lasmicon, mxcrg, norflash16, minimac3, framebuffer, gpio from misoclib.sdramphy import s6ddrphy from misoclib.gensoc import SDRAMSoC @@ -25,26 +25,9 @@ class _MXClockPads: self.eth_rx_clk = eth_clocks.rx self.eth_tx_clk = eth_clocks.tx -class MiniSoC(SDRAMSoC): +class BaseSoC(SDRAMSoC): default_platform = "mixxeo" # also supports m1 - csr_map = { - "minimac": 10, - "fb": 11, - "dvisampler0": 12, - "dvisampler0_edid_mem": 13, - "dvisampler1": 14, - "dvisampler1_edid_mem": 15, - } - csr_map.update(SDRAMSoC.csr_map) - - interrupt_map = { - "minimac": 2, - "dvisampler0": 3, - "dvisampler1": 4, - } - interrupt_map.update(SDRAMSoC.interrupt_map) - def __init__(self, platform, **kwargs): SDRAMSoC.__init__(self, platform, clk_freq=(83 + Fraction(1, 3))*1000000, @@ -72,25 +55,12 @@ class MiniSoC(SDRAMSoC): rd_bitslip=0, wr_bitslip=3, dqs_ddr_alignment="C1") self.register_sdram_phy(self.ddrphy.dfi, self.ddrphy.phy_settings, sdram_geom, sdram_timing) - # Wishbone self.submodules.norflash = norflash16.NorFlash16(platform.request("norflash"), self.ns(110), self.ns(50)) self.flash_boot_address = 0x001a0000 self.register_rom(self.norflash.bus) - self.submodules.minimac = minimac3.MiniMAC(platform.request("eth")) - self.add_wb_slave(lambda a: a[26:29] == 3, self.minimac.membus) - self.add_cpu_memory_region("minimac_mem", 0xb0000000, 0x1800) - - # CSR self.submodules.crg = mxcrg.MXCRG(_MXClockPads(platform), self.clk_freq) - if platform.name == "mixxeo": - self.submodules.leds = gpio.GPIOOut(platform.request("user_led")) - if platform.name == "m1": - self.submodules.buttons = gpio.GPIOIn(Cat(platform.request("user_btn", 0), platform.request("user_btn", 2))) - self.submodules.leds = gpio.GPIOOut(Cat(platform.request("user_led", i) for i in range(2))) - - # Clock glue self.comb += [ self.ddrphy.clk4x_wr_strb.eq(self.crg.clk4x_wr_strb), self.ddrphy.clk4x_rd_strb.eq(self.crg.clk4x_rd_strb) @@ -101,12 +71,34 @@ INST "mxcrg/rd_bufpll" LOC = "BUFPLL_X0Y3"; PIN "mxcrg/bufg_x1.O" CLOCK_DEDICATED_ROUTE = FALSE; """) + platform.add_source_dir(os.path.join("verilog", "mxcrg")) + +class MiniSoC(BaseSoC): + csr_map = { + "minimac": 10, + } + csr_map.update(BaseSoC.csr_map) + + interrupt_map = { + "minimac": 2, + } + interrupt_map.update(BaseSoC.interrupt_map) + + def __init__(self, platform, **kwargs): + BaseSoC.__init__(self, platform, **kwargs) + + if platform.name == "mixxeo": + self.submodules.leds = gpio.GPIOOut(platform.request("user_led")) + if platform.name == "m1": + self.submodules.buttons = gpio.GPIOIn(Cat(platform.request("user_btn", 0), platform.request("user_btn", 2))) + self.submodules.leds = gpio.GPIOOut(Cat(platform.request("user_led", i) for i in range(2))) - # add Verilog sources - for d in ["mxcrg", "minimac3"]: - platform.add_source_dir(os.path.join("verilog", d)) - -def _get_vga_dvi(platform): + self.submodules.minimac = minimac3.MiniMAC(platform.request("eth")) + self.add_wb_slave(lambda a: a[26:29] == 3, self.minimac.membus) + self.add_cpu_memory_region("minimac_mem", 0xb0000000, 0x1800) + platform.add_source_dir(os.path.join("verilog", "minimac3")) + +def get_vga_dvi(platform): try: pads_vga = platform.request("vga_out") except ConstraintError: @@ -121,7 +113,7 @@ PIN "dviout_pix_bufg.O" CLOCK_DEDICATED_ROUTE = FALSE; """) return pads_vga, pads_dvi -def _add_vga_tig(platform, fb): +def add_vga_tig(platform, fb): platform.add_platform_command(""" NET "{vga_clk}" TNM_NET = "GRPvga_clk"; NET "sys_clk" TNM_NET = "GRPsys_clk"; @@ -130,20 +122,15 @@ TIMESPEC "TSise_sucks2" = FROM "GRPsys_clk" TO "GRPvga_clk" TIG; """, vga_clk=fb.driver.clocking.cd_pix.clk) class FramebufferSoC(MiniSoC): + csr_map = { + "fb": 11, + } + csr_map.update(MiniSoC.csr_map) + def __init__(self, platform, **kwargs): MiniSoC.__init__(self, platform, **kwargs) - pads_vga, pads_dvi = _get_vga_dvi(platform) + pads_vga, pads_dvi = get_vga_dvi(platform) self.submodules.fb = framebuffer.Framebuffer(pads_vga, pads_dvi, self.lasmixbar.get_master()) - _add_vga_tig(platform, self.fb) + add_vga_tig(platform, self.fb) -class VideomixerSoC(MiniSoC): - def __init__(self, platform, **kwargs): - MiniSoC.__init__(self, platform, **kwargs) - pads_vga, pads_dvi = _get_vga_dvi(platform) - self.submodules.fb = framebuffer.MixFramebuffer(pads_vga, pads_dvi, - self.lasmixbar.get_master(), self.lasmixbar.get_master()) - _add_vga_tig(platform, self.fb) - self.submodules.dvisampler0 = dvisampler.DVISampler(platform.request("dvi_in", 2), self.lasmixbar.get_master()) - self.submodules.dvisampler1 = dvisampler.DVISampler(platform.request("dvi_in", 3), self.lasmixbar.get_master()) - -default_subtarget = VideomixerSoC +default_subtarget = FramebufferSoC diff --git a/targets/simple.py b/targets/ppro.py similarity index 98% rename from targets/simple.py rename to targets/ppro.py index 2f4d3734..5416316c 100644 --- a/targets/simple.py +++ b/targets/ppro.py @@ -57,7 +57,7 @@ class _CRG(Module): i_C0=self.cd_sys.clk, i_C1=~self.cd_sys.clk, o_Q=platform.request("sdram_clock")) -class SimpleSoC(SDRAMSoC): +class BaseSoC(SDRAMSoC): default_platform = "papilio_pro" def __init__(self, platform, **kwargs): @@ -92,4 +92,4 @@ class SimpleSoC(SDRAMSoC): self.flash_boot_address = 0x70000 self.register_rom(self.spiflash.bus) -default_subtarget = SimpleSoC +default_subtarget = BaseSoC -- 2.30.2