Skip to content

Commit a4f25d0

Browse files
committed
Add support for Machdyne Kolsch
1 parent 1f86f7f commit a4f25d0

File tree

2 files changed

+271
-0
lines changed

2 files changed

+271
-0
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#
2+
# This file is part of LiteX-Boards.
3+
#
4+
# Copyright (c) 2025 Miodrag Milanovic <[email protected]>
5+
#
6+
# SPDX-License-Identifier: BSD-2-Clause
7+
8+
from litex.build.generic_platform import *
9+
from litex.build.colognechip.platform import CologneChipPlatform
10+
from litex.build.openfpgaloader import OpenFPGALoader
11+
12+
# IOs ----------------------------------------------------------------------------------------------
13+
14+
_io = [
15+
# Clk / Rst
16+
("clk48", 0, Pins("IO_SB_A8"), Misc("SCHMITT_TRIGGER=true")),
17+
18+
# Leds
19+
("user_led_n", 0, Pins("IO_NB_B4")),
20+
21+
# Serial.
22+
("serial", 0,
23+
Subsignal("tx", Pins("IO_WC_A2")),
24+
Subsignal("rx", Pins("IO_WC_A0"))
25+
),
26+
27+
# SDRAM
28+
("sdram_clock", 0, Pins("IO_SA_A6"), Misc("SLEW=fast")),
29+
("sdram", 0,
30+
Subsignal("a", Pins(
31+
"IO_EA_A2 IO_SB_B2 IO_SB_B8 IO_EA_B2 IO_EA_B0 IO_EA_A0 IO_SB_B6 IO_SB_A3",
32+
"IO_EA_A1 IO_SB_B0 IO_SB_A2 IO_SB_A0 IO_EA_B1")),
33+
Subsignal("ba", Pins("IO_EA_B4 IO_SB_B1")),
34+
Subsignal("cs_n", Pins("IO_SB_A1")),
35+
Subsignal("cke", Pins("IO_SA_A7")),
36+
Subsignal("ras_n", Pins("IO_SB_B5")),
37+
Subsignal("cas_n", Pins("IO_SB_B3")),
38+
Subsignal("we_n", Pins("IO_SB_A5")),
39+
Subsignal("dq", Pins(
40+
"IO_SA_B5 IO_SA_B4 IO_SA_A5 IO_SB_B7 IO_SA_B7 IO_SB_A6 IO_SA_B6 IO_SA_B8",
41+
"IO_SA_A2 IO_SA_A4 IO_SA_B2 IO_SA_B0 IO_SA_A0 IO_SA_B3 IO_SA_B1 IO_SA_A3")),
42+
Subsignal("dm", Pins("IO_SA_A8 IO_SA_A1"))
43+
),
44+
45+
# VGA
46+
("vga", 0,
47+
Subsignal("hsync_n", Pins("IO_NA_A0")),
48+
Subsignal("vsync_n", Pins("IO_NA_B0")),
49+
Subsignal("r", Pins("IO_NA_B1 IO_NA_A1 IO_NA_A2 IO_NA_A4")),
50+
Subsignal("g", Pins("IO_NA_B4 IO_NA_A7 IO_NA_B7 IO_NA_B2")),
51+
Subsignal("b", Pins("IO_NA_A3 IO_NA_A5 IO_NA_A6 IO_NA_A8")),
52+
),
53+
54+
# SD card w/ SPI interface
55+
("spisdcard", 0,
56+
Subsignal("clk", Pins("IO_WC_A4")),
57+
Subsignal("mosi", Pins("IO_WC_A3")),
58+
Subsignal("cs_n", Pins("IO_WC_B5")),
59+
Subsignal("miso", Pins("IO_WC_B3")),
60+
),
61+
62+
# DUAL USB HOST
63+
("usb_host", 0,
64+
Subsignal("dp", Pins("IO_WB_A7 IO_WB_A5")),
65+
Subsignal("dm", Pins("IO_WB_B7 IO_WB_B5")),
66+
),
67+
68+
# 3.5MM AUDIO
69+
("audio_pwm", 0,
70+
Subsignal("left", Pins("IO_NB_B0")),
71+
Subsignal("right", Pins("IO_NB_A0")),
72+
),
73+
74+
# 3.5MM VIDEO
75+
("video_dac", 0,
76+
Subsignal("data", Pins("IO_EB_A8 IO_EB_B4 IO_EB_A4 IO_EB_B5")),
77+
),
78+
]
79+
80+
# Connectors ---------------------------------------------------------------------------------------
81+
82+
_connectors = [
83+
("PMOD", "IO_EB_B2 IO_EB_A2 IO_EB_B3 IO_EB_A3 IO_EB_A0 IO_EB_B0 IO_EB_A1 IO_EB_B1"),
84+
]
85+
86+
# Platform -----------------------------------------------------------------------------------------
87+
88+
class Platform(CologneChipPlatform):
89+
default_clk_name = "clk48"
90+
default_clk_period = 1e9/48e6
91+
92+
def __init__(self, toolchain="colognechip"):
93+
CologneChipPlatform.__init__(self, "CCGM1A1", _io, _connectors, toolchain=toolchain)
94+
95+
def create_programmer(self):
96+
return OpenFPGALoader(cable="dirtyJtag")
97+
98+
def do_finalize(self, fragment):
99+
CologneChipPlatform.do_finalize(self, fragment)
100+
self.add_period_constraint(self.lookup_request("clk48", loose=True), 1e9/48e6)
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
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

Comments
 (0)