How to Run Linux on RISCV in Arty A7-100T FPGA


It’s been tempting for me to try running open-source software on top of open-source hardware. SiFive provides a bitstream for Arty A7 called Freedom, but it seems that the repository is dead now.

The other interesting alternative to try is VexRiscv, and everyone keeps posting about Arty A7 35T while I only have the 100T version (which is the newer version) here with me.

I wrote here because I couldn’t find any tutorial for the 100T version.
Please note that this is basically just a guide for starting Linux on Litex that I modified to get working on Arty A7 100T.

So here we go!

Requirements

  • Arty A7 100T FPGA
  • Micro USB Cable
  • and a Linux PC (Here I am using Ubuntu)

Prerequisites

$ sudo apt install build-essential device-tree-compiler wget git python3-setuptools
$ git clone https://github.com/litex-hub/linux-on-litex-vexriscv
$ cd linux-on-litex-vexriscv/images
# Get the prebuild OpenSBI [https://github.com/litex-hub/linux-on-litex-vexriscv/issues/164]
$ wget https://github.com/litex-hub/linux-on-litex-vexriscv/files/5702162/opensbi_2020_12_15.zip
$ unzip opensbi_2020_12_15.zip

Build Linux using Buildroot

Since the provided linux images does not have eth0 interface, we need to build it ourself. (https://github.com/litex-hub/linux-on-litex-vexriscv/issues/261)

$ wget https://github.com/buildroot/buildroot/archive/refs/tags/2021.02.3.tar.gz
$ tar -zxvf 2021.02.3.tar.gz buildroot-2021.02.3/
$ cd buildroot-2021.02.3
$ make BR2_EXTERNAL=../linux-on-litex-vexriscv/buildroot/ litex_vexriscv_defconfig
$ make
# Copy the linux images into litex images directory
$ cp -r output/images/* ../linux-on-litex-vexriscv/images/

Install LiteX (we will need the litex terminal)

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

Install RISCV ToolChain

$ wget https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.1.0-2019.01.0-x86_64-linux-ubuntu14.tar.gz
$ tar -xvf riscv64-unknown-elf-gcc-8.1.0-2019.01.0-x86_64-linux-ubuntu14.tar.gz
$ export PATH=$PATH:$PWD/riscv64-unknown-elf-gcc-8.1.0-2019.01.0-x86_64-linux-ubuntu14/bin/

Install Verilator

$ sudo apt install verilator
$ sudo apt install libevent-dev libjson-c-dev

Install OpenOCD

$ sudo apt install libtool automake pkg-config libusb-1.0-0-dev
$ git clone https://github.com/ntfreak/openocd.git
$ cd openocd
$ ./bootstrap
$ ./configure --enable-ftdi
$ make
$ sudo make install

Build bitstream

Please note that the --variant=a7-100 argument is mandatory for our Arty A7 100T.

$ ./make.py --board=arty --variant=a7-100 --cpu-count=1 --build

If you get error message: "ERROR: [Common 17-39] ‘place_design’ failed due to earlier errors." then comment the "xadc", line in make.py file at line 92 into # “xadc”,. A recent update caused the xadc to be enabled, but we don’t need that for now.

Load bitstream

$ ./make.py --board=arty --variant=a7-100 --cpu-count=1 --load

Load Linux via Serial

$ litex_term --images=images/boot.json /dev/ttyUSBX --speed=1e6

The images should load and you should see Linux booting 🙂

derry@10700k:~/ws/linux-on-litex-vexriscv$ litex_term --images=images/boot.json /dev/ttyUSB1 --speed=1e6

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

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

 BIOS CRC passed (c3b60ea7)

 Migen git sha1: ac70301
 LiteX git sha1: 55a79030

--=============== SoC ==================--
CPU:		VexRiscv SMP-LINUX @ 100MHz
BUS:		WISHBONE 32-bit @ 4GiB
CSR:		32-bit data
ROM:		64KiB
SRAM:		8KiB
L2:		0KiB
FLASH:		16384KiB
SDRAM:		262144KiB 16-bit @ 800MT/s (CL-7 CWL-5)

--========== Initialization ============--
Ethernet init...
Initializing SDRAM @0x40000000...
Switching SDRAM to software control.
Read leveling:
  m0, b00: |00000000000000000000000000000000| delays: -
  m0, b01: |00000000000000000000000000000000| delays: -
  m0, b02: |11111111111111000000000000000000| delays: 07+-07
  m0, b03: |00000000000000011111111111111000| delays: 22+-07
  m0, b04: |00000000000000000000000000000000| delays: -
  m0, b05: |00000000000000000000000000000000| delays: -
  m0, b06: |00000000000000000000000000000000| delays: -
  m0, b07: |00000000000000000000000000000000| delays: -
  best: m0, b03 delays: 22+-07
  m1, b00: |00000000000000000000000000000000| delays: -
  m1, b01: |00000000000000000000000000000000| delays: -
  m1, b02: |11111111111110000000000000000000| delays: 06+-06
  m1, b03: |00000000000000011111111111111100| delays: 22+-07
  m1, b04: |00000000000000000000000000000000| delays: -
  m1, b05: |00000000000000000000000000000000| delays: -
  m1, b06: |00000000000000000000000000000000| delays: -
  m1, b07: |00000000000000000000000000000000| delays: -
  best: m1, b03 delays: 22+-07
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: 31.7MiB/s
   Read speed: 33.1MiB/s

Initializing S25FL128L SPI Flash @0x01000000...
SPI Flash clk configured to 50 MHz
Memspeed at 0x1000000 (Sequential, 4.0KiB)...
   Read speed: 4.9MiB/s
Memspeed at 0x1000000 (Random, 4.0KiB)...
   Read speed: 3.0MiB/s

--============== Boot ==================--
Booting from serial...
Press Q or ESC to abort boot completely.
sL5DdSMmkekro
[LITEX-TERM] Received firmware download request from the device.
[LITEX-TERM] Uploading images/Image to 0x40000000 (7531468 bytes)...
[LITEX-TERM] Upload calibration... (inter-frame: 10.00us, length: 64)
[LITEX-TERM] Upload complete (86.2KB/s).
[LITEX-TERM] Uploading images/rv32.dtb to 0x40ef0000 (12518 bytes)...
[LITEX-TERM] Upload calibration... (inter-frame: 10.00us, length: 64)
[LITEX-TERM] Upload complete (81.8KB/s).
[LITEX-TERM] Uploading images/rootfs.cpio to 0x41000000 (4010496 bytes)...
[LITEX-TERM] Upload calibration... (inter-frame: 10.00us, length: 64)
[LITEX-TERM] Upload complete (86.2KB/s).
[LITEX-TERM] Uploading images/opensbi.bin to 0x40f00000 (53640 bytes)...
[LITEX-TERM] Upload calibration... (inter-frame: 10.00us, length: 64)
[LITEX-TERM] Upload complete (85.3KB/s).
[LITEX-TERM] Booting the device.
[LITEX-TERM] Done.
Executing booted program at 0x40f00000

--============= Liftoff! ===============--

OpenSBI v0.8-1-gecf7701
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

Platform Name       : LiteX / VexRiscv-SMP
Platform Features   : timer,mfdeleg
Platform HART Count : 8
Boot HART ID        : 0
Boot HART ISA       : rv32imas
BOOT HART Features  : time
BOOT HART PMP Count : 0
Firmware Base       : 0x40f00000
Firmware Size       : 124 KB
Runtime SBI Version : 0.2

MIDELEG : 0x00000222
MEDELEG : 0x0000b101
[    0.000000] Linux version 5.14.0 (derry@10700k) (riscv32-buildroot-linux-gnu-gcc.br_real (Buildroot 2021.02.3) 10.3.0, GNU ld (GNU Binutils) 2.35.2) #1 SMP Mon Feb 7 18:24:23 KST 2022
[    0.000000] earlycon: liteuart0 at I/O port 0x0 (options '')
[    0.000000] Malformed early option 'console'
[    0.000000] earlycon: liteuart0 at MMIO 0xf0001000 (options '')
[    0.000000] printk: bootconsole [liteuart0] enabled
[    0.000000] Zone ranges:
[    0.000000]   Normal   [mem 0x0000000040000000-0x000000004fffffff]
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000040000000-0x000000004fffffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000040000000-0x000000004fffffff]
[    0.000000] SBI specification v0.2 detected
[    0.000000] SBI implementation ID=0x1 Version=0x8
[    0.000000] SBI TIME extension detected
[    0.000000] SBI IPI extension detected
[    0.000000] SBI RFENCE extension detected
[    0.000000] SBI v0.2 HSM extension detected
[    0.000000] Invalid cpuid [8] for hartid [8]
[    0.000000] riscv: ISA extensions aim
[    0.000000] riscv: ELF capabilities aim
[    0.000000] percpu: Embedded 8 pages/cpu s11340 r0 d21428 u32768
[    0.000000] CPU node for /cpus/cpu@8 exist but the possible cpu range is :0-7
[    0.000000] CPU node for /cpus/cpu@9 exist but the possible cpu range is :0-7
[    0.000000] CPU node for /cpus/cpu@10 exist but the possible cpu range is :0-7
[    0.000000] CPU node for /cpus/cpu@11 exist but the possible cpu range is :0-7
[    0.000000] CPU node for /cpus/cpu@12 exist but the possible cpu range is :0-7
[    0.000000] CPU node for /cpus/cpu@13 exist but the possible cpu range is :0-7
[    0.000000] CPU node for /cpus/cpu@14 exist but the possible cpu range is :0-7
[    0.000000] CPU node for /cpus/cpu@15 exist but the possible cpu range is :0-7
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 65024
[    0.000000] Kernel command line: console=liteuart earlycon=liteuart,0xf0001000 rootwait root=/dev/ram0
[    0.000000] Dentry cache hash table entries: 32768 (order: 5, 131072 bytes, linear)
[    0.000000] Inode-cache hash table entries: 16384 (order: 4, 65536 bytes, linear)
[    0.000000] Sorting __ex_table...
[    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[    0.000000] Memory: 243304K/262144K available (5685K kernel code, 572K rwdata, 883K rodata, 209K init, 221K bss, 18840K reserved, 0K cma-reserved)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=8, Nodes=1
[    0.000000] rcu: Hierarchical RCU implementation.
[    0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies.
[    0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[    0.000000] riscv-intc: 32 local interrupts mapped
[    0.000000] Couldn't find cpu id for hartid [8]
[    0.000000] Couldn't find cpu id for hartid [9]
[    0.000000] Couldn't find cpu id for hartid [10]
[    0.000000] Couldn't find cpu id for hartid [11]
[    0.000000] Couldn't find cpu id for hartid [12]
[    0.000000] Couldn't find cpu id for hartid [13]
[    0.000000] Couldn't find cpu id for hartid [14]
[    0.000000] Couldn't find cpu id for hartid [15]
[    0.000000] Couldn't find cpu id for hartid [8]
[    0.000000] plic: Invalid cpuid for context 17
[    0.000000] Couldn't find cpu id for hartid [9]
[    0.000000] plic: Invalid cpuid for context 19
[    0.000000] Couldn't find cpu id for hartid [10]
[    0.000000] plic: Invalid cpuid for context 21
[    0.000000] Couldn't find cpu id for hartid [11]
[    0.000000] plic: Invalid cpuid for context 23
[    0.000000] Couldn't find cpu id for hartid [12]
[    0.000000] plic: Invalid cpuid for context 25
[    0.000000] Couldn't find cpu id for hartid [13]
[    0.000000] plic: Invalid cpuid for context 27
[    0.000000] Couldn't find cpu id for hartid [14]
[    0.000000] plic: Invalid cpuid for context 29
[    0.000000] Couldn't find cpu id for hartid [15]
[    0.000000] plic: Invalid cpuid for context 31
[    0.000000] plic: interrupt-controller@f0c00000: mapped 32 interrupts with 8 handlers for 32 contexts.
[    0.000000] random: get_random_bytes called from start_kernel+0x4ac/0x63c with crng_init=0
[    0.000000] riscv_timer_init_dt: Registering clocksource cpuid [0] hartid [0]
[    0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x171024e7e0, max_idle_ns: 440795205315 ns
[    0.000019] sched_clock: 64 bits at 100MHz, resolution 10ns, wraps every 4398046511100ns
[    0.002596] Couldn't find cpu id for hartid [8]
[    0.003539] Invalid cpuid for hartid [8]
[    0.004263] Failed to initialize '/cpus/cpu@8': -2
[    0.005604] Couldn't find cpu id for hartid [9]
[    0.006552] Invalid cpuid for hartid [9]
[    0.007279] Failed to initialize '/cpus/cpu@9': -2
[    0.008622] Couldn't find cpu id for hartid [10]
[    0.009488] Invalid cpuid for hartid [10]
[    0.010251] Failed to initialize '/cpus/cpu@10': -2
[    0.011594] Couldn't find cpu id for hartid [11]
[    0.012461] Invalid cpuid for hartid [11]
[    0.013223] Failed to initialize '/cpus/cpu@11': -2
[    0.014559] Couldn't find cpu id for hartid [12]
[    0.015423] Invalid cpuid for hartid [12]
[    0.016188] Failed to initialize '/cpus/cpu@12': -2
[    0.017526] Couldn't find cpu id for hartid [13]
[    0.018395] Invalid cpuid for hartid [13]
[    0.019161] Failed to initialize '/cpus/cpu@13': -2
[    0.020496] Couldn't find cpu id for hartid [14]
[    0.021358] Invalid cpuid for hartid [14]
[    0.022125] Failed to initialize '/cpus/cpu@14': -2
[    0.023481] Couldn't find cpu id for hartid [15]
[    0.024351] Invalid cpuid for hartid [15]
[    0.025118] Failed to initialize '/cpus/cpu@15': -2
[    0.027988] Console: colour dummy device 80x25
[    0.029348] Calibrating delay loop (skipped), value calculated using timer frequency.. 200.00 BogoMIPS (lpj=400000)
[    0.032108] pid_max: default: 32768 minimum: 301
[    0.036345] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.038054] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.062082] ASID allocator using 9 bits (512 entries)
[    0.065022] rcu: Hierarchical SRCU implementation.
[    0.075437] smp: Bringing up secondary CPUs ...
[    1.160251] CPU1: failed to come online
[    2.229561] CPU2: failed to come online
[    3.298735] CPU3: failed to come online
[    4.368018] CPU4: failed to come online
[    5.437271] CPU5: failed to come online
[    6.506499] CPU6: failed to come online
[    7.575728] CPU7: failed to come online
[    7.576909] smp: Brought up 1 node, 1 CPU
[    7.582901] devtmpfs: initialized
[    7.693782] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
[    7.696375] futex hash table entries: 2048 (order: 5, 131072 bytes, linear)
[    7.705831] NET: Registered PF_NETLINK/PF_ROUTE protocol family
[    7.951347] pps_core: LinuxPPS API ver. 1 registered
[    7.952276] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti@linux.it>
[    7.954633] PTP clock support registered
[    7.958747] FPGA manager framework
[    7.973242] clocksource: Switched to clocksource riscv_clocksource
[    8.142164] NET: Registered PF_INET protocol family
[    8.144561] IP idents hash table entries: 4096 (order: 3, 32768 bytes, linear)
[    8.155145] tcp_listen_portaddr_hash hash table entries: 512 (order: 0, 6144 bytes, linear)
[    8.157712] TCP established hash table entries: 2048 (order: 1, 8192 bytes, linear)
[    8.159901] TCP bind hash table entries: 2048 (order: 2, 16384 bytes, linear)
[    8.162030] TCP: Hash tables configured (established 2048 bind 2048)
[    8.164690] UDP hash table entries: 256 (order: 1, 8192 bytes, linear)
[    8.166680] UDP-Lite hash table entries: 256 (order: 1, 8192 bytes, linear)
[    8.194934] Unpacking initramfs...
[    8.250612] workingset: timestamp_bits=30 max_order=16 bucket_order=0
[    8.481085] io scheduler mq-deadline registered
[    8.482043] io scheduler kyber registered
[    8.797837] No litex,nclkout entry in the dts file
[    8.814694] LiteX SoC Controller driver initialized
[    9.720183] Initramfs unpacking failed: invalid magic at start of compressed archive
[    9.780663] Freeing initrd memory: 8192K
[   10.374004] f0001000.serial: ttyLXU0 at MMIO 0x0 (irq = 0, base_baud = 0) is a liteuart
[   10.376077] printk: console [liteuart0] enabled
[   10.376077] printk: console [liteuart0] enabled
[   10.377862] printk: bootconsole [liteuart0] disabled
[   10.377862] printk: bootconsole [liteuart0] disabled
[   10.428267] liteeth f0002000.mac eth0: irq 2 slots: tx 2 rx 2 size 2048
[   10.433581] i2c_dev: i2c /dev entries driver
[   10.440636] i2c i2c-0: Not I2C compliant: can't read SCL
[   10.441827] i2c i2c-0: Bus may be unreliable
[   10.469813] fpga_manager fpga0: LiteX ICAPBitstream FPGA Manager registered
[   10.493529] NET: Registered PF_INET6 protocol family
[   10.511828] Segment Routing with IPv6
[   10.513357] In-situ OAM (IOAM) with IPv6
[   10.515245] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
[   10.530054] NET: Registered PF_PACKET protocol family
[   10.553245] litex-mmc f0009000.mmc: Requested clk_freq=12500000: set to 12500000 via div=8
[   10.572243] litex-mmc f0009000.mmc: Requested clk_freq=0: set to 390625 via div=256
[   10.588477] Freeing unused kernel image (initmem) memory: 204K
[   10.590050] Kernel memory protection not selected by kernel config.
[   10.591469] Run /init as init process
Starting syslogd: OK
Starting klogd: OK
Running sysctl: OK
Saving random seed: [   12.456085] random: dd: uninitialized urandom read (512 bytes read)
OK
Starting network: OK

Welcome to Buildroot
buildroot login: root
                   __   _
                  / /  (_)__  __ ____ __
                 / /__/ / _ \/ // /\ \ /
                /____/_/_//_/\_,_//_\_\
                      / _ \/ _ \
   __   _ __      _  _\___/_//_/         ___  _
  / /  (_) /____ | |/_/__| | / /____ __ / _ \(_)__ _____  __
 / /__/ / __/ -_)>  </___/ |/ / -_) \ // , _/ (_-</ __/ |/ /
/____/_/\__/\__/_/|_|____|___/\__/_\_\/_/|_/_/___/\__/|___/
                  / __/  |/  / _ \
                 _\ \/ /|_/ / ___/
                /___/_/  /_/_/
  32-bit RISC-V Linux running on LiteX / VexRiscv-SMP.

login[107]: root login on 'console'
root@buildroot:~# ls
root@buildroot:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop qlen 1000
    link/ether 5e:e8:81:84:1c:40 brd ff:ff:ff:ff:ff:ff
3: sit0@NONE: <NOARP> mtu 1480 qdisc noop qlen 1000
    link/sit 0.0.0.0 brd 0.0.0.0
root@buildroot:~# udhcpc -i eth0
udhcpc: started, v1.33.0
udhcpc: sending discover
udhcpc: sending select for 192.168.0.6
udhcpc: lease of 192.168.0.6 obtained, lease time 7200
deleting routers
adding dns 10.0.6.1
adding dns 168.126.63.2
root@buildroot:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 5e:e8:81:84:1c:40 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.6/24 brd 192.168.0.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5ce8:81ff:fe84:1c40/64 scope link 
       valid_lft forever preferred_lft forever
3: sit0@NONE: <NOARP> mtu 1480 qdisc noop qlen 1000
    link/sit 0.0.0.0 brd 0.0.0.0
root@buildroot:~# 

I guess I need to make the video about this to explain more about how to enable the ethernet connection. I’ll do when I have time. 🙂


6 responses to “How to Run Linux on RISCV in Arty A7-100T FPGA”

  1. Hello thanks for this great writeup
    I am stuck with the following error

    $ ./make.py –board=arty –variant=a7-100 –cpu-count=1 –build –toolchain=vivado

    ERROR: [Common 17-39] ‘place_design’ failed due to earlier errors.

  2. I am following step by step your instruction and I am getting the error ERROR: [Common 17-39] ‘place_design’ failed due to earlier errors.

  3. when I ran ./make.py –board=arty –variant=a7-100 –cpu-count=1 –build , i got an error

    ERROR:SoCCSRHandler:uart CSR name already used.
    ERROR:SoCCSRHandler:32-bit CSR Bus, 32-bit Aligned, 16.0KiB Address Space, 2048B Paging, big Ordering (Up to 32 Locations).
    CSR Locations: (3)
    – ctrl : 0
    – uart : 2
    – timer0 : 3

    Help me get rid of this, so that it wil be very helpful

Leave a Reply

Your email address will not be published. Required fields are marked *