Merge pull request #632 from andelf/feat/add-lckfb-ljpi-support

lckfb_ljpi: Add support for LCKFB LJPI
This commit is contained in:
enjoy-digital 2024-12-30 20:14:25 +01:00 committed by GitHub
commit fbaa9be731
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 384 additions and 0 deletions

View file

@ -0,0 +1,180 @@
#
# This file is part of LiteX-Boards.
#
# Copyright (c) 2024 Andelf <andelf@gmail.com>
# 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)

View file

@ -0,0 +1,204 @@
#!/usr/bin/env python3
#
# This file is part of LiteX-Boards.
#
# Copyright (c) 2024 Andelf <andelf@gmail.com>
# 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()