Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lckfb_ljpi: Add support for LCKFB LJPI #632

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 180 additions & 0 deletions litex_boards/platforms/lckfb_ljpi.py
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 <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)
204 changes: 204 additions & 0 deletions litex_boards/targets/lckfb_ljpi.py
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 <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()