Развертывание LiteX SoC с процессором VexRiscv на отладочной плате VE-10CL025

Продолжая цикл статей, посвященных RISC-V, в новой статье расскажем как создать собственную SOC с ядром процессора RISC-V, используя систему LiteX. LiteX это генератор Core/SoC основанный на Migen/MiSoC , который предоставляет инфраструктуру для легкого создания ядер SoC (с процессором или без него). Общие компоненты SoC предоставляются непосредственно: Шины и потоки (Wishbone, AXI, Avalon-ST), интерконнект, общие ядра (RAM, ROM, Timer, UART и т. д…), Процессорные оболочки/интеграция и т. д… а возможности создания SoC могут быть значительно расширены с помощью экосистемы ядер LiteX (DRAM, PCIe, Ethernet, SATA и т. д…), которые могут быть легко интегрированы/смоделированы/построены с помощью LiteX. Он также предоставляет бэкенды сборки для цепочек инструментов с открытым исходным кодом.

В общем случае Migen используется для создания дизайнов ПЛИС на языке Python, а LiteX как генератор SOC для создания/разработки/отладки систем на кристалле ПЛИС на языке Python.

Репозиторий проекта находится по адресу: https://github.com/enjoy-digital/litex. Для наших экспериментов будем использовать WSL (Windows Subsystem for Linux). Для начала запустим WSL и перейдем в домашний каталог:

$cd /home/vise

Далее создадим каталог для развертывания LiteX, и перейдем в него:

$ mkdir risc-v
cd risc-v

На следующем шаге необходимо установить Python 3.6+ в WSL, а в самой windows среду разработки для целевой FPGA. В нашем случае это Quartus V19.1.

Устанавливаеи Migen/LiteX и ядра LiteX’а:

$ wget https://raw.githubusercontent.com/enjoy-digital/litex/master/litex_setup.py
$ chmod +x litex_setup.py
$ sudo ./litex_setup.py init install --user (--user to install to user directory)

Далее, если необходимо, обновляем репозитории:

$ ./litex_setup.py update

Если вы используете MacOS, убедитесь, что пакет HomeBrew установлен. После выполните, brew install wget.

Если вы используете Windows, Возможно вам придется установить переменную SHELL в SHELL=cmd.exe.

Устанавливаем RISC-V тулчейн (Если вы собираетесь тестировать/создавать SoC с процессором):

$ ./litex_setup.py gcc

Добавляем путь до нашего компилятора в переменную PATH:

$ export PATH=$PATH:$(echo $PWD/riscv64-*/bin/)

В принципе, если бы вы использовали плату, которую поддерживает LiteX, после этого шага, мы уже могли бы создать целевую SOC. Но нашей платы пока в репозитории нет. Поэтому нам надо изменить некоторые файлы. Сначала по пути /home/vise/risc-v/litex-boards/litex_boards/platforms мы создадим файл ve10cl025.py описывающий архитектуру нашей платы:

# This file is Copyright (c) 2020 Dmitriy Kiryanov <v2016e@gmail.com>
# License: BSD
 
from litex.build.generic_platform import *
from litex.build.altera import AlteraPlatform
from litex.build.altera.programmer import USBBlaster
 
