UC头条:我眼中的ZYNQ
ZYNQ是一个神一样的存在,硬件设计师不需要绞尽脑汁去思考嵌入式应用如何移植BSP;同样软件工程师也不需要深入弄清楚硬件的实现原理和架构。听起来像是两全其美, 你觉得呢? ZYNQ到底好用吗?该如何用呢?如何开始呢?暂且放下放下这些疑问。你好我是竹海EE读了小编下文你就会有答案。
ps7_cortexa9上的舞者
1. zynq的结构如下图所示。麻雀虽小,五脏俱全。
曾有人问我,这个图如何用。关于这个问题。这样回答可能合适。首先看看框框有几个,有没有注意到大的PS和PL。这个很重要。不要只看到细节USB,SPI等。她在潜意识的告诉你嵌入式系统的边界,就拿MIO展开叙述,需求很简单我想通过系统操作一个LED。首先就要告诉系统我必须有GPIO。存在时根本。
点击加载图片
2 怎么追寻到IO
这是一个有意义的问题,这也许是初学者感觉烦恼的地方。在开始阐述之前,先说点别的。操作系统如何操作设备呢?简单的说设备主要有IO设备和存储设备。和本文紧密相关的是IO设备。linux系统在BIOS已经给每个设备的物理基础地址。简单的说我要去开门,我是不是需要钥匙,系统分配给IO一个钥匙。比较有趣的是,用户不能直接操作。原因呢?安全和硬件资源有限。安全是为了防止内核的崩溃,实际存储控制只有那么大,这让软件使用者感觉很不友好,写代码还要考虑地址是否出界,想想就是麻烦。虚拟地址就解决了用户使用友好性的问题。话题扯得有点远。如下图所示。说重点,系统给你一个假的地址,史称虚拟地址,然后通过内部的TLB,MMU等机制转换成你想要的地址。操作IO也是一样。用户可以IOMP。好了,追寻IO我们需要拿到操作io的地址。
姑且自己分配个地址,这些在生成平台时候会看到的。
#define XPAR_PS7_GPIO_0_DEVICE_ID 0
#define XPAR_PS7_GPIO_0_BASEADDR 0xE000A000
#define XPAR_PS7_GPIO_0_HIGHADDR 0xE000AFFF
点击加载图片
3. 有趣的hw_platfom和BSP
这点不打算多说。这张表很重要,给出了地址映射的信息,简单的说就是cortexa9如何地址分配。这两张表对于初学者的用处不大,主要给BSP用的。简单的说,hw_platfom给的是初始化API和基本的资源物理信息,用户用的较多的是BSP。
点击加载图片
点击加载图片
BSP看起来很抽象,类比一下,大家应该听说过linux的CDEV device bus框架,还有windows的KDMF和UDMF。都是给用户用的,用户只要知道意义即可。
考虑到实用性的问题,举一个实例,说明如何操作BSP的API。先谈一下套路,驱动开发的套路。
初始化设备
检查初始化正确性,
系统调用
然后就是如下图所示,读写等函数,就是不让你用其他的,嘿嘿。linux系统开发者可能会说,必须按我的规则(框架)来玩游戏。
点击加载图片
状态监控
设备销毁
那就来点具体的,想要弄清楚IO如果用,笔者建议认真阅读xgpiops.h头文件。只说和本文例子相关的两个结构体。如下所示,比折不绕弯,说的可能有定俗,还请海涵。
我要找操作IO入口地址 -> XGpioPs_Config
我要实例化IO设备->XGpioPs
笔者并不喜欢ZYNQ开发,总感觉用别人的BSP阴阳作怪。如下图所示,这命名我也是醉了。
点击加载图片
吐槽过了就说实体。一句话搭积木。
status= XGpioPs_CfgInitialize(&led_o,XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID),XPAR_PS7_GPIO_0_BASEADDR);
if(status != XST_SUCCESS)
printf('not config init..') ;
笔者认为代码简单使用至上,一句话就初始化IO口,之后的就是操作 XGpioPs类型的对象led_o。别漏了设置使能。
XGpioPs_SetDirectionPin(&led_o, pin_idx, 1 ) ;
XGpioPs_SetOutputEnablePin(&led_o, pin_idx, 1) ;
简单读写如下
XGpioPs_WritePin(&led_o, pin_idx, 0) ;
好了,在处理器上跳动就结束了,归根结底,放了一个嵌入式硬核在FPGA里面,然后在处理器上开发应用。前提是啥?平台在运转,应用才能用。和软件开发MFC是不是有点类似?
HLS硬件加速方案中的精灵
先谈点别的。算法开发有点恼火,但是现实中有很多算法都是基于系统开发的,而不是独立的。为什么呢?时间成本?人力成本?实现难度?
HLS C为根本
但是这种C,没有那么灵活。用起来感觉有点别扭。简单地说两个比较重要,数据类型和内建方法。以红外融合图象处理举例说明。
typedef hls::MatFAC_IMAGE;
typedef hls::MatRGB_IMAGE;
typedef hls::MatGRAY_IMAGE;
typedef hls::Scalar<1,unsigned char > PIX_UC1 ;
为什么用别人的函数类型,可重用性高。你不用再去写API。图像常用的几种格式,RGB,HSV,YCrCb等直接用很方便。但是有个问题,HLS_8UC3是啥?这个疑问是初学者必然存在的。这个必须积累,时间久了,你就懂了,他的命名规则了。笔者还是觉得自己写函数比较习惯,如下为笔者自己写的盒子滤波部分代码。关键是如何操作一个像素点,这个弄清楚所有迎刃而解。秘诀>> << 。
点击加载图片
这都是子组件,重要的是需要一个把所有的连接起来,还需要能综合。hls::AXIvideo2Mat,这个笔者认为并不是特别的友好,没办法配合这个仿真CV API挺好用。
IplImage* src = cvLoadImage(INPUT_IMAGE, 1);
IplImage* dst = cvCreateImage(cvGetSize(src), src->depth, src->nChannels);
AXI_STREAM src_axi;
AXI_STREAM dst_axi;
IplImage2AXIvideo(src, src_axi);
imageEngine(src_axi, dst_axi, src->height, src->width);
AXIvideo2IplImage(dst_axi, dst);
仔细看起来,就是image -> stream ->mat ->stream -> image,吐槽一下好麻烦。无奈,不过好处是可以和CV库无缝对接。开发时间减少。性能不好说。
重在优化,瓶颈也在优化
说的够多了,最后经过pipe,拆分等优化,实现了红外融合增强细节的硬件加速算法方案。迭代很多次优化好难,效果如下。
点击加载图片
点击加载图片
经过上文叙述,笔者给了两种FPGA嵌入式思维开发举例。感兴趣的多交流。笔者自认为篇幅有限,很多是点到为止。还请海涵。