From b278d8bcccf266178b0f754f09f4552119d26cbd Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 13 Oct 2020 11:10:36 +0100 Subject: [PATCH] Add CrossLink-NX VIP board platform and target --- litex_boards/platforms/crosslink_nx_vip.py | 356 +++++++++++++++++++++ litex_boards/targets/crosslink_nx_vip.py | 116 +++++++ 2 files changed, 472 insertions(+) create mode 100644 litex_boards/platforms/crosslink_nx_vip.py create mode 100644 litex_boards/targets/crosslink_nx_vip.py diff --git a/litex_boards/platforms/crosslink_nx_vip.py b/litex_boards/platforms/crosslink_nx_vip.py new file mode 100644 index 0000000..bbcb42e --- /dev/null +++ b/litex_boards/platforms/crosslink_nx_vip.py @@ -0,0 +1,356 @@ +# +# This file is part of LiteX-Boards. +# +# Copyright (c) 2020 David Corrigan +# Copyright (c) 2020 Alan Green +# Copyright (c) 2020 David Shah +# SPDX-License-Identifier: BSD-2-Clause + +from litex.build.generic_platform import * +from litex.build.lattice import LatticePlatform +from litex.build.lattice.programmer import LatticeProgrammer + +# IOs ---------------------------------------------------------------------------------------------- + +_io = [ + ("clk12", 0, Pins("L16"), IOStandard("LVCMOS33")), # Ensure JP2 is installed + + # Reference clocks. Why are there four 27MHz oscs. Is this really correct?? + ("clk27_0", 0, Pins("L5"), IOStandard("LVCMOS18")), + ("clk27_1", 0, Pins("L7"), IOStandard("LVCMOS18")), + ("clk27_2", 0, Pins("M2"), IOStandard("LVCMOS18")), + ("clk27_3", 0, Pins("Y2"), IOStandard("LVCMOS18")), + + # 8.1. General Purpose Push Buttons - all logic zero when pressed] + ("cam_reset", 0, Pins("T1"), IOStandard("LVCMOS18")), # SW1 + ("gsrn", 0, Pins("G13"), IOStandard("LVCMOS33")), # SW3 + ("programn", 0, Pins("E11"), IOStandard("LVCMOS33")), # SW4 + ("user_btn", 0, Pins("L20"), IOStandard("LVCMOS33")), # SW5 + ("user_btn", 1, Pins("L19"), IOStandard("LVCMOS33")), # SW6 + + ("serial", 0, + Subsignal("rx", Pins("F14"), IOStandard("LVCMOS33")), + Subsignal("tx", Pins("F16"), IOStandard("LVCMOS33")), + ), + + # Section 7.1 Status LEDs + ("user_led", 0, Pins("G14"), IOStandard("LVCMOS33")), # Green + ("user_led", 1, Pins("G15"), IOStandard("LVCMOS33")), # Green + ("user_led", 2, Pins("L13"), IOStandard("LVCMOS33")), # Green + ("user_led", 3, Pins("L14"), IOStandard("LVCMOS33")), # Green + + + # Section 8.1 DIP Switch + ("user_dip_btn", 0, Pins("R5"), IOStandard("LVCMOS18")), + ("user_dip_btn", 1, Pins("R6"), IOStandard("LVCMOS18")), + ("user_dip_btn", 2, Pins("Y5"), IOStandard("LVCMOS18")), + ("user_dip_btn", 3, Pins("W5"), IOStandard("LVCMOS18")), + + # SPI Flash + ("spiflash", 0, + Subsignal("cs_n", Pins("E13")), + Subsignal("clk", Pins("E12")), + Subsignal("mosi", Pins("D13")), + Subsignal("miso", Pins("D15")), + Subsignal("wp", Pins("D14")), + Subsignal("hold", Pins("D16")), + IOStandard("LVCMOS33") + ), + + ("spiflash4x", 0, + Subsignal("cs_n", Pins("E13")), + Subsignal("clk", Pins("E12")), + Subsignal("dq", Pins("D13 D15 D14 D16")), + IOStandard("LVCMOS33") + ), + + # Camera I2C buses + ("i2c", 0, + Subsignal("sda", Pins("N4")), + Subsignal("scl", Pins("N5")), + IOStandard("LVCMOS18") + ), + ("i2c", 1, + Subsignal("sda", Pins("N6")), + Subsignal("scl", Pins("N7")), + IOStandard("LVCMOS18") + ), + ("i2c", 2, + Subsignal("sda", Pins("P1")), + Subsignal("scl", Pins("P2")), + IOStandard("LVCMOS18") + ), + ("i2c", 3, + Subsignal("sda", Pins("P5")), + Subsignal("scl", Pins("P6")), + IOStandard("LVCMOS18") + ), + + # Shared camera control signals + ("cam_ctrl", + Subsignal("cam_reset", Pins("T1")), + Subsignal("cam_frame_sync", Pins("U1")), + ), + + # MIPI camera modules + # Note that use of MIPI_DPHY standard for + and LVCMOS12H for - is copied from Lattice PDC + ("camera", 0, + Subsignal("mclk", Pins("M3"), IOStandard("LVCMOS18")), + Subsignal("clkp", Pins("A2"), IOStandard("MIPI_DPHY")), + Subsignal("clkn", Pins("B1"), IOStandard("LVCMOS12H")), + Subsignal("dp", Pins("B2 A3 C2 A4"), IOStandard("MIPI_DPHY")), + Subsignal("dn", Pins("C1 B3 D1 B4"), IOStandard("LVCMOS12H")), + ), + ("camera", 1, + Subsignal("mclk", Pins("M4"), IOStandard("LVCMOS18")), + Subsignal("clkp", Pins("A8"), IOStandard("MIPI_DPHY")), + Subsignal("clkn", Pins("B8"), IOStandard("LVCMOS12H")), + Subsignal("dp", Pins("A7 A9 A6 A10"), IOStandard("MIPI_DPHY")), + Subsignal("dn", Pins("B7 B9 B6 B10"), IOStandard("LVCMOS12H")), + ), + ("camera", 2, + Subsignal("mclk", Pins("M5"), IOStandard("LVCMOS18")), + Subsignal("clkp", Pins("W11"), IOStandard("MIPI_DPHY")), + Subsignal("clkn", Pins("Y11"), IOStandard("LVCMOS12H")), + Subsignal("dp", Pins("V11 W13 U12 R12"), IOStandard("MIPI_DPHY")), + Subsignal("dn", Pins("U11 V12 T12 P12"), IOStandard("LVCMOS12H")), + ), + ("camera", 3, + Subsignal("mclk", Pins("M6"), IOStandard("LVCMOS18")), + Subsignal("clkp", Pins("T13"), IOStandard("MIPI_DPHY")), + Subsignal("clkn", Pins("T14"), IOStandard("LVCMOS12H")), + Subsignal("dp", Pins("Y15 U15 V17 P13"), IOStandard("MIPI_DPHY")), + Subsignal("dn", Pins("Y16 V16 U16 R13"), IOStandard("LVCMOS12H")), + ), +] + + +# Connectors --------------------------------------------------------------------------------------- + +_connectors = [ + # Link to ECP5 + ("UPSTREAM", { + "D0": "N14", + "D1": "M14", + "D2": "M16", + "D3": "M15", + "D4": "N15", + "D5": "N16", + "D6": "M17", + "D7": "M18", + "D8": "M19", + "D9": "M20", + "D10": "N19", + "D11": "N20", + "D12": "P19", + "D13": "P20", + "D14": "P17", + "D15": "P18", + "D16": "R17", + "D17": "R18", + "D18": "U20", + "D19": "T20", + "D20": "W20", + "D21": "V20", + "D22": "T18", + "D23": "U18", + "D24": "V18", + "D25": "V19", + "D26": "W19", + + "PCLK_DOWN": "Y19", + "GSRN": "G13", + "SDA": "E20", + "SCL": "F20", + + "UP_GPIO39": "F18", + "UP_GPIO40": "G19", + "UP_GPIO41": "L15", + "UP_GPIO42": "D17", + } + ), + # PMOD signal number: + # 1 2 3 4 7 8 9 10 + ("PMOD0", "D10 D9 D7 D8 D6 D5 D4 D3"), + ("PMOD1", "E10 E9 E7 E8 E4 E3 E2 F1"), + ("PMOD2", "J2 J1 K2 K1 K3 K4 E17 F13"), +] + +# Platform ----------------------------------------------------------------------------------------- + +class Platform(LatticePlatform): + def __init__(self, device="LIFCL", **kwargs): + assert device in ["LIFCL"] + LatticePlatform.__init__(self, device + "-40-9BG400C", _io, _connectors, toolchain="radiant", **kwargs) + + def create_programmer(self, mode = "direct"): + assert mode in ["direct","flash"] + + xcf_template_direct = """ + + + + + JTAG + + + 1 + Lattice + LIFCL + LIFCL-40 + 0x010f1043 + All + LIFCL-40 + + 8 + 11111111 + 1 + 0 + + {bitstream_file} + N/A + Static Random Access Memory (SRAM) + Fast Configuration + + + + + SEQUENTIAL + ENTIRED CHAIN + No Override + TLR + TLR + + 3 + + + USB2 + FTUSB-0 + + +""" + + xcf_template_flash = """ + + + + + JTAG2SPI + + + 1 + Lattice + LIFCL + LIFCL-40 + All + + 8 + 11111111 + 1 + 0 + + {bitstream_file} + External SPI Flash Memory (SPI FLASH) + Erase,Program,Verify + + + + + 1 + Lattice + LIFCL + LIFCL-40 + 0x010f1043 + All + LIFCL-40 + + 8 + 11111111 + 1 + 0 + + Static Random Access Memory (SRAM) + Refresh Verify ID + + + + + + 1 + Macronix + SPI Serial Flash + MX25L12833F + 0x18 + 8-pin SOP + Erase,Program,Verify + {bitstream_file} + 0x00000000 + 0x000F0000 + 128 + 1016029 + 1 + + + + + + 1 + + {bitstream_file} + + + + + + + + + + + SEQUENTIAL + ENTIRED CHAIN + No Override + TLR + TLR + + + 3 + + + USB2 + FTUSB-0 + Lattice ECP5 VIP Processor Board 0000 Serial FT4RXXZ5 + + +""" + + if mode == "direct": + xcf_template = xcf_template_direct + if mode == "flash": + xcf_template = xcf_template_flash + + return LatticeProgrammer(xcf_template) + + + diff --git a/litex_boards/targets/crosslink_nx_vip.py b/litex_boards/targets/crosslink_nx_vip.py new file mode 100644 index 0000000..9c503e5 --- /dev/null +++ b/litex_boards/targets/crosslink_nx_vip.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 + +# +# This file is part of LiteX-Boards. +# +# Copyright (c) 2020 David Corrigan +# Copyright (c) 2020 Alan Green +# Copyright (c) 2020 David Shah +# +# SPDX-License-Identifier: BSD-2-Clause + +import os +import argparse + +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer + +from litex_boards.platforms import crosslink_nx_vip + +from litex.soc.cores.nxlram import NXLRAM +from litex.soc.cores.spi_flash import SpiFlash +from litex.build.io import CRG +from litex.build.generic_platform import * + +from litex.soc.cores.clock import * +from litex.soc.integration.soc_core import * +from litex.soc.integration.builder import * +from litex.soc.cores.led import LedChaser + +kB = 1024 +mB = 1024*kB + + +# CRG ---------------------------------------------------------------------------------------------- + +class _CRG(Module): + def __init__(self, platform, sys_clk_freq): + self.clock_domains.cd_sys = ClockDomain() + self.clock_domains.cd_por = ClockDomain() + + # TODO: replace with PLL + # Clocking + self.submodules.sys_clk = sys_osc = NXOSCA() + sys_osc.create_hf_clk(self.cd_sys, sys_clk_freq) + platform.add_period_constraint(self.cd_sys.clk, 1e9/sys_clk_freq) + rst_n = platform.request("gsrn") + + # Power On Reset + por_cycles = 4096 + por_counter = Signal(log2_int(por_cycles), reset=por_cycles-1) + self.comb += self.cd_por.clk.eq(self.cd_sys.clk) + self.sync.por += If(por_counter != 0, por_counter.eq(por_counter - 1)) + self.specials += AsyncResetSynchronizer(self.cd_por, ~rst_n) + self.specials += AsyncResetSynchronizer(self.cd_sys, (por_counter != 0)) + + +# BaseSoC ------------------------------------------------------------------------------------------ + +class BaseSoC(SoCCore): + SoCCore.mem_map = { + "rom": 0x00000000, + "sram": 0x40000000, + "csr": 0xf0000000, + } + def __init__(self, sys_clk_freq, **kwargs): + platform = crosslink_nx_vip.Platform() + + platform.add_platform_command("ldc_set_sysconfig {{MASTER_SPI_PORT=SERIAL}}") + + # Disable Integrated SRAM since we want to instantiate LRAM specifically for it + kwargs["integrated_sram_size"] = 0 + + # SoCCore -----------------------------------------_---------------------------------------- + SoCCore.__init__(self, platform, sys_clk_freq, + ident = "LiteX SoC on Crosslink-NX VIP Input Board", + ident_version = True, + **kwargs) + + # CRG -------------------------------------------------------------------------------------- + self.submodules.crg = _CRG(platform, sys_clk_freq) + + # 128KB LRAM (used as SRAM) --------------------------------------------------------------- + size = 128*kB + self.submodules.spram = NXLRAM(32, size) + self.register_mem("sram", self.mem_map["sram"], self.spram.bus, size) + + # Leds ------------------------------------------------------------------------------------- + self.submodules.leds = LedChaser( + pads = Cat(*[platform.request("user_led", i) for i in range(4)]), + sys_clk_freq = sys_clk_freq) + self.add_csr("leds") + + +# Build -------------------------------------------------------------------------------------------- + +def main(): + parser = argparse.ArgumentParser(description="LiteX SoC on Crosslink-NX Eval Board") + parser.add_argument("--build", action="store_true", help="Build bitstream") + parser.add_argument("--load", action="store_true", help="Load bitstream") + parser.add_argument("--sys-clk-freq", default=75e6, help="System clock frequency (default=75MHz)") + parser.add_argument("--prog-target", default="direct", help="Programming Target: direct or flash") + builder_args(parser) + soc_core_args(parser) + args = parser.parse_args() + + soc = BaseSoC(sys_clk_freq=int(float(args.sys_clk_freq)), **soc_core_argdict(args)) + builder = Builder(soc, **builder_argdict(args)) + builder_kargs = {} + builder.build(**builder_kargs, run=args.build) + + if args.load: + prog = soc.platform.create_programmer(args.prog_target) + prog.load_bitstream(os.path.join(builder.gateware_dir, soc.build_name + ".bit")) + +if __name__ == "__main__": + main()