# IOs ----------------------------------------------------------------------------------------------
_io = [
    ("clk50", 0, Pins("23"), IOStandard("3.3-V LVTTL")),
 
  ("key", 0, Pins("24"), IOStandard("3.3-V LVTTL")),
    ("key", 1, Pins("25"), IOStandard("3.3-V LVTTL")),
  ("key", 0, Pins("52"), IOStandard("3.3-V LVTTL")),
    ("key", 1, Pins("53"), IOStandard("3.3-V LVTTL")),
 
    ("serial", 0,
      Subsignal("rx", Pins("10"), IOStandard("3.3-V LVTTL")),
        Subsignal("tx", Pins("11"), IOStandard("3.3-V LVTTL")),
 
    ),
 
  ("hdmi_out", 0,
        Subsignal("clk_p",       Pins("105"), Inverted(), IOStandard("LVDS")),
        Subsignal("clk_n",       Pins("106"), Inverted(), IOStandard("LVDS")),
        Subsignal("data0_p",     Pins("7"), IOStandard("LVDS")),
        Subsignal("data0_n",     Pins("98"), IOStandard("LVDS")),
        Subsignal("data1_p",     Pins("99"), IOStandard("LVDS")),
        Subsignal("data1_n",     Pins("100"), IOStandard("LVDS")),
        Subsignal("data2_p",     Pins("101"), IOStandard("LVDS")),
        Subsignal("data2_n",     Pins("103"), IOStandard("LVDS")),
        Misc("DRIVE=4"),
    ),
 
  # sdram (MT48LC8M8A2P-6A)
  ("sdram_clock", 0, Pins("76"), IOStandard("3.3-V LVTTL")),
    ("sdram", 0,
        Subsignal("a",     Pins("28 31 32 33 39 42 43 44 46 49 50 51")),
        Subsignal("dq",    Pins("58 59 60 65 66 67 68 69")),
        Subsignal("we_n",  Pins("77")),
        Subsignal("ras_n", Pins("83")),
        Subsignal("cas_n", Pins("80")),
        Subsignal("ba",    Pins("71 72")),
        Subsignal("dm",    Pins("85")),
        IOStandard("3.3-V LVTTL")
    ),
 
  # epcs (EPCS16SI8N)
  ("epcs", 0,
        Subsignal("data0", Pins("13")),
        Subsignal("dclk", Pins("12")),
        Subsignal("ncs0", Pins("8")),
        Subsignal("asd0", Pins("6")),
        IOStandard("3.3-V LVTTL")
    ),
 
  # ethernet (KSZ9031)
  ("eth_clocks", 0,
        Subsignal("tx", Pins("114")),
        Subsignal("rx", Pins("135")),
        IOStandard("3.3-V LVTTL"),
    ),
 
    ("eth", 0,
        Subsignal("rst_n",   Pins("113")),
        Subsignal("mdio",    Pins("111")),
        Subsignal("mdc",     Pins("112")),
        Subsignal("rx_dv",   Pins("136")),
        Subsignal("rx_er",   Pins("")),
        Subsignal("rx_data", Pins("137 141 142 143")),
        Subsignal("tx_en",   Pins("115")),
        Subsignal("tx_data", Pins("119 120 121 125")),
        Subsignal("col",     Pins("")),
        Subsignal("crs",     Pins("")),
        IOStandard("3.3-V LVTTL"),
    ),
 
]
 
# Platform -----------------------------------------------------------------------------------------
 
class Platform(AlteraPlatform):
    default_clk_name   = "clk50"
    default_clk_period = 1e9/12e6
 
    def __init__(self):
        AlteraPlatform.__init__(self, "10CL025YE144A7G", _io)
 
    def create_programmer(self):
        return USBBlaster()
 
    def do_finalize(self, fragment):
        AlteraPlatform.do_finalize(self, fragment)
        self.add_period_constraint(self.lookup_request("clk50", loose=True), 1e9/50e6)

Далее по пути /home/vise/risc-v/litex-boards/litex_boards/targets/ мы также создадим файл ve10cl025.py описывающий архитектуру нашей SOC:

#!/usr/bin/env python3
 
# This file is Copyright (c) 2020 Dmitriy Kiryanov <v2016e@gmail.com>
# License: BSD
 
import os
import argparse
 
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
 
from litex.build.io import DDROutput
 
from litex_boards.platforms import ve10cl025
 
from litex.soc.cores.clock import Cyclone10LPPLL
from litex.soc.integration.soc_core import *
from litex.soc.integration.soc_sdram import *
from litex.soc.integration.builder import *
 
from litedram.modules import MT48LC8M8
from litedram.phy import GENSDRPHY
 
from liteeth.phy.s7rgmii import LiteEthPHYRGMII
from litevideo.output import VideoOut
 
# CRG ----------------------------------------------------------------------------------------------
 
class _CRG(Module):
    def __init__(self, platform, sys_clk_freq):
        self.clock_domains.cd_sys    = ClockDomain()
        self.clock_domains.cd_sys_ps = ClockDomain(reset_less=True)
 
        # # #
 
        # Clk / Rst
        clk50 = platform.request("clk50")
 
        # PLL
        self.submodules.pll = pll = Cyclone10LPPLL(speedgrade="-A7")
        pll.register_clkin(clk50, 50e6)
        pll.create_clkout(self.cd_sys,    sys_clk_freq)
        pll.create_clkout(self.cd_sys_ps, sys_clk_freq, phase=90)
 
        # SDRAM clock
        self.comb += platform.request("sdram_clock").eq(self.cd_sys_ps.clk)
 
