一、顶层设计
#include "top.h"
#include "hls_opencv.h"
#include "iostream"
using namespace std;
using namespace cv;
int main (int argc, char** argv)
{
int len = 100;
ap_uint<32> tmp;
ap_uint<32> tmp1;
ap_uint<32> tmp2;
hls::stream<ap_uint<32> > src_axi;
hls::stream<ap_uint<32> > dst_axi;
hls::stream<ap_uint<32> > dst_axi1;
for(int i=0;i<100;i++){//row
tmp1(7,0) = i;
tmp1(15,8) = i;
tmp1(23,16) = i;
tmp1(31,24) = i;
src_axi << tmp1;//write
//std::cout<<std::hex <<tmp1<<std::endl;
}
hls_video_scaler_top(src_axi,dst_axi,5,20);
for(int i=0;i<100;i++){//
//tmp1 = dst_axi.read() ;
dst_axi >> tmp1;
std::cout<<std::hex <<tmp1<<std::endl;
}
//waitKey(0);
return 0;
}
二、核心代码设计
void hls_video_scaler_top
(
hls::stream<ap_uint<32> >& src_strm,//8*4
hls::stream<ap_uint<32> >& dst_strm,//8*4
ap_uint<16> width,
ap_uint<16> height
)
{
#pragma HLS INTERFACE s_axilite port=width bundle=para_cfg
#pragma HLS INTERFACE s_axilite port=height bundle=para_cfg
#pragma HLS DATAFLOW
//#pragma HLS INTERFACE ap_hs port=trans_en
#pragma HLS INTERFACE ap_ctrl_hs port=return
#pragma HLS INTERFACE axis port=src_strm
#pragma HLS INTERFACE axis port=dst_strm
//#pragma HLS INTERFACE ap_stable port=width
//#pragma HLS INTERFACE ap_stable port=height
ap_uint<32> clock1_data;
//-----------------------------------------------------------------------------------------------------------------
for(ap_uint<16> i=0;i<height;i++){
#pragma HLS LOOP_FLATTEN off
for(ap_uint<16> j=0;j< (width);j++){
#pragma HLS pipeline II=1
clock1_data = src_strm.read();
dst_strm << clock1_data;
}//col
}//row
}
三、rtl仿真

width和height总共两个寄存器,消耗10个clock才准备好,然后拉高ap_start,才进行数据流传输。
当把width和height两个寄存器通过axilite写的时候,是串行写的,消耗周期比较多,只有width和height都准备好,才进行数据的操作。
四、lite和stream并行传输
如何将axilite配置的寄存器和hls::stream传输保证是并行的,而不是串行的呢?如何实现呢??

图-1
lite参数存入RAM中,然后通过stream传递出来,需要注意的是fifo的位宽最大为1024,也就是32个32bit位宽的寄存器,也就是一个stream数据流最多一次传输32个寄存器,如果寄存器很多的话,就需要把axilite分成多个数据流给下游模块,这个问题是用户设计需要注意的!!!
这样设计的好处是,相比较下图的优点很多

图-2
图-2利用axilite配置参数,需要参数配置好了,才能启动stream的开启,否则数据不稳定,会对流程有影响,比如参数有宽和高,这个参数必须稳定才行。所以上述axilite寄存器配置是影响stream流程的开启时间的,这个会消耗时钟周期;但是图-1的架构,很好的解决了这个问题,能够保证寄存器配置和stream数据读写是独立的,因为将axilite参数存入到RAM中去,当数据流模块需要参数的时候从axilite转stream模块中去取一次参数就行了。
需要注意的是,高位宽的stream流会占用很大的block ram资源,这个用户要注意!当然可以将位宽设置小点,然后分多次读stream,这样就是需要时钟周期多一点,但是这样的效率远远高于一堆寄存器直接用axilite配置。也就是你自己要找到一个资源和时钟周期的平衡即可!
五、多次调用顶层设计的TB设计
#include "top.h"
#include "hls_opencv.h"
#include "iostream"
using namespace std;
using namespace cv;
int main (int argc, char** argv)
{
int len = 100;
ap_uint<32> tmp;
ap_uint<32> tmp1;
ap_uint<32> tmp2;
hls::stream<ap_uint<32> > src_axi;
hls::stream<ap_uint<32> > dst_axi;
hls::stream<ap_uint<32> > dst_axi1;
ap_uint<16> width = 5;
ap_uint<16> height = 10;
for(int ii=0;ii<2;ii++){
if(ii==0){
width = 5;
height = 20;
}
else{
width = 10;
height = 10;
}
for(int i=0;i<100;i++){//row
tmp1(7,0) = i;
tmp1(15,8) = i;
tmp1(23,16) = i;
tmp1(31,24) = i;
src_axi << tmp1;//write
//std::cout<<std::hex <<tmp1<<std::endl;
}
hls_video_scaler_top(src_axi,dst_axi,width,height);
for(int j=0;j<100;j++){//
//tmp1 = dst_axi.read() ;
dst_axi >> tmp1;
std::cout<<std::hex <<tmp1<<std::endl;
}
}
//waitKey(0);
return 0;
}
六、多次调用顶层设计的核心代码
void hls_video_scaler_top
(
hls::stream<ap_uint<32> >& src_strm,//8*4
hls::stream<ap_uint<32> >& dst_strm,//8*4
ap_uint<16> width,
ap_uint<16> height
)
{
#pragma HLS INTERFACE s_axilite port=width bundle=para_cfg
#pragma HLS INTERFACE s_axilite port=height bundle=para_cfg
//#pragma HLS DATAFLOW
//#pragma HLS INTERFACE ap_hs port=trans_en
#pragma HLS INTERFACE ap_ctrl_hs port=return
#pragma HLS INTERFACE axis port=src_strm
#pragma HLS INTERFACE axis port=dst_strm
//#pragma HLS INTERFACE ap_stable port=width
//#pragma HLS INTERFACE ap_stable port=height
ap_uint<32> clock1_data;
//-----------------------------------------------------------------------------------------------------------------
for(ap_uint<16> i=0;i<height;i++){
#pragma HLS LOOP_FLATTEN off
for(ap_uint<16> j=0;j< (width);j++){
#pragma HLS pipeline II=1
clock1_data = src_strm.read();
dst_strm << clock1_data;
}//col
}//row
}
七、代码综合和rtl仿真

1.上述仿真rtl可以看出ap_start拉高了两次,表示这个模块开启了两次图像数据传输。
2.axilite寄存器的配置都是在ap_start拉低期间配置的,也就是一轮图像传输完成后再开始更新lite寄存器。