当前位置: 首页 > news >正文

基于zynq实现一个边缘识别视频流(预学习HLS篇)

视频流通了之后,我要在上面添加算法模块,手撕verilog可能比较慢,可以先尝试使用hls。
前面通过block design搭建视频流通路,然后SDK控制写入分辨率这些东西,现在如果要加入算法模块简单实现边缘识别,必须先学习hls生成ip核,以及SDK的进一步学习(初始化中断,以及一些函数的调用)。
现在要学习一些hls基本语法,SDK基本开发,以黑金教程的浮点型求平均数,彩条显示为例(不涉及任何算法,也暂时不涉及测试)
HLS:
https://docs.amd.com/r/zh-CN/ug1399-vitis-hls/pragma-HLS-stream这个网站来学习基本语法是最好的。
要设计一个浮点数求平均数,数据有1024个,HLS能做的是访问DDR3获取数据,然后内部进行计算输出结果,接口我们要求规范化点用AXIlite控制和AXIS数据流传输。
float average_float(float *src, int num)
{

pragma HLS INTERFACE m_axi depth=1024 offset=slave port=src

pragma HLS INTERFACE s_axilite port=num

pragma HLS INTERFACE s_axilite port=return

sum = 0;
float_sum(src, num);
return (float)(sum/num);

}
image
image
这部分是顶层模块接口定义,这样意味着image
就相当于有了一个m_axi4,以及一个s_axilite接口,num和return分别是内部寄存器,利用axilite接口进行读写。
除此之外,内部还会有个start和done寄存器,done的话是用来读取的,不过也可以设置中断,即完成后通过中断进行控制,中断信号在s_axilite port=return会自带,除非你用ap_none换掉s_axilite,之后要在ps端开启start寄存器才会进行读取ddr3数据后工作。
*src是因为指向了庞大的数据(DDR3内部),所以用指针变量。(传递指向DDR3第一个数据的地址,直接用就已经解引用了)
void float_sum(float *src, int num)
{
int len;
float cachebuff[64];

pragma HLS STREAM variable=cachebuff depth=64

for(int i=0;i<num;i+=64)
{

pragma HLS loop_tripcount min=0 max=1024

pragma HLS dataflow

	len = ((num-i) >= 64) ? 64 : num-i;memcpy(cachebuff, src+i, len*4);for(int j=0;j<len;j++){

pragma HLS pipeline

		sum += cachebuff[j];}
}

}

pragma HLS STREAM variable=cachebuff depth=64标注格式,要求cachebuff以深度为64的fifo来输入数据

pragma HLS loop_tripcount min=0 max=1024 定义了最大和最小循环次数,若不在这个区间会出现错误状态,是必要的,没有这个的话综合不知道最高深度是多少,无法生成时序报告。

pragma HLS dataflow在循环内用,将这个循环包含的事务流水线化(与pipeline不一样,一个是事务间流水线化,一个是事务内的东西流水线化)

然后这里是分区间读入并计算的,也和loop_tripcount同个道理,hls不知道你的num最大值是多少,所以无法生成时序报告,然后哪怕你限定最大值了,时序也还不如减少容器容量,增大访问次数。还能减少bram占用(64*4字节)。
image

再来设计一个彩条显示模块。
这个模块功能在于以axis发送彩条数据给ddr3,需要一个axilite接口进行控制分辨率,需要一个axis接口发送彩条数据到ddr3

include <hls_stream.h>

include <hls_video.h>

include <ap_axi_sdata.h>

void colorbar(pixel_stream &dst, int rows, int cols)
{

pragma HLS INTERFACE axis port=dst

pragma HLS INTERFACE s_axilite port=rows

pragma HLS INTERFACE s_axilite port=cols

pragma HLS INTERFACE s_axilite port=return

pragma HLS INTERFACE ap_ctrl_none port=return

hls::Mat<1080, 1920, HLS_8UC3> imgColorbar(rows, cols);

pragma HLS dataflow

createColorBar(imgColorbar);
hls::Mat2AXIvideo(imgColorbar, dst);

}

pragma HLS INTERFACE ap_ctrl_none port=return会不让生成中断,并且覆盖前面的定义。

hls::Mat<1080, 1920, HLS_8UC3> imgColorbar(rows, cols);这个hls_video.h库内的一个定义,定义一个分辨率1920x1080,RGB888的图像数据。并且引出了行列信息。

pragma HLS dataflow

createColorBar(imgColorbar);
hls::Mat2AXIvideo(imgColorbar, dst);

}
两个事务并行执行。前者调用函数进行图像数据的编辑(不然你定义一个图像,数据是啥谁知道),后者通过函数将图像转为Video接口(在此处其实是转为axis接口,并不是标准的视频接口)。
typedef ap_axiu<24,1,1,1> pixel_data;
typedef hls::stream<pixel_data> pixel_stream;

