最近在研究如何在Zynq UltraScale+ MPSoC 平台的ARM端运行信号处理相关算法,在x86_Linux端使用了armdillo库完成了算法,目标就是先移植armdillo到ARM端。网上相关资料很少,并且参照了一些博客。并没有成功复现,踩了很多的坑,最后自己仔细阅读官方README文件,研究之后成功移植,这里做一下记录
一.平台介绍和编译环境
1.使用的Zynq UltraScale+ MPSoC
核心板使用 XILINX Zynq UltraScale+ EV 芯片 ZU4EV 的解决方案,它采用 Processing System(PS)+Programmable Logic(PL)技术将四核ARM Cortex-A53 和FPGA 可编程逻辑集成在一颗芯片上。另外核心板上 PS 端带有 4 片共 4GB 高速 DDR4 SDRAM 芯片,1 片 8GB的 eMMC 存储芯片和 1 片 256Mb 的 QSPI FLASH 芯片; 核心板上 PL 端带有 1 片 1GB 的DDR4 SDRAM 芯片。使用petalinux2020.2版本在ARM核上构建了Linux镜像系统。
2.交叉编译环境
虚拟机:ubuntu18.04.2
交叉编译工具链: aarch-xilinx-linux-gcc等
pc工具:GNU Make 4.1 cmake 3.15.3
如何获取交叉编译工具链:
参考正点原子的教程,使用petalinux构建根文件系统
petalinux-config -c rootfs
petalinux-build -c rootfs
petalinux-build --sdk
获取sdk.sh后sudo sdk.sh来将工具链安装在目录/opt/petalinux/2020.2/下
将环境配置environment-setup-aarch64-xilinx-linux放到~/.bashrc中,并source,此时可以取消petalinux的环境
之后的环境就是aarch64-xilinx-linux的工具链可以查看$CC $CXX验证
二.交叉编译armadillo的依赖库(lapack openblas)
因为这两个库实际上是Fortran的库,如果要编译就需要Fortran的交叉编译工具,可是打开arrch64-xilinx-linux的工具没有编译Fortran的,所以这里参考了以下的内容,使用原始的Clapack编译,其中使用了f2c库将Fortran变为了C接口,以便后续支持
但是根据上面的流程,做出来的库是有问题的,首先是编译blas的时候没有关掉f2c的命名Wrapper,导致导出的符号有带有f2c_前缀命名,后续编译出的armdillo库,有undefined reference报错,后面取消了之后,编译出程序运行时使用dgemm函数错误。
无奈,随后更改方案,按照aramdillo文件中的README.md指引的说法,使用openblas库代替老版本的转换的blas库,以及加上clapack库,成功编译出可以运行程序的armadillo.so库
1.通过Clapack编译出liblapack.a libf2c.a
2.仅编译openblas的库
3.openblas和lapack 编译出 armadillo.so
具体细节:
1.交叉编译Clapak(可参考上面给出的链接)
路径如下:https://netlib.org/clapack/clapack-3.2.1-CMAKE.tgz
然后解压,进入clapack-3.2.1-CMAKE路径,应该首先研读README.install
这里给出我的,make.inc的配置
但是编译f2c有一个坑,就是在/home/mlh/clapack-3.2.1-CMAKE/F2CLIBS/libf2c的Makefile中,规定了了生成arith.h的是运行一个临时的a.out生成,但是交叉编译出的a.out无法在ubuntu环境中运行
所以应将这一段在Makefile中删除,手动用gcc实现这一段编译命令,生成arith.h
gcc $(CFLAGS) -DNO_FPINIT arithchk.c -lm ||\
gcc -DNO_LONG_LONG $(CFLAGS) -DNO_FPINIT arithchk.c -lm
./a.out >arith.h
rm -f a.out arithchk.o
之后再改Makefile中的ld,按流程编译出f2c,blas, lapack即可。如果有找不到链接的库的,应该再链接命令后面加上指定sysroot的命令--sysroot=/opt/petalinux/2020.2/sysroots/aarch64-xilinx-linux或者指定lib搜索路径-L/opt/petalinux/2020.2/sysroots/aarch64-xilinx-linux/usr/lib
因为我们后续使用openblas,所以不用复制libblas.a到交叉编译lib路径下。
2.交叉编译openblas
在Rrelease版本中选择一个稳定的版本编译Releases · OpenMathLib/OpenBLAS · GitHub
我这里选择的是0.3.29版本,下载源码
同样进入之后先认真阅读,README.md
可以看到有给出cross_compile的例子
这里我们也可以通过给出的congfiue来配置,先搞清楚我们使用的ARM的架构,CUP是ARM Cortex-A53 ,所以我们的目标平台TARGET = ARMV8
然后我们只需要oepnblas,没有Fortran编译器区编译lapack,所以我们要加上限定条件NOFORTRAN=1
make BINARY=64 CC=arrch64-xilinx-linux-gcc NOFORTRAN=1 HOSTCC=gcc TARGET=ARMV8
最后,将编译出的内容安装到指定的文件夹中,可以得到openblas的库文件libopenblas.a
make PREFIX=../openblas_install install
···我们应将编译出的库复制到,交叉编译的路径下/opt/petalinux/2020.2/sysroots/aarch64-xilinx-linux/usr/lib
三.交叉编译armadillo库
1.下载armdillo
Armadillo: C++ library for linear algebra & scientific computing - Download
我这里选择了armadillo-14.0.3版本下载
2.交叉编译
根据阅读README.md来看,我们需要开启使用openblas库来编译,这里直接使用CMake编译,因为之前已经source好了环境,camke会自动配置工具链
1)修改CMakeLists.txt
2)直接cmake
cmake .
3)修改tests1路径下的CMakeLists.txt
添加target_link_libraries(smoke_test PRIVATE /opt/petalinux/2020.2/sysroots/aarch64-xilinx-linux/usr/lib/libpthread.so)
target_link_libraries(smoke_test PRIVATE /opt/petalinux/2020.2/sysroots/aarch64-xilinx-linux/usr/lib/libopenblas.a)
target_link_libraries(smoke_test PRIVATE /opt/petalinux/2020.2/sysroots/aarch64-xilinx-linux/usr/lib/liblapack.a)
target_link_libraries(smoke_test PRIVATE /opt/petalinux/2020.2/sysroots/aarch64-xilinx-linux/usr/lib/libf2c.a)
4)编译安装
make
make PREFIX=../armadillo_instll install
就可以编译出armadillo.so库,已经在tests下的smoke_test,库文件和头文件被安装到了指定的目录下~/armadillo_instl,将所有的库文件传输到板子上,并复制到/usr/lib下,在板子上运行smoke_test即可成功。
4 后续编译方法
单文件可以,编写Makefile来生成自己的测试文件,需要注意添加库的搜索路径和和头文件的搜所路径。也可以直接把库和头文件都放在交叉编译的路径下,直接添加sysroot
同时库的链接顺序应该是-lpthread -lopenblas -llapack -lf2c ,保证链接能够找到符号
LIB_FLAGS = -L/home/mlh/armadillo_insall/lib -larmadillo -L/opt/petalinux/2020.2/sysroots/aarch64-xilinx-linux/usr/lib -lpthread -lopenblas -llapack -lf2c
CXX_FLAGS = -I/home/mlh/armadillo_insall/include/ -std=c++14 -Wshadow -Wall -pedantic -Og
OBJECTS = music_test.o
%.o : %.cpp
$(CXX) $(CXX_FLAGS) -c $<
music_test: $(OBJECTS)
$(CXX) -o music_test $(OBJECTS) $(LIB_FLAGS)
all: music_test
.PHONY: clean
clean:
rm -f music_test *.o