Embedded Linux (riscv64)

This page describes how to build sherpa-onnx for embedded Linux (RISC-V, 64-bit) with cross-compiling on an x64 machine with Ubuntu OS. It also demonstrates how to use qemu to run the compiled binaries.

Hint

We provide a colab notebook build sherpa-onnx for risc-v colab notebook for you to try this section step by step.

If you are using Windows/macOS or you don’t want to setup your local environment for cross-compiling, please use the above colab notebook.

Note

You can download pre-compiled binaries for riscv64 from the following URL https://huggingface.co/csukuangfj/sherpa-onnx-libs/tree/main/riscv64

Please always download the latest version.

Example command to download the version 1.9.12:

# binaries built with shared libraries
wget https://huggingface.co/csukuangfj/sherpa-onnx-libs/resolve/main/riscv64/sherpa-onnx-v1.9.12-linux-riscv64-shared.tar.bz2

# For users from China
# 中国国内用户,如果访问不了 huggingface, 请使用

# binaries built with shared libraries
# wget https://hf-mirror.com/csukuangfj/sherpa-onnx-libs/resolve/main/riscv64/sherpa-onnx-v1.9.12-linux-riscv64-shared.tar.bz2

Install toolchain

The first step is to install a toolchain for cross-compiling.

mkdir -p $HOME/toolchain

wget -q https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/resource//1663142514282/Xuantie-900-gcc-linux-5.10.4-glibc-x86_64-V2.6.1-20220906.tar.gz

tar xf ./Xuantie-900-gcc-linux-5.10.4-glibc-x86_64-V2.6.1-20220906.tar.gz --strip-components 1 -C $HOME/toolchain

Next, we need to set the following environment variable:

export PATH=$HOME/toolchain/bin:$PATH

To check that you have installed the toolchain successfully, please run

$ riscv64-unknown-linux-gnu-gcc --version

  riscv64-unknown-linux-gnu-gcc (Xuantie-900 linux-5.10.4 glibc gcc Toolchain V2.6.1 B-20220906) 10.2.0
  Copyright (C) 2020 Free Software Foundation, Inc.
  This is free software; see the source for copying conditions.  There is NO
  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ riscv64-unknown-linux-gnu-g++ --version

  riscv64-unknown-linux-gnu-g++ (Xuantie-900 linux-5.10.4 glibc gcc Toolchain V2.6.1 B-20220906) 10.2.0
  Copyright (C) 2020 Free Software Foundation, Inc.
  This is free software; see the source for copying conditions.  There is NO
  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Build sherpa-onnx

Next, let us build sherpa-onnx.

Hint

Currently, only shared libraries are supported. We will support static linking in the future.

git clone https://github.com/k2-fsa/sherpa-onnx
cd sherpa-onnx
./build-riscv64-linux-gnu.sh

After building, you will get the following files

$ ls -lh build-riscv64-linux-gnu/install/bin
$ echo "---"
$ ls -lh build-riscv64-linux-gnu/install/lib

  total 292K
  -rwxr-xr-x 1 root root 23K Mar 20 09:41 sherpa-onnx
  -rwxr-xr-x 1 root root 27K Mar 20 09:41 sherpa-onnx-alsa
  -rwxr-xr-x 1 root root 31K Mar 20 09:41 sherpa-onnx-alsa-offline
  -rwxr-xr-x 1 root root 40K Mar 20 09:41 sherpa-onnx-alsa-offline-speaker-identification
  -rwxr-xr-x 1 root root 23K Mar 20 09:41 sherpa-onnx-keyword-spotter
  -rwxr-xr-x 1 root root 27K Mar 20 09:41 sherpa-onnx-keyword-spotter-alsa
  -rwxr-xr-x 1 root root 23K Mar 20 09:41 sherpa-onnx-offline
  -rwxr-xr-x 1 root root 39K Mar 20 09:41 sherpa-onnx-offline-parallel
  -rwxr-xr-x 1 root root 19K Mar 20 09:41 sherpa-onnx-offline-tts
  -rwxr-xr-x 1 root root 31K Mar 20 09:41 sherpa-onnx-offline-tts-play-alsa
  ---
  total 30M
  -rw-r--r-- 1 root root 256K Mar 20 09:41 libespeak-ng.so
  -rw-r--r-- 1 root root  71K Mar 20 09:41 libkaldi-decoder-core.so
  -rw-r--r-- 1 root root  67K Mar 20 09:41 libkaldi-native-fbank-core.so
  -rw-r--r-- 1 root root  13M Mar 20 09:35 libonnxruntime.so
  -rw-r--r-- 1 root root  13M Mar 20 09:35 libonnxruntime.so.1.14.1
  lrwxrwxrwx 1 root root   23 Mar 20 09:41 libpiper_phonemize.so -> libpiper_phonemize.so.1
  lrwxrwxrwx 1 root root   27 Mar 20 09:41 libpiper_phonemize.so.1 -> libpiper_phonemize.so.1.2.0
  -rw-r--r-- 1 root root 395K Mar 20 09:41 libpiper_phonemize.so.1.2.0
  -rw-r--r-- 1 root root 1.3M Mar 20 09:41 libsherpa-onnx-core.so
  lrwxrwxrwx 1 root root   23 Mar 20 09:41 libsherpa-onnx-fst.so -> libsherpa-onnx-fst.so.6
  -rw-r--r-- 1 root root 1.4M Mar 20 09:41 libsherpa-onnx-fst.so.6
  -rw-r--r-- 1 root root 752K Mar 20 09:41 libsherpa-onnx-kaldifst-core.so
  -rw-r--r-- 1 root root 202K Mar 20 09:41 libucd.so
  drwxr-xr-x 2 root root 4.0K Mar 20 09:41 pkgconfig
