Skip to content

Commit fbaa9be

Browse files
Merge pull request #632 from andelf/feat/add-lckfb-ljpi-support
lckfb_ljpi: Add support for LCKFB LJPI
2 parents 97b3824 + f66179b commit fbaa9be

File tree

2 files changed

+384
-0
lines changed

2 files changed

+384
-0
lines changed
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
#
2+
# This file is part of LiteX-Boards.
3+
#
4+
# Copyright (c) 2024 Andelf <[email protected]>
5+
# SPDX-License-Identifier: BSD-2-Clause
6+
#
7+
# LCKFB LJPI FPGA board: https://wiki.lckfb.com/zh-hans/fpga-ljpi/
8+
9+
from migen import *
10+
11+
from litex.build.generic_platform import *
12+
from litex.build.gowin.platform import GowinPlatform
13+
from litex.build.gowin.programmer import GowinProgrammer, GOWIN_CABLE_FT2CH
14+
from litex.build.openfpgaloader import OpenFPGALoader
15+
16+
17+
# IOs ----------------------------------------------------------------------------------------------
18+
19+
_io = [
20+
# Clk / Rst.
21+
("clk50", 0, Pins("T7"), IOStandard("LVCMOS33")),
22+
23+
# Serial.
24+
("serial", 0,
25+
Subsignal("tx", Pins("F12")),
26+
Subsignal("rx", Pins("F13")),
27+
IOStandard("LVCMOS33")
28+
),
29+
30+
# Leds. LED2 and LED3 are RGB LEDs.
31+
("led", 0, Pins( "R9"), IOStandard("LVCMOS33")),
32+
("led", 1, Pins("C10"), IOStandard("LVCMOS33")),
33+
("led", 3, Pins( "R7"), IOStandard("LVCMOS33")),
34+
("led", 2, Pins( "N6"), IOStandard("LVCMOS33")),
35+
("led", 4, Pins("T10"), IOStandard("LVCMOS33")),
36+
("led", 5, Pins( "P7"), IOStandard("LVCMOS33")),
37+
38+
# Buttons. SW2
39+
("btn_n", 0, Pins("D11"), IOStandard("LVCMOS33")),
40+
# ("btn_n", 1, Pins("F10"), IOStandard("LVCMOS33")),
41+
42+
# Reset. Use SW3 as reset button.
43+
("rst_n", 0, Pins("F10"), IOStandard("LVCMOS33")),
44+
45+
# SPIFlash.
46+
# W25Q64JVSSIQ
47+
("spiflash", 0,
48+
Subsignal("cs_n", Pins("M9"), IOStandard("LVCMOS33")),
49+
Subsignal("clk", Pins("L10"), IOStandard("LVCMOS33")),
50+
Subsignal("miso", Pins("P10"), IOStandard("LVCMOS33")),
51+
Subsignal("mosi", Pins("R10"), IOStandard("LVCMOS33")),
52+
),
53+
54+
# HDMI(LVDS).
55+
("hdmi", 0,
56+
Subsignal("clk_p", Pins("M10")),
57+
Subsignal("clk_n", Pins("N11")),
58+
Subsignal("data0_p", Pins("R13")),
59+
Subsignal("data0_n", Pins("T14")),
60+
Subsignal("data1_p", Pins("R11")),
61+
Subsignal("data1_n", Pins("T12")),
62+
Subsignal("data2_p", Pins("R12")),
63+
Subsignal("data2_n", Pins("P13")),
64+
IOStandard("LVCMOS33D DRIVE=8"),
65+
Misc("PULL_MODE=NONE"),
66+
),
67+
68+
# 8-segment LED display, Common Anode.
69+
("seg8", 0, Pins("G13"), IOStandard("LVCMOS33")),
70+
("seg8", 1, Pins("H16"), IOStandard("LVCMOS33")),
71+
("seg8", 2, Pins("H12"), IOStandard("LVCMOS33")),
72+
("seg8", 3, Pins("H13"), IOStandard("LVCMOS33")),
73+
("seg8", 4, Pins("H14"), IOStandard("LVCMOS33")),
74+
("seg8", 5, Pins("G12"), IOStandard("LVCMOS33")),
75+
("seg8", 6, Pins("G11"), IOStandard("LVCMOS33")),
76+
# ("seg8", 7, Pins("L14"), IOStandard("LVCMOS33")),
77+
78+
# DDR3 SDRAM
79+
# MT41J128M16JT-125K.
80+
("ddram", 0,
81+
Subsignal("a", Pins("F7 A4 D6 F8 C4 E6 B1 D8 A5 F9 K3 B7 A3 C8"),
82+
IOStandard("SSTL15")),
83+
Subsignal("ba", Pins("H4 D3 H5"), IOStandard("SSTL15")),
84+
Subsignal("ras_n", Pins("R4"), IOStandard("SSTL15")),
85+
Subsignal("cas_n", Pins("R6"), IOStandard("SSTL15")),
86+
Subsignal("we_n", Pins("L2"), IOStandard("SSTL15")),
87+
Subsignal("cs_n", Pins("P5"), IOStandard("SSTL15")),
88+
Subsignal("dm", Pins("G1 K5"), IOStandard("SSTL15")),
89+
Subsignal("dq", Pins(
90+
"G5 F5 F4 F3 E2 C1 E1 B3",
91+
"M3 K4 N2 L1 P4 H3 R1 M2"),
92+
IOStandard("SSTL15"),
93+
Misc("VREF=INTERNAL")),
94+
Subsignal("dqs_p", Pins("G2 J5"), IOStandard("SSTL15D")),
95+
Subsignal("dqs_n", Pins("G3 K6"), IOStandard("SSTL15D")),
96+
Subsignal("clk_p", Pins("J1"), IOStandard("SSTL15D")),
97+
Subsignal("clk_n", Pins("J3"), IOStandard("SSTL15D")),
98+
Subsignal("cke", Pins("J2"), IOStandard("SSTL15")),
99+
Subsignal("odt", Pins("R3"), IOStandard("SSTL15")),
100+
Subsignal("reset_n", Pins("B9"), IOStandard("SSTL15")),
101+
),
102+
103+
# USB Type-C.
104+
("usb", 0,
105+
Subsignal("d_p", Pins("M14")),
106+
Subsignal("d_n", Pins("M15")),
107+
Subsignal("pullup", Pins("L15")),
108+
IOStandard("LVCMOS33")
109+
),
110+
111+
# Wired to onboard GD32 MCU.
112+
("mcu_bus", 0,
113+
Subsignal("data", Pins("PA0 PA1 PA2 PA3 PA4 PA5 PA6 PA7"), IOStandard("LVCMOS33"))),
114+
]
115+
116+
# Connector IOs ------------------------------------------------------------------------------------
117+
118+
_connectors = [
119+
("h5", {
120+
1: "K16", 2: "J15",
121+
3: "J14", 4: "J16",
122+
5: "F14", 6: "F16",
123+
7: "J13", 8: "H11",
124+
9: "E16", 10: "F15",
125+
11: "C16", 12: "D15",
126+
13: "D16", 14: "E14",
127+
15: "B13", 16: "A14",
128+
17: "B14", 18: "A15",
129+
19: "A12", 20: "B11",
130+
21: "B12", 22: "C12",
131+
23: "C11", 24: "A11",
132+
25: "C9", 26: "A9",
133+
27: "E10", 28: "D10",
134+
29: "N10", 30: "M11",
135+
31: "P12", 32: "P11",
136+
33: "T13", 34: "T11",
137+
35: "R14", 36: "T15",
138+
}),
139+
("h6", {
140+
7: "G15", 8: "G14",
141+
9: "G16", 10: "H15",
142+
11: "L9",
143+
13: "L8", 14: "N9",
144+
15: "M6", 16: "M8",
145+
17: "M7", 18: "T6",
146+
19: "N7", 20: "N8",
147+
21: "P6", 22: "R8",
148+
23: "T9", 24: "P9",
149+
25: "T8", 26: "P8",
150+
27: "K14", 28: "K15",
151+
29: "K13", 30: "K12",
152+
31: "N15", 32: "P16",
153+
33: "P15", 34: "R16",
154+
35: "N16", 36: "N14",
155+
}),
156+
]
157+
158+
# Platform -----------------------------------------------------------------------------------------
159+
160+
class Platform(GowinPlatform):
161+
default_clk_name = "clk50"
162+
default_clk_period = 1e9/50e6
163+
164+
def __init__(self, toolchain="gowin"):
165+
166+
GowinPlatform.__init__(self, "GW2A-LV18PG256C8/I7", _io, _connectors, toolchain=toolchain, devicename="GW2A-18C")
167+
168+
self.toolchain.options["use_mspi_as_gpio"] = 1
169+
self.toolchain.options["use_sspi_as_gpio"] = 1
170+
171+
def create_programmer(self, kit="openfpgaloader"):
172+
if kit == "gowin":
173+
# The board provides an external programmer with an emulated FT2232
174+
return GowinProgrammer(self.devicename, cable=GOWIN_CABLE_FT2CH)
175+
else:
176+
return OpenFPGALoader(cable="ft2232")
177+
178+
def do_finalize(self, fragment):
179+
GowinPlatform.do_finalize(self, fragment)
180+
self.add_period_constraint(self.lookup_request("clk50", loose=True), 1e9/50e6)

