diff --git a/litex_boards/platforms/lattice_ecp5_vip.py b/litex_boards/platforms/lattice_ecp5_vip.py new file mode 100644 index 0000000..35acd6d --- /dev/null +++ b/litex_boards/platforms/lattice_ecp5_vip.py @@ -0,0 +1,144 @@ +# +# This file is part of LiteX-Boards. +# +# Copyright (c) 2019 Arnaud Durand +# SPDX-License-Identifier: BSD-2-Clause + +from litex.build.generic_platform import * +from litex.build.lattice import LatticePlatform +from litex.build.lattice.programmer import OpenOCDJTAGProgrammer + +import os + +# IOs ---------------------------------------------------------------------------------------------- + +_io = [ + # Clk / Rst + ("clk27", 0, Pins("E17"), IOStandard("LVCMOS33")), ## + ("clk100", 0, Pins("C5"), IOStandard("LVDS")), ## + ("ext_clk50", 0, Pins("B11"), IOStandard("LVCMOS33")), + ("ext_clk50_en", 0, Pins("C11"), IOStandard("LVCMOS33")), + ("rst_n", 0, Pins("AH1"), IOStandard("LVCMOS33")),## + + # Leds + ("user_led", 0, Pins("AG30"), IOStandard("LVCMOS25")), + ("user_led", 1, Pins("AK29"), IOStandard("LVCMOS25")), + ("user_led", 2, Pins("AK30"), IOStandard("LVCMOS25")), + ("user_led", 3, Pins("AH32"), IOStandard("LVCMOS25")), + ("user_led", 4, Pins("AG32"), IOStandard("LVCMOS25")), + ("user_led", 5, Pins("AJ29"), IOStandard("LVCMOS25")), + ("user_led", 6, Pins("AM28"), IOStandard("LVCMOS25")), + ("user_led", 7, Pins("AM29"), IOStandard("LVCMOS25")), + + # ws2812 + ("ws2812", 0, Pins("AK31"), IOStandard("LVCMOS25")), + + # Buttons + ("user_dip_btn", 1, Pins("J1"), IOStandard("LVCMOS33")), + ("user_dip_btn", 2, Pins("H1"), IOStandard("LVCMOS33")), + ("user_dip_btn", 3, Pins("K1"), IOStandard("LVCMOS33")), + ("user_dip_btn", 4, Pins("E15"), IOStandard("LVCMOS25")), + ("user_dip_btn", 5, Pins("D16"), IOStandard("LVCMOS25")), + ("user_dip_btn", 6, Pins("B16"), IOStandard("LVCMOS25")), + ("user_dip_btn", 7, Pins("C16"), IOStandard("LVCMOS25")), + ("user_dip_btn", 8, Pins("A16"), IOStandard("LVCMOS25")), + + ("button_1", 0, Pins("P4"), IOStandard("LVCMOS25")), + + # Serial + ("serial", 0, + Subsignal("rx", Pins("AL28"), IOStandard("LVCMOS25")), ##LVCMOS25 + Subsignal("tx", Pins("AK28"), IOStandard("LVCMOS25")), ##LVCMOS25 + ), + + # DDR3 SDRAM + ("ddram", 0, + Subsignal("a", Pins( + " W4 Y5 AB6 P3 AB5 W5 AC6 Y7", + "AC7 AD6 W1 Y6 U1 AD7"), + IOStandard("SSTL135_I")), + Subsignal("ba", Pins("U3 Y4 W3"), IOStandard("SSTL135_I")), + Subsignal("ras_n", Pins("C2"), IOStandard("SSTL135_I")), + Subsignal("cas_n", Pins("T1"), IOStandard("SSTL135_I")), + Subsignal("we_n", Pins("P1"), IOStandard("SSTL135_I")), + Subsignal("cs_n", Pins("P2"), IOStandard("SSTL135_I")), + Subsignal("dm", Pins("AB3 R6 F2 H6"), IOStandard("SSTL135_I")), + Subsignal("dq", Pins( + "AC5 AC2 AB4 AE3 W2 AD4 Y1 AB1", + "V6 P4 V7 T7 U6 T4 U7 U4", + "H2 K1 F1 L2 E1 K3 H3 H1", + "N7 J6 L4 K7 P7 L7 K6 H5"), + IOStandard("SSTL135_I"), + Misc("TERMINATION=75")), + Subsignal("dqs_p", Pins("AC3 R4 K2 N3"), IOStandard("SSTL135D_I"), + Misc("TERMINATION=OFF"), + Misc("DIFFRESISTOR=100")), + Subsignal("clk_p", Pins("R3 J4"), IOStandard("SSTL135D_I")), + Subsignal("cke", Pins("T2"), IOStandard("SSTL135_I")), + Subsignal("odt", Pins("V1"), IOStandard("SSTL135_I")), + Subsignal("reset_n", Pins("C4"), IOStandard("SSTL135_I")), + Misc("SLEWRATE=FAST"), + ), + + # SPIFlash + ("spiflash", 0, + Subsignal("cs_n", Pins("AJ3"), IOStandard("LVCMOS33")), + Subsignal("mosi", Pins("AK2"), IOStandard("LVCMOS33")), + Subsignal("miso", Pins("AJ2"), IOStandard("LVCMOS33")), + #Subsignal("wp", Pins("Y2"), IOStandard("LVCMOS33")), + #Subsignal("hold", Pins("W1"), IOStandard("LVCMOS33")), + ), + ("spiflash4x", 0, + Subsignal("cs_n", Pins("R2"), IOStandard("LVCMOS33")), + Subsignal("dq", Pins("W2 V2 Y2 W1"), IOStandard("LVCMOS33")), + ), + + # HDMI + ("hdmi", 0, + Subsignal("clk", Pins("E25")), + Subsignal("hsync_n", Pins("D25")), + Subsignal("vsync_n", Pins("A25")), + + Subsignal("de", Pins("C25")), + + Subsignal("r", Pins("AE27 AD27 AB29 AB30 AB28 AB27 AC26 Y27 D24 W28 F25 F17")), + Subsignal("g", Pins("AD26 T26 R26 A24 T32 AC30 AB31 V32 W32 Y26 W30 T30")), + Subsignal("b", Pins("T31 R32 Y32 W31 T29 U28 V27 V26 AC31 AB32 AC32 AD32")), + + Subsignal("sda", Pins("AJ1")), + Subsignal("scl", Pins("AG1")), + + IOStandard("LVCMOS25") + ), +] + +# Platform ----------------------------------------------------------------------------------------- + +class Platform(LatticePlatform): + default_clk_name = "clk12" + default_clk_period = 1e9/12e6 + + def __init__(self, toolchain="trellis", **kwargs): + LatticePlatform.__init__(self, "LFE5UM-85F-8BG756", _io, toolchain=toolchain, **kwargs) + + def request(self, *args, **kwargs): + import time + if "serial" in args: + msg = "FT2232H will be used as serial, make sure that:\n" + msg += " -the hardware has been modified: R22 and R23 should be removed, two 0 Ω resistors shoud be populated on R34 and R35.\n" + msg += " -the chip is configured as UART with virtual COM on port B (With FTProg or https://github.com/trabucayre/fixFT2232_ecp5evn)." + print(msg) + time.sleep(2) + if "ext_clk50" in args: + print("An oscillator must be populated on X5.") + time.sleep(2) + + return LatticePlatform.request(self, *args, **kwargs) + + def create_programmer(self): + return OpenOCDJTAGProgrammer("openocd_evn_ecp5.cfg") + + def do_finalize(self, fragment): + LatticePlatform.do_finalize(self, fragment) + self.add_period_constraint(self.lookup_request("clk12", loose=True), 1e9/12e6) + self.add_period_constraint(self.lookup_request("clk200", loose=True), 1e9/200e6) diff --git a/litex_boards/targets/lattice_ecp5_vip.py b/litex_boards/targets/lattice_ecp5_vip.py new file mode 100755 index 0000000..4a054a8 --- /dev/null +++ b/litex_boards/targets/lattice_ecp5_vip.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python3 + +# +# This file is part of LiteX-Boards. +# +# Copyright (c) 2019 Arnaud Durand +# Copyright (c) 2022 Martin Hubacek @hubmartin (Twitter) +# SPDX-License-Identifier: BSD-2-Clause + +import os +import argparse + +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer + +from litex_boards.platforms import ecp5_vip + +from litex.soc.cores.clock import * +from litex.soc.integration.soc_core import * +from litex.soc.integration.builder import * +from litex.soc.integration.soc import SoCRegion +from litex.soc.cores.led import LedChaser + +from litedram.modules import MT41K64M16 +from litedram.phy import ECP5DDRPHY +from litex.soc.cores.video import VideoVGAPHY +from litex.soc.cores.bitbang import I2CMaster + + +# CRG ---------------------------------------------------------------------------------------------- + +class _CRG_VERSA(Module): + def __init__(self, platform, sys_clk_freq): + self.rst = Signal() + self.clock_domains.cd_init = ClockDomain() + self.clock_domains.cd_por = ClockDomain(reset_less=True) + self.clock_domains.cd_sys = ClockDomain() + self.clock_domains.cd_sys2x = ClockDomain() + self.clock_domains.cd_sys2x_i = ClockDomain(reset_less=True) + + # # # + self.stop = Signal() + self.reset = Signal() + + # Clk / Rst + clk100 = platform.request("clk100") + rst_n = platform.request("rst_n") + + # Power on reset + por_count = Signal(16, reset=2**16-1) + por_done = Signal() + self.comb += self.cd_por.clk.eq(clk100) + self.comb += por_done.eq(por_count == 0) + self.sync.por += If(~por_done, por_count.eq(por_count - 1)) + + # PLL + self.submodules.pll = pll = ECP5PLL() + self.comb += pll.reset.eq(~por_done | ~rst_n | self.rst) + pll.register_clkin(clk100, 100e6) + pll.create_clkout(self.cd_sys2x_i, 2*sys_clk_freq) + pll.create_clkout(self.cd_init, 25e6) + self.specials += [ + Instance("ECLKSYNCB", + i_ECLKI = self.cd_sys2x_i.clk, + i_STOP = self.stop, + o_ECLKO = self.cd_sys2x.clk), + Instance("CLKDIVF", + p_DIV = "2.0", + i_ALIGNWD = 0, + i_CLKI = self.cd_sys2x.clk, + i_RST = self.reset, + o_CDIVX = self.cd_sys.clk), + AsyncResetSynchronizer(self.cd_sys, ~pll.locked | self.reset), + AsyncResetSynchronizer(self.cd_sys2x, ~pll.locked | self.reset), + ] + + # HDMI + self.clock_domains.cd_hdmi = ClockDomain(reset_less=True) + #pll.create_clkout(self.cd_hdmi, 148.5e6) # for terminal "1920x1080@60Hz" + #pll.create_clkout(self.cd_hdmi, 160e6) # for terminal "1920x1080@60Hz" + #pll.create_clkout(self.cd_hdmi, 80e6) # for terminal "1920x1080@30Hz" + pll.create_clkout(self.cd_hdmi, 40e6) # for terminal "800x600@60Hz" + + +# BaseSoC ------------------------------------------------------------------------------------------ + +class BaseSoC(SoCCore): + #mem_map = {**SoCCore.mem_map, **{"spiflash": 0x1000000}} + def __init__(self, sys_clk_freq=int(50e6), toolchain="trellis", + with_led_chaser=True, + with_video_terminal=True, + with_video_framebuffer=False,**kwargs): + platform = ecp5_vip.Platform(toolchain=toolchain) + + #bios_flash_offset = 0x400000 + + # Set CPU variant / reset address + #kwargs["cpu_reset_address"] = self.mem_map["spiflash"] + bios_flash_offset + kwargs["integrated_rom_size"] = 0x10000 + + # SoCCore ---------------------------------------------------------------------------------- + SoCCore.__init__(self, platform, sys_clk_freq, + ident = "LiteX SoC on ECP5 Evaluation Board", + #ident_version = True, + #integrated_main_ram_size = 0x4000, + integrated_main_ram_size = 0, + **kwargs) + + # CRG -------------------------------------------------------------------------------------- + crg_cls = _CRG_VERSA + self.submodules.crg = crg_cls(platform, sys_clk_freq) + + # DDR3 SDRAM ------------------------------------------------------------------------------- + if crg_cls == _CRG_VERSA: + self.submodules.ddrphy = ECP5DDRPHY( + platform.request("ddram"), + sys_clk_freq=sys_clk_freq) + 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 = MT41K64M16(sys_clk_freq, "1:2"), # Not entirely MT41J64M16 but similar and works(c) + l2_cache_size = kwargs.get("l2_size", 8192), + ) + + # Video ------------------------------------------------------------------------------------ + if with_video_terminal or with_video_framebuffer: + pads = platform.request("hdmi") + self.submodules.videophy = VideoVGAPHY(pads, clock_domain="hdmi") + self.submodules.videoi2c = I2CMaster(pads) + + # # 1920x1080@60Hz + # pixel_clock_hz = 160e6 + # framerate_hz = 60 + # pixels_horizontal = 2200 + # pixels_vertical = 1125 + + # 800x600@60Hz + pixel_clock_hz = 40e6 + framerate_hz = 60 + pixels_horizontal = 1056 + pixels_vertical = 628 + + # # 1920x1080@30Hz + # pixel_clock_hz = 80e6 + # framerate_hz = 30 + # pixels_horizontal = 2640 + # pixels_vertical = 1125 + + self.videoi2c.add_init(addr=0x3B, init=[ + (0xc7, 0x00), # HDMI configuration + (0xc7, 0x00), # Write twice, the first transfer fails for some reason + + (0x1e, 0x00), # Power up transmitter + (0x08, 0x60), # Input Bus/Pixel Repetition (default) + + (0x00, int((pixel_clock_hz/1e4) %256)), # Pixel clock in MHz * 100 + (0x01, int((pixel_clock_hz/1e4)//256)), # + + (0x02, int((framerate_hz*100) %256)), # Framerate * 100 + (0x03, int((framerate_hz*100)//256)), # + + (0x04, int((pixels_horizontal) %256)), # Pixels horizontal + (0x05, int((pixels_horizontal)//256)), # + + (0x06, int((pixels_vertical) %256)), # Pixels vertical + (0x07, int((pixels_vertical)//256)), # + + (0x1a, 0x00) # end + + ]) + if with_video_terminal: + #self.add_video_terminal(phy=self.videophy, timings="1920x1080@60Hz", clock_domain="hdmi") + #self.add_video_terminal(phy=self.videophy, timings="1920x1080@30Hz", clock_domain="hdmi") + self.add_video_terminal(phy=self.videophy, timings="800x600@60Hz", clock_domain="hdmi") + if with_video_framebuffer: + #self.add_video_framebuffer(phy=self.videophy, timings="800x600@60Hz", clock_domain="hdmi") + self.add_video_framebuffer(phy=self.videophy, timings="640x480@60Hz", clock_domain="hdmi") + + # Leds ------------------------------------------------------------------------------------- + if with_led_chaser: + self.submodules.leds = LedChaser( + pads = platform.request_all("user_led"), + sys_clk_freq = sys_clk_freq) + + # Running code from SPI flash had some side effects on BIOS with enabled DDR3 memory + # So I reverted to the FPGA BRAM for BIOS. + + # # SPI Flash -------------------------------------------------------------------------------- + # from litespi.modules import MX25L12835F + # from litespi.opcodes import SpiNorFlashOpCodes as Codes + # self.add_spi_flash(mode="1x", module=MX25L12835F(Codes.READ_1_1_1), with_master=False) + + # # Add ROM linker region -------------------------------------------------------------------- + # self.bus.add_region("rom", SoCRegion( + # origin = self.mem_map["spiflash"] + bios_flash_offset, + # size = (16-4)*1024*1024, + # linker = True) + # ) + +# Build -------------------------------------------------------------------------------------------- + +def main(): + parser = argparse.ArgumentParser(description="LiteX SoC on ECP5 Evaluation Board") + parser.add_argument("--build", action="store_true", help="Build bitstream") + parser.add_argument("--load", action="store_true", help="Load bitstream") + parser.add_argument("--toolchain", default="trellis", help="FPGA toolchain: trellis (default) or diamond") + parser.add_argument("--sys-clk-freq", default=60e6, help="System clock frequency (default: 60MHz)") + builder_args(parser) + soc_core_args(parser) + args = parser.parse_args() + + soc = BaseSoC(toolchain=args.toolchain, + sys_clk_freq = int(float(args.sys_clk_freq)), + **soc_core_argdict(args)) + builder = Builder(soc, **builder_argdict(args)) + builder.build(run=args.build) + + if args.load: + prog = soc.platform.create_programmer() + prog.load_bitstream(os.path.join(builder.gateware_dir, soc.build_name + ".svf")) + +if __name__ == "__main__": + main()