Papilio Pro LX9(Spartan6LX)でVexRiscv noMMU-Linuxを動かしてみる

Papilio Pro LX9(Spartan6LX)でVexRiscv noMMU-Linuxを動かしてみる

はじめに

前回、Tang Nano 9kでnoMMU-Linuxを動作させましたが、main ram(HyperRAM)のアクセス速度の影響で、Linuxの起動(コンソールShellのプロンプト出力)に60秒近くかかりました。
そこで今回はSDRAMを搭載しているPapilio Pro LX9でリベンジです。

Papilio Pro LX9のスペック

Spartan 6 LX XC6SLX9 TQG144
ロジックセル数 9152
ベースクロック発振 32MHz
External SPI FLASH 64Mb SPI Flash
SDRAM 64Mb

ちなみに、秋月電子通商さんで11,000円で販売されています。

準備するモノ

  • Ubuntu 22.04.2 LTS
  • Xilinx ISE (Integrated Software Environment)
  • Papilio Pro LX9

開発環境

  • Xilinx ISEのInstall
    Google先生に教えてもらって下さい。

  • Litex構築環境Install
    以下の手順でインストールして下さい。
    詳細は本家のLiteX を見てください。

$ wget https://github.com/enjoy-digital/litex/blob/master/litex_setup.py
$ chmod +x litex_setup.py
$ ./litex_setup.py --init --install --user

VexRiscvを組み込む

Papilio Pro LX9のデフォルトで、VexRiscvのビットストリームを合成するとLUT block不足でエラーとなる為、Integrated ROMをDisableして、spi-flash(SPI Flash Memory Mapped)にBIOSを配置することにします。
ついでに、L2 cacheも付け加えました。

LUT block不足時のエラー

Checking expanded design ...
WARNING:NgdBuild:440 - FF primitive 'FDPE_3' has unconnected output pin
WARNING:NgdBuild:440 - FF primitive 'FDPE_5' has unconnected output pin

Partition Implementation Status
-------------------------------

  No Partitions were found in this design.

-------------------------------

Writing file gadgetfactory_papilio_pro_map.ngm...
Running directed packing...
Running delay-based LUT packing...
Updating timing models...
ERROR:Pack:2412 - The number of logical LUT blocks exceeds the capacity for the target device.
ERROR:Map:237 - The design is too large to fit the device.  Please check the Design Summary section to see which resource requirement for
   your design exceeds the resources available in the device. Note that the number of slices reported may not be reflected accurately as
   their packing might not have been completed.

Mapping completed.

Integrated ROM Disableとspi-flashの変更内容以下です。

diff --git a/litex_boards/targets/gadgetfactory_papilio_pro.py b/litex_boards/targets/gadgetfactory_papilio_pro.py
index 2f53db6..788fbcd 100755
--- a/litex_boards/targets/gadgetfactory_papilio_pro.py
+++ b/litex_boards/targets/gadgetfactory_papilio_pro.py
@@ -18,6 +18,7 @@ from litex_boards.platforms import gadgetfactory_papilio_pro

 from litex.soc.cores.clock import *
 from litex.soc.integration.soc_core import *
+from litex.soc.integration.soc import SoCRegion
 from litex.soc.integration.builder import *
 from litex.soc.cores.video import VideoVGAPHY
 from litex.soc.cores.led import LedChaser
@@ -73,10 +74,26 @@ class BaseSoC(SoCCore):
         self.crg = _CRG(platform, sys_clk_freq)

         # SoCCore ----------------------------------------------------------------------------------
-        if kwargs.get("cpu_type", "vexriscv") == "vexriscv":
-            kwargs["cpu_variant"] = "minimal"
+        #if kwargs.get("cpu_type", "vexriscv") == "vexriscv":
+        #    kwargs["cpu_variant"] = "minimal"
+        # Disable Integrated ROM
+        kwargs["integrated_rom_size"] = 0
         SoCCore.__init__(self, platform, sys_clk_freq, ident="LiteX SoC on Papilio Pro", **kwargs)

+        # SPI Flash --------------------------------------------------------------------------------
+        from litespi.modules import MX25L6445E
+        from litespi.opcodes import SpiNorFlashOpCodes as Codes
+        self.add_spi_flash(mode="1x", module=MX25L6445E(Codes.READ_1_1_1), with_master=False)
+
+        # Add ROM linker region --------------------------------------------------------------------
+        self.bus.add_region("rom", SoCRegion(
+            origin = self.bus.regions["spiflash"].origin + 0x100000,
+            size   = 64*1024,
+            linker = True)
+        )
+        self.cpu.set_reset_address(self.bus.regions["rom"].origin)
+        self.add_constant("FLASH_BOOT_ADDRESS", 0x00200000)
+
         # SDR SDRAM --------------------------------------------------------------------------------
         if not self.integrated_main_ram_size:
             sdrphy_cls = GENSDRPHY
