Tag Archives: SoPC

OpenCV在基于FPGA的嵌入式系统中的移植研究

自己本科毕设的内容涉及到OpenCV
所以需要移植OpenCV到Nios平台上的嵌入式系统中
可是自己搜遍所有可能的资料
还是没有看到有人做过
即使在我毕设答辩前5天
自己依旧没有做成功

几度犹豫
曾想过腰斩题目改变毕设内容
最后忍住坚持了下来
论文末尾还都写上了移植失败分析
之后的几天
事情就像喜剧结局一样的峰回路转
原本以为不成功的移植居然在小组答辩演示的时候成功了
然后在大组答辩之前找出了不稳定的原因
于是
这篇文章就有了雏形

导师曾让我整理好自己毕设论文中的关于OpenCV的移植研究的部分
可以考虑发表出去
毕竟没人做过还是有些参考价值
自己这一段一是休息外加忙着聚会
二是不知道这样的技术含量值不值得发表
自知现在浮躁的环境发表个论文就像在刷个人数据
其实不见得文章都有什么价值
自己在查阅资料的时候也曾见过几篇文章的测试数据跟精度居然都能一模一样
只是换到自己要发表的时候
就诸多考虑
终究不想敷衍了事
只是想保持自己心中学术思想之正直
听起来多少有点自视清高的酸味
酸就酸吧
得瑟归得瑟,终究对得起自己

唠叨这么久终于开始要说正题了
感谢诸位忍到此刻的耐心
由于之前没人做过这方面的移植研究
所以本文能参考的资料不多
限于笔者的水平
若有错误之处还望见谅

移植整体步骤

  1. 配置交叉编译开发环境,移植uClinux系统
  2. 获取OpenCV源码,配置并编译,生成目标平台的静态链接库
  3. 自行编写文件读写以及图片显示函数,摒除对Highgui库的调用依赖
  4. 将改写后的程序与生成的静态链接库链接一起,下载到目标平台上测试运行

对于第一步,给个自己写的文章当参考——《uClinux在Nios II平台上的移植》,我将从第二步开始说起
值得注意的是:

  • Highgui是造成移植程序运行不稳定的一个原因,毕竟不同嵌入式硬件平台之间都有诸多区别,更何况与Pc的差异。所以要移植OpenCV程序到Nios II的嵌入式系统中,请修改源程序,摒除对Highgui库的调用依赖,自行编写文件读写以及对IplImage这种OpenCV特有数据结构的初始化。

本文使用nios2-gcc-3.4.6 + OpenCV 1.1 pre测试,OpenCV的1.0、2.0版本我也尝试过编译,均告失败,只有1.1测试通过。

配置编译OpenCV

获取OpenCV源码

你可以从http://sourceforge.net/projects/opencvlibrary/files/这里下载OpenCV的源码
点击View all files那个按钮,然后下载opencv-unix 1.1pre1,在终端输入

tar -zxvf opencv-1.1pre1.tar.gz

解压供后面使用
如果你还尝试了别的版本并且也成功了,请不吝告诉在下

配置OpenCV编译信息

进入刚才解压出来的源码文件夹,输入配置命令

$ ./configure --host=nios2-linux --without-gtk --without-carbon --without-quicktime --without-1394libs --without-ffmpeg --without-python --without-swig --enable-static --disable-shared --disable-apps CXX=nios2-linux-g++ --prefix=/home/lee/uccv

–host 指定要编译的目标平台

–witout-* 禁用各种函数库,一是uClinux本来就不支持这里的大部分库,除了gtk,其他的其实你不填也是一样不会编译进去;二是出于精简的需要,说不定日后真支持某个库了,这些我们不需要的东西还是明令去除的好

–enable-static –disable-shared 启动静态链接库,禁用动态链接,因为嵌入式系统对内核的大小要求很看重,不适合将OpenCV库编译成动态链接库,因为这样生成的库都是完整且较为庞大,所以在移植程序数量较少的情况下,通过静态链接库,只将程序用到的部分与程序编译链接到一起,是相对比较理想的选择。另外一个原因是我曾经想使用动态链接库来运行,可是编译出错,暂时无解。

–disable-apps 禁止编译范例程序,因为要编译可以在FPGA上的uClinux系统运行的OpenCV程序,需要在编译的时候额外添加参数,而且我们等会还要自行测试编译,这里一定得禁用掉,否则会导致之后make出错。

CXX 指定编译器。其实这个可以不加,只要host里面指定的平台系统能识别,最后生成的makefile里面的编译器配置信息都是一样的,之所以还保留是因为之前参考ARM下的移植文章,在此留着给大家之后编译若有错误当做一个切入点。