$ file build-riscv64-linux-gnu/install/bin/sherpa-onnx

build-riscv64-linux-gnu/install/bin/sherpa-onnx: ELF 64-bit LSB executable, UCB RISC-V, RVC, double-float ABI, version 1 (GNU/Linux), dynamically linked, interpreter /lib/ld-linux-riscv64-lp64d.so.1, for GNU/Linux 4.15.0, stripped
$ readelf -d build-riscv64-linux-gnu/install/bin/sherpa-onnx
$ find $HOME/toolchain/ -name ld-linux-riscv64-lp64d.so.1

   Dynamic section at offset 0x4d40 contains 39 entries:
     Tag        Type                         Name/Value
    0x0000000000000001 (NEEDED)             Shared library: [libsherpa-onnx-core.so]
    0x0000000000000001 (NEEDED)             Shared library: [libkaldi-native-fbank-core.so]
    0x0000000000000001 (NEEDED)             Shared library: [libkaldi-decoder-core.so]
    0x0000000000000001 (NEEDED)             Shared library: [libsherpa-onnx-kaldifst-core.so]
    0x0000000000000001 (NEEDED)             Shared library: [libsherpa-onnx-fst.so.6]
    0x0000000000000001 (NEEDED)             Shared library: [libpiper_phonemize.so.1]
    0x0000000000000001 (NEEDED)             Shared library: [libonnxruntime.so.1.14.1]
    0x0000000000000001 (NEEDED)             Shared library: [libespeak-ng.so]
    0x0000000000000001 (NEEDED)             Shared library: [libucd.so]
    0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
    0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
    0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
    0x0000000000000001 (NEEDED)             Shared library: [libpthread.so.0]
    0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
    0x000000000000000f (RPATH)              Library rpath: [$ORIGIN:$ORIGIN/../lib:$ORIGIN/../../../sherpa_onnx/lib]
    0x0000000000000020 (PREINIT_ARRAY)      0x15d20
    0x0000000000000021 (PREINIT_ARRAYSZ)    8 (bytes)
    0x0000000000000019 (INIT_ARRAY)         0x15d28
    0x000000000000001b (INIT_ARRAYSZ)       16 (bytes)
    0x000000000000001a (FINI_ARRAY)         0x15d38
    0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
    0x0000000000000004 (HASH)               0x10280
    0x000000006ffffef5 (GNU_HASH)           0x10418
    0x0000000000000005 (STRTAB)             0x10bd8
    0x0000000000000006 (SYMTAB)             0x105f0
    0x000000000000000a (STRSZ)              3652 (bytes)
    0x000000000000000b (SYMENT)             24 (bytes)
    0x0000000000000015 (DEBUG)              0x0
    0x0000000000000003 (PLTGOT)             0x16000
    0x0000000000000002 (PLTRELSZ)           1056 (bytes)
    0x0000000000000014 (PLTREL)             RELA
    0x0000000000000017 (JMPREL)             0x11bb0
    0x0000000000000007 (RELA)               0x11b80
    0x0000000000000008 (RELASZ)             1104 (bytes)
    0x0000000000000009 (RELAENT)            24 (bytes)
    0x000000006ffffffe (VERNEED)            0x11aa0
    0x000000006fffffff (VERNEEDNUM)         4
    0x000000006ffffff0 (VERSYM)             0x11a1c
    0x0000000000000000 (NULL)               0x0

 /root/toolchain/sysroot/lib/ld-linux-riscv64-lp64d.so.1

