From 98ddb97f5ee6bd0a794cfe9c8f887ad23b5912f3 Mon Sep 17 00:00:00 2001 From: Hans Baier Date: Sat, 18 Mar 2023 18:05:10 +0700 Subject: [PATCH] Support QMTech XC7A75T, XC7A100T core boards --- .../platforms/qmtech_artix7_fgg676.py | 181 ++++++++++++++++ litex_boards/targets/qmtech_artix7_fgg676.py | 200 ++++++++++++++++++ 2 files changed, 381 insertions(+) create mode 100644 litex_boards/platforms/qmtech_artix7_fgg676.py create mode 100644 litex_boards/targets/qmtech_artix7_fgg676.py diff --git a/litex_boards/platforms/qmtech_artix7_fgg676.py b/litex_boards/platforms/qmtech_artix7_fgg676.py new file mode 100644 index 0000000..7afcba2 --- /dev/null +++ b/litex_boards/platforms/qmtech_artix7_fgg676.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("U22"), IOStandard("LVCMOS33")), + + ("cpu_reset", 0, Pins("P4"), IOStandard("LVCMOS33")), + ("prog_b", 0, Pins("AE16"), 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 + # N25Q064A + ("spiflash4x", 0, # clock needs to be accessed through STARTUPE2 + Subsignal("cs_n", Pins("P18")), + Subsignal("clk", Pins("H13")), + Subsignal("dq", Pins("R14 R15 P14 N14")), + IOStandard("LVCMOS33") + ), + + # DDR3 SDRAM + # MT41K128M16JT-125K + ("ddram", 0, + Subsignal("a", Pins("E17 G17 F17 C17 G16 D16 H16 E16 H14 F15 F20 H15 C18 G15"), + IOStandard("SSTL135")), + Subsignal("ba", Pins("B17 D18 A17"), IOStandard("SSTL135")), + Subsignal("ras_n", Pins("A19"), IOStandard("SSTL135")), + Subsignal("cas_n", Pins("B19"), IOStandard("SSTL135")), + Subsignal("we_n", Pins("A18"), IOStandard("SSTL135")), + # cs_n is hardwired on the board + #Subsignal("cs_n", Pins(""), IOStandard("SSTL135")), + Subsignal("dm", Pins("A22 C22"), IOStandard("SSTL135")), + Subsignal("dq", Pins( + "D21 C21 B22 B21 D19 E20 C19 D20 C23 D23 B24 B25 C24 C26 A25 B26"), + IOStandard("SSTL135"), + Misc("IN_TERM=UNTUNED_SPLIT_40")), + Subsignal("dqs_p", Pins("B20 A23"), + IOStandard("DIFF_SSTL135"), + Misc("IN_TERM=UNTUNED_SPLIT_40")), + Subsignal("dqs_n", Pins("A20 A24"), + IOStandard("DIFF_SSTL135"), + Misc("IN_TERM=UNTUNED_SPLIT_40")), + Subsignal("clk_p", Pins("F18"), IOStandard("DIFF_SSTL135")), + Subsignal("clk_n", Pins("F19"), IOStandard("DIFF_SSTL135")), + Subsignal("cke", Pins("E18"), IOStandard("SSTL135")), + Subsignal("odt", Pins("G19"), IOStandard("SSTL135")), + Subsignal("reset_n", Pins("H17"), 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: "D26", 8: "E26", + 9: "D25", 10: "E25", + 11: "G26", 12: "H26", + 13: "E23", 14: "F23", + 15: "F22", 16: "G22", + 17: "J26", 18: "J25", + 19: "G21", 20: "G20", + 21: "H22", 22: "H21", + 23: "J21", 24: "K21", + 25: "K26", 26: "K25", + 27: "K23", 28: "K22", + 29: "M26", 30: "N26", + 31: "L23", 32: "L22", + 33: "P26", 34: "R26", + 35: "M25", 36: "M24", + 37: "N22", 38: "N21", + 39: "P24", 40: "P23", + 41: "P25", 42: "R25", + 43: "T25", 44: "T24", + 45: "V21", 46: "U21", + 47: "W23", 48: "V23", + 49: "Y23", 50: "Y22", + 51: "AA25", 52: "Y25", + 53: "AC24", 54: "AB24", + 55: "Y21", 56: "W21", + 57: "Y26", 58: "W25", + 59: "AC26", 60: "AB26", + }), + ("J3", { + # odd row even row + 7: "B5", 8: "A5", + 9: "B4", 10: "A4", + 11: "A3", 12: "A2", + 13: "D4", 14: "C4", + 15: "C2", 16: "B2", + 17: "E5", 18: "D5", + 19: "C1", 20: "B1", + 21: "E1", 22: "D1", + 23: "F2", 24: "E2", + 25: "G4", 26: "F4", + 27: "G2", 28: "G1", + 29: "J4", 30: "H4", + 31: "H2", 32: "H1", + 33: "H9", 34: "G9", + 35: "M2", 36: "L2", + 37: "L5", 38: "K5", + 39: "M4", 40: "L4", + 41: "N3", 42: "N2", + 43: "M6", 44: "M5", + 45: "K1", 46: "J1", + 47: "R3", 48: "P3", + 49: "T4", 50: "T3", + 51: "P6", 52: "P5", + 53: "N1", 54: "M1", + 55: "R1", 56: "P1", + 57: "T2", 58: "R2", + 59: "U2", 60: "U1", + }) +] + +# Platform ----------------------------------------------------------------------------------------- + +class Platform(Xilinx7SeriesPlatform): + default_clk_name = "clk50" + default_clk_period = 1e9/50e6 + kgates = None + + def __init__(self, kgates=100, toolchain="vivado", with_daughterboard=False): + assert(kgates in [75, 100], "kgates can only be 75 or 100 representing a XC7A75T, XC7TA100T") + self.kgates = kgates + device = f"xc7a{kgates}tfgg676-1" + io = _io + connectors = _connectors + + core_leds_name = "onboard_led" if with_daughterboard else "user_led" + io += [ + (core_leds_name, 0, Pins("T23"), IOStandard("LVCMOS33")), + (core_leds_name, 1, Pins("R23"), 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_fgg676.py b/litex_boards/targets/qmtech_artix7_fgg676.py new file mode 100644 index 0000000..5d71ced --- /dev/null +++ b/litex_boards/targets/qmtech_artix7_fgg676.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/4000170003461.html + +from migen import * + +from litex.gen import * + +from litex_boards.platforms import qmtech_artix7_fgg676 + +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=100, 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_fgg676.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_fgg676.Platform, description="LiteX SoC on QMTech XC7AXXXT.") + parser.add_target_argument("--kgates", default=100, 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()