–prefix 可以设置OpenCV编译结果想要安装的路径,这里设置为笔者Fedora中用户所在的个人文件夹。

然后经过一堆输出信息刷新,你可以看到如下信息:

v4l那是因为我毕设要做双目测距程序,需要用到摄像头,所以我留着(不过最后还是失败了,西奈……uClinux下摄像头驱动的移植又是个头痛的问题)。

一切正常的话,我们就开始编译

$ make

如果make没有出错,就开始安装目标平台上的OpenCV函数库吧

$ make install

然后在我们设置的路径下找到lib文件夹,进入就可以看到编译好的静态链接库了

摒除Highgui的依赖

由于不能再用Highgui库了,如果你的程序有用到Highgui,我们就得自己重写文件读写以及图片显示函数,这里着重讲下如何对图片文件读取以及之后如何使用自己读取的图片数据来初始化IplImage,以此为例,其他部分按照这样的思路同样去修改。

  • 删除所有Highgui的头文件引用

为了对原来写好的程序进行最低程度的修改,我们只删除Highgui的头文件引用,然后自行添加个头文件与源文件,在其中加入的图片读取函数的名称,跟原来一样为IplImage* cvLoadImage(char* filename, int iscolor = 1)

  • 读取图片函数的编写,将图像数据存在BYTE*指针指向的内存空间

在自行编写cvLoadImage()函数中,根据要读取的图片格式不同,对应自己编写读取算法,唯一相同的步骤是,最后将图片数据读取到一个BYTE指针指向的空间

  • 使用读取的图像数据初始化OpenCV的IplImage数据结构

读取好图片,可以使用OpenCV的cxcore中的两个函数来初始化IplImage

IplImage* cvLoadImage(char* fileName, int iscolor = 1)
{
	/*...省略其他代码...*/

	//创建IplImage变量的信息头
	IplImage* img = cvCreateImageHeader(cvSize(width,height), depth, channel);
	//利用BYTE指针数据初始化IplImage
	cvSetData(img, (void *)blocks, lineByte);

	/*...省略其他代码...*/
	return img;
}

width、height是要创建IplImage图像的宽度与高度
depth是图像的位深
channel是图像的通道数
blocks是读取来的图像数据的BYTE*指针

lineByte是OpenCV中图像每一行的数据宽度,OpenCV规定图像的每一行数据宽度都必须是4的倍数,但是图像不同位数、不同宽度,不一定满足这个条件,所以这个变量之前需要额外进行一个简单的计算处理

lineByte = (width * biBitCount/8)/4*4;
//biBitCount是图像一个像素占用的位宽大小

以上width、height、biBitCount等int变量的值都可以从图片的文件头读取得知,具体方法请查阅不同图片格式的资料说明。

需要说清楚,我们只是自己简单的写了一个读取特定图片格式的函数,比不上原来的highgui原来的API那么好用,各种格式都支持,不过一般应用的时候,格式都比较固定,这点相对不是什么大问题。

  • 最后对IplImage的释放就不像原来一句cvReleaseImage()就搞定,要分2次完成
delete[] img->imageDataOrigin;
cvReleaseImageHeader(&img);

至此,我们就完成了脱离Highgui自行完成图片文件读取函数,至于文件写入乃至图片显示,限于篇幅原因就不详细讲解,基本思想掌握,剩下的就是自由发挥了。
等我工作定下之后,闲暇再找机会补写看看。

这里留下一个自己写的BMP读取的源码,供大家参考下,写的很搓很随意,而且只读取彩色图像,随意瞥下了解就好。
<—传送门—>

链接整合程序

弄到这里,有了OpenCV的静态函数库,也有脱离Highgui的源码,就可以开始编译链接目标平台的程序了

#先编译文件读取程序
$ nios2-linux-g++ -c bmpDecoder.cpp bmpDecoder.o -I/home/lee/uccv/include/opencv

#再编译测试OpenCV程序
$ nios2-linux-g++ -c test.cpp test.o -I/home/lee/uccv/include/opencv

#最后与函数库链接在一起
$ nios2-linux-g++ -o test bmpDecoder.o test.o -L/home/lee/uccv/lib -lcv -lcvaux -lcxcore -lml -lpthread -elf2flt

-I 指定源码中的头文件引用库位置

-L 指定静态链接函数库位置

-lcv -lcvaux -lcxcore -lml 是需要链接的OpenCV的几个静态链接库,顺序很重要,更改顺序很可能导致undefined reference,需要根据自己编写的程序微调这个参数列表的顺序。

-lpthread 支持多线程程序,我是根据错误提示信息加上这个参数,更深入的理由我并未深究。