# BaseSoC ------------------------------------------------------------------------------------------
 
class BaseSoC(SoCCore):
    def __init__(self, sys_clk_freq=int(50e6), with_ethernet=True, with_hdmi=False, **kwargs):
        platform = ve10cl025.Platform()
 
        # SoCCore ----------------------------------------------------------------------------------
        SoCCore.__init__(self, platform, sys_clk_freq,
            ident          = "LiteX SoC on VE-10CL025",
            ident_version  = True,
            **kwargs)
 
        # CRG --------------------------------------------------------------------------------------
        self.submodules.crg = _CRG(platform, sys_clk_freq)
 
        # SDR SDRAM --------------------------------------------------------------------------------
        if not self.integrated_main_ram_size:
            self.submodules.sdrphy = GENSDRPHY(platform.request("sdram"))
            self.add_sdram("sdram",
                phy                     = self.sdrphy,
                module                  = MT48LC8M8(sys_clk_freq, "1:1"),
                origin                  = self.mem_map["main_ram"],
                size                    = kwargs.get("max_sdram_size", 0x04000000),
                l2_cache_size           = kwargs.get("l2_size", 8192),
                l2_cache_min_data_width = kwargs.get("min_l2_data_width", 128),
                l2_cache_reverse        = True
            )
 
        # Ethernet ---------------------------------------------------------------------------------
        if with_ethernet:
            self.submodules.ethphy = LiteEthPHYRGMII(
                clock_pads = self.platform.request("eth_clocks"),
                pads       = self.platform.request("eth"))
            self.add_csr("ethphy")
            self.add_ethernet(phy=self.ethphy)
 
        # hdmi out ---------------------------------------------------------------------------------
        if with_hdmi:
            hdmioutdramport = self.sdram.crossbar.getport(
                mode="read",
                dw=8,
                cd="hdmi_out0_pix" ,
                reverse=True)
 
            self.submodules.hdmi_out0 = VideoOut(
                platform.device,
                platform.request("hdmi_out", 0),
                hdmi_out0_dram_port,
                "ycbcr422",
                fifodepth = 4096)
# Build --------------------------------------------------------------------------------------------
 
def main():
    parser = argparse.ArgumentParser(description="LiteX SoC on VE-10CL025")
    parser.add_argument("--build", action="store_true", help="Build bitstream")
    parser.add_argument("--load",  action="store_true", help="Load bitstream")
    builder_args(parser)
    soc_sdram_args(parser)
    parser.add_argument("--with-ethernet", action="store_true", help="Enable Ethernet support")
    parser.add_argument("--with-hdmi", action="store_true", help="Enable HDMI support")
    args = parser.parse_args()
 
    soc = BaseSoC(with_ethernet=args.with_ethernet,
          **soc_sdram_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 + ".sof"))
 
if __name__ == "__main__":
    main()

В файл по адресу /home/vise/risc-v/litedram/litedram/modules.py добавляем модель памяти, установленной на нашей плате:

class MT48LC8M8(SDRModule):
    # geometry
    nbanks = 4
    nrows  = 4096
    ncols  = 1024
    # timings
    technology_timings = _TechnologyTimings(tREFI=64e6/8192, tWTR=(2, None), tCCD=(1, None), tRRD=(None, 15))
    speedgrade_timings = {"default": _SpeedgradeTimings(tRP=20, tRCD=20, tWR=15, tRFC=(None, 66), tFAW=None, tRAS=44)}

Заходим в директорию с нашей целевой платой:

$ cd /home/vise/risc-v/litex-boards/litex_boards/targets

Теперь можно запустить сборку нашего SOC!

$ ./ve10cl025.py

Мы должны увидеть подобную картину с консолью сборки.

После этих действий, по адресу /home/vise/risc-v/litex-boards/litex_boards/targets/build/ve10cl025/ появится две папки gateware и software. Папка gateware содержит дизайн для нашей платы, а папка software содержит программное обеспечение, для сгенерированной SOC. В принципе если установить Quartus в WSL, можно запустить скрипт gateware/build_ve10cl025.sh для генерации прошивки для нашей FPGA, а можно создать проект в Quartus для Window.

Ну и по традиции видео работы, и исходники:

 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *