from migen import * from migen.genlib.fifo import AsyncFIFO from migen.genlib.cdc import MultiReg from migen.bank.description import * from migen.flow.actor import * from misoc.framebuffer.format import bpc_phy, phy_layout from misoc.framebuffer import dvi class _FIFO(Module): def __init__(self, pack_factor): self.phy = Sink(phy_layout(pack_factor)) self.busy = Signal() self.pix_hsync = Signal() self.pix_vsync = Signal() self.pix_de = Signal() self.pix_r = Signal(bpc_phy) self.pix_g = Signal(bpc_phy) self.pix_b = Signal(bpc_phy) ### fifo = RenameClockDomains(AsyncFIFO(phy_layout(pack_factor), 512), {"write": "sys", "read": "pix"}) self.submodules += fifo self.comb += [ self.phy.ack.eq(fifo.writable), fifo.we.eq(self.phy.stb), fifo.din.eq(self.phy.payload), self.busy.eq(0) ] unpack_counter = Signal(max=pack_factor) assert(pack_factor & (pack_factor - 1) == 0) # only support powers of 2 self.sync.pix += [ unpack_counter.eq(unpack_counter + 1), self.pix_hsync.eq(fifo.dout.hsync), self.pix_vsync.eq(fifo.dout.vsync), self.pix_de.eq(fifo.dout.de) ] for i in range(pack_factor): pixel = getattr(fifo.dout, "p"+str(i)) self.sync.pix += If(unpack_counter == i, self.pix_r.eq(pixel.r), self.pix_g.eq(pixel.g), self.pix_b.eq(pixel.b) ) self.comb += fifo.re.eq(unpack_counter == (pack_factor - 1)) # This assumes a 50MHz base clock class _Clocking(Module, AutoCSR): def __init__(self, pads_vga, pads_dvi): self._cmd_data = CSRStorage(10) self._send_cmd_data = CSR() self._send_go = CSR() self._status = CSRStatus(4) self.clock_domains.cd_pix = ClockDomain(reset_less=True) if pads_dvi is not None: self._pll_reset = CSRStorage() self._pll_adr = CSRStorage(5) self._pll_dat_r = CSRStatus(16) self._pll_dat_w = CSRStorage(16) self._pll_read = CSR() self._pll_write = CSR() self._pll_drdy = CSRStatus() self.clock_domains.cd_pix2x = ClockDomain(reset_less=True) self.clock_domains.cd_pix10x = ClockDomain(reset_less=True) self.serdesstrobe = Signal() ### # Generate 1x pixel clock clk_pix_unbuffered = Signal() pix_progdata = Signal() pix_progen = Signal() pix_progdone = Signal() pix_locked = Signal() self.specials += Instance("DCM_CLKGEN", p_CLKFXDV_DIVIDE=2, p_CLKFX_DIVIDE=4, p_CLKFX_MD_MAX=1.0, p_CLKFX_MULTIPLY=2, p_CLKIN_PERIOD=20.0, p_SPREAD_SPECTRUM="NONE", p_STARTUP_WAIT="FALSE", i_CLKIN=ClockSignal("base50"), o_CLKFX=clk_pix_unbuffered, i_PROGCLK=ClockSignal(), i_PROGDATA=pix_progdata, i_PROGEN=pix_progen, o_PROGDONE=pix_progdone, o_LOCKED=pix_locked, i_FREEZEDCM=0, i_RST=ResetSignal()) remaining_bits = Signal(max=11) transmitting = Signal() self.comb += transmitting.eq(remaining_bits != 0) sr = Signal(10) self.sync += [ If(self._send_cmd_data.re, remaining_bits.eq(10), sr.eq(self._cmd_data.storage) ).Elif(transmitting, remaining_bits.eq(remaining_bits - 1), sr.eq(sr[1:]) ) ] self.comb += [ pix_progdata.eq(transmitting & sr[0]), pix_progen.eq(transmitting | self._send_go.re) ] # enforce gap between commands busy_counter = Signal(max=14) busy = Signal() self.comb += busy.eq(busy_counter != 0) self.sync += If(self._send_cmd_data.re, busy_counter.eq(13) ).Elif(busy, busy_counter.eq(busy_counter - 1) ) mult_locked = Signal() self.comb += self._status.status.eq(Cat(busy, pix_progdone, pix_locked, mult_locked)) # Clock multiplication and buffering if pads_dvi is None: # Just buffer 1x pixel clock self.specials += Instance("BUFG", i_I=clk_pix_unbuffered, o_O=self.cd_pix.clk) self.comb += mult_locked.eq(pix_locked) else: # Route unbuffered 1x pixel clock to PLL # Generate 1x, 2x and 10x IO pixel clocks clkfbout = Signal() pll_locked = Signal() pll_clk0 = Signal() pll_clk1 = Signal() pll_clk2 = Signal() locked_async = Signal() pll_drdy = Signal() self.sync += If(self._pll_read.re | self._pll_write.re, self._pll_drdy.status.eq(0) ).Elif(pll_drdy, self._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_pix_unbuffered, o_CLKOUT0=pll_clk0, o_CLKOUT1=pll_clk1, o_CLKOUT2=pll_clk2, o_CLKFBOUT=clkfbout, i_CLKFBIN=clkfbout, o_LOCKED=pll_locked, i_RST=~pix_locked | self._pll_reset.storage, i_DADDR=self._pll_adr.storage, o_DO=self._pll_dat_r.status, i_DI=self._pll_dat_w.storage, i_DEN=self._pll_read.re | self._pll_write.re, i_DWE=self._pll_write.re, o_DRDY=pll_drdy, i_DCLK=ClockSignal()), 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", name="dviout_pix_bufg", i_I=pll_clk2, o_O=self.cd_pix.clk), MultiReg(locked_async, mult_locked, "sys") ] # Drive VGA/DVI clock pads if pads_vga is not None: self.specials += Instance("ODDR2", p_DDR_ALIGNMENT="NONE", p_INIT=0, p_SRTYPE="SYNC", o_Q=pads_vga.clk, i_C0=ClockSignal("pix"), i_C1=~ClockSignal("pix"), i_CE=1, i_D0=1, i_D1=0, i_R=0, i_S=0) if pads_dvi is not None: dvi_clk_se = Signal() self.specials += Instance("ODDR2", p_DDR_ALIGNMENT="NONE", p_INIT=0, p_SRTYPE="SYNC", o_Q=dvi_clk_se, i_C0=ClockSignal("pix"), i_C1=~ClockSignal("pix"), i_CE=1, i_D0=1, i_D1=0, i_R=0, i_S=0) self.specials += Instance("OBUFDS", i_I=dvi_clk_se, o_O=pads_dvi.clk_p, o_OB=pads_dvi.clk_n) class Driver(Module, AutoCSR): def __init__(self, pack_factor, pads_vga, pads_dvi): fifo = _FIFO(pack_factor) self.submodules += fifo self.phy = fifo.phy self.busy = fifo.busy self.submodules.clocking = _Clocking(pads_vga, pads_dvi) if pads_vga is not None: self.comb += [ pads_vga.hsync_n.eq(~fifo.pix_hsync), pads_vga.vsync_n.eq(~fifo.pix_vsync), pads_vga.r.eq(fifo.pix_r), pads_vga.g.eq(fifo.pix_g), pads_vga.b.eq(fifo.pix_b), pads_vga.psave_n.eq(1) ] if pads_dvi is not None: self.submodules.dvi_phy = dvi.PHY(self.clocking.serdesstrobe, pads_dvi) self.comb += [ self.dvi_phy.hsync.eq(fifo.pix_hsync), self.dvi_phy.vsync.eq(fifo.pix_vsync), self.dvi_phy.de.eq(fifo.pix_de), self.dvi_phy.r.eq(fifo.pix_r), self.dvi_phy.g.eq(fifo.pix_g), self.dvi_phy.b.eq(fifo.pix_b) ]