-elf2flt 编译为flt格式程序,uClinux默认没有MMU,只支持绝对内存地址的二进制格式,这个记得一定要加上

假如没有什么意外的话,你就会获得最后编译出来的目标平台的OpenCV程序文件

最后唠叨

编译好了,链接成功了,按照我之前给的那篇文章的说明,再烧写下载程序到目标板上测试运行。

虽然我觉得以Cyclone II级别的FPGA器件运行OpenCV的效率,还不能到实际应用的程度,不过以现在FPGA的发展势头,多尝试新的SoPC应用开发,待硬件资源足够之时,也未必不是一大优势。

至于一些目前其他高端的FPGA的运行表现,由于自己没有条件测试,在此就不妄加评价了。

Tagged , , , ,

uClinux在Nios II平台上的移植

说起来网上关于如何在FPGA开发板上,对基于NIOS II软核的平台移植uClinux系统的资料真的很多,写下这篇文章,一是提到点自己测试的经验;二是整理资料,希望能有个更清晰明了的教程。

上次写魔兽插件的教程,越到最后觉得写得越仓促,主要是因为追求细节足够详细导致精力不够,毕竟写写东西也只是业余消遣。保证初学者有迹可循又简明扼要,的确不是容易办到的事情。这样冗长的技术教程,我会在给初学者留条小路的基础上尽量一针见血的说明事情。

对于开发板的选择,我也买错了,红色飓风这种板高不成低不就的,700块大洋花了真的一点都不值,至少在这次应用上,它的片上资源已经捉襟见肘。如果有可能,买块DE或者DE2的板,一块可以解决日后很多应用,我就是借用了实验室的DE2才得以继续进行下去的。

废话说完了,下面开始说正事。

移植实现思路

  1. 建立交叉编译开发环境
  2. SoPC Builder配置硬件资源
  3. 配置、编译内核
  4. 下载运行测试

这里采用Fedora 12为基础建立交叉编译环境,我是在Vmware虚拟机上安装的Fedora,如果可以推荐直接装在物理机上,装Ubuntu、CentOS之类的系统都可以。初学者请搜索“Vmware 安装linux”,自行学习基础,如果Vmware没有注册使用不了,为了版权的考虑,可以选用Virtual Box这样的开源虚拟机软件。

SoPC Builer是Quartus开发工具中的一部分,我使用的是Quartus 7.2版本,安装在物理机的Windows XP下

熟悉基本的Linux命令,以便后面解压源码等步骤使用

下载到板上测试,我使用的是NIOS Shell Command,所以要安装与Quartus同版本的NIOS开发工具,一样装在XP下。可以自行下载Linux版本的开发工具,这只是我懒得换版本而已。

搭建交叉编译环境

安装并配置linux系统

假设已经安装Fedora 12,要进行下一步开发,需要安装一些开发库。

在终端输入如下指令,可以一次性安装好本次开发需要的所有库:

$ sudo rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-3.noarch.rpm

$sudo yum install git-all git-gui make gcc ncurses-devel bison \
byacc flex gawk gettext ccache zlib-devel gtk2-devel lzo-devel \
pax-utils libglade2-devel

如果是Ubuntu之类的其他Linux系统,请看NiosWiki的安装说明:<—猛击我—>

获取nios2-linux源码

要进行操作系统的移植,一要移植bootloader,二要移植内核,三是根文件系统,移植的编译需要交叉编译工具,这里可以下一个源码包,里面已经整合了我们需要的各种东西

在终端输入下载


$wget http://www.niosftp.com/pub/uclinux/nios2-linux-20090730.tar

把nios2-linux-20090730.tar解压到当前目录:


$tar nios2-linux-20090730.tar

切换到nios2-linux 目录,验证文件


$./checkout

下载交叉编译工具

获得nios2-linux源码之后,可以在toolchain-build目录下自行编译生成交叉编译工具,也可以从http://www.nioswiki.com/OperatingSystems/UClinux/BinaryToolchain这里下载编译好的BinaryToolchain。笔者试过以nios2-linux-20090730版本的源码加上Fedora的gcc 4.4.3尝试自行编译,但是提示出错,最后选用现成的交叉编译工具,节省研究时间。


$wget http://www.niosftp.com/pub/gnutools/nios2gcc-20080203.tar.bz2

之后解压该文档,交叉编译工具在/opt/nios2/bin下,Fedora 12默认建立用户一开始无法使用sudo请自行搜索“Fedora 无法使用sudo”


$sudo tar -jxf nios2gcc-20080203.tar.bz2 –C /

