Skip to content
Merged
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ Some of the supported boards, see yours? Give LiteX-Boards a try!
├── kosagi_netv2
├── krtkl_snickerdoodle
├── lambdaconcept_ecpix5
├── lattice_certus_nx_versa
├── lattice_certuspro_nx_evn
├── lattice_certuspro_nx_versa
├── lattice_certuspro_nx_vvml
Expand Down
1 change: 1 addition & 0 deletions docs/boards_inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ python3 .github/scripts/generate_board_inventory.py --write
| `kosagi_netv2` | kosagi_netv2 | vivado | `100000000.0` | Ethernet, SDCard, SPI SDCard, PCIe | included |
| `krtkl_snickerdoodle` | krtkl_snickerdoodle | vivado | `100000000.0` | - | included |
| `lambdaconcept_ecpix5` | lambdaconcept_ecpix5 | trellis | `75000000.0` | Ethernet, Etherbone, SDCard, Video Terminal, Video Framebuffer | included |
| `lattice_certus_nx_versa` | lattice_certus_nx_versa | radiant | `75000000.0` | SPI Flash | included |
| `lattice_certuspro_nx_evn` | lattice_certuspro_nx_evn | radiant | `75000000.0` | - | included |
| `lattice_certuspro_nx_versa` | lattice_certuspro_nx_versa | radiant | `75000000.0` | PCIe | included |
| `lattice_certuspro_nx_vvml` | lattice_certuspro_nx_vvml | radiant | `75000000.0` | - | included |
Expand Down
123 changes: 123 additions & 0 deletions litex_boards/platforms/lattice_certus_nx_versa.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#
# This file is part of LiteX-Boards.
#
# Copyright (c) 2026 Myrtle Shah <gatecat@ds0.me>
# SPDX-License-Identifier: BSD-2-Clause

from litex.build.generic_platform import *
from litex.build.lattice import LatticeNexusPlatform
from litex.build.lattice.programmer import EcpprogProgrammer

# IOs ----------------------------------------------------------------------------------------------

_io = [
# 5.1. Clock Sources.
("clk12", 0, Pins("G13"), IOStandard("LVCMOS33")), # Ensure JP4 is installed.
("clk25", 0, Pins("F16"), IOStandard("LVCMOS33")),
("clk27", 0, Pins("G15"), IOStandard("LVCMOS33")),

# Clock signals are differential, but only the positive sides are named.
("clk100", 0, Pins("P1"), IOStandard("LVDS")),
("clk125", 0, Pins("L6"), IOStandard("LVDS")),

("clk100_clk_dis", 0, Pins("F15"), IOStandard("LVCMOS33")),
("clk125_clk_dis", 0, Pins("H10"), IOStandard("LVCMOS33")),

# 7.2. General Purpose Push Buttons (Active-Low).
("gsrn", 0, Pins("G3"), IOStandard("LVCMOS18")), # SW6
("programn", 0, Pins("C12"), IOStandard("LVCMOS18")), # SW4
("user_btn_n", 0, Pins("G4"), IOStandard("LVCMOS18")), # SW3
("user_btn_n", 1, Pins("G2"), IOStandard("LVCMOS18")), # SW5
("user_btn_n", 2, Pins("J16"), IOStandard("LVCMOS33")), # SW2

# 6.2. UART Topology (Close JP25 and JP26).
("serial", 0,
Subsignal("rx", Pins("F10"), IOStandard("LVCMOS33")),
Subsignal("tx", Pins("E13"), IOStandard("LVCMOS33")),
),

# 7.3. General Purpose LEDs (Active-Low).
("user_led_n", 0, Pins("B3"), IOStandard("LVCMOS33")), # Bank 2 Green.
("user_led_n", 1, Pins("A2"), IOStandard("LVCMOS33")), # Bank 2 Green.
("user_led_n", 2, Pins("H16"), IOStandard("LVCMOS33")), # Bank 2 Green.
("user_led_n", 3, Pins("B2"), IOStandard("LVCMOS33")), # Bank 2 Green.
("user_led_n", 4, Pins("H15"), IOStandard("LVCMOS33")), # Bank 2 Green.
("user_led_n", 5, Pins("H14"), IOStandard("LVCMOS33")), # Bank 2 Green.
("user_led_n", 6, Pins("H12"), IOStandard("LVCMOS33")), # Bank 2 Green.
("user_led_n", 7, Pins("J15"), IOStandard("LVCMOS33")), # Bank 2 Green.

("seven_seg_n", 0, Pins("G16 G14 G12 G11 E12 E10 E9 F9"), IOStandard("LVCMOS33")),

# 7.1. DIP Switches.
("user_dip_btn", 0, Pins("L10"), IOStandard("LVCMOS15H")),
("user_dip_btn", 1, Pins("E16"), IOStandard("LVCMOS18")),
("user_dip_btn", 2, Pins("L11"), IOStandard("LVCMOS15H")),
("user_dip_btn", 3, Pins("R3"), IOStandard("LVCMOS15H")),

# 6.3.1. SPI Configuration.
("spiflash", 0,
Subsignal("cs_n", Pins("C15")),
Subsignal("clk", Pins("C16")),
Subsignal("mosi", Pins("C14")),
Subsignal("miso", Pins("D16")),
Subsignal("wp", Pins("D15")),
Subsignal("hold", Pins("D12")),
IOStandard("LVCMOS18"),
),

("spiflash4x", 0,
Subsignal("cs_n", Pins("C15")),
Subsignal("clk", Pins("C16")),
Subsignal("dq", Pins("C14 D16 D15 D12")),
IOStandard("LVCMOS18"),
),

# DDR3 SDRAM.
("ddram", 0,
Subsignal("a", Pins(
"R4 P7 R2 L8 R6 T2 R5 P4 P6 T6 P8 T5 R7"),
IOStandard("SSTL15_I")),
Subsignal("ba", Pins("N8 R8 M8"), IOStandard("SSTL15_I")),
Subsignal("ras_n", Pins("R9"), IOStandard("SSTL15_I")),
Subsignal("cas_n", Pins("M10"), IOStandard("SSTL15_I")),
Subsignal("we_n", Pins("L9"), IOStandard("SSTL15_I")),
Subsignal("cs_n", Pins("M9"), IOStandard("SSTL15_I")),
Subsignal("dm", Pins("N12 P14"), IOStandard("SSTL15_I")),
Subsignal("dq", Pins(
"R13 N11 T12 R14 T11 P10 P11 R10 "
"M12 L12 M11 N13 M16 M13 M14 N14"),
IOStandard("SSTL15_I"),
Misc("TERMINATION=75"),
Misc("VREF=VREF1_LOAD")),
Subsignal("dqs_p", Pins("R12 N16"), IOStandard("SSTL15D_I"),
Misc("TERMINATION=OFF")),
Subsignal("clk_p", Pins("T9"), IOStandard("SSTL15D_I")),
Subsignal("cke", Pins("P9"), IOStandard("SSTL15_I")),
Subsignal("odt", Pins("N9"), IOStandard("SSTL15_I")),
Subsignal("reset_n", Pins("P5"), IOStandard("SSTL15_I")),
Misc("SLEWRATE=FAST"),
),
]