template<int ROWS, int COLS, int T>
void createColorBar(hls::Mat<ROWS, COLS, T> &imgColorbar)
{
hls::Scalar<HLS_MAT_CN(T), HLS_TNAME(T)> pixel;
int move, wBar;
int rows = imgColorbar.rows;
int cols = imgColorbar.cols;
static int moving = 0;

for(int row=0;row<rows;row++)
{

pragma HLS loop_flatten off

	if( (row < rows/3) || (row >= rows/3*2) ){move = 0;}else{move = moving;}wBar = cols>>3;for(int col=0;col<cols;col++){

pragma HLS pipeline

		int index = ((col+move)/wBar)&0x7;switch(index){case 0:pixel.val[0] = 0xff;pixel.val[1] = 0xff;pixel.val[2] = 0xff; //whitebreak;case 1:pixel.val[0] = 0x00;pixel.val[1] = 0xff;pixel.val[2] = 0xff; //yellowbreak;case 2:pixel.val[0] = 0xff;pixel.val[1] = 0xff;pixel.val[2] = 0x00; //cyanbreak;case 3:pixel.val[0] = 0x00;pixel.val[1] = 0xff;pixel.val[2] = 0x00; //greenbreak;case 4:pixel.val[0] = 0xff;pixel.val[1] = 0x00;pixel.val[2] = 0xff; //magentabreak;case 5:pixel.val[0] = 0x0;pixel.val[1] = 0x0;pixel.val[2] = 0xff; //redbreak;case 6:pixel.val[0] = 0xff;pixel.val[1] = 0x00;pixel.val[2] = 0x00; //bluebreak;default:pixel.val[0] = 0x00;pixel.val[1] = 0x00;pixel.val[2] = 0x00; //blackbreak;}imgColorbar << pixel;}
}
if(!moving)
{moving = cols;
}
else
{moving--;
}

}

pragma HLS loop_flatten off再嵌套循环中使用(第一层内),可以防止生成二层循环导致资源过度占用,会生成状态机进行嵌套循环。

template<int ROWS, int COLS, int T>
void createColorBar(hls::Mat<ROWS, COLS, T> &imgColorbar)修改输入参数类型。用法参考这里即可。
hls::Scalar<HLS_MAT_CN(T), HLS_TNAME(T)> pixel;定义一个面向RGB888的容器,这个容器里的数据要为什么由imgcolorbar的行列决定,相当于传入行列指示应该要哪个图像数据,传入RGB格式来定义这个pixl。记得把最后的数据传出去就行。
这里的pixl更像是一个变量而已,为多少由colorbar决定,最后又把这个数据传给pixl即可。
typedef ap_axiu<24,1,1,1> pixel_data;
typedef hls::stream<pixel_data> pixel_stream;在开头定义了dst数据的类型,就是24位的输出。
imgColorbar << pixel;把这个彩条数据传给图像。

再拿led_control和led_register进行补充。
ap_int<1> &led 一位宽的led输出,需要&做前缀,其实可以发现上面的dst由于是输出数据所以也用&,但是访问大量数据的src就应该为*类型了。

pragma HLS interface ap_none port=led

pragma HLS interface ap_ctrl_none port=return

这样就不会出现中断信号了。
void led_register(ap_int<1> &led, int total_cnt, int high_cnt)这两个计数器是内部寄存器用于被配置的,是外界要传入数据,所以不要*,而且数据量也不大。
那么HLS基础语法到这里先暂时结束。

SDK:SDK主要参考浮点那一节,因为彩条显示会涉及很多其他模块,而这些模块我在之前只进行控制,甚至直接调用例程的API函数,因此不打算自己写。非必要,而浮点这一节只在浮点模块上配置,并且只用浮点的宏,还包含了中断的基本配置格式。
算了放下一篇写

http://www.hskmm.com/?act=detail&tid=30360

相关文章:

  • 咬鼠
  • 10月13日日记
  • 分享一个知乎高赞回答生成AI指令:让技术人也能写出有深度的回答
  • 实用指南:C语言速成秘籍——循环结构(while、do while、for)和跳转语句(break,continue)
  • 描述https的加密过程
  • CSP-S 2025 提高级模拟赛 Day6 复盘 A.选择方案
  • SSL证书批量申请终极指南:一次搞定所有域名
  • npm install creat-vue命令使用报错解决方法
  • PDF转图片工具:基于PyQt5的完整实现与深度解析 - 详解
  • MongoDB安装及使用
  • 从Gemini Robotics看通用机器人的科技路径
  • 张量的基本操作
  • Windows7 隐藏用户
  • 10 月记录
  • 统计学习方法学习Day01
  • gpt-5-codex vs gpt-5
  • Jenkins Share Library开发入门(一)
  • 第十三篇
  • 虚树
  • 网络安全基础--第五课:跨站脚本攻击XSS - 实践
  • 成员内部类
  • 用 Fortran 进行英文数字验证码识别
  • webpack优化前端性能
  • 2025.10.13总结 - A
  • 洛谷版自我介绍
  • Windows五次shift漏洞复现
  • P8186 [USACO22FEB] Redistributing Gifts S 题解 - 符星珞
  • Windows续
  • uml九类例图详解
  • 继续学习,争取早日找到实习 - Irving11