然后设一下Linux用户的PATH变量,这样才可以等会编译的时候才找的到交叉编译器


$ vi ~/.bashrc

根据ToolChain所在位置在.bashrc中添加一行并保存,其中的路径为刚才下载的交叉编译器的解压位置


PATH=$PATH:/opt/nios2/bin

重新登录linux用户后,查看nios2-linux-gcc 版本,验证编译器是否可用


$nios2-linux-gcc -v

SoPC Builder配置

根据NiosWiki的说明,要在NIOS上移植uClinux系统,硬件需要的最低标准如下:

  • Nios II f/s级别的核,必须要有硬件乘法器(推荐f级别,s级别是最低要求)
  • sdram(至少需要8M)
  • 一个full featured的计时器(IRQ为0)
  • 一个jtag/serial uart

假设你已经会使用SoPC Builder,然后根据上面的要求,建立一个最小系统。在移植uClinux之前你可以使用NIOS建立一些HelloWorld的程序测试一下,是否可以运行,测试通过了就可以跳过继续下一阶段了。

对于使用DE2或者一些资料比较齐全的开发板的童鞋,在第一次进行移植工作的时候,可以先使用附带的范例中现成的SoPC工程项目,可以简化操作,提高成功率,避免因为Sopc Builder中配置硬件不正确,导致后期移植系统出错却误以为是配置编译过程中操作不当。

  • 按照nioswiki上的资料表明,对nios平台移植uClinux系统,需要将一个Full Featured的Timer设置为IRQ 0,但是笔者试验证明,即使使用Custom模式的Timer,而且IRQ不设置为0,依然可以移植成功。但是IRQ 0是linux默认自动检测的一个中断号,为了避免不必要的麻烦,对于那些附带的范例中IRQ不为0的,笔者推荐还是手动稍微修改一下设置比较好。

在SoPC Builder工程的目录下找到如下2个文件:

  • {SoPC项目名称}.ptf
  • {Quartus项目名称}.sof

将其复制保存以供后面使用,前者用来配置uClinux目标板的基本信息,后者用来烧写FPGA。

配置编译内核

配置内核

切换到nios2-linux下的uClinux-dist,执行


$ make menuconfig

如果你已经安装了ncurses库,就可以进入内核配置的图形界面

然后分别对Vendor以及Kernel的下级菜单进行如下设置:

Vendor/Product Selection —>
— Select the Vendor you wish to target
Vendor (Altera)  —>

— Select the Product you wish to target
Altera Products (nios2)   —>

Kernel/Library/Defaults Selection —>
— Kernel is linux-2.6.x

Libc Version (None)  —>
[*] Default all settings (lose changes)
[ ] Customize Kernel Settings
[ ] Customize Vendor/User Settings
[ ] Update Default Vendor Settings

按Y为选中项目,N为取消,M是动态加载,按2次ESC为退出到上一级菜单,最后退出的时候会提示保存设置,根据上面设置并保存退出。

本文这里使用内核的默认设置,并为进行过多剪裁。实际上笔者发现该内核开发包可以很完善的支持DE2等一些官方认证的开发板,默认的设置都可以开始并使用网卡等设备,若无特殊要求,默认设置就可以胜任。

你可以选择Customize Kernel Setting,保存退出,然后又会进入新的配置界面,里面有详细的内核剪裁、配置选项,可以尝试着自己修改看看。东西都是折腾中学习出来了。

配置硬件信息

将之前SoPC Builder建立的ptf文件复制到某一文件夹下面,然后根据自己保存的路径与文件名输入


$ make vendor_hwselect SYSPTF=/home/lee/nios2-linux/system_0.ptf

之后选择对应的cpu以及供内核运行的存储器,cpu只有altera_nios2一个,但是板上的存储器类型很多,这里选择使用SDRAM来运行内核。

如果是物理机上的Linux系统,复制粘贴这个文件我就不说了,如果是Vmware的虚拟机安装Linux,Quartus又装在物理机的Windows下,有几种方法:

  • 使用虚拟网卡与主机相连,用Samba之类的共享文件,这种设置折腾折腾之后,你的Linux基本操作设置应该也都还可以了
  • 给虚拟机的Linux系统安装Vmware Tools,然后用虚拟机的共享文件夹功能来复制文件,不过Fedora 12这种新系统,Tools是不好安装成功的,如果是版本比较低的安装就很顺畅
  • 直接用优盘当中转,物理机把文件复制到优盘上,然后挂载到虚拟机中复制出来……很傻很简单很实在

这里配置这一步的作用,可以看NiosWiki-LinuxHWselect,说明的很详细,主要就是生成板上IO口以及存储器资源

编译

