enjoy-digital / litex

Build your hardware, easily!
Other
3k stars 569 forks source link

16-bit mode video: SoC builder fails #1198

Closed suarezvictor closed 2 years ago

suarezvictor commented 2 years ago

When using "rgb565" the SoC builder fails

Example code for BaseSoC:

        if with_video_framebuffer:
            from litex.soc.cores.video import VideoVGAPHY
            self.submodules.videophy = VideoVGAPHY(platform.request("vga"), clock_domain="vga")
            self.add_video_framebuffer(phy=self.videophy, timings="800x600@60Hz", clock_domain="vga", format="rgb565")

Produces the following error (only if format is set to "rgb565"):

Traceback (most recent call last):
  File "./digilent_arty_video.py", line 210, in <module>
    main()
  File "./digilent_arty_video.py", line 203, in main
    builder.build(**builder_kwargs, run=args.build)
  File "/home/vsuarez/litex/litex/soc/integration/builder.py", line 336, in build
    vns = self.soc.build(build_dir=self.gateware_dir, **kwargs)
  File "/home/vsuarez/litex/litex/soc/integration/soc.py", line 1145, in build
    return self.platform.build(self, *args, **kwargs)
  File "/home/vsuarez/litex/litex/build/xilinx/platform.py", line 65, in build
    return self.toolchain.build(self, *args, **kwargs)
  File "/home/vsuarez/litex/litex/build/xilinx/vivado.py", line 349, in build
    v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
  File "/home/vsuarez/litex/litex/build/xilinx/platform.py", line 59, in get_verilog
    **kwargs)
  File "/home/vsuarez/litex/litex/build/generic_platform.py", line 420, in get_verilog
    return verilog.convert(fragment, platform=self, **kwargs)
  File "/home/vsuarez/litex/litex/gen/fhdl/verilog.py", line 602, in convert
    verilog += _print_combinatorial_logic_synth(f, ns)
  File "/home/vsuarez/litex/litex/gen/fhdl/verilog.py", line 471, in _print_combinatorial_logic_synth
    r += _print_node(ns, _AT_NONBLOCKING, 1, g[1])
  File "/home/vsuarez/litex/litex/gen/fhdl/verilog.py", line 287, in _print_node
    return "".join(_print_node(ns, at, level, n, target_filter) for n in node)
  File "/home/vsuarez/litex/litex/gen/fhdl/verilog.py", line 287, in <genexpr>
    return "".join(_print_node(ns, at, level, n, target_filter) for n in node)
  File "/home/vsuarez/litex/litex/gen/fhdl/verilog.py", line 292, in _print_node
    r += _print_node(ns, at, level + 1, node.t, target_filter)
  File "/home/vsuarez/litex/litex/gen/fhdl/verilog.py", line 287, in _print_node
    return "".join(_print_node(ns, at, level, n, target_filter) for n in node)
  File "/home/vsuarez/litex/litex/gen/fhdl/verilog.py", line 287, in <genexpr>
    return "".join(_print_node(ns, at, level, n, target_filter) for n in node)
  File "/home/vsuarez/litex/litex/gen/fhdl/verilog.py", line 283, in _print_node
    return "\t"*level + _print_expression(ns, node.l)[0] + assignment + _print_expression(ns, node.r)[0] + ";\n"
  File "/home/vsuarez/litex/litex/gen/fhdl/verilog.py", line 249, in _print_expression
    return _print_slice(ns, node)
  File "/home/vsuarez/litex/litex/gen/fhdl/verilog.py", line 212, in _print_slice
    assert (node.stop - node.start) >= 1
AssertionError
enjoy-digital commented 2 years ago

@suarezvictor: Thanks for reporting, the issue seems specific to your configuration since I don't seem to be able to reproduce it on other targets. Can you share your adaptations to the Arty platform/target to allow me to reproduce the issue?

enjoy-digital commented 2 years ago

