Exemplo n.º 1
0
    def elaborate(self, platform):
        # Constants
        pixel_f = self.timing.pixel_freq
        hsync_front_porch = self.timing.h_front_porch
        hsync_pulse_width = self.timing.h_sync_pulse
        hsync_back_porch = self.timing.h_back_porch
        vsync_front_porch = self.timing.v_front_porch
        vsync_pulse_width = self.timing.v_sync_pulse
        vsync_back_porch = self.timing.v_back_porch

        clk25 = platform.request("clk25")

        m = Module()

        # Clock generator.
        m.domains.sync = cd_sync = ClockDomain("sync")
        m.domains.pixel = cd_pixel = ClockDomain("pixel")
        m.domains.shift = cd_shift = ClockDomain("shift")

        m.submodules.ecp5pll = pll = ECP5PLL()
        pll.register_clkin(clk25, platform.default_clk_frequency)
        pll.create_clkout(cd_sync, platform.default_clk_frequency)
        pll.create_clkout(cd_pixel, pixel_f)
        pll.create_clkout(cd_shift, pixel_f * 5.0 * (1.0 if self.ddr else 2.0))

        platform.add_clock_constraint(cd_sync.clk,
                                      platform.default_clk_frequency)
        platform.add_clock_constraint(cd_pixel.clk, pixel_f)
        platform.add_clock_constraint(
            cd_shift.clk, pixel_f * 5.0 * (1.0 if self.ddr else 2.0))

        led = [platform.request("led", i) for i in range(8)]
        leds = Cat([i.o for i in led])
        ov7670 = platform.request("ov7670")
        btn1 = platform.request("button_fire", 0)
        btn2 = platform.request("button_fire", 1)
        up = platform.request("button_up", 0)
        down = platform.request("button_down", 0)
        sw0 = platform.request("switch", 0)
        sw1 = platform.request("switch", 1)
        sw2 = platform.request("switch", 2)
        sw3 = platform.request("switch", 3)
        sw8 = platform.request("sw8")

        # Add CamRead submodule
        camread = CamRead()
        m.submodules.camread = camread

        # Camera config
        camconfig = CamConfig()
        m.submodules.camconfig = camconfig

        # Configure and read the camera
        m.d.comb += [
            ov7670.cam_RESET.eq(1),
            ov7670.cam_PWON.eq(0),
            ov7670.cam_XCLK.eq(clk25.i),
            ov7670.cam_SIOC.eq(camconfig.sioc),
            ov7670.cam_SIOD.eq(camconfig.siod),
            camconfig.start.eq(btn1),
            camread.p_data.eq(Cat([ov7670.cam_data[i] for i in range(8)])),
            camread.href.eq(ov7670.cam_HREF),
            camread.vsync.eq(ov7670.cam_VSYNC),
            camread.p_clock.eq(ov7670.cam_PCLK)
        ]

        # Frame buffer
        buffer = Memory(width=16, depth=320 * 480)
        m.submodules.r = r = buffer.read_port()
        m.submodules.w = w = buffer.write_port()

        # Buttons and val
        debup = Debouncer()
        m.submodules.debup = debup

        val = Signal(signed(6))
        up_down = Signal()

        debdown = Debouncer()
        m.submodules.debdown = debdown

        debres = Debouncer()
        m.submodules.debres = debres

        m.d.comb += [
            debup.btn.eq(up),
            debdown.btn.eq(down),
            debres.btn.eq(btn2)
        ]

        with m.If(debup.btn_down):
            m.d.sync += val.eq(val + 1)

        with m.If(debdown.btn_down):
            m.d.sync += val.eq(val - 1)

        with m.If(debres.btn_down):
            m.d.sync += val.eq(0)

        # Image stream
        max_r = Signal(5)
        max_g = Signal(6)
        max_b = Signal(5)

        ims = ImageStream()
        m.submodules.image_stream = ims

        m.d.comb += [
            ims.valid.eq(camread.pixel_valid),
            ims.i_x.eq(camread.row[1:]),
            ims.i_y.eq(camread.col),
            ims.i_r.eq(camread.pixel_data[11:]),
            ims.i_g.eq(camread.pixel_data[5:11]),
            ims.i_b.eq(camread.pixel_data[0:5]),
            ims.edge.eq(sw8.sw1),
            ims.red.eq(sw8.sw2),
            ims.green.eq(sw8.sw3),
            ims.blue.eq(sw8.sw4),
            ims.invert.eq(sw8.sw5),
            ims.border.eq(sw8.sw6),
            ims.gamma.eq(sw8.sw7),
            ims.filter.eq(sw8.sw8),
            ims.mono.eq(sw0),
            ims.bright.eq(sw1),
            ims.x_flip.eq(sw2),
            ims.y_flip.eq(sw3),
            ims.val.eq(val)
        ]

        with m.If(ims.i_r > max_r):
            m.d.sync += max_r.eq(ims.i_r)
        with m.If(ims.i_g > max_g):
            m.d.sync += max_g.eq(ims.i_g)
        with m.If(ims.i_b > max_b):
            m.d.sync += max_b.eq(ims.i_b)

        with m.If(camread.frame_done):
            m.d.sync += [max_r.eq(0), max_g.eq(0), max_b.eq(0)]

        # Show value on leds
        m.d.comb += leds.eq(ims.p_x)

        # VGA signal generator.
        vga_r = Signal(8)
        vga_g = Signal(8)
        vga_b = Signal(8)
        vga_hsync = Signal()
        vga_vsync = Signal()
        vga_blank = Signal()

        psum = Signal(8)

        # Add VGA generator
        m.submodules.vga = vga = VGA(
            resolution_x=self.timing.x,
            hsync_front_porch=hsync_front_porch,
            hsync_pulse=hsync_pulse_width,
            hsync_back_porch=hsync_back_porch,
            resolution_y=self.timing.y,
            vsync_front_porch=vsync_front_porch,
            vsync_pulse=vsync_pulse_width,
            vsync_back_porch=vsync_back_porch,
            bits_x=16,  # Play around with the sizes because sometimes
            bits_y=16  # a smaller/larger value will make it pass timing.
        )

        # Connect frame buffer
        m.d.comb += [
            w.en.eq(ims.ready),
            w.addr.eq(ims.o_y * 320 + ims.o_x),
            w.data.eq(Cat(ims.o_b, ims.o_g, ims.o_r)),
            r.addr.eq(vga.o_beam_y * 320 + vga.o_beam_x[1:])
        ]

        # Generate VGA signals
        m.d.comb += [
            vga.i_clk_en.eq(1),
            vga.i_test_picture.eq(0),
            vga.i_r.eq(Cat(Const(0, unsigned(3)), r.data[11:16])),
            vga.i_g.eq(Cat(Const(0, unsigned(2)), r.data[5:11])),
            vga.i_b.eq(Cat(Const(0, unsigned(3)), r.data[0:5])),
            vga_r.eq(vga.o_vga_r),
            vga_g.eq(vga.o_vga_g),
            vga_b.eq(vga.o_vga_b),
            vga_hsync.eq(vga.o_vga_hsync),
            vga_vsync.eq(vga.o_vga_vsync),
            vga_blank.eq(vga.o_vga_blank),
        ]

        # VGA to digital video converter.
        tmds = [Signal(2) for i in range(4)]
        m.submodules.vga2dvid = vga2dvid = VGA2DVID(
            ddr=self.ddr, shift_clock_synchronizer=False)
        m.d.comb += [
            vga2dvid.i_red.eq(vga_r),
            vga2dvid.i_green.eq(vga_g),
            vga2dvid.i_blue.eq(vga_b),
            vga2dvid.i_hsync.eq(vga_hsync),
            vga2dvid.i_vsync.eq(vga_vsync),
            vga2dvid.i_blank.eq(vga_blank),
            tmds[3].eq(vga2dvid.o_clk),
            tmds[2].eq(vga2dvid.o_red),
            tmds[1].eq(vga2dvid.o_green),
            tmds[0].eq(vga2dvid.o_blue),
        ]

        if (self.ddr):
            # Vendor specific DDR modules.
            # Convert SDR 2-bit input to DDR clocked 1-bit output (single-ended)
            # onboard GPDI.
            m.submodules.ddr0_clock = Instance("ODDRX1F",
                                               i_SCLK=ClockSignal("shift"),
                                               i_RST=0b0,
                                               i_D0=tmds[3][0],
                                               i_D1=tmds[3][1],
                                               o_Q=self.o_gpdi_dp[3])
            m.submodules.ddr0_red = Instance("ODDRX1F",
                                             i_SCLK=ClockSignal("shift"),
                                             i_RST=0b0,
                                             i_D0=tmds[2][0],
                                             i_D1=tmds[2][1],
                                             o_Q=self.o_gpdi_dp[2])
            m.submodules.ddr0_green = Instance("ODDRX1F",
                                               i_SCLK=ClockSignal("shift"),
                                               i_RST=0b0,
                                               i_D0=tmds[1][0],
                                               i_D1=tmds[1][1],
                                               o_Q=self.o_gpdi_dp[1])
            m.submodules.ddr0_blue = Instance("ODDRX1F",
                                              i_SCLK=ClockSignal("shift"),
                                              i_RST=0b0,
                                              i_D0=tmds[0][0],
                                              i_D1=tmds[0][1],
                                              o_Q=self.o_gpdi_dp[0])
        else:
            m.d.comb += [
                self.o_gpdi_dp[3].eq(tmds[3][0]),
                self.o_gpdi_dp[2].eq(tmds[2][0]),
                self.o_gpdi_dp[1].eq(tmds[1][0]),
                self.o_gpdi_dp[0].eq(tmds[0][0]),
            ]

        return m