上面的步骤都没有错的话,直接输入


$make

第一次编译成功之后,可以在uClinux-dist的目录看到一个带有内核编号的文件夹,这里是linux-2.6.x,进去之后可以看到生成的一些相关文件与文件夹,在linux-2.6.x/arch/nios2/boot下,会有一个zImage的内核映像文件,这就是我们要的,按照上面的方法复制保存它,以供等会烧写使用。

这个uClinux-dist的开发包,默认设置就已经帮你把bootloader以及根文件系统配置好了,使用make的时候会对应一起生成。bootloader默认是u-boot,根文件系统是romfs。

下载运行测试

经过如上的操作,此刻我们会有2个文件,一个sof文件用于烧写FPGA芯片,一个zImage用于烧写uClinux内核。

打开NIOS II Command Shell,切换到文件保存的路径下,这里是优盘的盘符下,然后输入如下指令:


$ nios2-configure-sof DE2_NET.sof

看到下面的信息说明烧写成功

接着输入如下命令,-g是指下载后复位目标板,让内核立即运行,记得不要漏掉


$ nios2-download –g zImage

若成功会看到如下提示:

最后,输入


$ nios2-terminal

启动调试,就可以看到uClinux启动的画面

至此,最简单的一次移植我们已经都成功了,可以运行基本的linux命令,测试一下移植的uClinux系统。

我们这里只是在SDRAM上下载了内核映像,测试移植的话,这几个命令就可以了,但是每次断电之后,所有信息都会丢失。若你要真正烧写到flash之类的存储器上,你需要先将FPGA的下载方式改为AS,烧写FPGA芯片,然后接着才是烧写系统内核到flash上,对于第二步在这里转载gewanyong的文章,他已经说的很详细了,步骤如下:

(1)先把内核映像转换成可以直接烧到flash的文件,用以下命令:


$elf2flash --input=zImage --output=xx.flash --base=0x..... --reset=0x..... --end=0x.... --boot=xxxx.srec

其中:

  • zImage就是编译好的uClinux内核映像,
  • xx.flash是这条命令输出的文件,
  • –base和–end分别对应flash芯片的基地址和结束地址.–reset是CPU复位地址,一般和–base相同.
  • xxxx.srec是bootloader,可以从安装目录下的ip/altera_nios2下拷贝,名字是:boot_loader_cfi.srec,若不知具体位置可以搜索一下.

(2)把上面生成的xx.flash文件下载到目标板 flash中:


$nios2-flash-programmer -g -c usb-blaster --base=0x... --program xx.flash

usb- blaster是用到的下载电缆,-g表示下载后复位目标板,启动内核.

那几个地址可以从.ptf文件中得到.

添加简单程序

在第一次生成内核映像时,输入make之后会在uClinux-dist生成romfs这个文件夹,是对应根文件系统的编译内容文件夹,我们复制自己编写程序到其中,重新make一次,新产生的内核映像文件就包含了我们自己写的程序。

在这里笔者写了一个最简单的C语言的Hello World程序,测试自己编写程序移植的可能性。


#include <stdio.h>

int main(void)

{

printf("Hello,world!\n");

return 0;

}

将如上代码保存为.c文件,这里取名为test.c,然后输入编译命令


$ nios2-linux-gcc test.c –o test –elf2flt

由于uClinux没有MMU,所以不支持虚拟内存以及程序的相对地址运行,程序文件只能使用绝对物理地址的FLT格式,所以一定得加上-elf2flt参数,否则编译完的程序无法再uClinux上运行。

编译好可以使用如下命令验证是否是FLT格式


$ nios2-linux-flthdr test

检验成功后,在romfs下建立一个名为test的文件夹,把生成的test程序复制到其中,然后回到uClinux-dist下再次编译内核


$ make

之后重复之前的烧写步骤,直到进入uClinux系统,开始测试


$ cd test

$ ./test

到这里,我们也可以自行添加简单的程序了。当然你也可以配置好网卡跟ftp,使用ftp把编译好的程序传到板上运行,可以不用每次都重复一遍烧写过程,这个再google几下,就大概知道如何做了,甚至可以考虑改成NFS根文件系统,但是相对改动就大些,初学的时候,为了简单容易成功,麻烦就麻烦吧。

至于要再此基础上开发什么应用,那就任君选择了。

之前由于没有人在Nios平台上移植OpenCV,所以我自己尝试移植了一下,这几天有空我会整理出来,不过由于要发表到学术期刊上,所以可能会晚几天。

如果想深入学习,bootloader的自行修改,内核的剪裁以及驱动的编写都可以去了解下。

这次就说这么多吧,白白~~

Tagged , , ,