@suarezvictor: Can you provide the adapations done to be able to reproduce?

suarezvictor commented 2 years ago

yes, this is the full script tested with latest version of litex It works with "rgb888" but not with "rgb565"

#!/usr/bin/env python3

#
# This file is part of LiteX-Boards.
#
# Copyright (c) 2015-2019 Florent Kermarrec <florent@enjoy-digital.fr>
# Copyright (c) 2020 Antmicro <www.antmicro.com>
# SPDX-License-Identifier: BSD-2-Clause

import os
import argparse

from migen import *

from litex_boards.platforms import arty
from litex.build.xilinx.vivado import vivado_build_args, vivado_build_argdict

from litex.soc.cores.clock import *
from litex.soc.integration.soc import SoCRegion
from litex.soc.integration.soc_core import *
from litex.soc.integration.builder import *
from litex.soc.cores.led import LedChaser
from litex.soc.cores.gpio import GPIOTristate

from litedram.modules import MT41K128M16
from litedram.phy import s7ddrphy

from liteeth.phy.mii import LiteEthPHYMII

from litespi.modules import S25FL128L
from litespi.opcodes import SpiNorFlashOpCodes as Codes
from litespi.phy.generic import LiteSPIPHY
from litespi import LiteSPI

"""
##https://github.com/projf/projf-explore/blob/master/graphics/fpga-graphics/xc7/arty.xdc
## Project F: FPGA Graphics - Arty A7-35T Board Constraints
## (C)2021 Will Green, open source hardware released under the MIT License
## VGA Pmod on Header JB/JC
##("pmodb", "E15 E16 D15 C15 J17 J18 K15 J15"),
##("pmodc", "U12 V12 V10 V11 U14 V14 T13 U13"),

set_property -dict {PACKAGE_PIN E15 IOSTANDARD LVCMOS33} [get_ports {vga_r[0]}];
set_property -dict {PACKAGE_PIN E16 IOSTANDARD LVCMOS33} [get_ports {vga_r[1]}];
set_property -dict {PACKAGE_PIN D15 IOSTANDARD LVCMOS33} [get_ports {vga_r[2]}];
set_property -dict {PACKAGE_PIN C15 IOSTANDARD LVCMOS33} [get_ports {vga_r[3]}];
set_property -dict {PACKAGE_PIN J17 IOSTANDARD LVCMOS33} [get_ports {vga_b[0]}];
set_property -dict {PACKAGE_PIN J18 IOSTANDARD LVCMOS33} [get_ports {vga_b[1]}];
set_property -dict {PACKAGE_PIN K15 IOSTANDARD LVCMOS33} [get_ports {vga_b[2]}];
set_property -dict {PACKAGE_PIN J15 IOSTANDARD LVCMOS33} [get_ports {vga_b[3]}];

set_property -dict {PACKAGE_PIN U12 IOSTANDARD LVCMOS33} [get_ports {vga_g[0]}];
set_property -dict {PACKAGE_PIN V12 IOSTANDARD LVCMOS33} [get_ports {vga_g[1]}];
set_property -dict {PACKAGE_PIN V10 IOSTANDARD LVCMOS33} [get_ports {vga_g[2]}];
set_property -dict {PACKAGE_PIN V11 IOSTANDARD LVCMOS33} [get_ports {vga_g[3]}];
set_property -dict {PACKAGE_PIN U14 IOSTANDARD LVCMOS33} [get_ports {vga_hsync}];
set_property -dict {PACKAGE_PIN V14 IOSTANDARD LVCMOS33} [get_ports {vga_vsync}];
"""
# CRG ----------------------------------------------------------------------------------------------