@@ -84,7 +101,8 @@ class BaseSoC(SoCCore):
             self.add_sdram("sdram",
                 phy           = self.sdrphy,
                 module        = MT48LC4M16(sys_clk_freq, "1:2"),
-                l2_cache_size = 0
+                #l2_cache_size = 0
+                l2_cache_size = kwargs.get("l2_size", 8192)
             )

         # Leds -------------------------------------------------------------------------------------

spi-flashのメモリマップ

アドレス
0x000000 - 0x0fffff FPGA bitstream
0x100000 - 0x1fffff Litex BIOS(bios.bin)
0x200000 - Linux(loader_fbi.bin)

FPGA bitstreamデータの合成

以下の手順でFPGA bitstreamデータの合成とDTSファイルの生成を行う。

$ gadgetfactory_papilio_pro.py --timer-uptime  --csr-json papilio_pro_csr.json --cpu-type vexriscv --cpu-variant linux --build
$ litex_json2dts_linux_vexriscv.py papilio_pro_csr.json > papilio_pro_csr.dts

BuildRootによるnoMMU-Linuxの構築

GitHub(https://github.com/regymm/buildroot) からBuildRootをcloneする。

$ git clone https://github.com/regymm/buildroot
$ cd buildroot

外部設定ファイルを使って設定を行う。

$ make list-defconfigs BR2_EXTERNAL=../litex-buildroot
$ make litex_vexriscv_riscv32_nommu_tang_nano_9k_defconfig

外部設定ファイルにPapilio Pro LX9用のデフォルトコンフィグレーションは準備していませんので、DTSファイルをPapilio Pro LX9のものに変更する。

$ cp <file path>/papilio_pro_csr.dts output/build/linux-ae80e67c6b48bbedcd13db753237a25b3dec8301/arch/riscv/boot/dts/litex/papilio_pro_csr.dts
$ make linux-menuconfig
  [SoC selection  --->]
    [Source file for the Litex builtin DTB]
      papilio_pro_csr

$ make -j$(nproc)

無事に成功すれば、output/images/loader.binが生成されます。

loader.binをROMから起動出来る、FBIフォーマットに変換する。

$ litex-vexriscv/litex/litex/soc/software/crcfbigen.py --little --fbi  -o loader_fbi.bin loader.bin

Papilio Pro LX9への書き込み

openocdを使うことで、SPI-Flashへ書き込みが出来ます。

$ cd litex-boards/litex_boards/targets/build/gadgetfactory_papilio_pro
$ wget https://github.com/quartiq/bscan_spi_bitstreams/raw/master/bscan_spi_xc6slx9.bit 
$ openocd -f ../../../prog/openocd_xc6_ft2232.cfg -c "init; jtagspi_init 0 bscan_spi_xc6slx9.bit; jtagspi_program gateware/gadgetfactory_papilio_pro.bin 0 ; jtagspi_program software/bios/bios.bin 0x100000; exit"
$ openocd -f ../../../prog/openocd_xc6_ft2232.cfg -c "init; jtagspi_init 0 bscan_spi_xc6slx9.bit; jtagspi_program loader_fbi.bin 0x200000; exit"

起動

起動時のコンソール出力

        __   _ __      _  __
       / /  (_) /____ | |/_/
      / /__/ / __/ -_)>  <
     /____/_/\__/\__/_/|_|
   Build your hardware, easily!

 (c) Copyright 2012-2023 Enjoy-Digital
 (c) Copyright 2007-2015 M-Labs

 BIOS built on Aug  9 2023 12:36:32
 BIOS CRC passed (692b601b)

 LiteX git sha1: 0f1fdea8

--=============== SoC ==================--
CPU:            VexRiscv_Linux @ 80MHz
BUS:            WISHBONE 32-bit @ 4GiB
CSR:            32-bit data
ROM:            64.0KiB
SRAM:           8.0KiB
L2:             8.0KiB
FLASH:          8.0MiB
SDRAM:          8.0MiB 16-bit @ 80MT/s (CL-2 CWL-2)
MAIN-RAM:       8.0MiB

