From 0125ae4271900a2a73153bc1740a3f59b0867b14 Mon Sep 17 00:00:00 2001 From: Hans Baier Date: Mon, 20 Mar 2023 08:21:36 +0700 Subject: [PATCH] Add support for QMTech Artix7 200T FBG484 board --- .../platforms/qmtech_artix7_fbg484.py | 181 ++++++++++++++++ litex_boards/targets/qmtech_artix7_fbg484.py | 200 ++++++++++++++++++ 2 files changed, 381 insertions(+) create mode 100644 litex_boards/platforms/qmtech_artix7_fbg484.py create mode 100644 litex_boards/targets/qmtech_artix7_fbg484.py diff --git a/litex_boards/platforms/qmtech_artix7_fbg484.py b/litex_boards/platforms/qmtech_artix7_fbg484.py new file mode 100644 index 0000000..26b4f36 --- /dev/null +++ b/litex_boards/platforms/qmtech_artix7_fbg484.py @@ -0,0 +1,181 @@ +# +# This file is part of LiteX-Boards. +# +# Copyright (c) 2023 Hans Baier +# SPDX-License-Identifier: BSD-2-Clause + +from litex.build.generic_platform import Pins, Subsignal, IOStandard, Misc +from litex.build.xilinx import Xilinx7SeriesPlatform +from litex.build.openocd import OpenOCD + +# IOs ---------------------------------------------------------------------------------------------- + +_io = [ + # Clk / Rst + ("clk50", 0, Pins("W19"), IOStandard("LVCMOS33")), + + ("cpu_reset", 0, Pins("Y6"), IOStandard("LVCMOS33")), + ("prog_b", 0, Pins("N12"), IOStandard("LVCMOS33")), + + # The core board does not have a USB serial on it, + # so you will have to attach an USB to serial adapter + # on these pins + ("gpio_serial", 0, + Subsignal("tx", Pins("J2:7")), + Subsignal("rx", Pins("J2:8")), + IOStandard("LVCMOS33") + ), + + # SPIFlash + # 128Mbit SPI FLASH + ("spiflash4x", 0, # clock needs to be accessed through STARTUPE2 + Subsignal("cs_n", Pins("T19")), + Subsignal("clk", Pins("L12")), + Subsignal("dq", Pins("P22 R22 P21 R21")), + IOStandard("LVCMOS33") + ), + + # DDR3 SDRAM + # MT41K128M16JT-125K + ("ddram", 0, + Subsignal("a", Pins("A15 D14 A14 D15 E14 F14 E13 C13 E16 B13 C17 F13 F16 A13"), + IOStandard("SSTL135")), + Subsignal("ba", Pins("D16 E17 B15"), IOStandard("SSTL135")), + Subsignal("ras_n", Pins("B17"), IOStandard("SSTL135")), + Subsignal("cas_n", Pins("B16"), IOStandard("SSTL135")), + Subsignal("we_n", Pins("A16"), IOStandard("SSTL135")), + # cs_n is hardwired on the board + #Subsignal("cs_n", Pins(""), IOStandard("SSTL135")), + Subsignal("dm", Pins("F19 D20"), IOStandard("SSTL135")), + Subsignal("dq", Pins( + "B20 A18 A20 D19 A19 C18 C19 E19 C20 D22 D21 E21 C22 G21 B22 E22"), + IOStandard("SSTL135"), + Misc("IN_TERM=UNTUNED_SPLIT_40")), + Subsignal("dqs_p", Pins("F18 B21"), + IOStandard("DIFF_SSTL135"), + Misc("IN_TERM=UNTUNED_SPLIT_40")), + Subsignal("dqs_n", Pins("E18 A21"), + IOStandard("DIFF_SSTL135"), + Misc("IN_TERM=UNTUNED_SPLIT_40")), + Subsignal("clk_p", Pins("C14"), IOStandard("DIFF_SSTL135")), + Subsignal("clk_n", Pins("C15"), IOStandard("DIFF_SSTL135")), + Subsignal("cke", Pins("B18"), IOStandard("SSTL135")), + Subsignal("odt", Pins("D17"), IOStandard("SSTL135")), + Subsignal("reset_n", Pins("F15"), IOStandard("SSTL135")), + Misc("SLEW=FAST"), + ), +] + +# The connectors are named after the daughterboard, not the core board +# because on the different core boards the names vary, but on the +# daughterboard they stay the same, which we need to connect the +# daughterboard peripherals to the core board. +# On this board J2 is U2 and J3 is U4 +_connectors = [ + ("J2", { + # odd row even row + 7: "H22", 8: "J22", + 9: "H18", 10: "H17", + 11: "K22", 12: "K21", + 13: "G20", 14: "H20", + 15: "H19", 16: "J19", + 17: "J21", 18: "J20", + 19: "J17", 20: "K17", + 21: "L20", 22: "L19", + 23: "H14", 24: "J14", + 25: "K14", 26: "K13", + 27: "M16", 28: "M15", + 29: "M20", 30: "N20", + 31: "M22", 32: "N22", + 33: "L15", 34: "L14", + 35: "N19", 36: "N18", + 37: "P17", 38: "N17", + 39: "T18", 40: "R18", + 41: "Y22", 42: "Y21", + 43: "U21", 44: "T21", + 45: "V20", 46: "U20", + 47: "U18", 48: "U17", + 49: "V19", 50: "V18", + 51: "AB22", 52: "AB21", + 53: "AA21", 54: "AA20", + 55: "AB20", 56: "AA19", + 57: "Y19", 58: "Y18", + 59: "AB18", 60: "AA18", + }), + ("J3", { + # odd row even row + 7: "B1", 8: "A1", + 9: "C2", 10: "B2", + 11: "E1", 12: "D1", + 13: "E2", 14: "D2", + 15: "G1", 16: "F1", + 17: "H2", 18: "G2", + 19: "K1", 20: "J1", + 21: "K2", 22: "J2", + 23: "M1", 24: "L1", + 25: "K4", 26: "J4", + 27: "L3", 28: "K3", + 29: "M3", 30: "M2", + 31: "P2", 32: "N2", + 33: "R1", 34: "P1", + 35: "P5", 36: "P4", + 37: "R4", 38: "T4", + 39: "T5", 40: "U5", + 41: "T1", 42: "U1", + 43: "W1", 44: "Y1", + 45: "AA1", 46: "AB1", + 47: "AB3", 48: "AB2", + 49: "V4", 50: "W4", + 51: "Y3", 52: "AA3", + 53: "Y4", 54: "AA4", + 55: "AA5", 56: "AB5", + 57: "AB7", 58: "AB6", + 59: "AA8", 60: "AB8", + }) +] + +# Platform ----------------------------------------------------------------------------------------- + +class Platform(Xilinx7SeriesPlatform): + default_clk_name = "clk50" + default_clk_period = 1e9/50e6 + kgates = None + + def __init__(self, kgates=200, toolchain="vivado", with_daughterboard=False): + assert(kgates in [100, 200], "kgates can only be 100 or 200 representing a XC7A7100T, XC7TA200T") + self.kgates = kgates + device = f"xc7a{kgates}tfbg484-1" + io = _io + connectors = _connectors + + core_leds_name = "onboard_led" if with_daughterboard else "user_led" + io += [ + (core_leds_name, 0, Pins("F3"), IOStandard("LVCMOS33")), + (core_leds_name, 1, Pins("E3"), IOStandard("LVCMOS33")), + ] + + if with_daughterboard: + from litex_boards.platforms.qmtech_daughterboard import QMTechDaughterboard + daughterboard = QMTechDaughterboard(IOStandard("LVCMOS33")) + io += daughterboard.io + connectors += daughterboard.connectors + + Xilinx7SeriesPlatform.__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 16]") + self.add_platform_command("set_property CFGBVS VCCO [current_design]") + self.add_platform_command("set_property CONFIG_VOLTAGE 3.3 [current_design]") + self.toolchain.f4pga_device = device + + def create_programmer(self): + bscan_spi = f"bscan_spi_xc7a{self.kgates}t.bit" + return OpenOCD("openocd_xc7_ft2232.cfg", bscan_spi) + + + def do_finalize(self, fragment): + Xilinx7SeriesPlatform.do_finalize(self, fragment) + self.add_period_constraint(self.lookup_request("clk50", loose=True), 1e9/50e6) \ No newline at end of file diff --git a/litex_boards/targets/qmtech_artix7_fbg484.py b/litex_boards/targets/qmtech_artix7_fbg484.py new file mode 100644 index 0000000..294e801 --- /dev/null +++ b/litex_boards/targets/qmtech_artix7_fbg484.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 + +# +# This file is part of LiteX-Boards. +# +# Copyright (c) 2021 Hans Baier +# SPDX-License-Identifier: BSD-2-Clause + +# https://www.aliexpress.com/item/1005002960622091.html + +from migen import * + +from litex.gen import * + +from litex_boards.platforms import qmtech_artix7_fbg484 + +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(LiteXModule): + def __init__(self, platform, sys_clk_freq, with_ethernet, with_vga): + self.rst = Signal() + self.cd_sys = ClockDomain() + self.cd_sys4x = ClockDomain() + self.cd_sys4x_dqs = ClockDomain() + self.cd_idelay = ClockDomain() + self.cd_eth = ClockDomain() + if with_ethernet: + self.cd_eth = ClockDomain() + if with_vga: + self.cd_vga = ClockDomain() + + # # # + + self.pll = pll = S7PLL(speedgrade=-1) + try: + reset_button = platform.request("cpu_reset") + self.comb += pll.reset.eq(~reset_button | self.rst) + except: + self.comb += pll.reset.eq(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.idelayctrl = S7IDELAYCTRL(self.cd_idelay) + +# BaseSoC ------------------------------------------------------------------------------------------ + +class BaseSoC(SoCCore): + def __init__(self, toolchain="vivado", kgates=200, sys_clk_freq=100e6, with_daughterboard=False, + with_ethernet = False, + with_etherbone = False, + eth_ip = "192.168.1.50", + eth_dynamic_ip = False, + with_led_chaser = True, + with_video_terminal = False, + with_video_framebuffer = False, + with_jtagbone = True, + with_spi_flash = False, + **kwargs): + platform = qmtech_artix7_fbg484.Platform(kgates=kgates, toolchain=toolchain, with_daughterboard=with_daughterboard) + + # CRG -------------------------------------------------------------------------------------- + self.crg = _CRG(platform, sys_clk_freq, + with_ethernet = (with_ethernet or with_etherbone), + with_vga = (with_video_terminal or with_video_framebuffer) + ) + + # SoCCore ---------------------------------------------------------------------------------- + if (kwargs["uart_name"] == "serial") and (not with_daughterboard): + kwargs["uart_name"] = "gpio_serial" + SoCCore.__init__(self, platform, sys_clk_freq, + ident = f"LiteX SoC on QMTech XC7A{kgates}T" + (" + Daughterboard" if with_daughterboard else ""), + **kwargs) + + # DDR3 SDRAM ------------------------------------------------------------------------------- + if not self.integrated_main_ram_size: + self.ddrphy = s7ddrphy.A7DDRPHY(platform.request("ddram"), + 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.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]") + + # Jtagbone --------------------------------------------------------------------------------- + if with_jtagbone: + self.add_jtagbone() + + # 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: + self.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") + + # Leds ------------------------------------------------------------------------------------- + if with_led_chaser: + self.leds = LedChaser( + pads = platform.request_all("user_led"), + sys_clk_freq = sys_clk_freq) + + if not with_daughterboard and kwargs["uart_name"] == "serial": + kwargs["uart_name"] = "jtag_serial" + +# Build -------------------------------------------------------------------------------------------- + +def main(): + from litex.build.parser import LiteXArgumentParser + parser = LiteXArgumentParser(platform=qmtech_artix7_fbg484.Platform, description="LiteX SoC on QMTech Artix7 FBG484.") + parser.add_target_argument("--kgates", default=200, type=int, help="Number of kgates. Allowed values: 75, 100, 200, representing XC7A75T, XC7A100T and XC7A200T") + parser.add_target_argument("--sys-clk-freq", default=100e6, type=float, help="System clock frequency.") + parser.add_target_argument("--with-daughterboard", action="store_true", help="Board plugged into the QMTech daughterboard.") + ethopts = parser.target_group.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_target_argument("--eth-ip", default="192.168.1.50", help="Ethernet/Etherbone IP address.") + parser.add_target_argument("--eth-dynamic-ip", action="store_true", help="Enable dynamic Ethernet IP addresses setting.") + sdopts = parser.target_group.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_target_argument("--with-jtagbone", action="store_true", help="Enable Jtagbone support.") + parser.add_target_argument("--with-spi-flash", action="store_true", help="Enable SPI Flash (MMAPed).") + viopts = parser.target_group.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).") + args = parser.parse_args() + + soc = BaseSoC( + toolchain = args.toolchain, + kgates = args.kgates, + sys_clk_freq = args.sys_clk_freq, + with_daughterboard = args.with_daughterboard, + 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_spi_flash = args.with_spi_flash, + with_video_terminal = args.with_video_terminal, + with_video_framebuffer = args.with_video_framebuffer, + **parser.soc_argdict + ) + + if args.with_spi_sdcard: + soc.add_spi_sdcard() + if args.with_sdcard: + soc.add_sdcard() + + builder = Builder(soc, **parser.builder_argdict) + if args.build: + builder.build(**parser.toolchain_argdict) + + if args.load: + prog = soc.platform.create_programmer() + prog.load_bitstream(builder.get_bitstream_filename(mode="sram")) + +if __name__ == "__main__": + main()