That’s it!

Please create an issue at https://github.com/k2-fsa/sherpa-onnx/issues if you have any problems.

Read more if you want to run the binaries with qemu.

qemu

Hint

This subsection works only on x64 Linux.

Caution

Please don’t use any other methods to install qemu-riscv64. Only the method listed in this subsection is known to work.

Please use the following command to download the qemu-riscv64 binary.

mkdir -p $HOME/qemu

mkdir -p /tmp
cd /tmp
wget -q https://files.pythonhosted.org/packages/21/f4/733f29c435987e8bb264a6504c7a4ea4c04d0d431b38a818ab63eef082b9/xuantie_qemu-20230825-py3-none-manylinux1_x86_64.whl

unzip xuantie_qemu-20230825-py3-none-manylinux1_x86_64.whl
cp -v ./qemu/qemu-riscv64 $HOME/qemu

export PATH=$HOME/qemu:$PATH

To check that we have installed qemu-riscv64 successfully, please run:

qemu-riscv64 -h

which should give the following output:

usage: qemu-riscv64 [options] program [arguments...]
Linux CPU emulator (compiled for riscv64 emulation)

Options and associated environment variables:

Argument             Env-variable      Description
-h                                     print this help
-help
-g port              QEMU_GDB          wait gdb connection to 'port'
-L path              QEMU_LD_PREFIX    set the elf interpreter prefix to 'path'
-s size              QEMU_STACK_SIZE   set the stack size to 'size' bytes
-cpu model           QEMU_CPU          select CPU (-cpu help for list)
-E var=value         QEMU_SET_ENV      sets targets environment variable (see below)
-U var               QEMU_UNSET_ENV    unsets targets environment variable (see below)
-0 argv0             QEMU_ARGV0        forces target process argv[0] to be 'argv0'
-r uname             QEMU_UNAME        set qemu uname release string to 'uname'
-B address           QEMU_GUEST_BASE   set guest_base address to 'address'
-R size              QEMU_RESERVED_VA  reserve 'size' bytes for guest virtual address space
-d item[,...]        QEMU_LOG          enable logging of specified items (use '-d help' for a list of items)
-dfilter range[,...] QEMU_DFILTER      filter logging based on address range
-D logfile           QEMU_LOG_FILENAME write logs to 'logfile' (default stderr)
-p pagesize          QEMU_PAGESIZE     set the host page size to 'pagesize'
-singlestep          QEMU_SINGLESTEP   run in singlestep mode
-strace              QEMU_STRACE       log system calls
-pctrace             QEMU_PCTRACE      log pctrace
-seed                QEMU_RAND_SEED    Seed for pseudo-random number generator
-trace               QEMU_TRACE        [[enable=]<pattern>][,events=<file>][,file=<file>]
-csky-extend         CSKY_EXTEND       [tb_trace=<on|off>][,jcount_start=<addr>][,jcount_end=<addr>][vdsp=<vdsp>][exit_addr=<addr>][denormal=<on|off>]
-CPF                 CSKY_PROFILING
-csky-trace          CSKY_TRACE        [port=<port>][,tb_trace=<on|off>][,mem_trace=<on|off>][,auto_trace=<on|off>][,start=addr][,exit=addr]
-plugin              QEMU_PLUGIN       [file=]<file>[,arg=<string>]
-version             QEMU_VERSION      display version information and exit

Defaults:
QEMU_LD_PREFIX  = /usr/gnemul/qemu-riscv64
QEMU_STACK_SIZE = 8388608 byte

You can use -E and -U options or the QEMU_SET_ENV and
QEMU_UNSET_ENV environment variables to set and unset
environment variables for the target process.
It is possible to provide several variables by separating them
by commas in getsubopt(3) style. Additionally it is possible to
provide the -E and -U options multiple times.
The following lines are equivalent:
    -E var1=val2 -E var2=val2 -U LD_PRELOAD -U LD_DEBUG
    -E var1=val2,var2=val2 -U LD_PRELOAD,LD_DEBUG
    QEMU_SET_ENV=var1=val2,var2=val2 QEMU_UNSET_ENV=LD_PRELOAD,LD_DEBUG
Note that if you provide several changes to a single variable
the last change will stay in effect.

See <https://qemu.org/contribute/report-a-bug> for how to report bugs.
More information on the QEMU project at <https://qemu.org>.

