|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +# |
| 4 | +# This file is part of LiteX-Boards. |
| 5 | +# |
| 6 | +# Copyright (c) 2025 Miodrag Milanovic <[email protected]> |
| 7 | +# |
| 8 | +# SPDX-License-Identifier: BSD-2-Clause |
| 9 | + |
| 10 | +from migen import * |
| 11 | + |
| 12 | +from litex.gen import * |
| 13 | + |
| 14 | +from litex_boards.platforms import machdyne_kolsch |
| 15 | + |
| 16 | +from litex.soc.cores.clock.colognechip import GateMatePLL |
| 17 | +from litex.soc.integration.soc_core import * |
| 18 | +from litex.soc.integration.builder import * |
| 19 | +from litex.build.io import DDROutput |
| 20 | +from litex.build.generic_platform import Pins |
| 21 | + |
| 22 | +from litex.soc.cores.led import LedChaser |
| 23 | +from litex.soc.cores.video import VideoVGAPHY |
| 24 | + |
| 25 | +from litedram.modules import W989D6DBGX6 |
| 26 | + |
| 27 | +from litedram.phy import GENSDRPHY |
| 28 | + |
| 29 | +# CRG ---------------------------------------------------------------------------------------------- |
| 30 | + |
| 31 | +class _CRG(LiteXModule): |
| 32 | + def __init__(self, platform, sys_clk_freq, with_video_terminal): |
| 33 | + self.rst = Signal() |
| 34 | + rst_n = Signal() |
| 35 | + self.cd_sys = ClockDomain() |
| 36 | + self.cd_sys_ps = ClockDomain() |
| 37 | + if with_video_terminal: |
| 38 | + self.cd_vga = ClockDomain() |
| 39 | + |
| 40 | + # Clk / Rst |
| 41 | + clk48 = platform.request("clk48") |
| 42 | + |
| 43 | + self.specials += Instance("CC_USR_RSTN", o_USR_RSTN = rst_n) |
| 44 | + |
| 45 | + # PLL |
| 46 | + self.pll = pll = GateMatePLL(perf_mode="economy") |
| 47 | + self.comb += pll.reset.eq(~rst_n) |
| 48 | + pll.register_clkin(clk48, 48e6) |
| 49 | + pll.create_clkout(self.cd_sys, sys_clk_freq) |
| 50 | + pll.create_clkout(self.cd_sys_ps, sys_clk_freq, phase=90) |
| 51 | + platform.add_period_constraint(self.cd_sys.clk, 1e9/sys_clk_freq) |
| 52 | + self.specials += DDROutput(1, 0, platform.request("sdram_clock"), ClockSignal("sys_ps")) |
| 53 | + |
| 54 | + if with_video_terminal: |
| 55 | + self.pll_video = pll_video = GateMatePLL(perf_mode="economy") |
| 56 | + self.comb += pll_video.reset.eq(~rst_n) |
| 57 | + pll_video.register_clkin(clk48, 48e6) |
| 58 | + pll_video.create_clkout(self.cd_vga, 65e6) |
| 59 | + platform.add_period_constraint(self.cd_vga.clk, 1e9/65e6) |
| 60 | + |
| 61 | +# BaseSoC ------------------------------------------------------------------------------------------ |
| 62 | + |
| 63 | +class BaseSoC(SoCCore): |
| 64 | + def __init__(self, sys_clk_freq=24e6, toolchain="colognechip", |
| 65 | + with_video_terminal = False, |
| 66 | + with_ethernet = False, |
| 67 | + with_etherbone = False, |
| 68 | + eth_ip = "192.168.1.50", |
| 69 | + remote_ip = None, |
| 70 | + with_led_chaser = True, |
| 71 | + **kwargs): |
| 72 | + platform = machdyne_kolsch.Platform(toolchain) |
| 73 | + |
| 74 | + # CRG -------------------------------------------------------------------------------------- |
| 75 | + self.crg = _CRG(platform, sys_clk_freq, with_video_terminal) |
| 76 | + |
| 77 | + # SoCCore ---------------------------------------------------------------------------------- |
| 78 | + SoCCore.__init__(self, platform, sys_clk_freq, ident="LiteX SoC on Kolsch", **kwargs) |
| 79 | + |
| 80 | + # Video Terminal --------------------------------------------------------------------------- |
| 81 | + if with_video_terminal: |
| 82 | + vga_pads = platform.request("vga") |
| 83 | + self.videophy = VideoVGAPHY(vga_pads, clock_domain="vga") |
| 84 | + self.add_video_terminal(phy=self.videophy, timings="1024x768@60Hz", clock_domain="vga") |
| 85 | + |
| 86 | + # Leds ------------------------------------------------------------------------------------- |
| 87 | + if with_led_chaser: |
| 88 | + self.leds = LedChaser( |
| 89 | + pads = platform.request_all("user_led_n"), |
| 90 | + sys_clk_freq = sys_clk_freq) |
| 91 | + |
| 92 | + # DRAM ------------------------------------------------------------------------------------- |
| 93 | + if not self.integrated_main_ram_size: |
| 94 | + self.sdrphy = GENSDRPHY(platform.request("sdram"), sys_clk_freq) |
| 95 | + |
| 96 | + self.add_sdram("sdram", |
| 97 | + phy = self.sdrphy, |
| 98 | + module = W989D6DBGX6(sys_clk_freq, "1:1"), |
| 99 | + l2_cache_size = kwargs.get("l2_size", 0) |
| 100 | + ) |
| 101 | + |
| 102 | + # Ethernet / Etherbone --------------------------------------------------------------------- |
| 103 | + if with_ethernet or with_etherbone: |
| 104 | + from litex.build.generic_platform import Subsignal |
| 105 | + def eth_lan8720_rmii_pmod_io(pmod): |
| 106 | + # Lan8720 RMII PHY "PMOD": To be used as a PMOD, MDIO should be disconnected and TX1 connected to PMOD8 IO. |
| 107 | + return [ |
| 108 | + ("eth_rmii_clocks", 0, |
| 109 | + Subsignal("ref_clk", Pins(f"{pmod}:6")), |
| 110 | + ), |
| 111 | + ("eth_rmii", 0, |
| 112 | + Subsignal("rx_data", Pins(f"{pmod}:5 {pmod}:1")), |
| 113 | + Subsignal("crs_dv", Pins(f"{pmod}:2")), |
| 114 | + Subsignal("tx_en", Pins(f"{pmod}:4")), |
| 115 | + Subsignal("tx_data", Pins(f"{pmod}:0 {pmod}:7")), |
| 116 | + ), |
| 117 | + ] |
| 118 | + platform.add_extension(eth_lan8720_rmii_pmod_io("PMOD")) |
| 119 | + |
| 120 | + from liteeth.phy.rmii import LiteEthPHYRMII |
| 121 | + self.ethphy = LiteEthPHYRMII( |
| 122 | + clock_pads = platform.request("eth_rmii_clocks"), |
| 123 | + pads = platform.request("eth_rmii"), |
| 124 | + refclk_cd = None |
| 125 | + ) |
| 126 | + |
| 127 | + if with_ethernet: |
| 128 | + self.add_ethernet(phy=self.ethphy, local_ip=eth_ip, remote_ip=remote_ip, software_debug=False) |
| 129 | + if with_etherbone: |
| 130 | + self.add_etherbone(phy=self.ethphy, ip_address=eth_ip) |
| 131 | + |
| 132 | +# Build -------------------------------------------------------------------------------------------- |
| 133 | + |
| 134 | +def main(): |
| 135 | + from litex.build.parser import LiteXArgumentParser |
| 136 | + parser = LiteXArgumentParser(platform=machdyne_kolsch.Platform, description="LiteX SoC on Kolsch") |
| 137 | + parser.add_target_argument("--sys-clk-freq", default=24e6, type=float, help="System clock frequency.") |
| 138 | + parser.add_target_argument("--with-video-terminal", action="store_true", help="Enable Video Terminal (VGA).") |
| 139 | + parser.add_target_argument("--flash", action="store_true", help="Flash bitstream.") |
| 140 | + parser.add_target_argument("--with-spi-sdcard", action="store_true", help="Enable SPI-mode SDCard support.") |
| 141 | + pmodopts = parser.target_group.add_mutually_exclusive_group() |
| 142 | + pmodopts.add_argument("--with-ethernet", action="store_true", help="Enable Ethernet support.") |
| 143 | + pmodopts.add_argument("--with-etherbone", action="store_true", help="Enable Etherbone support.") |
| 144 | + parser.add_target_argument("--eth-ip", default="192.168.1.50", help="Ethernet/Etherbone IP address.") |
| 145 | + parser.add_target_argument("--remote-ip", default="192.168.1.100", help="Remote IP address of TFTP server.") |
| 146 | + |
| 147 | + args = parser.parse_args() |
| 148 | + |
| 149 | + soc = BaseSoC( |
| 150 | + sys_clk_freq = args.sys_clk_freq, |
| 151 | + toolchain = args.toolchain, |
| 152 | + with_video_terminal = args.with_video_terminal, |
| 153 | + with_ethernet = args.with_ethernet, |
| 154 | + with_etherbone = args.with_etherbone, |
| 155 | + eth_ip = args.eth_ip, |
| 156 | + remote_ip = args.remote_ip, |
| 157 | + **parser.soc_argdict) |
| 158 | + |
| 159 | + if args.with_spi_sdcard: |
| 160 | + soc.add_spi_sdcard() |
| 161 | + |
| 162 | + builder = Builder(soc, **parser.builder_argdict) |
| 163 | + if args.build: |
| 164 | + builder.build(**parser.toolchain_argdict) |
| 165 | + |
| 166 | + if args.load: |
| 167 | + prog = soc.platform.create_programmer() |
| 168 | + prog.load_bitstream(builder.get_bitstream_filename(mode="sram")) |
| 169 | + |
| 170 | +if __name__ == "__main__": |
| 171 | + main() |
0 commit comments