class _CRG(Module):
    def __init__(self, platform, sys_clk_freq, with_rst=True):
        self.rst = Signal()
        self.clock_domains.cd_sys       = ClockDomain()
        self.clock_domains.cd_sys4x     = ClockDomain(reset_less=True)
        self.clock_domains.cd_sys4x_dqs = ClockDomain(reset_less=True)
        self.clock_domains.cd_idelay    = ClockDomain()
        self.clock_domains.cd_eth       = ClockDomain()
        self.clock_domains.cd_vga       = ClockDomain(reset_less=True)

        # # #

        self.submodules.pll = pll = S7PLL(speedgrade=-1)
        rst    = ~platform.request("cpu_reset") if with_rst else 0
        self.comb += pll.reset.eq(rst | self.rst)
        pll.register_clkin(platform.request("clk100"), 100e6)
        pll.create_clkout(self.cd_sys,       sys_clk_freq)
        pll.create_clkout(self.cd_sys4x,     4*sys_clk_freq)
        pll.create_clkout(self.cd_sys4x_dqs, 4*sys_clk_freq, phase=90)
        pll.create_clkout(self.cd_idelay,    200e6)
        pll.create_clkout(self.cd_eth,       25e6)
        pll.create_clkout(self.cd_vga, 25e6, margin=1e-3)
        platform.add_false_path_constraints(self.cd_sys.clk, pll.clkin) # Ignore sys_clk to pll.clkin path created by SoC's rst.

        self.submodules.idelayctrl = S7IDELAYCTRL(self.cd_idelay)

        self.comb += platform.request("eth_ref_clk").eq(self.cd_eth.clk)

# BaseSoC ------------------------------------------------------------------------------------------

