diff --git a/README.md b/README.md index e149b3156..751254077 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,7 @@ Some of the supported boards, see yours? Give LiteX-Boards a try! ├── microphase_a7_lite ├── microsoft_catapult_v3 ├── mist + ├── mlk_cu07_ku15p ├── mlkpai_fs01_dr1v90m ├── mnt_rkx7 ├── muselab_icesugar diff --git a/docs/boards_inventory.md b/docs/boards_inventory.md index bda0be396..7292ba14b 100644 --- a/docs/boards_inventory.md +++ b/docs/boards_inventory.md @@ -131,6 +131,7 @@ python3 .github/scripts/generate_board_inventory.py --write | `microphase_a7_lite` | microphase_a7_lite | vivado | `100000000.0` | Ethernet, SDCard, SPI SDCard, SPI Flash | included | | `microsoft_catapult_v3` | microsoft_catapult_v3 | quartus | `100000000.0` | - | included | | `mist` | mist | quartus | `50000000.0` | Video Terminal | included | +| `mlk_cu07_ku15p` | mlk_cu07_ku15p | vivado | `75000000.0` | SDCard | included | | `mlkpai_fs01_dr1v90m` | mlkpai_fs01_dr1v90m | td | `25000000.0` | - | included | | `mnt_rkx7` | mnt_rkx7 | vivado | `100000000.0` | Ethernet, Etherbone, SDCard, SPI SDCard, SPI Flash, USB Host | included | | `muselab_icesugar` | muselab_icesugar | icestorm | `24000000.0` | - | included | diff --git a/litex_boards/platforms/mlk_cu07_ku15p.py b/litex_boards/platforms/mlk_cu07_ku15p.py new file mode 100644 index 000000000..2588fbe71 --- /dev/null +++ b/litex_boards/platforms/mlk_cu07_ku15p.py @@ -0,0 +1,125 @@ +# +# This file is part of LiteX-Boards. +# +# Copyright (c) 2026 Yu Jin +# SPDX-License-Identifier: BSD-2-Clause + +from litex.build.generic_platform import * +from litex.build.xilinx import XilinxUSPPlatform +from litex.build.openfpgaloader import OpenFPGALoader + +# IOs ---------------------------------------------------------------------------------------------- + +_io = [ + # Clk / Rst. + ("clk100", 0, Pins("AH18"), IOStandard("LVCMOS18")), + ("clk100_ddr", 0, + Subsignal("p", Pins("AK17")), + Subsignal("n", Pins("AK16")), + IOStandard("DIFF_SSTL12"), + ), + ("cpu_resetn", 0, Pins("J23"), IOStandard("LVCMOS18"), Misc("PULLTYPE PULLUP")), + + # Buttons. + ("user_btn", 0, Pins("G26"), IOStandard("LVCMOS18")), + ("user_btn", 1, Pins("G25"), IOStandard("LVCMOS18")), + ("user_btn", 2, Pins("H24"), IOStandard("LVCMOS18")), + + # Leds. + ("user_led", 0, Pins("AP9"), IOStandard("LVCMOS33")), + ("user_led", 1, Pins("AN9"), IOStandard("LVCMOS33")), + ("user_led", 2, Pins("AP8"), IOStandard("LVCMOS33")), + ("user_led", 3, Pins("AN8"), IOStandard("LVCMOS33")), + ("user_led", 4, Pins("AL10"), IOStandard("LVCMOS33")), + ("user_led", 5, Pins("AM10"), IOStandard("LVCMOS33")), + ("user_led", 6, Pins("AE11"), IOStandard("LVCMOS33")), + + # Serial. + ("serial", 0, + Subsignal("tx", Pins("AN13")), + Subsignal("rx", Pins("AP13")), + IOStandard("LVCMOS33"), + ), + + # SDCard. + ("sdcard", 0, + Subsignal("clk", Pins("B9")), + Subsignal("cmd", Pins("A10"), Misc("PULLTYPE PULLUP")), + Subsignal("data", Pins("B11 A9 C8 B10"), Misc("PULLTYPE PULLUP")), + Subsignal("cd", Pins("C11"), Misc("PULLTYPE PULLUP")), + IOStandard("LVCMOS18"), + ), + + # Runtime QSPI is intentionally omitted. The source pin table assigns C8 + # to both SD D2 and flash CS, and the dedicated flash clock needs STARTUPE3. + + # I2C EEPROM / RTC. + ("i2c", 0, + Subsignal("scl", Pins("AP11")), + Subsignal("sda", Pins("AP10")), + IOStandard("LVCMOS33"), + ), + + # DDR4 SDRAM (4GB / 4 x Hynix H5AN8G6NCJR-VKI, modeled as MT40A512M16). + ("ddram", 0, + Subsignal("a", Pins( + "AM34 AN26 AP34 AK26 AL32 AK28 AK32 AL30", + "AL34 AP26 AK31 AL33 AH32 AM32"), + IOStandard("SSTL12_DCI")), + Subsignal("ba", Pins("AJ31 AK27"), IOStandard("SSTL12_DCI")), + Subsignal("bg", Pins("AJ30"), IOStandard("SSTL12_DCI")), + Subsignal("ras_n", Pins("AJ33"), IOStandard("SSTL12_DCI")), + Subsignal("cas_n", Pins("AJ34"), IOStandard("SSTL12_DCI")), + Subsignal("we_n", Pins("AJ28"), IOStandard("SSTL12_DCI")), + Subsignal("cs_n", Pins("AH33"), IOStandard("SSTL12_DCI")), + Subsignal("act_n", Pins("AH31"), IOStandard("SSTL12_DCI")), + Subsignal("dm", Pins("Y26 V27 AA22 W23 AE25 AD21 AM21 AJ21"), + IOStandard("POD12_DCI")), + Subsignal("dq", Pins( + "AD25 AA27 AC24 AB25 AB24 AB27 AD26 AB26", + "U25 W28 W26 W29 U24 V29 V26 Y28", + "AB20 Y23 AC22 AA24 AA20 AA25 AC23 AA23", + "U22 T22 T23 W21 U21 Y25 V21 W25", + "AJ23 AF23 AJ24 AG25 AH23 AF24 AH22 AG24", + "AG20 AE22 AF20 AF22 AD20 AE23 AG22 AE20", + "AM22 AM24 AN22 AN24 AN23 AP24 AP23 AP25", + "AL24 AL25 AK22 AL22 AK23 AM20 AL20 AL23"), + IOStandard("POD12_DCI")), + Subsignal("dqs_p", Pins("AC26 U26 AB21 V22 AH24 AG21 AP20 AJ20"), + IOStandard("DIFF_POD12_DCI")), + Subsignal("dqs_n", Pins("AC27 U27 AC21 V23 AJ25 AH21 AP21 AK20"), + IOStandard("DIFF_POD12_DCI")), + Subsignal("clk_p", Pins("AJ29"), IOStandard("DIFF_SSTL12_DCI")), + Subsignal("clk_n", Pins("AK30"), IOStandard("DIFF_SSTL12_DCI")), + Subsignal("cke", Pins("AH27"), IOStandard("SSTL12_DCI")), + Subsignal("odt", Pins("AH28"), IOStandard("SSTL12_DCI")), + Subsignal("reset_n", Pins("AJ26"), IOStandard("LVCMOS12")), + Misc("SLEW=FAST"), + ), +] + +# Platform ----------------------------------------------------------------------------------------- + +class Platform(XilinxUSPPlatform): + default_clk_name = "clk100" + default_clk_period = 1e9/100e6 + + def __init__(self, toolchain="vivado"): + XilinxUSPPlatform.__init__(self, "xcku15p-ffva1156-2-e", _io, toolchain=toolchain) + + def create_programmer(self): + return OpenFPGALoader(cable="ft2232", fpga_part="xcku15p-ffva1156") + + def do_finalize(self, fragment): + XilinxUSPPlatform.do_finalize(self, fragment) + self.add_period_constraint(self.lookup_request("clk100", loose=True), 1e9/100e6) + + # Bitstream generation options. + self.add_platform_command("set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]") + self.add_platform_command("set_property BITSTREAM.CONFIG.CONFIGRATE 85.0 [current_design]") + self.add_platform_command("set_property BITSTREAM.CONFIG.SPI_FALL_EDGE YES [current_design]") + self.add_platform_command("set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]") + self.add_platform_command("set_property CONFIG_VOLTAGE 1.8 [current_design]") + self.add_platform_command("set_property CFGBVS GND [current_design]") + self.add_platform_command("set_property CONFIG_MODE SPIx4 [current_design]") + self.add_platform_command("set_property BITSTREAM.CONFIG.UNUSEDPIN PULLDOWN [current_design]") diff --git a/litex_boards/targets/mlk_cu07_ku15p.py b/litex_boards/targets/mlk_cu07_ku15p.py new file mode 100644 index 000000000..7ecc1e890 --- /dev/null +++ b/litex_boards/targets/mlk_cu07_ku15p.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 + +# +# This file is part of LiteX-Boards. +# +# Copyright (c) 2026 Yu Jin +# SPDX-License-Identifier: BSD-2-Clause + +from migen import * + +from litex.gen import * + +from litex_boards.platforms import mlk_cu07_ku15p + +from litex.soc.cores.clock import USPMMCM, USPIDELAYCTRL +from litex.soc.cores.led import LedChaser +from litex.soc.integration.builder import Builder +from litex.soc.integration.soc_core import SoCCore + +from litedram.modules import MT40A512M16 +from litedram.phy import usddrphy + +# CRG ---------------------------------------------------------------------------------------------- + +class _CRG(LiteXModule): + def __init__(self, platform, sys_clk_freq): + self.rst = Signal() + self.cd_sys = ClockDomain() + self.cd_sys4x = ClockDomain() + self.cd_idelay = ClockDomain() + self.cd_por = ClockDomain(reset_less=True) + + # # # + + # Clk. + clk100 = platform.request("clk100") + + # Power-on Reset. + # Keep the POR independent of cpu_resetn, which can float low on some board revisions. + por_count = Signal(16, reset=2**16 - 1) + por_done = Signal() + self.comb += [ + self.cd_por.clk.eq(clk100), + por_done.eq(por_count == 0), + ] + self.sync.por += If(~por_done, por_count.eq(por_count - 1)) + + # MMCM. + self.pll = pll = USPMMCM(speedgrade=-2) + self.comb += pll.reset.eq(~por_done | self.rst) + pll.register_clkin(clk100, 100e6) + pll.create_clkout(self.cd_sys, sys_clk_freq, with_reset=False) + pll.create_clkout(self.cd_sys4x, 4*sys_clk_freq, with_reset=False) + pll.create_clkout(self.cd_idelay, 400e6) + platform.add_false_path_constraints(self.cd_sys.clk, pll.clkin) + + # IDelayCtrl. + self.idelayctrl = USPIDELAYCTRL(cd_ref=self.cd_idelay, cd_sys=self.cd_sys) + +# BaseSoC ------------------------------------------------------------------------------------------ + +class BaseSoC(SoCCore): + def __init__(self, sys_clk_freq=int(75e6), with_sdcard=False, with_led_chaser=False, **kwargs): + platform = mlk_cu07_ku15p.Platform() + + # CRG -------------------------------------------------------------------------------------- + self.crg = _CRG(platform, sys_clk_freq) + + # SoCCore ---------------------------------------------------------------------------------- + SoCCore.__init__(self, platform, sys_clk_freq, ident="LiteX SoC on Milianke MLK-CU07-KU15P", **kwargs) + + # DDR4 SDRAM ------------------------------------------------------------------------------- + if not self.integrated_main_ram_size: + self.ddrphy = usddrphy.USPDDRPHY(platform.request("ddram"), + memtype = "DDR4", + sys_clk_freq = sys_clk_freq, + iodelay_clk_freq = 400e6) + self.add_sdram("sdram", + phy = self.ddrphy, + module = MT40A512M16(sys_clk_freq, "1:4"), + size = 0x40000000, + l2_cache_size = kwargs.get("l2_size", 8192) + ) + + # SDCard ----------------------------------------------------------------------------------- + if with_sdcard: + self.add_sdcard() + + # Leds ------------------------------------------------------------------------------------- + if with_led_chaser: + self.leds = LedChaser( + pads = platform.request_all("user_led"), + sys_clk_freq = sys_clk_freq) + +# Build -------------------------------------------------------------------------------------------- + +def main(): + from litex.build.parser import LiteXArgumentParser + parser = LiteXArgumentParser(platform=mlk_cu07_ku15p.Platform, description="LiteX SoC on Milianke MLK-CU07-KU15P.") + parser.add_target_argument("--sys-clk-freq", default=75e6, type=float, help="System clock frequency.") + parser.add_target_argument("--with-sdcard", action="store_true", help="Enable SDCard support.") + parser.add_target_argument("--with-led-chaser", action="store_true", help="Enable LED Chaser.") + args = parser.parse_args() + + soc = BaseSoC( + sys_clk_freq = args.sys_clk_freq, + with_sdcard = args.with_sdcard, + with_led_chaser = args.with_led_chaser, + **parser.soc_argdict + ) + 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()