litex_boards/targets/lckfb_ljpi.py

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
#!/usr/bin/env python3
2+
3+
#
4+
# This file is part of LiteX-Boards.
5+
#
6+
# Copyright (c) 2024 Andelf <[email protected]>
7+
# SPDX-License-Identifier: BSD-2-Clause
8+
9+
from migen import *
10+
from migen.genlib.resetsync import AsyncResetSynchronizer
11+
12+
from litex.gen import *
13+
14+
from litex.soc.cores.clock.gowin_gw2a import GW2APLL
15+
from litex.soc.integration.soc_core import *
16+
from litex.soc.integration.builder import *
17+
from litex.soc.cores.led import LedChaser
18+
from litex.soc.cores.gpio import GPIOIn
19+
from litex.soc.cores.video import *
20+
21+
from litex_boards.platforms import lckfb_ljpi
22+
23+
from litedram.modules import MT41J128M16
24+
from litedram.phy import GW2DDRPHY
25+
26+
# CRG ----------------------------------------------------------------------------------------------
27+
28+
class _CRG(LiteXModule):
29+
def __init__(self, platform, sys_clk_freq, with_hdmi=False, with_dram=False):
30+
self.rst = Signal()
31+
self.cd_sys = ClockDomain()
32+
self.cd_por = ClockDomain()
33+
if with_dram:
34+
self.cd_init = ClockDomain()
35+
self.cd_sys2x = ClockDomain()
36+
self.cd_sys2x_i = ClockDomain()
37+
if with_hdmi:
38+
self.cd_hdmi = ClockDomain()
39+
self.cd_hdmi5x = ClockDomain()
40+
41+
# # #
42+
43+
self.stop = Signal()
44+
self.reset = Signal()
45+
46+
# Clk / Rst
47+
clk50 = platform.request("clk50")
48+
rst_n = platform.request("rst_n")
49+
50+
# Power on reset (the onboard POR is not aware of reprogramming)
51+
por_count = Signal(16, reset=2**16-1)
52+
por_done = Signal()
53+
self.comb += self.cd_por.clk.eq(clk50)
54+
self.comb += por_done.eq(por_count == 0)
55+
self.sync.por += If(~por_done, por_count.eq(por_count - 1))
56+
57+
# PLL
58+
self.pll = pll = GW2APLL(devicename=platform.devicename, device=platform.device)
59+
self.comb += pll.reset.eq(~por_done | ~rst_n)
60+
pll.register_clkin(clk50, 50e6)
61+
if with_dram:
62+
# 2:1 clock needed for DDR
63+
pll.create_clkout(self.cd_sys2x_i, 2*sys_clk_freq)
64+
self.specials += [
65+
Instance("DHCEN",
66+
i_CLKIN = self.cd_sys2x_i.clk,
67+
i_CE = self.stop,
68+
o_CLKOUT = self.cd_sys2x.clk),
69+
Instance("CLKDIV",
70+
p_DIV_MODE = "2",
71+
i_CALIB = 0,
72+
i_HCLKIN = self.cd_sys2x.clk,
73+
i_RESETN = ~self.reset,
74+
o_CLKOUT = self.cd_sys.clk),
75+
]
76+
77+
# Init clock domain
78+
self.comb += self.cd_init.clk.eq(clk50)
79+
self.comb += self.cd_init.rst.eq(pll.reset)
80+
else:
81+
pll.create_clkout(self.cd_sys, sys_clk_freq)
82+
83+
self.specials += AsyncResetSynchronizer(self.cd_sys, ~pll.locked | self.rst | self.reset)
84+
85+
# Video PLL
86+
if with_hdmi:
87+
self.video_pll = video_pll = GW2APLL(devicename=platform.devicename, device=platform.device)
88+
video_pll.register_clkin(clk50, 50e6)
89+
video_pll.create_clkout(self.cd_hdmi5x, 125e6, margin=1e-3)
90+
self.specials += Instance("CLKDIV",
91+
p_DIV_MODE = "5",
92+
i_RESETN = 1, # Disable reset signal.
93+
i_CALIB = 0, # No calibration.
94+
i_HCLKIN = self.cd_hdmi5x.clk,
95+
o_CLKOUT = self.cd_hdmi.clk
96+
)
97+
98+
# BaseSoC ------------------------------------------------------------------------------------------
99+
100+
class BaseSoC(SoCCore):
101+
def __init__(self, toolchain="gowin", sys_clk_freq=50e6,
102+
with_spi_flash = False,
103+
with_led_chaser = True,
104+
with_buttons = True,
105+
with_video_terminal = False,
106+
with_video_colorbars = False,
107+
**kwargs):
108+
109+
platform = lckfb_ljpi.Platform(toolchain=toolchain)
110+
111+
with_hdmi = with_video_terminal or with_video_colorbars
112+
113+
# CRG --------------------------------------------------------------------------------------
114+
with_dram = (kwargs.get("integrated_main_ram_size", 0) == 0)
115+
assert not (toolchain == "apicula" and with_dram)
116+
self.crg = _CRG(platform, sys_clk_freq, with_hdmi=with_hdmi, with_dram=with_dram)
117+
118+
# SoCCore ----------------------------------------------------------------------------------
119+
SoCCore.__init__(self, platform, sys_clk_freq, ident="LiteX SoC on LCKFB LJPI", **kwargs)
120+
121+
# DDR3 SDRAM -------------------------------------------------------------------------------
122+
# if not self.integrated_main_ram_size:
123+
if with_dram:
124+
self.ddrphy = GW2DDRPHY(
125+
pads = platform.request("ddram"),
126+
sys_clk_freq = sys_clk_freq
127+
)
128+
self.ddrphy.settings.rtt_nom = "disabled"
129+
self.comb += self.crg.stop.eq(self.ddrphy.init.stop)
130+
self.comb += self.crg.reset.eq(self.ddrphy.init.reset)
131+
self.add_sdram("sdram",
132+
phy = self.ddrphy,
133+
module = MT41J128M16(sys_clk_freq, "1:4"),
134+
l2_cache_size = kwargs.get("l2_size", 8192)
135+
)
136+
137+
# SPI Flash --------------------------------------------------------------------------------
138+
if with_spi_flash:
139+
from litespi.modules import W25Q64JV as SpiFlashModule
140+
from litespi.opcodes import SpiNorFlashOpCodes as Codes
141+
self.add_spi_flash(mode="1x", module=SpiFlashModule(Codes.READ_1_1_1))
142+
143+
# Video ------------------------------------------------------------------------------------
144+
if with_hdmi:
145+
hdmi_pads = platform.request("hdmi")
146+
self.videophy = VideoHDMIPHY(hdmi_pads, clock_domain="hdmi")
147+
if with_video_terminal:
148+
# self.add_video_terminal(phy=self.videophy, timings="640x480@75Hz", clock_domain="hdmi")
149+
self.add_video_terminal(phy=self.videophy, timings="800x600@75Hz", clock_domain="hdmi")
150+
if with_video_colorbars:
151+
# self.add_video_colorbars(phy=self.videophy, timings="640x480@60Hz", clock_domain="hdmi")
152+
# self.add_video_colorbars(phy=self.videophy, timings="800x600@75Hz", clock_domain="hdmi")
153+
self.add_video_colorbars(phy=self.videophy, timings="1024x768@60Hz", clock_domain="hdmi")
154+
155+
# Leds -------------------------------------------------------------------------------------
156+
if with_led_chaser:
157+
self.leds = LedChaser(
158+
pads = platform.request_all("led"),
159+
sys_clk_freq = sys_clk_freq
160+
)
161+
162+
# Buttons ----------------------------------------------------------------------------------
163+
if with_buttons:
164+
self.buttons = GPIOIn(pads=~platform.request_all("btn_n"))
165+
166+
167+
# Build --------------------------------------------------------------------------------------------
168+
169+
def main():
170+
from litex.build.parser import LiteXArgumentParser
171+
parser = LiteXArgumentParser(platform=lckfb_ljpi.Platform, description="LiteX SoC on LCKFB LJPI.")
172+
parser.add_target_argument("--flash", action="store_true", help="Flash Bitstream.")
173+
parser.add_target_argument("--sys-clk-freq", default=50e6, type=float, help="System clock frequency.")
174+
parser.add_target_argument("--with-spi-flash", action="store_true", help="Enable SPI Flash (MMAPed).")
175+
viopts = parser.target_group.add_mutually_exclusive_group()
176+
viopts.add_argument("--with-video-terminal", action="store_true", help="Enable Video Terminal (HDMI).")
177+
viopts.add_argument("--with-video-colorbars", action="store_true", help="Enable Video Colorbars (HDMI).")
178+
parser.add_target_argument("--prog-kit", default="gpwin", help="Programmer select from Gowin/openFPGALoader.")
179+
180+
args = parser.parse_args()
181+
182+
soc = BaseSoC(
183+
toolchain = args.toolchain,
184+
sys_clk_freq = args.sys_clk_freq,
185+
with_spi_flash = args.with_spi_flash,
186+
with_video_terminal = args.with_video_terminal,
187+
with_video_colorbars = args.with_video_colorbars,
188+
**parser.soc_argdict
189+
)
190+
191+
builder = Builder(soc, **parser.builder_argdict)
192+
if args.build:
193+
builder.build(**parser.toolchain_argdict)
194+
195+
if args.load:
196+
prog = soc.platform.create_programmer(kit=args.prog_kit)
197+
prog.load_bitstream(builder.get_bitstream_filename(mode="sram"))
198+
199+
if args.flash:
200+
prog = soc.platform.create_programmer(kit=args.prog_kit)
201+
prog.flash(0, builder.get_bitstream_filename(mode="flash", ext=".fs"))
202+
203+
if __name__ == "__main__":
204+
main()

0 commit comments

Comments
 (0)