class BaseSoC(SoCCore):
    def __init__(self, variant="a7-35", toolchain="vivado", sys_clk_freq=int(100e6), with_ethernet=False, with_etherbone=False, eth_ip="192.168.1.50", eth_dynamic_ip=False, with_jtagbone=True, with_mapped_flash=False, with_spi_flash = False,
        with_pmod_gpio = False, with_led_chaser = True, **kwargs):
        platform = arty.Platform(variant=variant, toolchain=toolchain)

        # SoCCore ----------------------------------------------------------------------------------
        SoCCore.__init__(self, platform, sys_clk_freq, **kwargs)

        # CRG --------------------------------------------------------------------------------------
        self.submodules.crg = _CRG(platform, sys_clk_freq, False) #, with_mapped_flash)

        # DDR3 SDRAM -------------------------------------------------------------------------------
        if not self.integrated_main_ram_size:
            self.submodules.ddrphy = s7ddrphy.A7DDRPHY(platform.request("ddram"),
                memtype        = "DDR3",
                nphases        = 4,
                sys_clk_freq   = sys_clk_freq)
            self.add_sdram("sdram",
                phy           = self.ddrphy,
                module        = MT41K128M16(sys_clk_freq, "1:4"),
                l2_cache_size = kwargs.get("l2_size", 8192)
            )

        # Ethernet / Etherbone ---------------------------------------------------------------------
        if with_ethernet or with_etherbone:
            self.submodules.ethphy = LiteEthPHYMII(
                clock_pads = self.platform.request("eth_clocks"),
                pads       = self.platform.request("eth"))
            if with_ethernet:
                self.add_ethernet(phy=self.ethphy, dynamic_ip=eth_dynamic_ip)
            if with_etherbone:
                self.add_etherbone(phy=self.ethphy, ip_address=eth_ip)

        # Jtagbone ---------------------------------------------------------------------------------
        if with_jtagbone:
            self.add_jtagbone()

        # Flash (through LiteSPI, experimental).
        if with_mapped_flash:
            self.submodules.spiflash_phy  = LiteSPIPHY(platform.request("spiflash4x"), S25FL128L(Codes.READ_1_1_4))
            self.submodules.spiflash_mmap = LiteSPI(self.spiflash_phy, clk_freq=sys_clk_freq, mmap_endianness=self.cpu.endianness)
            spiflash_region = SoCRegion(origin=self.mem_map.get("spiflash", None), size=S25FL128L.total_size, cached=False)
            self.bus.add_slave(name="spiflash", slave=self.spiflash_mmap.bus, region=spiflash_region)

        # SPI Flash --------------------------------------------------------------------------------
        if with_spi_flash:
            from litespi.modules import S25FL128L
            from litespi.opcodes import SpiNorFlashOpCodes as Codes
            self.add_spi_flash(mode="4x", module=S25FL128L(Codes.READ_1_1_4), with_master=True)

        # Leds -------------------------------------------------------------------------------------
        if with_led_chaser:
            self.submodules.leds = LedChaser(
                pads         = platform.request_all("user_led"),
                sys_clk_freq = sys_clk_freq)

        # GPIOs ------------------------------------------------------------------------------------
        if with_pmod_gpio:
            platform.add_extension(arty.raw_pmod_io("pmoda"))
            self.submodules.gpio = GPIOTristate(platform.request("pmoda"))

        """
        if with_spi:
            #SPI pins and GPIO pins cannot be the same
            platform.add_extension([("spi0", 0,
                #Subsignal("cs_n", Pins("A3")), #A3=JP1:6
                #Subsignal("clk",  Pins("D3")), #D3=JP1:2
                #Subsignal("mosi", Pins("C3")), #C3=JP1:4
                #Subsignal("miso", Pins("A2")), #A2=JP1:5
                Subsignal("cs_n", Pins("A10")), #A10=ADC CS
                Subsignal("clk",  Pins("B14")), #B14=ADC CLK
                Subsignal("mosi", Pins("B10")), #B10=ADC SADDR
                Subsignal("miso", Pins("A9")),  # A9=ADC SDAT
                IOStandard("3.3-V LVTTL"))])

            self.add_spi_port(name="spi0");
        """
        with_video_framebuffer = True
        if with_video_framebuffer:
            from litex.build.generic_platform import Pins, IOStandard, Subsignal
            platform.add_extension([("vga", 0, #PMOD VGA on pmod B & C
                Subsignal("hsync", Pins("U14")), #pmodc.4
                Subsignal("vsync", Pins("V14")), #pmodc.5
                Subsignal("r", Pins("E15 E16 D15 C15")), #pmodb.0-3
                Subsignal("g", Pins("U12 V12 V10 V11")), #pmodc.0-3
                Subsignal("b", Pins("J17 J18 K15 J15")), #pmodb.4-7
                IOStandard("LVCMOS33"))])

            from litex.soc.cores.video import VideoVGAPHY
            self.submodules.videophy = VideoVGAPHY(platform.request("vga"), clock_domain="vga")
            self.add_video_framebuffer(phy=self.videophy, timings="640x480@60Hz", clock_domain="vga", format="rgb565")

        #DMA
        with_dma = True
        if with_dma:
            from litex.soc.cores.dma import WishboneDMAReader
            from litex.soc.interconnect import wishbone
            dma_width = 32 #FIXME read memory width
            self.submodules.dma_reader = WishboneDMAReader(endianness="big", bus=wishbone.Interface(data_width=dma_width), with_csr=True)
            self.bus.add_master("dma_reader", self.dma_reader.bus)
            if True: #needed in Arty
                from litex.soc.cores.dma import WishboneDMAWriter
                self.submodules.dma_writer = WishboneDMAWriter(endianness="big", bus=wishbone.Interface(data_width=dma_width), with_csr=True)
                self.bus.add_master("dma_writer", self.dma_writer.bus)
            else:
                from litedram.frontend.dma import LiteDRAMDMAWriter
                port = self.sdram.crossbar.get_port("write")
                self.submodules.dma_writer = LiteDRAMDMAWriter(port, fifo_depth=16, with_csr=True)

            self.comb += self.dma_reader.source.connect(self.dma_writer.sink) #Connect Reader to Writer

# Build --------------------------------------------------------------------------------------------