We describe below how to use qemu-riscv64 to run speech-to-text and text-to-speech.

Run speech-to-text with qemu

We use csukuangfj/sherpa-onnx-streaming-zipformer-en-20M-2023-02-17 (English) as the test model.

Note

You can select any model from Pre-trained models.

Please use the following command to download the model:

cd /path/to/sherpa-onnx

wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-streaming-zipformer-en-20M-2023-02-17.tar.bz2
tar xvf sherpa-onnx-streaming-zipformer-en-20M-2023-02-17.tar.bz2
rm sherpa-onnx-streaming-zipformer-en-20M-2023-02-17.tar.bz2

Now you can use the following command to run it with qemu-riscv64:

cd /path/to/sherpa-onnx

export PATH=$HOME/qemu:$PATH

qemu-riscv64 build-riscv64-linux-gnu/install/bin/sherpa-onnx \
  --tokens=./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/tokens.txt \
  --encoder=./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/encoder-epoch-99-avg-1.onnx \
  --decoder=./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/decoder-epoch-99-avg-1.onnx \
  --joiner=./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/joiner-epoch-99-avg-1.onnx \
  ./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/test_wavs/0.wav

It will throw the following error:

qemu-riscv64: Could not open '/lib/ld-linux-riscv64-lp64d.so.1': No such file or directory

Please use the following command instead:

cd /path/to/sherpa-onnx

export PATH=$HOME/qemu:$PATH
export QEMU_LD_PREFIX=$HOME/toolchain/sysroot

qemu-riscv64 build-riscv64-linux-gnu/install/bin/sherpa-onnx \
  --tokens=./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/tokens.txt \
  --encoder=./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/encoder-epoch-99-avg-1.onnx \
  --decoder=./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/decoder-epoch-99-avg-1.onnx \
  --joiner=./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/joiner-epoch-99-avg-1.onnx \
  ./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/test_wavs/0.wav

It will throw a second error:

build-riscv64-linux-gnu/install/bin/sherpa-onnx: error while loading shared libraries: ld-linux-riscv64xthead-lp64d.so.1: cannot open shared object file: No such file or directory

Please use the following command instead:

cd /path/to/sherpa-onnx

export PATH=$HOME/qemu:$PATH
export QEMU_LD_PREFIX=$HOME/toolchain/sysroot
export LD_LIBRARY_PATH=$HOME/toolchain/sysroot/lib:$LD_LIBRARY_PATH

qemu-riscv64 build-riscv64-linux-gnu/install/bin/sherpa-onnx \
  --tokens=./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/tokens.txt \
  --encoder=./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/encoder-epoch-99-avg-1.onnx \
  --decoder=./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/decoder-epoch-99-avg-1.onnx \
  --joiner=./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/joiner-epoch-99-avg-1.onnx \
  ./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/test_wavs/0.wav

Finally, it prints the following output:

/content/sherpa-onnx/sherpa-onnx/csrc/parse-options.cc:Read:361 build-riscv64-linux-gnu/install/bin/sherpa-onnx --tokens=./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/tokens.txt --encoder=./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/encoder-epoch-99-avg-1.onnx --decoder=./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/decoder-epoch-99-avg-1.onnx --joiner=./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/joiner-epoch-99-avg-1.onnx ./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/test_wavs/0.wav