# Connectors ---------------------------------------------------------------------------------------

_connectors = [
# 8.7. PMOD Headers.
# PMOD signal number:
# 1 2 3 4 7 8 9 10
("PMOD0", "J11 J12 K11 K12 C8 C7 B7 A7"),
("PMOD1", "A6 B6 B5 A5 A4 B4 C4 C5"),
("PMOD2", "H1 H2 N1 K5 N2 L3 M3 L5"),
]

# Platform -----------------------------------------------------------------------------------------

class Platform(LatticeNexusPlatform):
default_clk_name = "clk12"
default_clk_period = 1e9 / 12e6

def __init__(self, toolchain="radiant", **kwargs):
LatticeNexusPlatform.__init__(self, "LFD2NX-40-8BG256C", _io, _connectors, toolchain=toolchain, **kwargs)

def create_programmer(self):
return EcpprogProgrammer()
167 changes: 167 additions & 0 deletions litex_boards/targets/lattice_certus_nx_versa.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#!/usr/bin/env python3

#
# This file is part of LiteX-Boards.
#
# Copyright (c) 2020 David Corrigan <davidcorrigan714@gmail.com>
# Copyright (c) 2020 Alan Green <avg@google.com>
# Copyright (c) 2026 Myrtle Shah <gatecat@ds0.me>
# SPDX-License-Identifier: BSD-2-Clause

from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer

from litex.gen import *

from litex_boards.platforms import lattice_certus_nx_versa

from litex.soc.cores.clock import NXPLL
from litex.soc.cores.led import LedChaser

from litex.soc.integration.soc import SoCCore
from litex.soc.integration.builder import Builder

from litedram.modules import MT41K64M16
from litedram.phy import NexusDDRPHY

# CRG ----------------------------------------------------------------------------------------------

class _CRG(LiteXModule):
def __init__(self, platform, sys_clk_freq, with_ddr3=False):
self.rst = Signal()
self.cd_init = ClockDomain()
self.cd_por = ClockDomain(reset_less=True)
self.cd_sys = ClockDomain()
self.cd_sys2x = ClockDomain()
self.cd_sys2x_i = ClockDomain()

# # #

self.stop = Signal()
self.reset = Signal()