def main():
    parser = argparse.ArgumentParser(description="LiteX SoC on Arty A7")
    parser.add_argument("--toolchain",           default="vivado",                 help="Toolchain use to build (default: vivado)")
    parser.add_argument("--build",               action="store_true",              help="Build bitstream")
    parser.add_argument("--load",                action="store_true",              help="Load bitstream")
    parser.add_argument("--variant",             default="a7-35",                  help="Board variant: a7-35 (default) or a7-100")
    parser.add_argument("--sys-clk-freq",        default=100e6,                    help="System clock frequency (default: 100MHz)")
    ethopts = parser.add_mutually_exclusive_group()
    ethopts.add_argument("--with-ethernet",      action="store_true",              help="Enable Ethernet support")
    ethopts.add_argument("--with-etherbone",     action="store_true",              help="Enable Etherbone support")
    parser.add_argument("--eth-ip",              default="192.168.1.50", type=str, help="Ethernet/Etherbone IP address")
    parser.add_argument("--eth-dynamic-ip",      action="store_true",              help="Enable dynamic Ethernet IP addresses setting")
    sdopts = parser.add_mutually_exclusive_group()
    sdopts.add_argument("--with-spi-sdcard",     action="store_true",              help="Enable SPI-mode SDCard support")
    sdopts.add_argument("--with-sdcard",         action="store_true",              help="Enable SDCard support")
    parser.add_argument("--sdcard-adapter",      type=str,                         help="SDCard PMOD adapter: digilent (default) or numato")
    parser.add_argument("--with-jtagbone",       action="store_true",              help="Enable Jtagbone support")
    parser.add_argument("--with-mapped-flash",   action="store_true",              help="Enable Memory Mapped Flash")
    parser.add_argument("--with-spi-flash",      action="store_true",              help="Enable SPI Flash (MMAPed)")
    parser.add_argument("--with-pmod-gpio",      action="store_true",              help="Enable GPIOs through PMOD") # FIXME: Temporary test.
    builder_args(parser)
    soc_core_args(parser)
    vivado_build_args(parser)
    args = parser.parse_args()

    assert not (args.with_etherbone and args.eth_dynamic_ip)

    soc = BaseSoC(
        variant        = args.variant,
        toolchain      = args.toolchain,
        sys_clk_freq   = int(float(args.sys_clk_freq)),
        with_ethernet  = args.with_ethernet,
        with_etherbone = args.with_etherbone,
        eth_ip         = args.eth_ip,
        eth_dynamic_ip = args.eth_dynamic_ip,
        with_jtagbone  = args.with_jtagbone,
        with_mapped_flash = args.with_mapped_flash,
        with_spi_flash = args.with_spi_flash,
        with_pmod_gpio = args.with_pmod_gpio,
        **soc_core_argdict(args)
    )
    if args.sdcard_adapter == "numato":
        soc.platform.add_extension(arty._numato_sdcard_pmod_io)
    else:
        soc.platform.add_extension(arty._sdcard_pmod_io)
    if args.with_spi_sdcard:
        soc.add_spi_sdcard()
    if args.with_sdcard:
        soc.add_sdcard()
    builder = Builder(soc, **builder_argdict(args))
    builder_kwargs = vivado_build_argdict(args) if args.toolchain == "vivado" else {}
    builder.build(**builder_kwargs, run=args.build)

    if args.load:
        prog = soc.platform.create_programmer()
        prog.load_bitstream(os.path.join(builder.gateware_dir, soc.build_name + ".bit"))

if __name__ == "__main__":
    main()
enjoy-digital commented 2 years ago

@suarezvictor: Thanks for reporting and sorry for the delay, this is fixed with https://github.com/enjoy-digital/litex/issues/1198. The issue was that the depth checks were done in FPGA logic, not in Python and this was generating invalid logic. I haven't noticed this when merging the 16-bit support PR. This should now be fine (at least compiles fine).