OnlineRecognizerConfig(feat_config=FeatureExtractorConfig(sampling_rate=16000, feature_dim=80), model_config=OnlineModelConfig(transducer=OnlineTransducerModelConfig(encoder="./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/encoder-epoch-99-avg-1.onnx", decoder="./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/decoder-epoch-99-avg-1.onnx", joiner="./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/joiner-epoch-99-avg-1.onnx"), paraformer=OnlineParaformerModelConfig(encoder="", decoder=""), wenet_ctc=OnlineWenetCtcModelConfig(model="", chunk_size=16, num_left_chunks=4), zipformer2_ctc=OnlineZipformer2CtcModelConfig(model=""), tokens="./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/tokens.txt", num_threads=1, debug=False, provider="cpu", model_type=""), lm_config=OnlineLMConfig(model="", scale=0.5), endpoint_config=EndpointConfig(rule1=EndpointRule(must_contain_nonsilence=False, min_trailing_silence=2.4, min_utterance_length=0), rule2=EndpointRule(must_contain_nonsilence=True, min_trailing_silence=1.2, min_utterance_length=0), rule3=EndpointRule(must_contain_nonsilence=False, min_trailing_silence=0, min_utterance_length=20)), enable_endpoint=True, max_active_paths=4, hotwords_score=1.5, hotwords_file="", decoding_method="greedy_search", blank_penalty=0)
./sherpa-onnx-streaming-zipformer-en-20M-2023-02-17/test_wavs/0.wav
Elapsed seconds: 70, Real time factor (RTF): 11
 THE YELLOW LAMPS WOULD LIGHT UP HERE AND THERE THE SQUALID QUARTER OF THE BRAFFLELS
{ "text": " THE YELLOW LAMPS WOULD LIGHT UP HERE AND THERE THE SQUALID QUARTER OF THE BRAFFLELS", "tokens": [ " THE", " YE", "LL", "OW", " LA", "M", "P", "S", " WOULD", " LIGHT", " UP", " HE", "RE", " AND", " THERE", " THE", " S", "QUA", "LI", "D", " ", "QUA", "R", "TER", " OF", " THE", " B", "RA", "FF", "L", "EL", "S" ], "timestamps": [ 2.04, 2.16, 2.28, 2.36, 2.52, 2.64, 2.68, 2.76, 2.92, 3.08, 3.40, 3.60, 3.72, 3.88, 4.12, 4.48, 4.64, 4.68, 4.84, 4.96, 5.16, 5.20, 5.32, 5.36, 5.60, 5.72, 5.92, 5.96, 6.08, 6.24, 6.36, 6.60 ], "ys_probs": [ -0.454799, -0.521409, -0.345871, -0.001244, -0.240359, -0.013972, -0.010445, -0.051701, -0.000371, -0.171570, -0.002205, -0.026703, -0.006903, -0.021168, -0.011662, -0.001059, -0.005089, -0.000273, -0.575480, -0.024973, -0.159344, -0.000042, -0.011082, -0.187136, -0.004002, -0.292751, -0.084873, -0.241302, -0.543844, -0.428164, -0.853198, -0.093776 ], "lm_probs": [  ], "context_scores": [  ], "segment": 0, "start_time": 0.00, "is_final": false}

Hint

As you can see, the RTF is 11, indicating that it is very slow to run the model with the qemu simulator. Running on a real RISC-V board should be much faster.

Run text-to-speech with qemu

Please visit https://github.com/k2-fsa/sherpa-onnx/releases/tag/tts-models to download a text-to-speech model. We use the following model vits-piper-en_US-amy-low.tar.bz2:

cd /path/to/sherpa-onnx

wget https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/vits-piper-en_US-amy-low.tar.bz2
tar xf vits-piper-en_US-amy-low.tar.bz2
rm vits-piper-en_US-amy-low.tar.bz2

After downloading the model, we can use the following command to run it:

cd /path/to/sherpa-onnx

export PATH=$HOME/qemu:$PATH
export QEMU_LD_PREFIX=$HOME/toolchain/sysroot
export LD_LIBRARY_PATH=$HOME/toolchain/sysroot/lib:$LD_LIBRARY_PATH

qemu-riscv64 build-riscv64-linux-gnu/install/bin/sherpa-onnx-offline-tts \
  --vits-model=./vits-piper-en_US-amy-low/en_US-amy-low.onnx \
  --vits-tokens=./vits-piper-en_US-amy-low/tokens.txt \
  --vits-data-dir=./vits-piper-en_US-amy-low/espeak-ng-data \
  --output-filename=./a-test.wav \
  "Friends fell out often because life was changing so fast. The easiest thing in the world was to lose touch with someone."

The log of the above command is given below:

/content/sherpa-onnx/sherpa-onnx/csrc/parse-options.cc:Read:361 build-riscv64-linux-gnu/install/bin/sherpa-onnx-offline-tts --vits-model=./vits-piper-en_US-amy-low/en_US-amy-low.onnx --vits-tokens=./vits-piper-en_US-amy-low/tokens.txt --vits-data-dir=./vits-piper-en_US-amy-low/espeak-ng-data --output-filename=./a-test.wav 'Friends fell out often because life was changing so fast. The easiest thing in the world was to lose touch with someone.'

Elapsed seconds: 270.745 s
Audio duration: 7.904 s
Real-time factor (RTF): 270.745/7.904 = 34.254
The text is: Friends fell out often because life was changing so fast. The easiest thing in the world was to lose touch with someone.. Speaker ID: 0
Saved to ./a-test.wav successfully!
Wave filename Content Text
a-test.wav Friends fell out often because life was changing so fast. The easiest thing in the world was to lose touch with someone.