--========== Initialization ============--
Initializing SDRAM @0x40000000...
Switching SDRAM to software control.
Switching SDRAM to hardware control.
Memtest at 0x40000000 (2.0MiB)...
  Write: 0x40000000-0x40200000 2.0MiB
   Read: 0x40000000-0x40200000 2.0MiB
Memtest OK
Memspeed at 0x40000000 (Sequential, 2.0MiB)...
  Write speed: 22.4MiB/s
   Read speed: 35.1MiB/s

Initializing MX25L6445E SPI Flash @0x00000000...
SPI Flash clk configured to 40 MHz
Memspeed at 0 (Sequential, 4.0KiB)...
   Read speed: 4.1MiB/s
Memspeed at 0 (Random, 4.0KiB)...
   Read speed: 1.9MiB/s

--============== Boot ==================--
Booting from serial...
Press Q or ESC to abort boot completely.
sL5DdSMmkekro
             Timeout
Booting from flash...
Copying 0x00200000 to 0x40000000 (2456660 bytes)...
[########################################]
Executing booted program at 0x40000000

--============= Liftoff! ===============--
[    0.000000] Linux version 6.1.0-rc2 (developer@developer-Diginnos-PC) (riscv32-buildroot-linux-uclibc-gcc.br_real (Buildroot -g91b88fa1ad) 10.3.0, GNU ld (GNU Binutils) 2.37) #7 Fri Aug  4 13:01:31 JST 2023
[    0.000000] Forcing kernel command line to: earlycon console=liteuart
[    0.000000] earlycon: liteuart0 at MMIO 0xf0003800 (options '115200n8')
[    0.000000] printk: bootconsole [liteuart0] enabled
[    0.000000] Zone ranges:
[    0.000000]   Normal   [mem 0x0000000040000000-0x00000000407fffff]
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000040000000-0x00000000407fffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000040000000-0x00000000407fffff]
[    0.000000] riscv: base ISA extensions im
[    0.000000] riscv: ELF capabilities im
[    0.000000] Built 1 zonelists, mobility grouping off.  Total pages: 2032
[    0.000000] Kernel command line: earlycon console=liteuart
[    0.000000] Dentry cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.000000] Inode-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[    0.000000] Memory: 5620K/8192K available (1318K kernel code, 89K rwdata, 161K rodata, 825K init, 52K bss, 2572K reserved, 0K cma-reserved)
[    0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[    0.000000] riscv-intc: 32 local interrupts mapped
[    0.000000] irqchip: LiteX VexRiscv irqchip driver initialized. IE: 0, mask: 0x00000000, pending: 0x00000000
[    0.000000] irqchip: LiteX VexRiscv irqchip settings: mask CSR 0xbc0, pending CSR 0xfc0
[    0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x127350b881, max_idle_ns: 440795202125 ns
[    0.000039] sched_clock: 64 bits at 80MHz, resolution 12ns, wraps every 4398046511100ns
[    0.010318] Console: colour dummy device 80x25
[    0.014552] Calibrating delay loop (skipped), value calculated using timer frequency.. 160.00 BogoMIPS (lpj=4000000)
[    0.024609] pid_max: default: 4096 minimum: 301
[    0.030478] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.037409] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.080574] devtmpfs: initialized
[    0.137501] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 95563022313750000 ns
[    0.146753] pinctrl core: initialized pinctrl subsystem
[    0.316875] clocksource: Switched to clocksource riscv_clocksource
[    0.779992] workingset: timestamp_bits=30 max_order=11 bucket_order=0
[    1.625749] LiteX SoC Controller driver initialized
[    1.635512] f0003800.serial: ttyLXU0 at MMIO 0x0 (irq = 0, base_baud = 0) is a liteuart
[    1.648123] printk: console [liteuart0] enabled
[    1.648123] printk: console [liteuart0] enabled
[    1.656712] printk: bootconsole [liteuart0] disabled
[    1.656712] printk: bootconsole [liteuart0] disabled
[    2.091799] Freeing unused kernel image (initmem) memory: 824K
[    2.096944] This architecture does not have kernel memory protection.
[    2.103595] Run /init as init process

------------------------
| VexRiscv nommu Linux |
------------------------
Mounting /proc
Starting shell
/ #

最後に

RAMサイズが8M Byteと少ないですが、ちょっとしたプログラムなら問題なく動くと思われるので、色々と遊べそうな環境を構築することが出来ました。

機会があれば、拡張基板(Papilio Arcade MegaWing)も動かして見たいと思います。