mirror of
https://github.com/rdolbeau/NuBusFPGA.git
synced 2024-12-23 17:31:00 +00:00
123 lines
5.6 KiB
Python
123 lines
5.6 KiB
Python
|
import os
|
||
|
import math
|
||
|
|
||
|
from migen import *
|
||
|
from migen.genlib.cdc import MultiReg
|
||
|
|
||
|
from litex.soc.interconnect.csr import *
|
||
|
from litex.soc.interconnect import stream
|
||
|
from litex.soc.cores.code_tmds import TMDSEncoder
|
||
|
|
||
|
from litex.build.io import SDROutput, DDROutput
|
||
|
|
||
|
from litex.soc.cores.video import *
|
||
|
|
||
|
|
||
|
# FB Video Timing Generator ---------------------------------------------------------------------------
|
||
|
# Same as the normal one except (a) _enable isn't a CSR
|
||
|
|
||
|
class FBVideoTimingGenerator(Module, AutoCSR):
|
||
|
def __init__(self, default_video_timings="800x600@60Hz"):
|
||
|
# Check / Get Video Timings (can be str or dict)
|
||
|
if isinstance(default_video_timings, str):
|
||
|
try:
|
||
|
self.video_timings = vt = video_timings[default_video_timings]
|
||
|
except KeyError:
|
||
|
msg = [f"Video Timings {default_video_timings} not supported, availables:"]
|
||
|
for video_timing in video_timings.keys():
|
||
|
msg.append(f" - {video_timing} / {video_timings[video_timing]['pix_clk']/1e6:3.2f}MHz.")
|
||
|
raise ValueError("\n".join(msg))
|
||
|
else:
|
||
|
self.video_timings = vt = default_video_timings
|
||
|
|
||
|
# MMAP Control/Status Registers.
|
||
|
self._enable = Signal(reset = 0)
|
||
|
|
||
|
self._hres = CSRStorage(hbits, vt["h_active"])
|
||
|
self._hsync_start = CSRStorage(hbits, vt["h_active"] + vt["h_sync_offset"])
|
||
|
self._hsync_end = CSRStorage(hbits, vt["h_active"] + vt["h_sync_offset"] + vt["h_sync_width"])
|
||
|
self._hscan = CSRStorage(hbits, vt["h_active"] + vt["h_blanking"])
|
||
|
|
||
|
self._vres = CSRStorage(vbits, vt["v_active"])
|
||
|
self._vsync_start = CSRStorage(vbits, vt["v_active"] + vt["v_sync_offset"])
|
||
|
self._vsync_end = CSRStorage(vbits, vt["v_active"] + vt["v_sync_offset"] + vt["v_sync_width"])
|
||
|
self._vscan = CSRStorage(vbits, vt["v_active"] + vt["v_blanking"])
|
||
|
|
||
|
# Video Timing Source
|
||
|
self.source = source = stream.Endpoint(video_timing_layout)
|
||
|
|
||
|
# # #
|
||
|
|
||
|
# Resynchronize Enable to Video clock domain.
|
||
|
self.enable = enable = Signal()
|
||
|
self.specials += MultiReg(self._enable, enable)
|
||
|
|
||
|
# Resynchronize Horizontal Timings to Video clock domain.
|
||
|
self.hres = hres = Signal(hbits)
|
||
|
self.hsync_start = hsync_start = Signal(hbits)
|
||
|
self.hsync_end = hsync_end = Signal(hbits)
|
||
|
self.hscan = hscan = Signal(hbits)
|
||
|
self.specials += MultiReg(self._hres.storage, hres)
|
||
|
self.specials += MultiReg(self._hsync_start.storage, hsync_start)
|
||
|
self.specials += MultiReg(self._hsync_end.storage, hsync_end)
|
||
|
self.specials += MultiReg(self._hscan.storage, hscan)
|
||
|
|
||
|
# Resynchronize Vertical Timings to Video clock domain.
|
||
|
self.vres = vres = Signal(vbits)
|
||
|
self.vsync_start = vsync_start = Signal(vbits)
|
||
|
self.vsync_end = vsync_end = Signal(vbits)
|
||
|
self.vscan = vscan = Signal(vbits)
|
||
|
self.specials += MultiReg(self._vres.storage, vres)
|
||
|
self.specials += MultiReg(self._vsync_start.storage, vsync_start)
|
||
|
self.specials += MultiReg(self._vsync_end.storage, vsync_end)
|
||
|
self.specials += MultiReg(self._vscan.storage, vscan)
|
||
|
|
||
|
# Generate timings.
|
||
|
hactive = Signal()
|
||
|
vactive = Signal()
|
||
|
fsm = FSM(reset_state="IDLE")
|
||
|
fsm = ResetInserter()(fsm)
|
||
|
self.submodules.fsm = fsm
|
||
|
self.comb += fsm.reset.eq(~enable)
|
||
|
fsm.act("IDLE",
|
||
|
NextValue(hactive, 0),
|
||
|
NextValue(vactive, 0),
|
||
|
NextValue(source.hres, hres),
|
||
|
NextValue(source.vres, vres),
|
||
|
NextValue(source.hcount, 0),
|
||
|
NextValue(source.vcount, 0),
|
||
|
NextState("RUN")
|
||
|
)
|
||
|
self.comb += source.de.eq(hactive & vactive) # DE when both HActive and VActive.
|
||
|
self.sync += source.first.eq((source.hcount == 0) & (source.vcount == 0)),
|
||
|
self.sync += source.last.eq( (source.hcount == hscan) & (source.vcount == vscan)),
|
||
|
fsm.act("RUN",
|
||
|
source.valid.eq(1),
|
||
|
If(source.ready,
|
||
|
# Increment HCount.
|
||
|
NextValue(source.hcount, source.hcount + 1),
|
||
|
# Generate HActive / HSync.
|
||
|
If(source.hcount == 0, NextValue(hactive, 1)), # Start of HActive.
|
||
|
If(source.hcount == hres, NextValue(hactive, 0)), # End of HActive.
|
||
|
If(source.hcount == hsync_start, NextValue(source.hsync, 1)), # Start of HSync.
|
||
|
If(source.hcount == hsync_end, NextValue(source.hsync, 0)), # End of HSync.
|
||
|
# End of HScan.
|
||
|
If(source.hcount == hscan,
|
||
|
# Reset HCount.
|
||
|
NextValue(source.hcount, 0),
|
||
|
# Increment VCount.
|
||
|
NextValue(source.vcount, source.vcount + 1),
|
||
|
# Generate VActive / VSync.
|
||
|
If(source.vcount == 0, NextValue(vactive, 1)), # Start of VActive.
|
||
|
If(source.vcount == vres, NextValue(vactive, 0)), # End of HActive.
|
||
|
If(source.vcount == vsync_start, NextValue(source.vsync, 1)), # Start of VSync.
|
||
|
If(source.vcount == vsync_end, NextValue(source.vsync, 0)), # End of VSync.
|
||
|
# End of VScan.
|
||
|
If(source.vcount == vscan,
|
||
|
# Reset VCount.
|
||
|
NextValue(source.vcount, 0),
|
||
|
)
|
||
|
)
|
||
|
)
|
||
|
)
|