-
Notifications
You must be signed in to change notification settings - Fork 297
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #632 from andelf/feat/add-lckfb-ljpi-support
lckfb_ljpi: Add support for LCKFB LJPI
- Loading branch information
Showing
2 changed files
with
384 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
# | ||
# This file is part of LiteX-Boards. | ||
# | ||
# Copyright (c) 2024 Andelf <[email protected]> | ||
# SPDX-License-Identifier: BSD-2-Clause | ||
# | ||
# LCKFB LJPI FPGA board: https://wiki.lckfb.com/zh-hans/fpga-ljpi/ | ||
|
||
from migen import * | ||
|
||
from litex.build.generic_platform import * | ||
from litex.build.gowin.platform import GowinPlatform | ||
from litex.build.gowin.programmer import GowinProgrammer, GOWIN_CABLE_FT2CH | ||
from litex.build.openfpgaloader import OpenFPGALoader | ||
|
||
|
||
# IOs ---------------------------------------------------------------------------------------------- | ||
|
||
_io = [ | ||
# Clk / Rst. | ||
("clk50", 0, Pins("T7"), IOStandard("LVCMOS33")), | ||
|
||
# Serial. | ||
("serial", 0, | ||
Subsignal("tx", Pins("F12")), | ||
Subsignal("rx", Pins("F13")), | ||
IOStandard("LVCMOS33") | ||
), | ||
|
||
# Leds. LED2 and LED3 are RGB LEDs. | ||
("led", 0, Pins( "R9"), IOStandard("LVCMOS33")), | ||
("led", 1, Pins("C10"), IOStandard("LVCMOS33")), | ||
("led", 3, Pins( "R7"), IOStandard("LVCMOS33")), | ||
("led", 2, Pins( "N6"), IOStandard("LVCMOS33")), | ||
("led", 4, Pins("T10"), IOStandard("LVCMOS33")), | ||
("led", 5, Pins( "P7"), IOStandard("LVCMOS33")), | ||
|
||
# Buttons. SW2 | ||
("btn_n", 0, Pins("D11"), IOStandard("LVCMOS33")), | ||
# ("btn_n", 1, Pins("F10"), IOStandard("LVCMOS33")), | ||
|
||
# Reset. Use SW3 as reset button. | ||
("rst_n", 0, Pins("F10"), IOStandard("LVCMOS33")), | ||
|
||
# SPIFlash. | ||
# W25Q64JVSSIQ | ||
("spiflash", 0, | ||
Subsignal("cs_n", Pins("M9"), IOStandard("LVCMOS33")), | ||
Subsignal("clk", Pins("L10"), IOStandard("LVCMOS33")), | ||
Subsignal("miso", Pins("P10"), IOStandard("LVCMOS33")), | ||
Subsignal("mosi", Pins("R10"), IOStandard("LVCMOS33")), | ||
), | ||
|
||
# HDMI(LVDS). | ||
("hdmi", 0, | ||
Subsignal("clk_p", Pins("M10")), | ||
Subsignal("clk_n", Pins("N11")), | ||
Subsignal("data0_p", Pins("R13")), | ||
Subsignal("data0_n", Pins("T14")), | ||
Subsignal("data1_p", Pins("R11")), | ||
Subsignal("data1_n", Pins("T12")), | ||
Subsignal("data2_p", Pins("R12")), | ||
Subsignal("data2_n", Pins("P13")), | ||
IOStandard("LVCMOS33D DRIVE=8"), | ||
Misc("PULL_MODE=NONE"), | ||
), | ||
|
||
# 8-segment LED display, Common Anode. | ||
("seg8", 0, Pins("G13"), IOStandard("LVCMOS33")), | ||
("seg8", 1, Pins("H16"), IOStandard("LVCMOS33")), | ||
("seg8", 2, Pins("H12"), IOStandard("LVCMOS33")), | ||
("seg8", 3, Pins("H13"), IOStandard("LVCMOS33")), | ||
("seg8", 4, Pins("H14"), IOStandard("LVCMOS33")), | ||
("seg8", 5, Pins("G12"), IOStandard("LVCMOS33")), | ||
("seg8", 6, Pins("G11"), IOStandard("LVCMOS33")), | ||
# ("seg8", 7, Pins("L14"), IOStandard("LVCMOS33")), | ||
|
||
# DDR3 SDRAM | ||
# MT41J128M16JT-125K. | ||
("ddram", 0, | ||
Subsignal("a", Pins("F7 A4 D6 F8 C4 E6 B1 D8 A5 F9 K3 B7 A3 C8"), | ||
IOStandard("SSTL15")), | ||
Subsignal("ba", Pins("H4 D3 H5"), IOStandard("SSTL15")), | ||
Subsignal("ras_n", Pins("R4"), IOStandard("SSTL15")), | ||
Subsignal("cas_n", Pins("R6"), IOStandard("SSTL15")), | ||
Subsignal("we_n", Pins("L2"), IOStandard("SSTL15")), | ||
Subsignal("cs_n", Pins("P5"), IOStandard("SSTL15")), | ||
Subsignal("dm", Pins("G1 K5"), IOStandard("SSTL15")), | ||
Subsignal("dq", Pins( | ||
"G5 F5 F4 F3 E2 C1 E1 B3", | ||
"M3 K4 N2 L1 P4 H3 R1 M2"), | ||
IOStandard("SSTL15"), | ||
Misc("VREF=INTERNAL")), | ||
Subsignal("dqs_p", Pins("G2 J5"), IOStandard("SSTL15D")), | ||
Subsignal("dqs_n", Pins("G3 K6"), IOStandard("SSTL15D")), | ||
Subsignal("clk_p", Pins("J1"), IOStandard("SSTL15D")), | ||
Subsignal("clk_n", Pins("J3"), IOStandard("SSTL15D")), | ||
Subsignal("cke", Pins("J2"), IOStandard("SSTL15")), | ||
Subsignal("odt", Pins("R3"), IOStandard("SSTL15")), | ||
Subsignal("reset_n", Pins("B9"), IOStandard("SSTL15")), | ||
), | ||
|
||
# USB Type-C. | ||
("usb", 0, | ||
Subsignal("d_p", Pins("M14")), | ||
Subsignal("d_n", Pins("M15")), | ||
Subsignal("pullup", Pins("L15")), | ||
IOStandard("LVCMOS33") | ||
), | ||
|
||
# Wired to onboard GD32 MCU. | ||
("mcu_bus", 0, | ||
Subsignal("data", Pins("PA0 PA1 PA2 PA3 PA4 PA5 PA6 PA7"), IOStandard("LVCMOS33"))), | ||
] | ||
|
||
# Connector IOs ------------------------------------------------------------------------------------ | ||
|
||
_connectors = [ | ||
("h5", { | ||
1: "K16", 2: "J15", | ||
3: "J14", 4: "J16", | ||
5: "F14", 6: "F16", | ||
7: "J13", 8: "H11", | ||
9: "E16", 10: "F15", | ||
11: "C16", 12: "D15", | ||
13: "D16", 14: "E14", | ||
15: "B13", 16: "A14", | ||
17: "B14", 18: "A15", | ||
19: "A12", 20: "B11", | ||
21: "B12", 22: "C12", | ||
23: "C11", 24: "A11", | ||
25: "C9", 26: "A9", | ||
27: "E10", 28: "D10", | ||
29: "N10", 30: "M11", | ||
31: "P12", 32: "P11", | ||
33: "T13", 34: "T11", | ||
35: "R14", 36: "T15", | ||
}), | ||
("h6", { | ||
7: "G15", 8: "G14", | ||
9: "G16", 10: "H15", | ||
11: "L9", | ||
13: "L8", 14: "N9", | ||
15: "M6", 16: "M8", | ||
17: "M7", 18: "T6", | ||
19: "N7", 20: "N8", | ||
21: "P6", 22: "R8", | ||
23: "T9", 24: "P9", | ||
25: "T8", 26: "P8", | ||
27: "K14", 28: "K15", | ||
29: "K13", 30: "K12", | ||
31: "N15", 32: "P16", | ||
33: "P15", 34: "R16", | ||
35: "N16", 36: "N14", | ||
}), | ||
] | ||
|
||
# Platform ----------------------------------------------------------------------------------------- | ||
|
||
class Platform(GowinPlatform): | ||
default_clk_name = "clk50" | ||
default_clk_period = 1e9/50e6 | ||
|
||
def __init__(self, toolchain="gowin"): | ||
|
||
GowinPlatform.__init__(self, "GW2A-LV18PG256C8/I7", _io, _connectors, toolchain=toolchain, devicename="GW2A-18C") | ||
|
||
self.toolchain.options["use_mspi_as_gpio"] = 1 | ||
self.toolchain.options["use_sspi_as_gpio"] = 1 | ||
|
||
def create_programmer(self, kit="openfpgaloader"): | ||
if kit == "gowin": | ||
# The board provides an external programmer with an emulated FT2232 | ||
return GowinProgrammer(self.devicename, cable=GOWIN_CABLE_FT2CH) | ||
else: | ||
return OpenFPGALoader(cable="ft2232") | ||
|
||
def do_finalize(self, fragment): | ||
GowinPlatform.do_finalize(self, fragment) | ||
self.add_period_constraint(self.lookup_request("clk50", loose=True), 1e9/50e6) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
#!/usr/bin/env python3 | ||
|
||
# | ||
# This file is part of LiteX-Boards. | ||
# | ||
# Copyright (c) 2024 Andelf <[email protected]> | ||
# SPDX-License-Identifier: BSD-2-Clause | ||
|
||
from migen import * | ||
from migen.genlib.resetsync import AsyncResetSynchronizer | ||
|
||
from litex.gen import * | ||
|
||
from litex.soc.cores.clock.gowin_gw2a import GW2APLL | ||
from litex.soc.integration.soc_core import * | ||
from litex.soc.integration.builder import * | ||
from litex.soc.cores.led import LedChaser | ||
from litex.soc.cores.gpio import GPIOIn | ||
from litex.soc.cores.video import * | ||
|
||
from litex_boards.platforms import lckfb_ljpi | ||
|
||
from litedram.modules import MT41J128M16 | ||
from litedram.phy import GW2DDRPHY | ||
|
||
# CRG ---------------------------------------------------------------------------------------------- | ||
|
||
class _CRG(LiteXModule): | ||
def __init__(self, platform, sys_clk_freq, with_hdmi=False, with_dram=False): | ||
self.rst = Signal() | ||
self.cd_sys = ClockDomain() | ||
self.cd_por = ClockDomain() | ||
if with_dram: | ||
self.cd_init = ClockDomain() | ||
self.cd_sys2x = ClockDomain() | ||
self.cd_sys2x_i = ClockDomain() | ||
if with_hdmi: | ||
self.cd_hdmi = ClockDomain() | ||
self.cd_hdmi5x = ClockDomain() | ||
|
||
# # # | ||
|
||
self.stop = Signal() | ||
self.reset = Signal() | ||
|
||
# Clk / Rst | ||
clk50 = platform.request("clk50") | ||
rst_n = platform.request("rst_n") | ||
|
||
# Power on reset (the onboard POR is not aware of reprogramming) | ||
por_count = Signal(16, reset=2**16-1) | ||
por_done = Signal() | ||
self.comb += self.cd_por.clk.eq(clk50) | ||
self.comb += por_done.eq(por_count == 0) | ||
self.sync.por += If(~por_done, por_count.eq(por_count - 1)) | ||
|
||
# PLL | ||
self.pll = pll = GW2APLL(devicename=platform.devicename, device=platform.device) | ||
self.comb += pll.reset.eq(~por_done | ~rst_n) | ||
pll.register_clkin(clk50, 50e6) | ||
if with_dram: | ||
# 2:1 clock needed for DDR | ||
pll.create_clkout(self.cd_sys2x_i, 2*sys_clk_freq) | ||
self.specials += [ | ||
Instance("DHCEN", | ||
i_CLKIN = self.cd_sys2x_i.clk, | ||
i_CE = self.stop, | ||
o_CLKOUT = self.cd_sys2x.clk), | ||
Instance("CLKDIV", | ||
p_DIV_MODE = "2", | ||
i_CALIB = 0, | ||
i_HCLKIN = self.cd_sys2x.clk, | ||
i_RESETN = ~self.reset, | ||
o_CLKOUT = self.cd_sys.clk), | ||
] | ||
|
||
# Init clock domain | ||
self.comb += self.cd_init.clk.eq(clk50) | ||
self.comb += self.cd_init.rst.eq(pll.reset) | ||
else: | ||
pll.create_clkout(self.cd_sys, sys_clk_freq) | ||
|
||
self.specials += AsyncResetSynchronizer(self.cd_sys, ~pll.locked | self.rst | self.reset) | ||
|
||
# Video PLL | ||
if with_hdmi: | ||
self.video_pll = video_pll = GW2APLL(devicename=platform.devicename, device=platform.device) | ||
video_pll.register_clkin(clk50, 50e6) | ||
video_pll.create_clkout(self.cd_hdmi5x, 125e6, margin=1e-3) | ||
self.specials += Instance("CLKDIV", | ||
p_DIV_MODE = "5", | ||
i_RESETN = 1, # Disable reset signal. | ||
i_CALIB = 0, # No calibration. | ||
i_HCLKIN = self.cd_hdmi5x.clk, | ||
o_CLKOUT = self.cd_hdmi.clk | ||
) | ||
|
||
# BaseSoC ------------------------------------------------------------------------------------------ | ||
|
||
class BaseSoC(SoCCore): | ||
def __init__(self, toolchain="gowin", sys_clk_freq=50e6, | ||
with_spi_flash = False, | ||
with_led_chaser = True, | ||
with_buttons = True, | ||
with_video_terminal = False, | ||
with_video_colorbars = False, | ||
**kwargs): | ||
|
||
platform = lckfb_ljpi.Platform(toolchain=toolchain) | ||
|
||
with_hdmi = with_video_terminal or with_video_colorbars | ||
|
||
# CRG -------------------------------------------------------------------------------------- | ||
with_dram = (kwargs.get("integrated_main_ram_size", 0) == 0) | ||
assert not (toolchain == "apicula" and with_dram) | ||
self.crg = _CRG(platform, sys_clk_freq, with_hdmi=with_hdmi, with_dram=with_dram) | ||
|
||
# SoCCore ---------------------------------------------------------------------------------- | ||
SoCCore.__init__(self, platform, sys_clk_freq, ident="LiteX SoC on LCKFB LJPI", **kwargs) | ||
|
||
# DDR3 SDRAM ------------------------------------------------------------------------------- | ||
# if not self.integrated_main_ram_size: | ||
if with_dram: | ||
self.ddrphy = GW2DDRPHY( | ||
pads = platform.request("ddram"), | ||
sys_clk_freq = sys_clk_freq | ||
) | ||
self.ddrphy.settings.rtt_nom = "disabled" | ||
self.comb += self.crg.stop.eq(self.ddrphy.init.stop) | ||
self.comb += self.crg.reset.eq(self.ddrphy.init.reset) | ||
self.add_sdram("sdram", | ||
phy = self.ddrphy, | ||
module = MT41J128M16(sys_clk_freq, "1:4"), | ||
l2_cache_size = kwargs.get("l2_size", 8192) | ||
) | ||
|
||
# SPI Flash -------------------------------------------------------------------------------- | ||
if with_spi_flash: | ||
from litespi.modules import W25Q64JV as SpiFlashModule | ||
from litespi.opcodes import SpiNorFlashOpCodes as Codes | ||
self.add_spi_flash(mode="1x", module=SpiFlashModule(Codes.READ_1_1_1)) | ||
|
||
# Video ------------------------------------------------------------------------------------ | ||
if with_hdmi: | ||
hdmi_pads = platform.request("hdmi") | ||
self.videophy = VideoHDMIPHY(hdmi_pads, clock_domain="hdmi") | ||
if with_video_terminal: | ||
# self.add_video_terminal(phy=self.videophy, timings="640x480@75Hz", clock_domain="hdmi") | ||
self.add_video_terminal(phy=self.videophy, timings="800x600@75Hz", clock_domain="hdmi") | ||
if with_video_colorbars: | ||
# self.add_video_colorbars(phy=self.videophy, timings="640x480@60Hz", clock_domain="hdmi") | ||
# self.add_video_colorbars(phy=self.videophy, timings="800x600@75Hz", clock_domain="hdmi") | ||
self.add_video_colorbars(phy=self.videophy, timings="1024x768@60Hz", clock_domain="hdmi") | ||
|
||
# Leds ------------------------------------------------------------------------------------- | ||
if with_led_chaser: | ||
self.leds = LedChaser( | ||
pads = platform.request_all("led"), | ||
sys_clk_freq = sys_clk_freq | ||
) | ||
|
||
# Buttons ---------------------------------------------------------------------------------- | ||
if with_buttons: | ||
self.buttons = GPIOIn(pads=~platform.request_all("btn_n")) | ||
|
||
|
||
# Build -------------------------------------------------------------------------------------------- | ||
|
||
def main(): | ||
from litex.build.parser import LiteXArgumentParser | ||
parser = LiteXArgumentParser(platform=lckfb_ljpi.Platform, description="LiteX SoC on LCKFB LJPI.") | ||
parser.add_target_argument("--flash", action="store_true", help="Flash Bitstream.") | ||
parser.add_target_argument("--sys-clk-freq", default=50e6, type=float, help="System clock frequency.") | ||
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 (HDMI).") | ||
viopts.add_argument("--with-video-colorbars", action="store_true", help="Enable Video Colorbars (HDMI).") | ||
parser.add_target_argument("--prog-kit", default="gpwin", help="Programmer select from Gowin/openFPGALoader.") | ||
|
||
args = parser.parse_args() | ||
|
||
soc = BaseSoC( | ||
toolchain = args.toolchain, | ||
sys_clk_freq = args.sys_clk_freq, | ||
with_spi_flash = args.with_spi_flash, | ||
with_video_terminal = args.with_video_terminal, | ||
with_video_colorbars = args.with_video_colorbars, | ||
**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(kit=args.prog_kit) | ||
prog.load_bitstream(builder.get_bitstream_filename(mode="sram")) | ||
|
||
if args.flash: | ||
prog = soc.platform.create_programmer(kit=args.prog_kit) | ||
prog.flash(0, builder.get_bitstream_filename(mode="flash", ext=".fs")) | ||
|
||
if __name__ == "__main__": | ||
main() |