Merge pull request #348 from hubmartin/ecp5-vip

Draft: Add Lattice ECP5 VIP board + HDMI output board support
This commit is contained in:
enjoy-digital 2022-02-15 08:53:57 +01:00 committed by GitHub
commit 30afe26669
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 368 additions and 0 deletions

View File

@ -0,0 +1,144 @@
#
# This file is part of LiteX-Boards.
#
# Copyright (c) 2019 Arnaud Durand <arnaud.durand@unifr.ch>
# 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)

View File

@ -0,0 +1,224 @@
#!/usr/bin/env python3
#
# This file is part of LiteX-Boards.
#
# Copyright (c) 2019 Arnaud Durand <arnaud.durand@unifr.ch>
# 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()