From 27b03df88623b8a2b4b8c201489e13a20d322d78 Mon Sep 17 00:00:00 2001 From: hubmartin Date: Tue, 8 Feb 2022 21:47:58 +0100 Subject: [PATCH 1/2] Add Lattice ECP5 VIP board + HDMI output board support --- litex_boards/platforms/lattice_ecp5_vip.py | 144 +++++++++++++ litex_boards/targets/lattice_ecp5_vip.py | 239 +++++++++++++++++++++ 2 files changed, 383 insertions(+) create mode 100644 litex_boards/platforms/lattice_ecp5_vip.py create mode 100755 litex_boards/targets/lattice_ecp5_vip.py 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..f4120e5 --- /dev/null +++ b/litex_boards/targets/lattice_ecp5_vip.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python3 + +# +# This file is part of LiteX-Boards. +# +# Copyright (c) 2019 Arnaud Durand +# 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, WS2812 + +from litedram.modules import MT41J128M16, 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, 25.175e6) # 40e6) + #pll.create_clkout(self.cd_hdmi, 40e6) # for terminal "800x600@60Hz" + + #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), x5_clk_freq=None, 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) + + # WS2812 + self.submodules.ws2812 = WS2812(platform.request("ws2812"), nleds=4, sys_clk_freq=sys_clk_freq) + self.bus.add_slave(name="ws2812", slave=self.ws2812.bus, region=SoCRegion( + origin = 0x2000_0000, + size = 4*4, + )) + + # 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)") + parser.add_argument("--x5-clk-freq", type=int, help="Use X5 oscillator as system clock at the specified frequency") + 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)), + x5_clk_freq = args.x5_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() From c2cd4410dfac46dbe23c08cbef973cc9fc0d4fb6 Mon Sep 17 00:00:00 2001 From: hubmartin Date: Sat, 12 Feb 2022 17:50:07 +0100 Subject: [PATCH 2/2] Code cleanup, add copyright Signed-off-by: hubmartin --- litex_boards/targets/lattice_ecp5_vip.py | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/litex_boards/targets/lattice_ecp5_vip.py b/litex_boards/targets/lattice_ecp5_vip.py index f4120e5..4a054a8 100755 --- a/litex_boards/targets/lattice_ecp5_vip.py +++ b/litex_boards/targets/lattice_ecp5_vip.py @@ -4,6 +4,7 @@ # 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 @@ -18,9 +19,9 @@ 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, WS2812 +from litex.soc.cores.led import LedChaser -from litedram.modules import MT41J128M16, MT41K64M16 +from litedram.modules import MT41K64M16 from litedram.phy import ECP5DDRPHY from litex.soc.cores.video import VideoVGAPHY from litex.soc.cores.bitbang import I2CMaster @@ -38,7 +39,6 @@ class _CRG_VERSA(Module): self.clock_domains.cd_sys2x_i = ClockDomain(reset_less=True) # # # - self.stop = Signal() self.reset = Signal() @@ -76,22 +76,17 @@ class _CRG_VERSA(Module): # HDMI self.clock_domains.cd_hdmi = ClockDomain(reset_less=True) - #pll.create_clkout(self.cd_hdmi, 25.175e6) # 40e6) - #pll.create_clkout(self.cd_hdmi, 40e6) # for terminal "800x600@60Hz" - #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), x5_clk_freq=None, toolchain="trellis", + def __init__(self, sys_clk_freq=int(50e6), toolchain="trellis", with_led_chaser=True, with_video_terminal=True, with_video_framebuffer=False,**kwargs): @@ -182,20 +177,12 @@ class BaseSoC(SoCCore): #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) - # WS2812 - self.submodules.ws2812 = WS2812(platform.request("ws2812"), nleds=4, sys_clk_freq=sys_clk_freq) - self.bus.add_slave(name="ws2812", slave=self.ws2812.bus, region=SoCRegion( - origin = 0x2000_0000, - size = 4*4, - )) - # Running code from SPI flash had some side effects on BIOS with enabled DDR3 memory # So I reverted to the FPGA BRAM for BIOS. @@ -219,14 +206,12 @@ def main(): 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)") - parser.add_argument("--x5-clk-freq", type=int, help="Use X5 oscillator as system clock at the specified frequency") 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)), - x5_clk_freq = args.x5_clk_freq, **soc_core_argdict(args)) builder = Builder(soc, **builder_argdict(args)) builder.build(run=args.build)