diff --git a/litex_boards/platforms/qmtech_kintex7_devboard.py b/litex_boards/platforms/qmtech_kintex7_devboard.py new file mode 100644 index 0000000..fdb6f89 --- /dev/null +++ b/litex_boards/platforms/qmtech_kintex7_devboard.py @@ -0,0 +1,171 @@ +# +# This file is part of LiteX-Boards. +# +# Copyright (c) 2023 Kazumoto Kojima +# Copyright (c) 2023 Hans Baier +# Copyright (c) 2023 Ruurd Keizer +# +# SPDX-License-Identifier: BSD-2-Clause + +from litex.build.generic_platform import Pins, Subsignal, IOStandard, Misc +from litex.build.xilinx import XilinxPlatform +from litex.build.openocd import OpenOCD + +# IOs ---------------------------------------------------------------------------------------------- + +_io = [ + # Clk / Rst + ("clk50", 0, Pins("F22"), IOStandard("LVCMOS33")), + ("cpu_reset", 0, Pins("U26"), IOStandard("LVCMOS33")), + + # Switches + ("sw2", 0, Pins("U26"), IOStandard("LVCMOS33")), # cpu_reset + ("sw3", 0, Pins("V26"), IOStandard("LVCMOS33")), + + # Leds + ("user_led", 0, Pins("R26"), IOStandard("LVCMOS33")), + ("user_led", 1, Pins("P26"), IOStandard("LVCMOS33")), + ("user_led", 2, Pins("N26"), IOStandard("LVCMOS33")), + + # The board does not have a USB serial connected to the FPGA, + # so you will have to either connect through the rpi uart through gpio pins (and use serial uart hw or software), + # or attach an USB to serial adapter on JP5 pins (default) + ("JP5_serial", 0, + Subsignal("tx", Pins("JP5:7")), + Subsignal("rx", Pins("JP5:8")), + IOStandard("LVCMOS33") + ), + #("gpio_serial", 0, + # Subsignal("tx", Pins("GPIO:14")), + # Subsignal("rx", Pins("GPIO:15")), + # IOStandard("LVCMOS33") + # ) + + # SPIFlash + # S25FL128L + ("spiflash4x", 0, # clock needs to be accessed through STARTUPE2 + Subsignal("cs_n", Pins("C23")), + Subsignal("clk", Pins("C8")), + Subsignal("dq", Pins("B24", "A25", "B22", "A22")), + IOStandard("LVCMOS33") + ), + + # DDR3 SDRAM + # MT41J128M16JT-125K + ("ddram", 0, + Subsignal("a", Pins("AF5 AF2 AD6 AC6 AD4 AB6 AE2 Y5 AA4 AE6 AE3 AD5 AB4 Y6"), + IOStandard("SSTL15")), + Subsignal("ba", Pins("AD3 AE1 AE5"), IOStandard("SSTL15")), + Subsignal("ras_n", Pins("AC3"), IOStandard("SSTL15")), + Subsignal("cas_n", Pins("AC4"), IOStandard("SSTL15")), + Subsignal("we_n", Pins("AF4"), IOStandard("SSTL15")), + #Subsignal("cs_n", Pins("--"), IOStandard("SSTL15")), + Subsignal("dm", Pins("V1 V3"), IOStandard("SSTL15")), + Subsignal("dq", Pins( + "W1 V2 Y1 Y3 AC2 Y2 AB2 AA3", + "U1 V4 U6 W3 V6 U2 U7 U5"), + IOStandard("SSTL15")), # _T_DCI")), + + Subsignal("dqs_p", Pins("AB1 W6"), IOStandard("DIFF_SSTL15")), # _T_DCI")), + Subsignal("dqs_n", Pins("AC1 W5"), IOStandard("DIFF_SSTL15")), # _T_DCI")), + + Subsignal("clk_p", Pins("AA5"), IOStandard("DIFF_SSTL15")), + Subsignal("clk_n", Pins("AB5"), IOStandard("DIFF_SSTL15")), + + Subsignal("cke", Pins("AD1"), IOStandard("SSTL15")), + Subsignal("odt", Pins("AF3"), IOStandard("SSTL15")), + Subsignal("reset_n", Pins("W4"), IOStandard("LVCMOS15")), + Misc("SLEW=FAST"), + ), + +# ("csi", 0, +# Subsignal("csi", Pins("")), +# IOStandard("LVCMOS33") +# ), +] + +_connectors = [ + # 25x2 header + ("JP5", { + # odd row even row + 5: "AD21", 6: "AE21", + 7: "AE22", 8: "AF22", + 9: "AE23", 10: "AF23", + 11: "V21", 12: "W21", + 13: "Y22", 14: "AA22", + 15: "AF24", 16: "AF25", + 17: "AB21", 18: "AC21", + 19: "AB22", 20: "AC22", + 21: "AD23", 22: "AD24", + 23: "AC23", 24: "AC24", + 25: "AD25", 26: "AE25", + 27: "AA23", 28: "AB24", + 29: "AA25", 30: "AB25", + 31: "Y23", 32: "AA24", + 33: "AD26", 34: "AE26", + 35: "AB26", 36: "AC26", + 37: "W23", 38: "W24", + 39: "Y25", 40: "Y26", + 41: "W25", 42: "W26", + 43: "V23", 44: "V24", + 45: "U24", 46: "U25", + }), + # PMOD_1 + ("J11", { + 1: "C16", 7: "B16", + 2: "A17", 8: "B17", + 3: "A18", 9: "A19", + 4: "A20", 10: "B20", + }), + # PMOD_2 + ("J12", { + 1: "E21", 7: "E22", + 2: "D23", 8: "D24", + 3: "D25", 9: "E25", + 4: "F23", 10: "F24", + }), + # PMOD_3 + ("J13", { + 1: "A24", 7: "A23", + 2: "B26", 8: "B25", + 3: "D26", 9: "C26", + 4: "F25", 10: "E26", + }), + # defining the pins to the rpi's GPIO as virtual connector - signals will still depend on gpio functions + ("GPIO", { + 0: "C12", 1: "B11", 2: "C18", 3: "D18", 4: "E18", 5: "C11", 6: "D10", 7: "B12", 8: "A12", 9: "D14", + 10: "C13", 11: "D13", 12: "A10", 13: "E10", 14: "C17", 15: "A15", 16: "B10", 17: "D16", 18: "B15", 19: "B9", + 20: "A9", 21: "A8", 22: "C14", 23: "A14", 24: "B14", 25: "A13", 26: "C9", 27: "D15", + }) +] + +# Platform ----------------------------------------------------------------------------------------- + +class Platform(XilinxPlatform): + default_clk_name = "clk50" + default_clk_period = 1e9/50e6 + + def __init__(self, toolchain="vivado"): + device = "xc7k325tffg676-1" + io = _io + connectors = _connectors + + XilinxPlatform.__init__(self, device, io, connectors, toolchain=toolchain) + + self.toolchain.bitstream_commands = \ + ["set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]"] + self.toolchain.additional_commands = \ + ["write_cfgmem -force -format bin -interface spix4 -size 16 " + "-loadbit \"up 0x0 {build_name}.bit\" -file {build_name}.bin"] + + self.add_platform_command("set_property INTERNAL_VREF 0.750 [get_iobanks 34]") + self.add_platform_command("set_property INTERNAL_VREF 0.90 [get_iobanks 33]") + + def create_programmer(self): + bscan_spi = "bscan_spi_xc7k325t.bit" + return OpenOCD("openocd_xc7_ft2232.cfg", bscan_spi) + + + def do_finalize(self, fragment): + XilinxPlatform.do_finalize(self, fragment) + self.add_period_constraint(self.lookup_request("clk50", loose=True), 1e9/50e6) diff --git a/litex_boards/targets/qmtech_kintex7_devboard.py b/litex_boards/targets/qmtech_kintex7_devboard.py new file mode 100644 index 0000000..eb56415 --- /dev/null +++ b/litex_boards/targets/qmtech_kintex7_devboard.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python3 +# +# This file is part of LiteX-Boards. +# +# Copyright (c) 2023 Kazumoto Kojima +# Copyright (c) 2023 Hans Baier +# Copyright (c) 2023 Ruurd Keizer +# +# SPDX-License-Identifier: BSD-2-Clause + +import os +import argparse + +from migen import * + +from litex_boards.platforms import qmtech_kintex7_devboard +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.video import VideoVGAPHY +from litex.soc.cores.led import LedChaser + +from litedram.modules import MT41J128M16 +from litedram.phy import s7ddrphy + +from liteeth.phy.mii import LiteEthPHYMII + +# CRG ---------------------------------------------------------------------------------------------- + +class _CRG(Module): + def __init__(self, platform, sys_clk_freq, with_ethernet, with_vga): + 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() + if with_ethernet: + self.clock_domains.cd_eth = ClockDomain() + if with_vga: + self.clock_domains.cd_vga = ClockDomain(reset_less=True) + + # # # + + self.submodules.pll = pll = S7PLL(speedgrade=-1) + reset_button = platform.request("cpu_reset") + self.comb += pll.reset.eq(~reset_button | self.rst) + + pll.register_clkin(platform.request("clk50"), 50e6) + 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) + if with_ethernet: + pll.create_clkout(self.cd_eth, 25e6) + if with_vga: + pll.create_clkout(self.cd_vga, 40e6) + + 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) + +# BaseSoC ------------------------------------------------------------------------------------------ + +class BaseSoC(SoCCore): + def __init__(self, toolchain="vivado", sys_clk_freq=int(100e6), + with_ethernet=False, with_etherbone=False, eth_ip="192.168.1.50", eth_dynamic_ip=False, + local_ip="", remote_ip="", + with_led_chaser=True, with_video_terminal=False, with_video_framebuffer=False, with_video_colorbars=False, + with_spi_flash=False, **kwargs): + platform = qmtech_kintex7_devboard.Platform(toolchain=toolchain) + + # SoCCore ---------------------------------------------------------------------------------- + print(f"{str(kwargs)}") + if (kwargs["uart_name"] == "serial"): + kwargs["uart_name"] = "JP5_serial" + + SoCCore.__init__(self, platform, sys_clk_freq, + ident = "LiteX SoC on QMTech Kintex 7 Development board", + **kwargs) + + # CRG -------------------------------------------------------------------------------------- + self.submodules.crg = _CRG(platform, sys_clk_freq, with_ethernet or with_etherbone, with_video_terminal or with_video_framebuffer or with_video_colorbars) + + # DDR3 SDRAM ------------------------------------------------------------------------------- + if not self.integrated_main_ram_size: + from litedram.common import PHYPadsReducer + self.submodules.ddrphy = s7ddrphy.A7DDRPHY( + pads = PHYPadsReducer(platform.request("ddram", 0), [0, 1]), + memtype = "DDR3", + nphases = 4, + sys_clk_freq = sys_clk_freq) + self.add_sdram("sdram", + phy = self.ddrphy, + module = MT41J128M16(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) + # The daughterboard has the tx clock wired to a non-clock pin, so we can't help it + #self.platform.add_platform_command("set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets eth_clocks_tx_IBUF]") + self.add_constant("TARGET_BIOS_INIT", 1) + + if local_ip: + local_ip = local_ip.split(".") + self.add_constant("LOCALIP1", int(local_ip[0])) + self.add_constant("LOCALIP2", int(local_ip[1])) + self.add_constant("LOCALIP3", int(local_ip[2])) + self.add_constant("LOCALIP4", int(local_ip[3])) + + if remote_ip: + remote_ip = remote_ip.split(".") + self.add_constant("REMOTEIP1", int(remote_ip[0])) + self.add_constant("REMOTEIP2", int(remote_ip[1])) + self.add_constant("REMOTEIP3", int(remote_ip[2])) + self.add_constant("REMOTEIP4", int(remote_ip[3])) + + # SPI Flash -------------------------------------------------------------------------------- + if with_spi_flash: + from litespi.modules import MT25QL128 + from litespi.opcodes import SpiNorFlashOpCodes as Codes + self.add_spi_flash(mode="4x", module=MT25QL128(Codes.READ_1_1_1), with_master=True) + + # Video ------------------------------------------------------------------------------------ + if with_video_terminal or with_video_framebuffer or with_video_colorbars: + self.submodules.videophy = VideoVGAPHY(platform.request("vga"), clock_domain="vga") + if with_video_terminal: + self.add_video_terminal(phy=self.videophy, timings="800x600@60Hz", clock_domain="vga") + if with_video_framebuffer: + self.add_video_framebuffer(phy=self.videophy, timings="800x600@60Hz", clock_domain="vga", format="rgb565") + if with_video_colorbars: + self.add_video_colorbars(phy=self.videophy, timings="800x600@60Hz", clock_domain="vga") + + # Leds ------------------------------------------------------------------------------------- + if with_led_chaser: + self.submodules.leds = LedChaser( + pads = platform.request_all("user_led"), + sys_clk_freq = sys_clk_freq) + + if kwargs["uart_name"] == "serial": + kwargs["uart_name"] = "jtag_serial" + +# Build -------------------------------------------------------------------------------------------- + +def main(): + parser = argparse.ArgumentParser(description="LiteX SoC on QMTech XC7K325T") + parser.add_argument("--toolchain", default="vivado", help="FPGA toolchain (vivado, symbiflow or yosys+nextpnr).") + parser.add_argument("--build", action="store_true", help="Build bitstream.") + parser.add_argument("--load", action="store_true", help="Load bitstream.") + parser.add_argument("--sys-clk-freq", default=100e6, help="System clock frequency.") + 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.") + parser.add_argument("--remote-ip", default="192.168.1.100", + help="Remote IP address of TFTP server.") + parser.add_argument("--local-ip", default="192.168.1.50", + help="Local IP address.") + 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("--with-spi-flash", action="store_true", help="Enable SPI Flash (MMAPed).") + viopts = parser.add_mutually_exclusive_group() + viopts.add_argument("--with-video-terminal", action="store_true", help="Enable Video Terminal (VGA).") + viopts.add_argument("--with-video-framebuffer", action="store_true", help="Enable Video Framebuffer (VGA).") + viopts.add_argument("--with-video-colorbars", action="store_true", help="Enable Video Colorbars (VGA).") + builder_args(parser) + soc_core_args(parser) + vivado_build_args(parser) + args = parser.parse_args() + + soc = BaseSoC( + 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, + local_ip = args.local_ip, + remote_ip = args.remote_ip, + with_spi_flash = args.with_spi_flash, + with_video_terminal = args.with_video_terminal, + with_video_framebuffer = args.with_video_framebuffer, + with_video_colorbars = args.with_video_colorbars, + **soc_core_argdict(args) + ) + + if args.with_spi_sdcard: + soc.add_spi_sdcard() + if args.with_sdcard: + soc.add_sdcard() + + builder = Builder(soc, **builder_argdict(args)) + if args.with_ethernet or args.with_etherbone: + os.makedirs(os.path.join(builder.software_dir, "include/generated"), + exist_ok=True) + write_to_file( + os.path.join(builder.software_dir, "include/generated", "target.h"), + "// Force 100Base-T speed\n" + "#define TARGET_ETHPHY_INIT_FUNC() mdio_write(0, 0, 0x2100)") + + builder_kwargs = vivado_build_argdict(args) if args.toolchain == "vivado" else {} + if args.build: + builder.build(**builder_kwargs) + + 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()