Exemplo n.º 2
0
    def elaborate(self, platform):
        # VGA constants
        pixel_f = self.timing.pixel_freq
        hsync_front_porch = self.timing.h_front_porch
        hsync_pulse_width = self.timing.h_sync_pulse
        hsync_back_porch = self.timing.h_back_porch
        vsync_front_porch = self.timing.v_front_porch
        vsync_pulse_width = self.timing.v_sync_pulse
        vsync_back_porch = self.timing.v_back_porch

        # Pins
        clk25 = platform.request("clk25")
        ov7670 = platform.request("ov7670")
        led = [platform.request("led", i) for i in range(8)]
        leds = Cat([i.o for i in led])
        led8_2 = platform.request("led8_2")
        leds8_2 = Cat([led8_2.leds[i] for i in range(8)])
        led8_3 = platform.request("led8_3")
        leds8_3 = Cat([led8_3.leds[i] for i in range(8)])
        leds16 = Cat(leds8_3, leds8_2)
        btn1 = platform.request("button_fire", 0)
        btn2 = platform.request("button_fire", 1)
        up = platform.request("button_up", 0)
        down = platform.request("button_down", 0)
        pwr = platform.request("button_pwr", 0)
        left = platform.request("button_left", 0)
        right = platform.request("button_right", 0)
        sw = Cat([platform.request("switch", i) for i in range(4)])
        uart = platform.request("uart")
        divisor = int(platform.default_clk_frequency // 460800)
        esp32 = platform.request("esp32_spi")

        csn = esp32.csn
        sclk = esp32.sclk
        copi = esp32.copi
        cipo = esp32.cipo

        m = Module()

        # Clock generator.
        m.domains.sync = cd_sync = ClockDomain("sync")
        m.domains.pixel = cd_pixel = ClockDomain("pixel")
        m.domains.shift = cd_shift = ClockDomain("shift")

        m.submodules.ecp5pll = pll = ECP5PLL()
        pll.register_clkin(clk25, platform.default_clk_frequency)
        pll.create_clkout(cd_sync, platform.default_clk_frequency)
        pll.create_clkout(cd_pixel, pixel_f)
        pll.create_clkout(cd_shift, pixel_f * 5.0 * (1.0 if self.ddr else 2.0))

        # Add CamRead submodule
        camread = CamRead()
        m.submodules.camread = camread

        # Camera config
        cam_x_res = 640
        cam_y_res = 480

        camconfig = CamConfig()
        m.submodules.camconfig = camconfig

        # Connect the camera pins and config and read modules
        m.d.comb += [
            ov7670.cam_RESET.eq(1),
            ov7670.cam_PWON.eq(0),
            ov7670.cam_XCLK.eq(clk25.i),
            ov7670.cam_SIOC.eq(camconfig.sioc),
            ov7670.cam_SIOD.eq(camconfig.siod),
            camconfig.start.eq(btn1),
            camread.p_data.eq(Cat([ov7670.cam_data[i] for i in range(8)])),
            camread.href.eq(ov7670.cam_HREF),
            camread.vsync.eq(ov7670.cam_VSYNC),
            camread.p_clock.eq(ov7670.cam_PCLK)
        ]

        # Create the uart
        m.submodules.serial = serial = AsyncSerial(divisor=divisor, pins=uart)

        # Frame buffer
        x_res = cam_x_res // 2
        y_res = cam_y_res

        buffer = Memory(width=16, depth=x_res * y_res)
        m.submodules.r = r = buffer.read_port()
        m.submodules.w = w = buffer.write_port()

        # Button debouncers
        m.submodules.debup = debup = Debouncer()
        m.submodules.debdown = debdown = Debouncer()
        m.submodules.debosd = debosd = Debouncer()
        m.submodules.debsel = debsel = Debouncer()
        m.submodules.debsnap = debsnap = Debouncer()
        m.submodules.debhist = debhist = Debouncer()

        # Connect the buttons to debouncers
        m.d.comb += [
            debup.btn.eq(up),
            debdown.btn.eq(down),
            debosd.btn.eq(pwr),
            debsel.btn.eq(right),
            debsnap.btn.eq(left),
            debhist.btn.eq(btn2)
        ]

        # Image processing configuration registers
        flip = Signal(2, reset=1)  # Flip the image horizontally or vertically
        mono_en = Signal(reset=0)  # Convert to monochrome
        invert = Signal(reset=0)  # Invert monochrome image
        thresh_en = Signal(reset=0)  # Apply threshold to monochrome image
        threshold = Signal(8, reset=0)  # Threshold value
        border = Signal(reset=0)  # Use OSD to show a border
        filt_en = Signal(reset=0)  # Apply a color filter
        l = Rgb565(reset=(18, 12, 6))  # Image filter low values
        h = Rgb565(reset=(21, 22, 14))  # Image filter high values
        grid = Signal(reset=0)  # Use OSD to show a grid
        hist_view = Signal(reset=1)  # Switch to histogram view
        hist_chan = Signal(2, reset=0)  # The histogram channel to calculate
        ccr = CC(reset=(0, 0, 18, 12, 16))  # Color control record
        sharpness = Signal(
            unsigned(4), reset=0
        )  # Used to select image convolution kernel for blur/sharpness
        roi = Roi()  # Region on interest
        frozen = Signal(reset=1)  # Freeze/unfreeze video display
        sat_en = Signal()
        saturation = Signal(5, reset=16)

        # Control synchronization of camera with fifo
        sync_fifo = Signal(reset=0)

        # OSD control signals
        osd_val = Signal(
            4, reset=0)  # Account for spurious start-up button pushes
        osd_on = Signal(reset=1)
        osd_sel = Signal(reset=1)

        # Snapshot signals
        snap = Signal(reset=0)
        writing = Signal(reset=0)
        written = Signal(reset=0)
        byte = Signal(reset=0)
        w_addr = Signal(18)

        # Signals for calculating histogram
        hist_val = Signal(6)

        # Signals for displaying histogram
        hist_color = Signal(8)
        hbin = Signal(6, reset=0)
        bin_cnt = Signal(5, reset=0)
        old_x = Signal(10)

        # Frame buffer coordinates
        frame_x = Signal(10)
        frame_y = Signal(9)

        # VGA signals
        vga_r = Signal(8)
        vga_g = Signal(8)
        vga_b = Signal(8)
        vga_hsync = Signal()
        vga_vsync = Signal()
        vga_blank = Signal()

        # Pixel from camera
        pix = Rgb565()

        # Fifo stream
        m.submodules.fifo_stream = fs = FifoStream()

        # SPI memory for remote configuration
        m.submodules.spimem = spimem = SpiMem(addr_bits=32)

        # Color Control
        m.submodules.cc = cc = ColorControl()

        # Image convolution
        m.submodules.imc = imc = ImageConv()

        # Statistics
        m.submodules.stats = stats = Stats()

        # Histogram
        m.submodules.hist = hist = Hist()

        # Filter
        m.submodules.fil = fil = Filt()

        # Monochrome
        m.submodules.mon = mon = Mono()

        # Saturation
        m.submodules.sat = sat = Saturation()

        # Sync the fifo with the camera
        with m.If(~sync_fifo & (camread.col == cam_x_res - 1)
                  & (camread.row == cam_y_res - 1)):
            m.d.sync += sync_fifo.eq(1)

        with m.If(btn1):
            m.d.sync += sync_fifo.eq(0)

        # Set histogram value to the data for the chosen channel
        with m.Switch(hist_chan):
            with m.Case(0):
                m.d.comb += hist_val.eq(cc.o.r)
            with m.Case(1):
                m.d.comb += hist_val.eq(cc.o.g)
            with m.Case(2):
                m.d.comb += hist_val.eq(cc.o.b)
            with m.Case(3):
                m.d.comb += hist_val.eq(mon.o_m)

        # Copy camera data to Rgb565 record
        m.d.comb += [
            pix.r.eq(camread.pixel_data[11:]),
            pix.g.eq(camread.pixel_data[5:11]),
            pix.b.eq(camread.pixel_data[:5])
        ]

        # Input image processing pipeline
        pipeline = [
            [
                fs,
                {
                    "i": pix,  # Fifo stream
                    "i_valid": camread.pixel_valid & camread.col[0],
                    "i_ready": cc.o_ready,
                    "i_en": sync_fifo
                },
                True
            ],
            [sat, {
                "i_en": sat_en,
                "i_saturation": saturation
            }, True],
            [cc, {
                "i_cc": ccr
            }, True],  # Color control
            [
                fil,
                {
                    "i_en": filt_en,  # Color filter
                    "i_frame_done": fs.o_eof,
                    "i_l": l,
                    "i_h": h
                },
                True
            ],
            [
                mon,
                {
                    "i_en": mono_en | invert
                    | thresh_en,  # Monochrome, invert and threshold
                    "i_invert": invert,
                    "i_thresh": thresh_en,
                    "i_threshold": threshold
                },
                True
            ],
            [
                imc,
                {
                    "i_ready": 1,  # Image convolution
                    "i_reset": ~fs.i_en,
                    "i_sel": sharpness
                },
                True
            ],
            [
                stats,
                {
                    "i":
                    cc.o,  # Statistics               
                    "i_valid":
                    cc.o_valid,
                    "i_avg_valid": (fs.o_x >= 32) & (fs.o_x < 288) &
                    (fs.o_y >= 112) & (fs.o_y < 368),
                    "i_frame_done":
                    fs.o_eof,
                    "i_x":
                    fs.o_x,
                    "i_y":
                    fs.o_y,
                    "i_roi":
                    roi
                },
                False
            ],
            [
                hist,
                {
                    "i_p": hist_val,  # Histogram         
                    "i_valid": mon.o_valid,
                    "i_clear": fs.o_eof,
                    "i_x": fs.o_x,
                    "i_y": fs.o_y,
                    "i_roi": roi,
                    "i_bin": hbin
                },
                False
            ]
        ]

        def execute(pl):
            us = None  # Upstream
            for p in pl:
                mod = p[0]
                d = p[1]
                st = p[2]  # Stream or Sink
                if st and us is not None:
                    m.d.comb += mod.i.eq(us.o)
                    m.d.comb += mod.i_valid.eq(us.o_valid)
                    m.d.comb += us.i_ready.eq(mod.o_ready)
                if st:
                    us = mod
                for k in d:
                    m.d.comb += mod.__dict__[k].eq(d[k])

        execute(pipeline)

        # Take a snapshot, freeze the camera, and write the framebuffer to the uart
        # Note that this suspends video output
        with m.If(debsnap.btn_down | (spimem.wr & (spimem.addr == 22))):
            with m.If(frozen):
                m.d.sync += frozen.eq(0)
            with m.Else():
                m.d.sync += [
                    snap.eq(1),
                    frozen.eq(0),
                    w_addr.eq(0),
                    written.eq(0),
                    byte.eq(0)
                ]

        # Wait to end of frame after requesting snapshot, before start of writing to uart
        with m.If(imc.o_eof & snap):
            m.d.sync += [frozen.eq(1), snap.eq(0)]
            with m.If(~written):
                m.d.sync += writing.eq(1)

        # Connect the uart
        m.d.comb += [
            serial.tx.data.eq(Mux(byte, r.data[8:], r.data[:8])),
            serial.tx.ack.eq(writing)
        ]

        # Write to the uart from frame buffer (affects video output)
        with m.If(writing):
            with m.If(w_addr == x_res * y_res):
                m.d.sync += [writing.eq(0), written.eq(1)]
            with m.Elif(serial.tx.ack & serial.tx.rdy):
                m.d.sync += byte.eq(~byte)
                with m.If(byte):
                    m.d.sync += w_addr.eq(w_addr + 1)

        # Connect spimem
        m.d.comb += [
            spimem.csn.eq(~csn),
            spimem.sclk.eq(sclk),
            spimem.copi.eq(copi),
            cipo.eq(spimem.cipo),
        ]

        # Writable configuration registers
        spi_wr_vals = Array([
            ccr.brightness, ccr.redness, ccr.greenness, ccr.blueness, l.r, h.r,
            l.g, h.g, l.b, h.b, sharpness, filt_en, border, mono_en, invert,
            grid, hist_view, roi.x[1:], roi.y[1:], roi.w[1:], roi.h[1:],
            roi.en, None, None, None, threshold, thresh_en, hist_chan, flip,
            None, None, None, None, None, None, None, None, None, frozen, None,
            None, sat_en, saturation, ccr.offset
        ])

        with m.If(spimem.wr):
            with m.Switch(spimem.addr):
                for i in range(len(spi_wr_vals)):
                    if spi_wr_vals[i] is not None:
                        with m.Case(i):
                            m.d.sync += spi_wr_vals[i].eq(spimem.dout)

        # Readable configuration registers
        spi_rd_vals = Array([
            ccr.brightness, ccr.redness, ccr.greenness, ccr.blueness, l.r, h.r,
            l.g, h.g, l.b, h.b, sharpness, filt_en, border, mono_en, invert,
            grid, hist_view, roi.x[1:], roi.y[1:], roi.w[1:], roi.h[1:],
            roi.en, fil.o_nz[16:], fil.o_nz[8:16], fil.o_nz[:8], threshold,
            thresh_en, hist_chan, flip, stats.o_min.r, stats.o_min.g,
            stats.o_min.b, stats.o_max.r, stats.o_max.g, stats.o_max.b,
            stats.o_avg.r, stats.o_avg.g, stats.o_avg.b, frozen, writing,
            written, sat_en, saturation, ccr.offset
        ])

        with m.If(spimem.rd):
            with m.Switch(spimem.addr):
                for i in range(len(spi_rd_vals)):
                    with m.Case(i):
                        m.d.sync += spimem.din.eq(spi_rd_vals[i])

        # Add VGA generator
        m.submodules.vga = vga = VGA(
            resolution_x=self.timing.x,
            hsync_front_porch=hsync_front_porch,
            hsync_pulse=hsync_pulse_width,
            hsync_back_porch=hsync_back_porch,
            resolution_y=self.timing.y,
            vsync_front_porch=vsync_front_porch,
            vsync_pulse=vsync_pulse_width,
            vsync_back_porch=vsync_back_porch,
            bits_x=16,  # Play around with the sizes because sometimes
            bits_y=16  # a smaller/larger value will make it pass timing.
        )

        # Fetch histogram for display
        m.d.sync += old_x.eq(vga.o_beam_x)

        with m.If(vga.o_beam_x == 0):
            m.d.sync += [hbin.eq(0), bin_cnt.eq(0)]
        with m.Elif(vga.o_beam_x != old_x):
            m.d.sync += bin_cnt.eq(bin_cnt + 1)
            with m.If(bin_cnt == 19):
                m.d.sync += [bin_cnt.eq(0), hbin.eq(hbin + 1)]

        # Switch between camera and histogram view
        with m.If(debhist.btn_down):
            m.d.sync += hist_view.eq(~hist_view)

        # Connect frame buffer, with optional x and y flip
        m.d.comb += [
            frame_x.eq(
                Mux(flip[0], x_res - 1 - vga.o_beam_x[1:], vga.o_beam_x[1:])),
            frame_y.eq(Mux(flip[1], y_res - 1 - vga.o_beam_y, vga.o_beam_y)),
            w.en.eq(imc.o_valid & ~frozen),
            w.addr.eq(imc.o_y * x_res + imc.o_x),
            w.data.eq(imc.o.as_data()),
            r.addr.eq(Mux(writing, w_addr, frame_y * x_res + frame_x))
        ]

        # Apply the On-Screen Display (OSD)
        m.submodules.osd = osd = OSD()

        m.d.comb += [
            osd.x.eq(vga.o_beam_x),
            osd.y.eq(vga.o_beam_y),
            hist_color.eq(Mux((479 - osd.y) < hist.o_val[8:], 0xff, 0x00)),
            osd.i_r.eq(
                Mux(hist_view,
                    Mux((hist_chan == 0) | (hist_chan == 3), hist_color, 0),
                    Cat(Const(0, unsigned(3)), r.data[11:16]))),
            osd.i_g.eq(
                Mux(hist_view,
                    Mux((hist_chan == 1) | (hist_chan == 3), hist_color, 0),
                    Cat(Const(0, unsigned(2)), r.data[5:11]))),
            osd.i_b.eq(
                Mux(hist_view,
                    Mux((hist_chan == 2) | (hist_chan == 3), hist_color, 0),
                    Cat(Const(0, unsigned(3)), r.data[0:5]))),
            osd.on.eq(osd_on),
            osd.osd_val.eq(osd_val),
            osd.sel.eq(osd_sel),
            osd.grid.eq(grid),
            osd.border.eq(border),
            osd.roi.eq(roi.en & ~hist_view),
            osd.roi_x.eq(roi.x),
            osd.roi_y.eq(roi.y),
            osd.roi_w.eq(roi.w),
            osd.roi_h.eq(roi.h)
        ]

        # OSD control
        dummy = Signal()
        osd_vals = Array([
            ccr.offset, ccr.brightness, ccr.redness, ccr.greenness,
            ccr.blueness, sharpness, sat_en, saturation, mono_en, invert,
            thresh_en, threshold, hist_chan,
            Cat(border, grid), flip, filt_en
        ])

        with m.If(debosd.btn_down):
            m.d.sync += osd_on.eq(~osd_on)

        with m.If(osd_on):
            with m.If(debsel.btn_down):
                m.d.sync += osd_sel.eq(~osd_sel)

            with m.If(debup.btn_down):
                with m.If(~osd_sel):
                    m.d.sync += osd_val.eq(osd_val - 1)
                with m.Else():
                    with m.Switch(osd_val):
                        for i in range(len(osd_vals)):
                            with m.Case(i):
                                if (len(osd_vals[i]) == 1):
                                    m.d.sync += osd_vals[i].eq(1)
                                else:
                                    m.d.sync += osd_vals[i].eq(osd_vals[i] + 1)

            with m.If(debdown.btn_down):
                with m.If(~osd_sel):
                    m.d.sync += osd_val.eq(osd_val + 1)
                with m.Else():
                    with m.Switch(osd_val):
                        for i in range(len(osd_vals)):
                            with m.Case(i):
                                if (len(osd_vals[i]) == 1):
                                    m.d.sync += osd_vals[i].eq(0)
                                else:
                                    m.d.sync += osd_vals[i].eq(osd_vals[i] - 1)

        # Show configuration values on leds
        with m.Switch(osd_val):
            for i in range(len(osd_vals)):
                with m.Case(i):
                    m.d.comb += leds.eq(osd_vals[i])

        # Generate VGA signals
        m.d.comb += [
            vga.i_clk_en.eq(1),
            vga.i_test_picture.eq(0),
            vga.i_r.eq(osd.o_r),
            vga.i_g.eq(osd.o_g),
            vga.i_b.eq(osd.o_b),
            vga_r.eq(vga.o_vga_r),
            vga_g.eq(vga.o_vga_g),
            vga_b.eq(vga.o_vga_b),
            vga_hsync.eq(vga.o_vga_hsync),
            vga_vsync.eq(vga.o_vga_vsync),
            vga_blank.eq(vga.o_vga_blank),
        ]

        # VGA to digital video converter.
        tmds = [Signal(2) for i in range(4)]
        m.submodules.vga2dvid = vga2dvid = VGA2DVID(
            ddr=self.ddr, shift_clock_synchronizer=False)
        m.d.comb += [
            vga2dvid.i_red.eq(vga_r),
            vga2dvid.i_green.eq(vga_g),
            vga2dvid.i_blue.eq(vga_b),
            vga2dvid.i_hsync.eq(vga_hsync),
            vga2dvid.i_vsync.eq(vga_vsync),
            vga2dvid.i_blank.eq(vga_blank),
            tmds[3].eq(vga2dvid.o_clk),
            tmds[2].eq(vga2dvid.o_red),
            tmds[1].eq(vga2dvid.o_green),
            tmds[0].eq(vga2dvid.o_blue),
        ]

        # GPDI pins
        if (self.ddr):
            # Vendor specific DDR modules.
            # Convert SDR 2-bit input to DDR clocked 1-bit output (single-ended)
            # onboard GPDI.
            m.submodules.ddr0_clock = Instance("ODDRX1F",
                                               i_SCLK=ClockSignal("shift"),
                                               i_RST=0b0,
                                               i_D0=tmds[3][0],
                                               i_D1=tmds[3][1],
                                               o_Q=self.o_gpdi_dp[3])
            m.submodules.ddr0_red = Instance("ODDRX1F",
                                             i_SCLK=ClockSignal("shift"),
                                             i_RST=0b0,
                                             i_D0=tmds[2][0],
                                             i_D1=tmds[2][1],
                                             o_Q=self.o_gpdi_dp[2])
            m.submodules.ddr0_green = Instance("ODDRX1F",
                                               i_SCLK=ClockSignal("shift"),
                                               i_RST=0b0,
                                               i_D0=tmds[1][0],
                                               i_D1=tmds[1][1],
                                               o_Q=self.o_gpdi_dp[1])
            m.submodules.ddr0_blue = Instance("ODDRX1F",
                                              i_SCLK=ClockSignal("shift"),
                                              i_RST=0b0,
                                              i_D0=tmds[0][0],
                                              i_D1=tmds[0][1],
                                              o_Q=self.o_gpdi_dp[0])
        else:
            m.d.comb += [
                self.o_gpdi_dp[3].eq(tmds[3][0]),
                self.o_gpdi_dp[2].eq(tmds[2][0]),
                self.o_gpdi_dp[1].eq(tmds[1][0]),
                self.o_gpdi_dp[0].eq(tmds[0][0]),
            ]

        return m
Exemplo n.º 3
0
    def elaborate(self, platform: Platform) -> Module:
        m = Module()

        if platform:
            clk_in = platform.request(platform.default_clk, dir='-')[0]

            # Constants
            pixel_f     = self.timing.pixel_freq
            hsync_front_porch = self.timing.h_front_porch
            hsync_pulse_width = self.timing.h_sync_pulse
            hsync_back_porch  = self.timing.h_back_porch
            vsync_front_porch = self.timing.v_front_porch
            vsync_pulse_width = self.timing.v_sync_pulse
            vsync_back_porch  = self.timing.v_back_porch

            # Clock generator.
            m.domains.sync  = cd_sync  = ClockDomain("sync")
            m.domains.pixel = cd_pixel = ClockDomain("pixel")

            m.submodules.ecp5pll = pll = ECP5PLL()
            pll.register_clkin(clk_in,  platform.default_clk_frequency)
            pll.create_clkout(cd_sync,  platform.default_clk_frequency)
            pll.create_clkout(cd_pixel, pixel_f)

            platform.add_clock_constraint(cd_sync.clk,  platform.default_clk_frequency)
            platform.add_clock_constraint(cd_pixel.clk, pixel_f)

            # VGA signal generator.
            vga_r = Signal(8)
            vga_g = Signal(8)
            vga_b = Signal(8)
            vga_hsync = Signal()
            vga_vsync = Signal()
            vga_blank = Signal()

            m.submodules.vga = vga = VGA(
                resolution_x      = self.timing.x,
                hsync_front_porch = hsync_front_porch,
                hsync_pulse       = hsync_pulse_width,
                hsync_back_porch  = hsync_back_porch,
                resolution_y      = self.timing.y,
                vsync_front_porch = vsync_front_porch,
                vsync_pulse       = vsync_pulse_width,
                vsync_back_porch  = vsync_back_porch,
                bits_x            = 16, # Play around with the sizes because sometimes
                bits_y            = 16  # a smaller/larger value will make it pass timing.
            )
            with m.If(vga.o_beam_y < 240):
                m.d.comb += [
                    vga.i_r.eq(0xff),
                    vga.i_g.eq(0),
                    vga.i_b.eq(0)
                ]
            with m.Else():
                m.d.comb += [
                    vga.i_r.eq(0),
                    vga.i_g.eq(0xff),
                    vga.i_b.eq(0)
                ]
            m.d.comb += [
                vga.i_clk_en.eq(1),
                vga.i_test_picture.eq(0),
                vga_r.eq(vga.o_vga_r),
                vga_g.eq(vga.o_vga_g),
                vga_b.eq(vga.o_vga_b),
                vga_hsync.eq(vga.o_vga_hsync),
                vga_vsync.eq(vga.o_vga_vsync),
                vga_blank.eq(vga.o_vga_blank),
            ]

            vga_out = platform.request("vga")

            m.d.comb += [
                vga_out.red.eq(vga_r[4:]),
                vga_out.green.eq(vga_g[4:]),
                vga_out.blue.eq(vga_b[4:]),
                vga_out.hs.eq(vga_hsync),
                vga_out.vs.eq(vga_vsync)
            ]

            # LED blinky
            counter_width = 28
            countblink = Signal(8)
            m.submodules.blink = blink = Blink(counter_width)
            m.d.comb += [
                countblink.eq(blink.o_led),
                self.o_led[3:5].eq(countblink[6:8]),
                self.o_led[0].eq(vga_vsync),
                self.o_led[1].eq(vga_hsync),
                self.o_led[2].eq(vga_blank),
            ]

        return m
Exemplo n.º 4
0
    def elaborate(self, platform: Platform) -> Module:
        m = Module()

        if platform:
            clk_in = platform.request(platform.default_clk, dir='-')[0]

            # Constants
            pixel_f = self.timing.pixel_freq
            hsync_front_porch = self.timing.h_front_porch
            hsync_pulse_width = self.timing.h_sync_pulse
            hsync_back_porch = self.timing.h_back_porch
            vsync_front_porch = self.timing.v_front_porch
            vsync_pulse_width = self.timing.v_sync_pulse
            vsync_back_porch = self.timing.v_back_porch

            # o_wifi_gpio0 = 1 keeps board from rebooting
            # Hold btn0 to let ESP32 take control of the board.
            m.d.comb += self.o_wifi_gpio0.eq(self.i_btn[0])

            # Press btn0 to exit this bitstream.
            R_delay_reload = Signal(20, reset=0)
            with m.If(R_delay_reload[19] == 0):
                m.d.sync += R_delay_reload.eq(R_delay_reload + 1)
            m.d.comb += self.o_user_programn.eq((~self.i_btn[0])
                                                | (~R_delay_reload[19]))

            # Clock generator.
            m.domains.sync = cd_sync = ClockDomain("sync")
            m.domains.pixel = cd_pixel = ClockDomain("pixel")
            m.domains.shift = cd_shift = ClockDomain("shift")

            m.submodules.ecp5pll = pll = ECP5PLL()
            pll.register_clkin(clk_in, platform.default_clk_frequency)
            pll.create_clkout(cd_sync, platform.default_clk_frequency)
            pll.create_clkout(cd_pixel, pixel_f)
            pll.create_clkout(cd_shift,
                              pixel_f * 5.0 * (1.0 if self.ddr else 2.0))

            platform.add_clock_constraint(cd_sync.clk,
                                          platform.default_clk_frequency)
            platform.add_clock_constraint(cd_pixel.clk, pixel_f)
            platform.add_clock_constraint(
                cd_shift.clk, pixel_f * 5.0 * (1.0 if self.ddr else 2.0))

            # VGA signal generator.
            vga_r = Signal(8)
            vga_g = Signal(8)
            vga_b = Signal(8)
            vga_hsync = Signal()
            vga_vsync = Signal()
            vga_blank = Signal()

            m.submodules.vga = vga = VGA(
                resolution_x=self.timing.x,
                hsync_front_porch=hsync_front_porch,
                hsync_pulse=hsync_pulse_width,
                hsync_back_porch=hsync_back_porch,
                resolution_y=self.timing.y,
                vsync_front_porch=vsync_front_porch,
                vsync_pulse=vsync_pulse_width,
                vsync_back_porch=vsync_back_porch,
                bits_x=16,  # Play around with the sizes because sometimes
                bits_y=16  # a smaller/larger value will make it pass timing.
            )
            with m.If(vga.o_beam_y < 400):
                m.d.comb += [vga.i_r.eq(0xff), vga.i_g.eq(0), vga.i_b.eq(0)]
            with m.Else():
                m.d.comb += [vga.i_r.eq(0), vga.i_g.eq(0xff), vga.i_b.eq(0)]
            m.d.comb += [
                vga.i_clk_en.eq(1),
                vga.i_test_picture.eq(0),
                vga_r.eq(vga.o_vga_r),
                vga_g.eq(vga.o_vga_g),
                vga_b.eq(vga.o_vga_b),
                vga_hsync.eq(vga.o_vga_hsync),
                vga_vsync.eq(vga.o_vga_vsync),
                vga_blank.eq(vga.o_vga_blank),
            ]

            # VGA to digital video converter.
            tmds = [Signal(2) for i in range(4)]
            m.submodules.vga2dvid = vga2dvid = VGA2DVID(
                ddr=self.ddr, shift_clock_synchronizer=False)
            m.d.comb += [
                vga2dvid.i_red.eq(vga_r),
                vga2dvid.i_green.eq(vga_g),
                vga2dvid.i_blue.eq(vga_b),
                vga2dvid.i_hsync.eq(vga_hsync),
                vga2dvid.i_vsync.eq(vga_vsync),
                vga2dvid.i_blank.eq(vga_blank),
                tmds[3].eq(vga2dvid.o_clk),
                tmds[2].eq(vga2dvid.o_red),
                tmds[1].eq(vga2dvid.o_green),
                tmds[0].eq(vga2dvid.o_blue),
            ]

            # LED blinky
            counter_width = 28
            countblink = Signal(8)
            m.submodules.blink = blink = Blink(counter_width)
            m.d.comb += [
                countblink.eq(blink.o_led),
                self.o_led[6:8].eq(countblink[6:8]),
                self.o_led[0].eq(vga_vsync),
                self.o_led[1].eq(vga_hsync),
                self.o_led[2].eq(vga_blank),
            ]

            if (self.ddr):
                # Vendor specific DDR modules.
                # Convert SDR 2-bit input to DDR clocked 1-bit output (single-ended)
                # onboard GPDI.
                m.submodules.ddr0_clock = Instance("ODDRX1F",
                                                   i_SCLK=ClockSignal("shift"),
                                                   i_RST=0b0,
                                                   i_D0=tmds[3][0],
                                                   i_D1=tmds[3][1],
                                                   o_Q=self.o_gpdi_dp[3])
                m.submodules.ddr0_red = Instance("ODDRX1F",
                                                 i_SCLK=ClockSignal("shift"),
                                                 i_RST=0b0,
                                                 i_D0=tmds[2][0],
                                                 i_D1=tmds[2][1],
                                                 o_Q=self.o_gpdi_dp[2])
                m.submodules.ddr0_green = Instance("ODDRX1F",
                                                   i_SCLK=ClockSignal("shift"),
                                                   i_RST=0b0,
                                                   i_D0=tmds[1][0],
                                                   i_D1=tmds[1][1],
                                                   o_Q=self.o_gpdi_dp[1])
                m.submodules.ddr0_blue = Instance("ODDRX1F",
                                                  i_SCLK=ClockSignal("shift"),
                                                  i_RST=0b0,
                                                  i_D0=tmds[0][0],
                                                  i_D1=tmds[0][1],
                                                  o_Q=self.o_gpdi_dp[0])
            else:
                m.d.comb += [
                    self.o_gpdi_dp[3].eq(tmds[3][0]),
                    self.o_gpdi_dp[2].eq(tmds[2][0]),
                    self.o_gpdi_dp[1].eq(tmds[1][0]),
                    self.o_gpdi_dp[0].eq(tmds[0][0]),
                ]

        return m
Exemplo n.º 5
0
    def elaborate(self, platform):
        m = Module()

        # Get pins
        led = [platform.request("led", count) for count in range(8)]
        leds = Cat([i.o for i in led])
        led8_0 = platform.request("led8_0")
        led8_1 = platform.request("led8_1")
        led8_2 = platform.request("led8_2")
        led8_3 = platform.request("led8_3")
        led16 = [i for i in led8_0] + [i for i in led8_1]
        leds16 = Cat([i for i in led16])
        led16_2 = [i for i in led8_3] + [i for i in led8_2]
        leds16_2 = Cat([i for i in led16_2])
        clk_in = platform.request(platform.default_clk, dir='-')[0]

        # Clock generation
        # PLL - 100MHz for sdram
        sdram_freq = 100000000
        m.domains.sdram = cd_sdram = ClockDomain("sdram")
        m.domains.sdram_clk = cd_sdram_clk = ClockDomain("sdram_clk")

        m.submodules.ecp5pll = pll = ECP5PLL()
        pll.register_clkin(clk_in, platform.default_clk_frequency)
        pll.create_clkout(cd_sdram, sdram_freq)
        pll.create_clkout(cd_sdram_clk, sdram_freq, phase=180)

        # Divide clock by 8
        div = Signal(3)
        m.d.sdram += div.eq(div + 1)

        # Make sync domain 8MHz
        m.domains.sync = cd_sync = ClockDomain("sync")
        m.d.comb += ClockSignal().eq(div[2])

        # Power-on reset, used to setup SDRAM as using pll.locked does not work
        reset_cnt = Signal(5, reset=0)
        with m.If(~reset_cnt.all()):
            m.d.sync += reset_cnt.eq(reset_cnt + 1)

        # Add the SDRAM controller
        m.submodules.mem = mem = sdram_controller()

        # RAM test
        addr = Signal(24, reset=0)  # word address
        count = Signal(5, reset=0)  # width control speed of read back
        we = Signal(1, reset=0)  # request write
        oe = Signal(1, reset=0)  # request read
        read = Signal(1, reset=0)  # Set for read back phase
        err = Signal(1, reset=0)  # Set when error is detected
        passed = Signal(1, reset=0)  # Set if test passed

        m.d.comb += [
            mem.init.eq(reset_cnt == 0),  # Initialize SDRAM
            mem.sync.eq(div[2]),  # Sync with sync domain clock
            mem.address.eq(addr),
            mem.req_read.eq(oe),
            mem.req_write.eq(we),
            mem.data_in.eq(
                addr[:16]),  # Write least significant 16 bits of address
            leds16_2.eq(addr[:16]),  # Show the address on leds
            leds16.eq(mem.data_out)  # Put the data read on the debug leds
        ]

        # Set the error flag if read gives the wrong value
        with m.If((count > 0) & read & (mem.data_out != addr[:16])):
            m.d.sync += err.eq(1)

        # Increment count and do transfer when count is 0
        m.d.sync += [
            count.eq(count + 1),
            we.eq((count == 0) & ~read),
            oe.eq((count == 0) & read)
        ]

        # Increment address every other cycle for write or when
        # count is exhausted for reads
        with m.If(reset_cnt.all() & ((~read & (count == 1)) | count.all())):
            with m.If(~read & (count == 1)):
                m.d.sync += count.eq(0)

            m.d.sync += addr.eq(addr + 1)

            with m.If(addr.all()):
                # Switch to read when all data is written
                m.d.sync += read.eq(1)

                # Set passed flag when all data has been read without an error
                with m.If(read & ~err):
                    m.d.sync += passed.eq(1)

        # Show flags on the leds
        # Blue led on during write phase, green led means passed, red means error
        m.d.comb += leds.eq(Cat([err, C(0, 1), passed, ~read]))

        return m
Exemplo n.º 6
0
    def elaborate(self, platform):
        leds = Cat([platform.request("led", i) for i in range(8)])
        ov7670 = platform.request("ov7670")
        btn1 = platform.request("button_fire", 0)
        pwr = platform.request("button_pwr", 0)
        led8_2 = platform.request("led8_2")
        led8_3 = platform.request("led8_3")
        led16 = [i for i in led8_2] + [i for i in led8_3]
        leds16 = Cat([i for i in led16])

        clk_in = platform.request(platform.default_clk, dir='-')[0]

        m = Module()

        # Clock generation
        # PLL - 100MHz for sdram
        sdram_freq = 100000000
        m.domains.sdram = cd_sdram = ClockDomain("sdram")
        m.domains.sdram_clk = cd_sdram_clk = ClockDomain("sdram_clk")

        m.submodules.ecp5pll = pll = ECP5PLL()
        pll.register_clkin(clk_in, platform.default_clk_frequency)
        pll.create_clkout(cd_sdram, sdram_freq)
        pll.create_clkout(cd_sdram_clk, sdram_freq, phase=180)

        # Divide clock by 4
        div = Signal(3)
        m.d.sdram += div.eq(div + 1)

        # Power-on reset
        reset_cnt = Signal(5, reset=0)
        with m.If(~reset_cnt.all()):
            m.d.sdram += reset_cnt.eq(reset_cnt + 1)

        # Make sync domain 25MHz
        m.domains.sync = cd_sync = ClockDomain("sync")
        #m.d.comb += ResetSignal().eq(~reset.all() | pwr)
        m.d.comb += ClockSignal().eq(div[1])

        # Add the SDRAM controller
        m.submodules.mem = mem = sdram_controller()

        # Add CamRead submodule
        camread = CamRead()
        m.submodules.camread = camread

        # Add ST7789 submodule
        st7789 = ST7789(150000)
        m.submodules.st7789 = st7789

        # OLED
        oled_clk = platform.request("oled_clk")
        oled_mosi = platform.request("oled_mosi")
        oled_dc = platform.request("oled_dc")
        oled_resn = platform.request("oled_resn")
        oled_csn = platform.request("oled_csn")

        cnt = Signal(26)
        m.d.sync += cnt.eq(cnt + 1)

        # Camera config
        camconfig = CamConfig()
        m.submodules.camconfig = camconfig

        m.d.comb += [
            oled_clk.eq(st7789.spi_clk),
            oled_mosi.eq(st7789.spi_mosi),
            oled_dc.eq(st7789.spi_dc),
            oled_resn.eq(st7789.spi_resn),
            oled_csn.eq(1),
            ov7670.cam_RESET.eq(1),
            ov7670.cam_PWON.eq(0),
            ov7670.cam_XCLK.eq(div[1]),
            camread.p_data.eq(Cat([ov7670.cam_data[i] for i in range(8)])),
            camread.href.eq(ov7670.cam_HREF),
            camread.vsync.eq(ov7670.cam_VSYNC),
            camread.p_clock.eq(ov7670.cam_PCLK),
            st7789.color.eq(mem.data_out),
            camconfig.start.eq(btn1),
            ov7670.cam_SIOC.eq(camconfig.sioc),
            ov7670.cam_SIOD.eq(camconfig.siod),
        ]

        pixel_valid2 = Signal()
        pixel_valid = Signal()

        m.d.sync += [pixel_valid2.eq(camread.pixel_valid)]

        m.d.comb += pixel_valid.eq(camread.pixel_valid | pixel_valid2)

        raddr = Signal(24)
        waddr = Signal(24)
        sync = Signal()

        # Write to SDRAM
        m.d.comb += [
            sync.eq(~div[2]),
            raddr.eq(((239 - st7789.x) * 320) + st7789.y),
            waddr.eq((camread.row[1:] * 320) + camread.col[1:]),
            mem.init.eq(reset_cnt == 0),
            mem.sync.eq(sync),  # Sync with 25MHz clock
            mem.address.eq(Mux(st7789.next_pixel, raddr, waddr)),
            mem.data_in.eq(camread.pixel_data),
            mem.req_read.eq(
                sync & st7789.next_pixel),  # Always read when pixel requested
            mem.req_write.eq(sync & ~st7789.next_pixel
                             & pixel_valid),  # Delay write one cycle if needed
            leds16.eq(mem.data_out)
        ]

        with m.If(camread.frame_done):
            m.d.sync += leds.eq(leds + 1)

        return m
Exemplo n.º 7
0
    def elaborate(self, platform: Platform) -> Module:
        m = Module()

        if platform:
            clk_in = platform.request(platform.default_clk, dir='-')[0]
            leds = Cat([platform.request("led", i) for i in range(8)])
            btn = Cat([platform.request("button", i) for i in range(6)])
            pwr = platform.request("button_pwr")
            usb = platform.request("usb")
            ps2_pullup = platform.request("ps2_pullup")
            stereo = platform.request("stereo", 0)
            led8_0 = platform.request("led8_0")
            leds8_0 = Cat([led8_0.leds[i] for i in range(8)])
            led8_1 = platform.request("led8_1")
            leds8_1 = Cat([led8_1.leds[i] for i in range(8)])
            leds16 = Cat(leds8_0, leds8_1)
            led8_2 = platform.request("led8_2")
            leds8_2 = Cat([led8_2.leds[i] for i in range(8)])
            led8_3 = platform.request("led8_3")
            leds8_3 = Cat([led8_3.leds[i] for i in range(8)])
            leds16_2 = Cat(leds8_3, leds8_2)

            esp32 = platform.request("esp32_spi")
            csn = esp32.csn
            sclk = esp32.sclk
            copi = esp32.copi
            cipo = esp32.cipo
            irq = esp32.irq

            # Constants
            pixel_f = self.timing.pixel_freq
            hsync_front_porch = self.timing.h_front_porch
            hsync_pulse_width = self.timing.h_sync_pulse
            hsync_back_porch = self.timing.h_back_porch
            vsync_front_porch = self.timing.v_front_porch
            vsync_pulse_width = self.timing.v_sync_pulse
            vsync_back_porch = self.timing.v_back_porch

            # Clock generator.
            m.domains.sync = cd_sync = ClockDomain("sync")
            m.domains.pixel = cd_pixel = ClockDomain("pixel")
            m.domains.shift = cd_shift = ClockDomain("shift")

            m.submodules.ecp5pll = pll = ECP5PLL()
            pll.register_clkin(clk_in, platform.default_clk_frequency)
            pll.create_clkout(cd_sync, platform.default_clk_frequency)
            pll.create_clkout(cd_pixel, pixel_f)
            pll.create_clkout(cd_shift,
                              pixel_f * 5.0 * (1.0 if self.ddr else 2.0))

            platform.add_clock_constraint(cd_sync.clk,
                                          platform.default_clk_frequency)
            platform.add_clock_constraint(cd_pixel.clk, pixel_f)
            platform.add_clock_constraint(
                cd_shift.clk, pixel_f * 5.0 * (1.0 if self.ddr else 2.0))

            m.domains.ph1 = ph1 = ClockDomain("ph1")
            m.domains.ph2 = ph2 = ClockDomain("ph2")

            # CPU clock domains
            clk_freq = platform.default_clk_frequency
            timer = Signal(range(0, int(7)), reset=int(7 - 1))
            tick = Signal()
            sync = ClockDomain()

            cpu_control = Signal(8)
            spi_load = Signal()
            pia_cra = Signal(8)
            pia_dra = Signal(8)
            pia_crb = Signal(8)
            pia_drb = Signal(8)
            r_btn = Signal(6)

            m.d.sync += r_btn.eq(btn)

            with m.If(timer == 0):
                m.d.sync += timer.eq(timer.reset)
                m.d.sync += tick.eq(~tick)
            with m.Else():
                m.d.sync += timer.eq(timer - 1)
            m.d.comb += [
                ph1.rst.eq(sync.rst),
                ph2.rst.eq(sync.rst),
                ph1.clk.eq(tick),
                ph2.clk.eq(~tick),
            ]

            # Add CPU
            cpu = Core()
            m.submodules += cpu

            # VGA signal generator.
            vga_r = Signal(8)
            vga_g = Signal(8)
            vga_b = Signal(8)
            vga_hsync = Signal()
            vga_vsync = Signal()
            vga_blank = Signal()
            r_vsync = Signal()

            # Save previous value of vsync
            m.d.sync += r_vsync.eq(vga_vsync)

            # Vsync sets IRQ
            with m.If(vga_vsync & ~r_vsync):
                m.d.sync += cpu.IRQ.eq(1)

            # Reading PIA Data Register B clears the interrupt
            with m.If(cpu.RW & (cpu.Addr == 0x2002)):
                m.d.sync += cpu.IRQ.eq(0)

            m.submodules.vga = vga = VGA(
                resolution_x=self.timing.x,
                hsync_front_porch=hsync_front_porch,
                hsync_pulse=hsync_pulse_width,
                hsync_back_porch=hsync_back_porch,
                resolution_y=self.timing.y,
                vsync_front_porch=vsync_front_porch,
                vsync_pulse=vsync_pulse_width,
                vsync_back_porch=vsync_back_porch,
                bits_x=16,  # Play around with the sizes because sometimes
                bits_y=16  # a smaller/larger value will make it pass timing.
            )

            # Use 1Kb of RAM
            ram = Memory(width=8, depth=1024)
            m.submodules.dr = dr = ram.read_port()
            m.submodules.vr = vr = ram.read_port()
            m.submodules.dw = dw = ram.write_port()

            # And 2kb of ROM
            rom_data = readhex("roms/apf_4000.mem")

            rom = Memory(width=8, depth=2048, init=rom_data)
            m.submodules.rr = rr = rom.read_port()

            # And 8kb of cartridge rom
            cart = Memory(width=8, depth=8192)
            m.submodules.cr = cr = rom.read_port()
            m.submodules.cw = cw = rom.write_port()

            # Add SpiRamBtn for OSD control
            m.submodules.rambtn = rambtn = SpiRamBtn()

            # Add PS/2 keyboard controller
            m.submodules.ps2 = ps2 = PS2()

            # Add character rom and video controller
            font = readhex("roms/charrom.mem")

            charrom = Memory(width=8, depth=512, init=font)
            m.submodules.fr = fr = charrom.read_port()

            m.submodules.video = video = Video()

            m.d.comb += [
                # Connect rambtn
                rambtn.csn.eq(~csn),
                rambtn.sclk.eq(sclk),
                rambtn.copi.eq(copi),
                rambtn.btn.eq(Cat(~pwr, btn)),
                cipo.eq(rambtn.cipo),
                irq.eq(~rambtn.irq),
                # Connect memory
                dr.addr.eq(cpu.Addr),
                rambtn.din.eq(vr.data),
                cw.data.eq(rambtn.dout),
                cw.addr.eq(rambtn.addr),
                cw.en.eq(rambtn.wr & (rambtn.addr[24:] == 0)),
                rr.addr.eq(cpu.Addr),
                cpu.Din.eq(
                    Mux(
                        cpu.Addr == 0xffff,
                        0x00,
                        Mux(
                            cpu.Addr == 0xfffe,
                            0x40,  # reset vector
                            Mux(
                                cpu.Addr == 0xfff9,
                                0xA4,
                                Mux(
                                    cpu.Addr == 0xfff8,
                                    0x42,  # irq vector
                                    Mux(
                                        cpu.Addr[13:] == 0,
                                        dr.data,
                                        Mux(
                                            cpu.Addr[13:] == 2,
                                            rr.data,  # ram or system rom
                                            Mux(
                                                cpu.Addr[14:] == 1,
                                                cr.data,
                                                Mux(
                                                    cpu.Addr == 0x2002,
                                                    pia_drb,
                                                    Mux(
                                                        cpu.Addr ==
                                                        0x2000,  # PIA_DRA has keyboard row
                                                        Mux(
                                                            pia_drb[:4] == C(
                                                                0b1110, 4),
                                                            Cat([
                                                                ~r_btn[0],
                                                                Repl(1, 7)
                                                            ]),
                                                            Mux(
                                                                pia_drb[:4] ==
                                                                C(0b1101, 4),
                                                                Cat([
                                                                    Repl(1, 1),
                                                                    ~r_btn[5],
                                                                    Repl(1, 1),
                                                                    ~r_btn[4],
                                                                    Repl(1, 1),
                                                                    ~r_btn[5],
                                                                    Repl(1, 1),
                                                                    ~r_btn[4]
                                                                ]),
                                                                Mux(
                                                                    pia_drb[:4]
                                                                    == C(
                                                                        0b1011,
                                                                        4),
                                                                    Cat([
                                                                        Repl(
                                                                            1,
                                                                            8)
                                                                    ]),
                                                                    Mux(
                                                                        pia_drb[:
                                                                                4]
                                                                        == C(
                                                                            0b0111,
                                                                            4),
                                                                        Cat([
                                                                            Repl(
                                                                                1,
                                                                                1
                                                                            ),
                                                                            ~r_btn[
                                                                                2],
                                                                            Repl(
                                                                                1,
                                                                                2
                                                                            ),
                                                                            ~r_btn[
                                                                                1:
                                                                                3],
                                                                            Repl(
                                                                                1,
                                                                                2
                                                                            )
                                                                        ]),
                                                                        0xff)))
                                                        ),
                                                        0xff)))))))))),
                dw.addr.eq(cpu.Addr),
                dw.data.eq(cpu.Dout),
                dw.en.eq(~cpu.RW & cpu.VMA & (cpu.Addr[13:] == 0)),
                vr.addr.eq(Mux(spi_load, rambtn.addr, video.c_addr)),
                # PS/2 keyboard
                usb.pullup.eq(1),
                ps2_pullup.eq(1),
                ps2.ps2_clk.eq(usb.d_p),
                ps2.ps2_data.eq(usb.d_n),
                spi_load.eq(cpu_control[1]),
                stereo.r.eq(stereo.l)
            ]

            # CPU control from ESP32
            with m.If(rambtn.wr & (rambtn.addr[24:] == 0xFF)):
                m.d.sync += cpu_control.eq(rambtn.dout)

            # PIA control and data registers
            with m.If(~cpu.RW & cpu.VMA & cpu.Addr[13]):
                with m.Switch(cpu.Addr[:2]):
                    with m.Case(0):
                        m.d.sync += pia_dra.eq(cpu.Dout)
                    with m.Case(1):
                        m.d.sync += pia_cra.eq(cpu.Dout)
                    with m.Case(2):
                        m.d.sync += pia_drb.eq(cpu.Dout)
                    with m.Case(3):
                        m.d.sync += pia_crb.eq(cpu.Dout)
                        m.d.sync += stereo.l.eq(Mux(cpu.Dout[3], 0x7, 0x0))

            mode = Signal(2)

            with m.If(~cpu.RW & (cpu.Addr == 0x1fc) & (cpu.Dout != 0)):
                m.d.sync += mode.eq(0b11)

            # OSD
            m.submodules.osd = osd = SpiOsd(start_x=62,
                                            start_y=80,
                                            chars_x=64,
                                            chars_y=20)

            m.d.comb += [
                # Connect video
                video.x.eq(vga.o_beam_x),
                video.y.eq(vga.o_beam_y),
                video.din.eq(vr.data),
                video.fin.eq(fr.data),
                video.mode.eq(mode),
                fr.addr.eq(video.f_addr),
                # Connect osd
                osd.i_csn.eq(~csn),
                osd.i_sclk.eq(sclk),
                osd.i_copi.eq(copi),
                osd.clk_ena.eq(1),
                osd.i_hsync.eq(vga.o_vga_hsync),
                osd.i_vsync.eq(vga.o_vga_vsync),
                osd.i_blank.eq(vga.o_vga_blank),
                osd.i_r.eq(video.r),
                osd.i_g.eq(video.g),
                osd.i_b.eq(video.b),
                # led diagnostics
                leds.eq(pia_drb),
                leds16_2.eq(cpu.sp),
                leds16.eq(cpu.Addr)
            ]

            m.d.comb += [
                vga.i_clk_en.eq(1),
                vga.i_test_picture.eq(0),
                vga_r.eq(osd.o_r),
                vga_g.eq(osd.o_g),
                vga_b.eq(osd.o_b),
                vga_hsync.eq(osd.o_hsync),
                vga_vsync.eq(osd.o_vsync),
                vga_blank.eq(osd.o_blank),
            ]

            # VGA to digital video converter.
            tmds = [Signal(2) for i in range(4)]
            m.submodules.vga2dvid = vga2dvid = VGA2DVID(
                ddr=self.ddr, shift_clock_synchronizer=False)
            m.d.comb += [
                vga2dvid.i_red.eq(vga_r),
                vga2dvid.i_green.eq(vga_g),
                vga2dvid.i_blue.eq(vga_b),
                vga2dvid.i_hsync.eq(vga_hsync),
                vga2dvid.i_vsync.eq(vga_vsync),
                vga2dvid.i_blank.eq(vga_blank),
                tmds[3].eq(vga2dvid.o_clk),
                tmds[2].eq(vga2dvid.o_red),
                tmds[1].eq(vga2dvid.o_green),
                tmds[0].eq(vga2dvid.o_blue),
            ]

            if (self.ddr):
                # Vendor specific DDR modules.
                # Convert SDR 2-bit input to DDR clocked 1-bit output (single-ended)
                # onboard GPDI.
                m.submodules.ddr0_clock = Instance("ODDRX1F",
                                                   i_SCLK=ClockSignal("shift"),
                                                   i_RST=0b0,
                                                   i_D0=tmds[3][0],
                                                   i_D1=tmds[3][1],
                                                   o_Q=self.o_gpdi_dp[3])
                m.submodules.ddr0_red = Instance("ODDRX1F",
                                                 i_SCLK=ClockSignal("shift"),
                                                 i_RST=0b0,
                                                 i_D0=tmds[2][0],
                                                 i_D1=tmds[2][1],
                                                 o_Q=self.o_gpdi_dp[2])
                m.submodules.ddr0_green = Instance("ODDRX1F",
                                                   i_SCLK=ClockSignal("shift"),
                                                   i_RST=0b0,
                                                   i_D0=tmds[1][0],
                                                   i_D1=tmds[1][1],
                                                   o_Q=self.o_gpdi_dp[1])
                m.submodules.ddr0_blue = Instance("ODDRX1F",
                                                  i_SCLK=ClockSignal("shift"),
                                                  i_RST=0b0,
                                                  i_D0=tmds[0][0],
                                                  i_D1=tmds[0][1],
                                                  o_Q=self.o_gpdi_dp[0])
            else:
                m.d.comb += [
                    self.o_gpdi_dp[3].eq(tmds[3][0]),
                    self.o_gpdi_dp[2].eq(tmds[2][0]),
                    self.o_gpdi_dp[1].eq(tmds[1][0]),
                    self.o_gpdi_dp[0].eq(tmds[0][0]),
                ]

        return m
Exemplo n.º 8
0
    def elaborate(self, platform):
        # VGA constants
        pixel_f           = self.timing.pixel_freq
        hsync_front_porch = self.timing.h_front_porch
        hsync_pulse_width = self.timing.h_sync_pulse
        hsync_back_porch  = self.timing.h_back_porch
        vsync_front_porch = self.timing.v_front_porch
        vsync_pulse_width = self.timing.v_sync_pulse
        vsync_back_porch  = self.timing.v_back_porch

        # Pins
        clk25   = platform.request("clk25")
        ov7670  = platform.request("ov7670")
        led     = [platform.request("led", i) for i in range(8)]
        leds    = Cat([i.o for i in led])
        led8_2  = platform.request("led8_2")
        leds8_2 = Cat([led8_2.leds[i] for i in range(8)])
        led8_3  = platform.request("led8_3")
        leds8_3 = Cat([led8_3.leds[i] for i in range(8)])
        leds16  = Cat(leds8_3, leds8_2)
        btn1    = platform.request("button_fire", 0)
        btn2    = platform.request("button_fire", 1)
        up      = platform.request("button_up", 0)
        down    = platform.request("button_down", 0)
        pwr     = platform.request("button_pwr", 0)
        left    = platform.request("button_left", 0)
        right   = platform.request("button_right", 0)
        sw      =  Cat([platform.request("switch",i) for i in range(4)])
        uart    = platform.request("uart")
        divisor = int(platform.default_clk_frequency // 460800)
        esp32   = platform.request("esp32_spi")

        csn     = esp32.csn
        sclk    = esp32.sclk
        copi    = esp32.copi
        cipo    = esp32.cipo

        m = Module()
        
        # Clock generator.
        m.domains.sync  = cd_sync  = ClockDomain("sync")
        m.domains.pixel = cd_pixel = ClockDomain("pixel")
        m.domains.shift = cd_shift = ClockDomain("shift")

        m.submodules.ecp5pll = pll = ECP5PLL()
        pll.register_clkin(clk25,  platform.default_clk_frequency)
        pll.create_clkout(cd_sync,  platform.default_clk_frequency)
        pll.create_clkout(cd_pixel, pixel_f)
        pll.create_clkout(cd_shift, pixel_f * 5.0 * (1.0 if self.ddr else 2.0))

        # Add CamRead submodule
        camread = CamRead()
        m.submodules.camread = camread

        # Camera config
        cam_x_res = 640
        cam_y_res = 480

        camconfig = CamConfig()
        m.submodules.camconfig = camconfig

        # Connect the camera pins and config and read modules
        m.d.comb += [
            ov7670.cam_RESET.eq(1),
            ov7670.cam_PWON.eq(0),
            ov7670.cam_XCLK.eq(clk25.i),
            ov7670.cam_SIOC.eq(camconfig.sioc),
            ov7670.cam_SIOD.eq(camconfig.siod),
            camconfig.start.eq(btn1),
            camread.p_data.eq(Cat([ov7670.cam_data[i] for i in range(8)])),
            camread.href.eq(ov7670.cam_HREF),
            camread.vsync.eq(ov7670.cam_VSYNC),
            camread.p_clock.eq(ov7670.cam_PCLK)
        ]

        # Create the uart
        m.submodules.serial = serial = AsyncSerial(divisor=divisor, pins=uart)

        # Input fifo
        fifo_depth=1024

        m.submodules.fifo = fifo = SyncFIFOBuffered(width=16,depth=fifo_depth)

        # Frame buffer
        x_res= cam_x_res // 2
        y_res= cam_y_res

        buffer = Memory(width=16, depth=x_res * y_res)
        m.submodules.r = r = buffer.read_port()
        m.submodules.w = w = buffer.write_port()

        # Button debouncers
        m.submodules.debup   = debup = Debouncer()
        m.submodules.debdown = debdown = Debouncer()
        m.submodules.debosd  = debosd = Debouncer()
        m.submodules.debsel  = debsel = Debouncer()
        m.submodules.debsnap = debsnap = Debouncer()
        m.submodules.debhist = debhist = Debouncer()

        # Connect the buttons to debouncers
        m.d.comb += [
            debup.btn.eq(up),
            debdown.btn.eq(down),
            debosd.btn.eq(pwr),
            debsel.btn.eq(right),
            debsnap.btn.eq(left),
            debhist.btn.eq(btn2)
        ]

        # Image processing options
        flip        = Signal(2, reset=1)
        mono        = Signal(reset=0)
        invert      = Signal(reset=0)
        gamma       = Signal(reset=0)
        border      = Signal(reset=0)
        filt        = Signal(reset=0)
        grid        = Signal(reset=0)
        histo       = Signal(reset=1)
        hbin        = Signal(6, reset=0)
        bin_cnt     = Signal(5, reset=0)
        thresh      = Signal(reset=0)
        threshold   = Signal(8, reset=0)
        hist_chan   = Signal(2, reset=0)

        ccc         = CC(reset=(0,18,12,16))
        sharpness   = Signal(unsigned(4), reset=0)

        osd_val     = Signal(4, reset=0) # Account for spurious start-up button pushes
        osd_on      = Signal(reset=1)
        osd_sel     = Signal(reset=1)
        snap        = Signal(reset=0)
        frozen      = Signal(reset=1)
        writing     = Signal(reset=0)
        written     = Signal(reset=0)
        byte        = Signal(reset=0)
        w_addr      = Signal(18)

        # Color filter
        l           = Rgb565(reset=(18,12,6)) # Initialised to red LEGO filter
        h           = Rgb565(reset=(21,22,14))

        # Region of interest
        roi         = Roi()

        # VGA signals
        vga_r       = Signal(8)
        vga_g       = Signal(8)
        vga_b       = Signal(8)
        vga_hsync   = Signal()
        vga_vsync   = Signal()
        vga_blank   = Signal()

        # Fifo co-ordinates
        f_x          = Signal(9)
        f_y          = Signal(9)
        f_frame_done = Signal()

        # Pixel from fifo
        pix         = Rgb565()

        # SPI memory for remote configuration
        m.submodules.spimem = spimem = SpiMem(addr_bits=32)

        # Color Control
        m.submodules.cc = cc = ColorControl()

        # Image convolution
        m.submodules.imc = imc = ImageConv()

        # Statistics
        m.submodules.stats = stats = Stats()

        # Histogram
        m.submodules.hist = hist = Hist()

        # Filter
        m.submodules.fil = fil = Filt()

        # Monochrome
        m.submodules.mon = mon = Mono()

        # Sync the fifo with the camera
        sync_fifo = Signal(reset=0)
        with m.If(~sync_fifo & ~fifo.r_rdy & (camread.col == cam_x_res - 1) & (camread.row == cam_y_res -1)):
            m.d.sync += [
                sync_fifo.eq(1),
                f_x.eq(0),
                f_y.eq(0)
            ]

        with m.If(btn1):
            m.d.sync += sync_fifo.eq(0)

        # Connect the fifo
        m.d.comb += [
            fifo.w_en.eq(camread.pixel_valid & camread.col[0] & sync_fifo), # Only write every other pixel
            fifo.w_data.eq(camread.pixel_data), 
            fifo.r_en.eq(fifo.r_rdy & ~imc.o_stall)
        ]

        # Calculate fifo co-ordinates
        m.d.sync += f_frame_done.eq(0)

        with m.If(fifo.r_en & sync_fifo):
            m.d.sync += f_x.eq(f_x + 1)
            with m.If(f_x == x_res - 1):
                m.d.sync += [
                    f_x.eq(0),
                    f_y.eq(f_y + 1)
                ]
                with m.If(f_y == y_res - 1):
                    m.d.sync += [
                        f_y.eq(0),
                        f_frame_done.eq(1)
                    ]
        
        # Extract pixel from fifo data
        m.d.comb += [
            pix.r.eq(fifo.r_data[11:]),
            pix.g.eq(fifo.r_data[5:11]),
            pix.b.eq(fifo.r_data[:5])
        ]

        # Connect color control
        m.d.comb += [
            cc.i.eq(pix),
            cc.i_cc.eq(ccc)
        ]

        # Calculate per-frame statistics, after applying color correction
        m.d.comb += [
            stats.i.eq(cc.o),
            stats.i_valid.eq(fifo.r_rdy),
            # This is not valid when a region of interest is active
            stats.i_avg_valid.eq((f_x >= 32) & (f_x < 288) &
                                 (f_y >= 112) & (f_y < 368)),
            stats.i_frame_done.eq(f_frame_done),
            stats.i_x.eq(f_x),
            stats.i_y.eq(f_y),
            stats.i_roi.eq(roi)
        ]
        
        # Produce histogram, after applying color correction, and after monochrome, for monochrome histogram
        with m.Switch(hist_chan):
            with m.Case(0):
                m.d.comb += hist.i_p.eq(cc.o.r)
            with m.Case(1):
                m.d.comb += hist.i_p.eq(cc.o.g)
            with m.Case(2):
                m.d.comb += hist.i_p.eq(cc.o.b)
            with m.Case(3):
                m.d.comb += hist.i_p.eq(mon.o_m)

        m.d.comb += [
            hist.i_valid.eq(fifo.r_rdy),
            hist.i_clear.eq(f_frame_done),
            hist.i_x.eq(f_x),
            hist.i_y.eq(f_y),
            hist.i_roi.eq(roi),
            hist.i_bin.eq(hbin) # Used when displaying histogram
        ]

        # Apply filter, after color correction
        m.d.comb += [
            fil.i.eq(cc.o),
            fil.i_valid.eq(fifo.r_en),
            fil.i_en.eq(filt),
            fil.i_frame_done.eq(f_frame_done),
            fil.i_l.eq(l),
            fil.i_h.eq(h)
        ]

        # Apply mono, after color correction and filter
        m.d.comb += [
            mon.i.eq(fil.o),
            mon.i_en.eq(mono),
            mon.i_invert.eq(invert),
            mon.i_thresh.eq(thresh),
            mon.i_threshold.eq(threshold)
        ]

        # Apply image convolution, after other transformations
        m.d.comb += [
            imc.i.eq(mon.o),
            imc.i_valid.eq(fifo.r_rdy),
            imc.i_reset.eq(~sync_fifo),
            # Select image convolution
            imc.i_sel.eq(sharpness)
        ]

        # Take a snapshot, freeze the camera, and write the framebuffer to the uart
        # Note that this suspends video output
        with m.If(debsnap.btn_down | (spimem.wr & (spimem.addr == 22))):
            with m.If(frozen):
                m.d.sync += frozen.eq(0)
            with m.Else():
                m.d.sync += [
                    snap.eq(1),
                    frozen.eq(0),
                    w_addr.eq(0),
                    written.eq(0),
                    byte.eq(0)
                ]

        # Wait to end of frame after requesting snapshot, before start of writing to uart
        with m.If(imc.o_frame_done & snap):
            m.d.sync += [
                frozen.eq(1),
                snap.eq(0)
            ]
            with m.If(~written):
                m.d.sync += writing.eq(1)

        # Connect the uart
        m.d.comb += [
            serial.tx.data.eq(Mux(byte, r.data[8:], r.data[:8])),
            serial.tx.ack.eq(writing)
        ]

        # Write to the uart from frame buffer (affects video output)
        with m.If(writing):
            with m.If(w_addr == x_res * y_res):
                m.d.sync += [
                    writing.eq(0),
                    written.eq(1)
                ]
            with m.Elif(serial.tx.ack & serial.tx.rdy):
                m.d.sync += byte.eq(~byte)
                with m.If(byte):
                    m.d.sync += w_addr.eq(w_addr+1)

        # Connect spimem
        m.d.comb += [
            spimem.csn.eq(~csn),
            spimem.sclk.eq(sclk),
            spimem.copi.eq(copi),
            cipo.eq(spimem.cipo),
        ]

        # Writable configuration registers
        spi_wr_vals = Array([ccc.brightness, ccc.redness, ccc.greenness, ccc.blueness, l.r, h.r, l.g, h.g, l.b, h.b,
                             sharpness, filt, border, mono, invert, grid, histo,
                             roi.x[1:], roi.y[1:], roi.w[1:], roi.h[1:], roi.en, None, None, None,
                             threshold, thresh, hist_chan, flip,
                             None, None, None, None, None, None, None, None, None,
                             frozen])

        with m.If(spimem.wr):
            with m.Switch(spimem.addr):
                for i in range(len(spi_wr_vals)):
                    if spi_wr_vals[i] is not None:
                        with m.Case(i):
                            m.d.sync += spi_wr_vals[i].eq(spimem.dout)

        # Readable configuration registers
        spi_rd_vals = Array([ccc.brightness, ccc.redness, ccc.greenness, ccc.blueness, l.r, h.r, l.g, h.g, l.b, h.b,
                             sharpness, filt, border, mono, invert, grid, histo,
                             roi.x[1:], roi.y[1:], roi.w[1:], roi.h[1:], roi.en, fil.o_nz[16:], fil.o_nz[8:16], fil.o_nz[:8],
                             threshold, thresh, hist_chan, flip,
                             stats.o_min.r, stats.o_min.g, stats.o_min.b,
                             stats.o_max.r, stats.o_max.g, stats.o_max.b,
                             stats.o_avg.r, stats.o_avg.g, stats.o_avg.b,
                             frozen, writing, written])

        with m.If(spimem.rd):
            with m.Switch(spimem.addr):
                for i in range(len(spi_rd_vals)):
                    with m.Case(i):
                        m.d.sync += spimem.din.eq(spi_rd_vals[i])

        # Add VGA generator
        m.submodules.vga = vga = VGA(
           resolution_x      = self.timing.x,
           hsync_front_porch = hsync_front_porch,
           hsync_pulse       = hsync_pulse_width,
           hsync_back_porch  = hsync_back_porch,
           resolution_y      = self.timing.y,
           vsync_front_porch = vsync_front_porch,
           vsync_pulse       = vsync_pulse_width,
           vsync_back_porch  = vsync_back_porch,
           bits_x            = 16, # Play around with the sizes because sometimes
           bits_y            = 16  # a smaller/larger value will make it pass timing.
        )

        # Fetch histogram for display
        old_x = Signal(10)
        m.d.sync += old_x.eq(vga.o_beam_x)

        with m.If(vga.o_beam_x == 0):
            m.d.sync += [
                hbin.eq(0),
                bin_cnt.eq(0)
            ]
        with m.Elif(vga.o_beam_x != old_x):
            m.d.sync += bin_cnt.eq(bin_cnt+1)
            with m.If(bin_cnt == 19):
                m.d.sync += [
                    bin_cnt.eq(0),
                    hbin.eq(hbin+1)
                ]

        # Switch between camera and histogram view
        with m.If(debhist.btn_down):
            m.d.sync += histo.eq(~histo)
       
        # Connect frame buffer, with optional x and y flip
        x = Signal(10)
        y = Signal(9)
        
        m.d.comb += [
            w.en.eq(imc.o_valid & ~frozen),
            w.addr.eq(imc.o_y * x_res + imc.o_x),
            w.data.eq(Cat(imc.o.b, imc.o.g, imc.o.r)),
            y.eq(Mux(flip[1], y_res - 1 - vga.o_beam_y, vga.o_beam_y)),
            x.eq(Mux(flip[0], x_res - 1 - vga.o_beam_x[1:], vga.o_beam_x[1:])),
            r.addr.eq(Mux(writing, w_addr, y * x_res + x))
        ]

        # Apply the On-Screen Display (OSD)
        m.submodules.osd = osd = OSD()

        hist_col = Signal(8)

        m.d.comb += [
            osd.x.eq(vga.o_beam_x),
            osd.y.eq(vga.o_beam_y),
            hist_col.eq(Mux((479 - osd.y) < hist.o_val[8:], 0xff, 0x00)),
            osd.i_r.eq(Mux(histo, Mux((hist_chan == 0) | (hist_chan == 3), hist_col, 0), Cat(Const(0, unsigned(3)), r.data[11:16]))),
            osd.i_g.eq(Mux(histo, Mux((hist_chan == 1) | (hist_chan == 3), hist_col, 0), Cat(Const(0, unsigned(2)), r.data[5:11]))),
            osd.i_b.eq(Mux(histo, Mux((hist_chan == 2) | (hist_chan == 3), hist_col, 0), Cat(Const(0, unsigned(3)), r.data[0:5]))),
            osd.on.eq(osd_on),
            osd.osd_val.eq(osd_val),
            osd.sel.eq(osd_sel),
            osd.grid.eq(grid),
            osd.border.eq(border),
            osd.roi.eq(roi.en & ~histo),
            osd.roi_x.eq(roi.x),
            osd.roi_y.eq(roi.y),
            osd.roi_w.eq(roi.w),
            osd.roi_h.eq(roi.h)
        ]

        # OSD control
        osd_vals = Array([ccc.brightness, ccc.redness, ccc.greenness, ccc.blueness, mono, flip[0], flip[1],
                          border, sharpness, invert, grid, filt])

        with m.If(debosd.btn_down):
            m.d.sync += osd_on.eq(~osd_on)

        with m.If(osd_on):
            with m.If(debsel.btn_down):
                m.d.sync += osd_sel.eq(~osd_sel)

            with m.If(debup.btn_down):
                with m.If(~osd_sel):
                    m.d.sync += osd_val.eq(Mux(osd_val == 0, 11, osd_val-1))
                with m.Else():
                    with m.Switch(osd_val):
                        for i in range(len(osd_vals)):
                            with m.Case(i):
                                if (len(osd_vals[i]) == 1):
                                    m.d.sync += osd_vals[i].eq(1)
                                else:
                                    m.d.sync += osd_vals[i].eq(osd_vals[i]+1)

            with m.If(debdown.btn_down):
                with m.If(~osd_sel):
                    m.d.sync += osd_val.eq(Mux(osd_val == 11, 0, osd_val+1))
                with m.Else():
                    with m.Switch(osd_val):
                        for i in range(len(osd_vals)):
                            with m.Case(i):
                                if (len(osd_vals[i]) == 1):
                                    m.d.sync += osd_vals[i].eq(0)
                                else:
                                    m.d.sync += osd_vals[i].eq(osd_vals[i]-1)

        # Show configuration values on leds
        with m.Switch(osd_val):
            for i in range(len(osd_vals)):
                with m.Case(i):
                    m.d.comb += leds.eq(osd_vals[i])

        # Generate VGA signals
        m.d.comb += [
            vga.i_clk_en.eq(1),
            vga.i_test_picture.eq(0),
            vga.i_r.eq(osd.o_r), 
            vga.i_g.eq(osd.o_g), 
            vga.i_b.eq(osd.o_b), 
            vga_r.eq(vga.o_vga_r),
            vga_g.eq(vga.o_vga_g),
            vga_b.eq(vga.o_vga_b),
            vga_hsync.eq(vga.o_vga_hsync),
            vga_vsync.eq(vga.o_vga_vsync),
            vga_blank.eq(vga.o_vga_blank),
        ]

        # VGA to digital video converter.
        tmds = [Signal(2) for i in range(4)]
        m.submodules.vga2dvid = vga2dvid = VGA2DVID(ddr=self.ddr, shift_clock_synchronizer=False)
        m.d.comb += [
            vga2dvid.i_red.eq(vga_r),
            vga2dvid.i_green.eq(vga_g),
            vga2dvid.i_blue.eq(vga_b),
            vga2dvid.i_hsync.eq(vga_hsync),
            vga2dvid.i_vsync.eq(vga_vsync),
            vga2dvid.i_blank.eq(vga_blank),
            tmds[3].eq(vga2dvid.o_clk),
            tmds[2].eq(vga2dvid.o_red),
            tmds[1].eq(vga2dvid.o_green),
            tmds[0].eq(vga2dvid.o_blue),
        ]

        # GPDI pins
        if (self.ddr):
            # Vendor specific DDR modules.
            # Convert SDR 2-bit input to DDR clocked 1-bit output (single-ended)
            # onboard GPDI.
            m.submodules.ddr0_clock = Instance("ODDRX1F",
                i_SCLK = ClockSignal("shift"),
                i_RST  = 0b0,
                i_D0   = tmds[3][0],
                i_D1   = tmds[3][1],
                o_Q    = self.o_gpdi_dp[3])
            m.submodules.ddr0_red   = Instance("ODDRX1F",
                i_SCLK = ClockSignal("shift"),
                i_RST  = 0b0,
                i_D0   = tmds[2][0],
                i_D1   = tmds[2][1],
                o_Q    = self.o_gpdi_dp[2])
            m.submodules.ddr0_green = Instance("ODDRX1F",
                i_SCLK = ClockSignal("shift"),
                i_RST  = 0b0,
                i_D0   = tmds[1][0],
                i_D1   = tmds[1][1],
                o_Q    = self.o_gpdi_dp[1])
            m.submodules.ddr0_blue  = Instance("ODDRX1F",
                i_SCLK = ClockSignal("shift"),
                i_RST  = 0b0,
                i_D0   = tmds[0][0],
                i_D1   = tmds[0][1],
                o_Q    = self.o_gpdi_dp[0])
        else:
            m.d.comb += [
                self.o_gpdi_dp[3].eq(tmds[3][0]),
                self.o_gpdi_dp[2].eq(tmds[2][0]),
                self.o_gpdi_dp[1].eq(tmds[1][0]),
                self.o_gpdi_dp[0].eq(tmds[0][0]),
            ]

        return m