# Clk / Rst.
clk100 = platform.request("clk100")
rst_n = platform.request("gsrn")

clk100_clk_dis = platform.request("clk100_clk_dis")

# Power-On Reset.
por_count = Signal(16, reset=2**16 - 1)
por_done = Signal()
self.comb += [
clk100_clk_dis.eq(1),
self.cd_por.clk.eq(clk100),
por_done.eq(por_count == 0),
]
self.sync.por += If(~por_done, por_count.eq(por_count - 1))

# PLL.
self.pll = pll = NXPLL(platform=platform, create_output_port_clocks=True)
self.comb += pll.reset.eq(~por_done | ~rst_n | self.rst)
pll.register_clkin(clk100, 100e6)
if with_ddr3:
pll.create_clkout(self.cd_sys2x_i, 2*sys_clk_freq)
pll.create_clkout(self.cd_init, 25e6)
self.specials += [
Instance("ECLKSYNC",
p_STOP_EN = "ENABLE",
i_ECLKIN = self.cd_sys2x_i.clk,
i_STOP = self.stop,
o_ECLKOUT = self.cd_sys2x.clk),
Instance("ECLKDIV",
p_ECLK_DIV = "2",
i_SLIP = 0,
i_ECLKIN = self.cd_sys2x.clk,
i_DIVRST = self.reset,
o_DIVOUT = self.cd_sys.clk),
AsyncResetSynchronizer(self.cd_init, ~pll.locked),
AsyncResetSynchronizer(self.cd_sys, ~pll.locked | self.reset),
]
else:
pll.create_clkout(self.cd_sys, sys_clk_freq)
self.specials += AsyncResetSynchronizer(self.cd_sys, ~pll.locked)


# BaseSoC ------------------------------------------------------------------------------------------

class BaseSoC(SoCCore):
def __init__(self, sys_clk_freq=75e6, toolchain="radiant",
with_led_chaser = True,
with_spi_flash = False,
with_ddr3 = False,
**kwargs):
platform = lattice_certus_nx_versa.Platform(toolchain=toolchain)

# CRG --------------------------------------------------------------------------------------
self.crg = _CRG(platform, sys_clk_freq, with_ddr3=with_ddr3)

# SoCCore ----------------------------------------------------------------------------------
SoCCore.__init__(self, platform, sys_clk_freq,
ident = "LiteX SoC on Certus-NX Versa Evaluation Board",
**kwargs)

# DDR3 SDRAM -------------------------------------------------------------------------------
if with_ddr3 and not self.integrated_main_ram_size:
self.ddrphy = NexusDDRPHY(platform.request("ddram"),
sys_clk_freq = sys_clk_freq)
self.comb += [
self.crg.stop.eq(self.ddrphy.init.stop),
self.crg.reset.eq(self.ddrphy.init.reset),
]
self.add_sdram("sdram",
phy = self.ddrphy,
module = MT41K64M16(sys_clk_freq, "1:2"),
l2_cache_size = kwargs.get("l2_size", 8192))

# Leds -------------------------------------------------------------------------------------
if with_led_chaser:
self.leds = LedChaser(
pads = platform.request_all("user_led_n"),
sys_clk_freq = sys_clk_freq,
polarity = 1)

# SPI Flash --------------------------------------------------------------------------------
if with_spi_flash:
from litespi.modules import MX25L12833F
from litespi.opcodes import SpiNorFlashOpCodes as Codes
self.add_spi_flash(
mode = "4x",
clk_freq = 100_000,
module = MX25L12833F(Codes.READ_4_4_4),
with_master = True)


# Build --------------------------------------------------------------------------------------------

def main():
from litex.build.parser import LiteXArgumentParser
parser = LiteXArgumentParser(platform=lattice_certus_nx_versa.Platform,
description = "LiteX SoC on Certus-NX Versa Evaluation Board.")
parser.add_target_argument("--flash", action="store_true", help="Flash bitstream.")
parser.add_target_argument("--sys-clk-freq", default=75e6, type=float, help="System clock frequency.")
parser.add_target_argument("--with-spi-flash", action="store_true", help="Enable memory-mapped SPI flash.")
parser.add_target_argument("--with-ddr3", action="store_true", help="Enable DDR3 SDRAM.")
args = parser.parse_args()

soc = BaseSoC(
sys_clk_freq = args.sys_clk_freq,
toolchain = args.toolchain,
with_spi_flash = args.with_spi_flash,
with_ddr3 = args.with_ddr3,
**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()
prog.load_bitstream(builder.get_bitstream_filename(mode="sram"))

if args.flash:
prog = soc.platform.create_programmer()
prog.flash(0, builder.get_bitstream_filename(mode="flash"))


if __name__ == "__main__":
main()