Продолжая цикл статей, посвященных 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 и перейдем в домашний каталог:
Bash Code:
$cd /home/vise
Далее создадим каталог для развертывания LiteX, и перейдем в него:
Bash Code:
$ mkdir risc-v cd risc-v
На следующем шаге необходимо установить Python 3.6+ в WSL, а в самой windows среду разработки для целевой FPGA. В нашем случае это Quartus V19.1.
Устанавливаем Migen/LiteX и ядра LiteX'а:
Bash Code:
$ 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)
Далее, если необходимо, обновляем репозитории:
Bash Code:
$ ./litex_setup.py update
Если вы используете MacOS, убедитесь, что пакет HomeBrew установлен. После выполните, brew install wget.
Если вы используете Windows, Возможно вам придется установить переменную SHELL в SHELL=cmd.exe.
Устанавливаем RISC-V тулчейн (Если вы собираетесь тестировать/создавать SoC с процессором):
Bash Code:
$ ./litex_setup.py gcc
Добавляем путь до нашего компилятора в переменную PATH:
Bash Code:
$ export PATH=$PATH:$(echo $PWD/riscv64-*/bin/)
В принципе, если бы вы использовали плату, которую поддерживает LiteX, после этого шага, мы уже могли бы создать целевую SOC. Но нашей платы пока в репозитории нет. Поэтому нам надо изменить некоторые файлы. Сначала по пути /home/vise/risc-v/litex-boards/litex_boards/platforms мы создадим файл ve10cl025.py описывающий архитектуру нашей платы:
Python Code:
# This file is Copyright (c) 2020 Dmitriy Kiryanov <Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. > # 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:
Python Code:
#!/usr/bin/env python3 # This file is Copyright (c) 2020 Dmitriy Kiryanov <Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. > # 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 добавляем модель памяти, установленной на нашей плате:
Python Code:
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)}
Заходим в директорию с нашей целевой платой:
Bash Code:
$ cd /home/vise/risc-v/litex-boards/litex_boards/targets
Теперь можно запустить сборку нашего SOC!
Bash Code:
$ ./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 для Windows:
Ну и по традиции видео работы, и исходники:
Проект: litex_riscv