HLS第十五课(clahe)

本例利用之前介绍的基础函数进行功能级实现。

void clahe_accl(hls::stream<T_AXIU(IMAGE_BPP, IMAGE_NPPC)>& src, hls::stream<T_AXIU(IMAGE_BPP, IMAGE_NPPC)>& dst, u16 width, u16 height, u32 limit_th, u32 scale_th, u8 mode)
{
#pragma HLS INTERFACE axis register both port=src
#pragma HLS INTERFACE axis register both port=dst

#pragma HLS INTERFACE s_axilite port=return bundle=CONTROL_BUS
#pragma HLS INTERFACE s_axilite port=width bundle=CONTROL_BUS
#pragma HLS INTERFACE s_axilite port=height bundle=CONTROL_BUS
#pragma HLS INTERFACE s_axilite port=limit_th bundle=CONTROL_BUS
#pragma HLS INTERFACE s_axilite port=scale_th bundle=CONTROL_BUS
#pragma HLS INTERFACE s_axilite port=mode bundle=CONTROL_BUS

#pragma HLS INTERFACE ap_stable port=width  
#pragma HLS INTERFACE ap_stable port=height 
#pragma HLS INTERFACE ap_stable port=limit_th
#pragma HLS INTERFACE ap_stable port=scale_th 
#pragma HLS INTERFACE ap_stable port=mode
 

	HistLutMap<WIN_W, WIN_H> hist;
	
    static HistLutMap<WIN_W, WIN_H> hist_last;
  
    clahe_compute(src, dst, hist, hist_last, width, height, limit_th, scale_th, mode);
    
    for (u16 i = 0; i < WIN_H; i++)
    {
        for (u16 j = 0; j < WIN_W; j++)
        {
            for (u16 k = 0; k < 256; k++)
            { 
                hist_last.data_[i][j][k] = hist.data_[i][j][k];
            }
        }
    } 
}

本函数,利用算法级函数clahe_compute进行数据计算,并输出计算结果,
然后,用一个三层嵌套for循环体,进行逐点处理。
将hist中的数据,逐点拷贝到hist2中。

这里,定义的hist2,是一个static局部对象,
所以,它的作用,是记录last_result。

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
来看看clahe_compute函数。

void clahe_compute(hls::stream<T_AXIU(IMAGE_BPP, IMAGE_NPPC)>& src, hls::stream<T_AXIU(IMAGE_BPP, IMAGE_NPPC)>& dst, HistLutMap<WIN_W, WIN_H>& hist, HistLutMap<WIN_W, WIN_H>& hist2, u16 width, u16 height, u32 limit_th, u32 scale_th, u8 mode)
{ 
	xf::Mat<IMAGE_TYPE, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_NPPC> img0(height, width);
	xf::Mat<IMAGE_TYPE, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_NPPC> img1(height, width);
	xf::Mat<XF_8UC1, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_NPPC> img2(height, width);
    xf::Mat<XF_8UC1, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_NPPC> img3(height, width);
    xf::Mat<XF_8UC1, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_NPPC> img4(height, width);
    xf::Mat<XF_8UC1, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_NPPC> img4_0(height, width);
    xf::Mat<XF_8UC1, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_NPPC> img4_1(height, width);
    xf::Mat<XF_8UC1, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_NPPC> img4_resize(height, width/2);
    xf::Mat<XF_8UC1, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_NPPC> img4_blur(height, width);
    xf::Mat<IMAGE_TYPE, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_NPPC> img5(height, width);
    xf::Mat<IMAGE_TYPE, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_NPPC> img6(height, width);
    
#pragma HLS stream variable=img0.data dim=1 depth=1024
#pragma HLS stream variable=img1.data dim=1 depth=64
#pragma HLS stream variable=img2.data dim=1 depth=4096
#pragma HLS stream variable=img3.data dim=1 depth=4096
#pragma HLS stream variable=img4.data dim=1 depth=64
#pragma HLS stream variable=img4_0.data dim=1 depth=64
#pragma HLS stream variable=img4_1.data dim=1 depth=64
#pragma HLS stream variable=img4_resize.data dim=1 depth=64
#pragma HLS stream variable=img4_blur.data dim=1 depth=64
#pragma HLS stream variable=img5.data dim=1 depth=64
#pragma HLS stream variable=img6.data dim=1 depth=1024

#pragma HLS dataflow
    xf::AXIvideo2xfMat(src, img0);

	xf::rgb2hsv<IMAGE_TYPE, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_NPPC>(img0, img1);

    xfMat_split<IMAGE_TYPE, IMAGE_BPP, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_NPPC, XF_8UC1, 8>(img1, img2, img3, img4);

    xfMat_duplicate(img4, img4_0, img4_1);

    xfMat_skip_pixel<XF_8UC1, 8, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_NPPC>(img4_0, img4_resize);
    
    calc_block_hist<XF_8UC1, 8, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_NPPC, WIN_W, WIN_H>(img4_resize, hist, limit_th, scale_th);

    clahe_lut<XF_8UC1, 8, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_NPPC, WIN_W, WIN_H>(img4_1, img4_blur, hist2, mode);

    xfMat_merge<XF_8UC1, 8, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_NPPC, IMAGE_TYPE, IMAGE_BPP>(img2, img3, img4_blur, img5);
    
	xf::hsv2rgb<IMAGE_TYPE, IMAGE_TYPE, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_NPPC>(img5, img6);

    xf::xfMat2AXIvideo(img6, dst);
	
}

其中用到了xfopencv提供的一些函数,也用到了自定义的公共函数。
首先,调用xf::AXIvideo2xfMat,将输入的AXIS数据流,转换成xfMat,
然后,调用xf::rgb2hsv,将RGB转换成HSV色彩空间,
然后,调用 xfMat_split,将xfMat分割成H,S,V三个xfMat,成为灰度图像,
然后,调用xfMat_duplicat,将xfmat复制成两个。
然后,调用xfMat_skip_pixel,进行抽点处理,缩小图像尺寸,
然后,调用calc_block_hist,计算直方图,
然后,调用clahe_lut,计算LUT映射关系,进行灰度重建。
然后,调用xfMat_merge,将重建后的三个灰度图合并,
然后,调用xf::hsv2rgb,将HSV色彩空间转换成RGB空间,
然后,调用xf::xfMat2AXIvideo,将xfMat转换成AXIS数据流。

这其中,涉及到两个算法级函数,calc_block_hist,clahe_lut,
+++++++++++++++++++++++++++++++++++++++++++++++++++++
先来看看calc_block_hist
首先,是封装了一个struct,在C++中,推荐封装为class。

template<int W, int H>
class HistLutMap
{
public:
	u8 data_[H][W][256];
};

这是一个三维的数组,每个像素,都对应一个size为256的一维数组,每个单元,存储一个直方概率值。

calc_block_hist中需要用到这个struct。

template<int TYPE, int BPP, int ROWS, int COLS, int NPPC, int W, int H>
void calc_block_hist(xf::Mat<TYPE, ROWS, COLS, NPPC>& src, HistLutMap<W, H>& lut_map, u32 limit_th, u32 scale_th)
{ 
    u16 width = src.cols;
    u16 height = src.rows;
    u16 bw = width / W;//计算一个block的column尺寸
    u16 bh = height / H;//计算一个block的row尺寸
    u32 throw_pixels = bw * bh / 8;//计算一个block的总像素个数,并计算出丢弃阈值,

    ap_fixed<32,16> bw_div = div_to_mux(W, width); //求bw的倒数
    ap_fixed<32,16> bh_div = div_to_mux(H, height); //求bh的倒数
#pragma HLS ALLOCATION instances=div_to_mux limit=1 function

    syslog("width=%d, height=%d, bw=%d, bh=%d\n", width, height, bw, bh);

#if DEBUG
    std::cout << "bw_div: " << bw_div << std::endl;
    std::cout << "bh_div: " << bh_div << std::endl;
#endif

	u32 hist[W][256];
	u32 sum_hist[256];
    u32 src_idx = 0;
  
  	//最外层循环体,按row block处理。合并到H行内。	整个循环体,处理一个合并后的直方图的一行。	
	for (u16 i = 0; i < H; i++)
	{
#pragma HLS loop_tripcount avg = H max = H 
		

		//按列逐点处理,初始化hist。
		for (u16 j = 0; j < W; j++)
		{
#pragma HLS loop_tripcount avg = W max = W 
			for (u16 k = 0; k < 256; k++)
			{
#pragma HLS pipeline II = 1
#pragma HLS loop_flatten off
				hist[j][k] = 0;
			}				
		}
	
		//一个row block内,按行逐行处理,合并到一行内。
		for (u16 m = 0; m < bh; m++) 
		{ 
#pragma HLS loop_tripcount avg = ROWS/H max = ROWS/H 			
			
			//一行内,按列逐点计算直方统计值,合并到W个点内。
			for (u16 j = 0; j < W; j++)
			{
#pragma HLS loop_tripcount avg = W max = W 

				//一个column block内,按列逐点读取src,并更新统计值。合并到一个点内。
				for (u16 n = 0; n < bw; n++)
				{
#pragma HLS loop_tripcount avg = COLS/W max = COLS/W 
#pragma HLS pipeline II = 2
#pragma HLS loop_flatten off 
					u8 srcpixel = src.read(src_idx++);  
					hist[j][srcpixel]++;
					 
				}	
			} 
		}

		//按列逐点处理,对合并后的W宽的直方图进行处理,
        for (u16 j = 0; j < W; j++)
		{
#pragma HLS loop_tripcount avg = W max = W 
            u32 sum_low = 0, sum_high = 0, k_low = 0, k_high = 255;
            
            //对当前点,按值逐值处理,计算左区间累加和,及右区间累加和,
			for (u16 k = 0; k < 256; k++)
			{
				// 如果之前的左区间累加和,小于丢弃阈值,则继续累加,否则跳过,
				if (sum_low < throw_pixels) {
					sum_low += hist[j][k];
					k_low = k;
				}
				
				// 如果之前的右区间累加和,小于丢弃阈值,则继续累加,否则跳过,
				if (sum_high < throw_pixels) {
					sum_high += hist[j][255 - k];
					k_high = 255 - k;
				}
			}	
			
			//计算limit值
            u32 limit = limit_th + limit_th * (k_high - k_low) * scale_th / 256;
			u32 steal = 0; 

			//对当前点,按值逐值处理,
			for (u16 k = 0; k < 256; k++)
			{
#pragma HLS pipeline II = 1
#pragma HLS loop_flatten off


				// 如果当前直方统计值,高于limit限值,则进行限幅处理,并将差值累加到折损值steal中。
				if (hist[j][k] > limit)
				{
					steal += (hist[j][k] - limit);
					hist[j][k] = limit; 
				}	
			}	
			
			//将折损值均分到每个值
			u32 bonus = steal / 256;  

			// 对当前点,按值逐值处理
			for (u16 k = 0; k < 256; k++)
			{
#pragma HLS pipeline II = 1
#pragma HLS loop_flatten off
				
				// 将均分的折损值,叠加到每个值的直方统计值中。
				hist[j][k] += bonus;
			}	
			
			// 对当前点,按值逐值处理,计算各个值的左区间累加和
			for (u16 k = 0; k < 256; k++)
			{
#pragma HLS pipeline II = 2
#pragma HLS loop_flatten off 
				
				// 对当前值,计算左区间累加和,
				if( k == 0) {
					sum_hist[k] = hist[j][k];
				} 
				else {
					sum_hist[k] = sum_hist[k - 1] + hist[j][k];
				}  
			}

			// 对当前点,按值逐值处理,
			for (u16 k = 0; k < 256; k++)
			{
#pragma HLS pipeline II = 1
#pragma HLS loop_flatten off
				
				// 利用计算的各个值的左区间累加和,计算各个值的左区间累加概率,并乘以最大值MAX_VALUE,
				// 从而求出当前点的LUT映射表。
				//LUT表用于将原值转换成均衡后的值。 
                ap_fixed<64,48> sum = sum_hist[k];
                sum *= 255;
                sum *= bw_div;
                sum *= bh_div;
                
                //限幅保护
				if (sum > 255)
                    sum = 255;
                
                //对当前点,当前值,将计算出的均衡值,写入LUT的对应位置    
				lut_map.data_[i][j][k] = sum;
			}		
		}
	}	
	
}	

++++++++++++++++++++++++++++++++++++++++++++++++++++++++
再来看看clahe_lut

template<int TYPE, int BPP, int ROWS, int COLS, int NPPC, int W, int H>
void clahe_lut(xf::Mat<TYPE, ROWS, COLS, NPPC>& src, xf::Mat<TYPE, ROWS, COLS, NPPC>& dst, HistLutMap<W, H>& lut_map, u8 mode)
{ 
    u16 width = src.cols;
    u16 height = src.rows;
    
    u16 nw = W * 2;
    u16 nh = H * 2;
    
	u16 bw = width / nw; //计算block的尺寸
	u16 bh = height / nh; //计算block的尺寸

    ap_fixed<32,16> bw_div = div_to_mux(nw, width);// 求倒数
    ap_fixed<32,16> bh_div = div_to_mux(nh, height); // 求倒数
#pragma HLS ALLOCATION instances=div_to_mux limit=1 function


#if DEBUG
    std::cout << "bw_div: " << bw_div << std::endl;
    std::cout << "bh_div: " << bh_div << std::endl;
#endif

    u32 src_idx = 0, dst_idx = 0;
    u32 tmp_map[W * 2][256];
   
	//输入的LUT表,和图像的尺寸之间,存在比例关系,
	//将图像分成一个个block,每个block对应于LUT表中的一个点。


   	// 按row block处理,逐个row block处理,
	for (u16 i = 0; i < nh; i++)
	{ 
#pragma HLS loop_tripcount avg = H max = H 
    	
    	//按column block处理,逐个column block处理,
    	//相邻的两个column block,参考同一个LUT中的点。
    	//所以,需要计算折半游标。
        for (u16 j = 0; j < nw; j++)
        {
#pragma HLS loop_tripcount avg = W max = W 
			
			//计算折半的游标,作为LUT表中的索引
            u16 ni = (i == 0) ? 0 : ((i - 1) / 2);
            u16 nj = (j == 0) ? 0 : ((j - 1) / 2);  

			//对当前点,按值逐值处理,
            for (u16 k = 0; k < 256; k++)
            {
#pragma HLS pipeline
#pragma HLS loop_flatten off 
				
				//对当前值,获取LUT中对应点处的均衡值,
                u32 a0 = lut_map.data_[ni][nj][k]; 
                //对当前值,获取LUT中的2X2邻域处的均衡值,
                u32 a1 = (ni < (H - 1)) ? lut_map.data_[ni + 1][nj][k] : a0;
                u32 a2 = (nj < (W - 1))? lut_map.data_[ni][nj + 1][k] : a0;
                u32 a3 = (ni < (H - 1) && (nj < (W - 1))) ? lut_map.data_[ni + 1][nj + 1][k] : a0;

				//对当前值,将2X2邻域的均衡值,打包到一个u32中去。
                u32 mv = a0 | (a1 << 8) | (a2 << 16) | (a3 << 24);
                //将打包后的均衡值,写入临时数组。
                tmp_map[j][k] = mv;
            }
        }

		//在一个row block内,逐行处理,所有的行,公用同一个tmp_map
		for (u16 m = 0; m < bh; m++)	
		{ 
#pragma HLS loop_tripcount avg = ROWS/H max = ROWS/H 


			//按column block,逐个block处理
			for (u16 j = 0; j < nw; j++)
			{
#pragma HLS loop_tripcount avg = W max = W 

				// 在一个column block中,逐点处理,
				for (u16 n = 0; n < bw; n++)
				{
#pragma HLS loop_tripcount avg = COLS/W max = COLS/W 
#pragma HLS pipeline II = 1
#pragma HLS loop_flatten off

					//对当前点,读取数据,
					u8 val = src.read(src_idx++);
					
					//从临时映射表中,根据值,找到对应的均衡值。
					//同一个column block,公用同一个点的LUT。
                    u32 mv = tmp_map[j][val];
					
					//取出各个邻域点均衡值
					u8 a0 = mv & 0xff; 
					u8 a1 = (mv >> 8) & 0xff;
					u8 a2 = (mv >> 16) & 0xff;
					u8 a3 = (mv >> 24) & 0xff;

					//计算扩展坐标
					//对于Y坐标,根据当前的row block号,选择
                    u16 ny = ((i & 0x1) == 0) ? (bh + m) : m;
                   //对于X坐标,根据当前的column block号,选择
                    u16 nx = ((j & 0x1) == 0) ? (bw + n) : n;
					
					// 计算对应在邻域中的线性亚坐标。并进行限幅保护。
					ap_fixed<32,16> u = ny * bh_div * 0.5;
					ap_fixed<32,16> v = nx * bw_div * 0.5;
                    if ((i == 0) || (i == (nh - 1))) 
                        u = 0;
                    if ((j == 0) || (j == (nw - 1)))
                        v = 0; 
                     
                     //计算双线性插值,并限幅保护
					ap_fixed<32,16> out= a3 * u * v + a2 * (1 - u) * v + a1 * u * (1 - v) + a0 * (1 - u) * (1 - v);
                    if (out > 255)
                        out = 255; 
					
					// MUX,选取源数据或者双线性插值数据,
                    u8 dstpixel = (mode == 0) ? (val) : ((u8)out);
                    //将数据写入dst中。
					dst.write(dst_idx++, dstpixel); 
				}	
			}	
		}	
	}
}

+++++++++++++++++++++++++++++++++++++++++++++++++++++
再来看testbench。

#include "clahe_accl.h"
#include "clahe_accl_tb.h"
#include "opencv/cv.h"
#include "opencv/highgui.h"
#include "opencv2/imgproc/imgproc.hpp"
//#include "common/xf_sw_utils.h"
#include "common/xf_axi.h"
using namespace cv;

需要使用xfopencv库,以及namespace。

int main(int argc, char *argv[])
{	
	if (argc != 2)
	{
		printf("usage: %s bayer.png\n", argv[0]);
		return -1;
	}	

	cv::Mat img;
	img = cv::imread(argv[1]);
	if (img.data == NULL)
	{
		fprintf(stderr,"Cannot open image at %s\n", argv[1]);
		return 0;
	}

	int width = img.size().width;
	int height = img.size().height;

    width = 2112;//ceil(((float)width) / 16) * 16;
    height = 1216;//ceil(((float)height) / 16) * 16;

    printf("(%dx%d)\n", width, height);

    cv::Mat in_img;
    cv::resize(img, in_img, cv::Size(width, height));

	u32 limit_th = (width * height / 2 / WIN_W / WIN_H / 256 * 1 );
    u32 scale_th = 2;
	u8 mode = 1;
 
	cv::Mat out_img;
	out_img.create(height, width, CV_8UC3);
	
	 
	hls::stream<T_AXIU(IMAGE_BPP, IMAGE_NPPC)> src;
	hls::stream<T_AXIU(IMAGE_BPP, IMAGE_NPPC)> dst;

	cvMat2AXIvideoxf<IMAGE_NPPC>(in_img, src);
    clahe_accl(src, dst, width, height, limit_th, scale_th, mode); 
    AXIvideo2cvMatxf<IMAGE_NPPC>(dst, out_img);
    
    cvMat2AXIvideoxf<IMAGE_NPPC>(in_img, src);
    clahe_accl(src, dst, width, height, limit_th, scale_th, mode); 
    AXIvideo2cvMatxf<IMAGE_NPPC>(dst, out_img);
    
    cv::imwrite("hls.bmp", out_img);
	imwrite("image.bmp", in_img);
	
	printf("test ok!\n");
	return 0;
}

主体上和基本框架类似,
这里,调用 cv::resize调整尺寸,并用到了cv::Size类的对象。
后面,通过连续两次执行DUT,起到刷新static局部变量的作用。

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
补充:
全图直方均衡表,
如果是进行全图直方均衡,那么只需要全图统计后,只需要一个LUT映射表即可。这是一个一维数组。

如果要得到更精细的直方均衡,那么需要使用
区块直方均衡图,
将原图分为多个区块,在每个区块内进行直方统计,并针对每一个区块,输出一个LUT映射表,这是一个一维数组,但是每个区块的直方均衡表,则共同组成了一个三维数组。
可以看出,原图和区块图之间,存在一个缩放比例关系。

在后续使用区块直方均衡图时,需要使用到邻域关系,用来做双线性插值。

线性插值,
是基于邻域共同作用,共同影响的思想设计的。
单线性插值,
是假设某个点的值,应该为两个相邻的离散的参考点的值的线性叠加。加权程度,以该点分别距离两个参考点的远近来衡量。归一化尺度为两个参考点的距离。
即,两个参考点的距离,归一化为一。
假设,A距离参考点R0,归一化距离为u,A距离参考点R1,归一化距离为v,u+v=1,
则A的值,应该为

Va = Vr0 + (Vr1- Vr0)*u,
     = Vr0 + Vr1*u -Vr0*u
     = Vr0*(1-u) + Vr1*u
     = Vr0 * v + Vr1 * u

可以看出,参考点的值,不是乘以相对于自己的距离,而是乘以相对于另一个参考点的距离,或者说,乘以相对于自己的互补距离,简称补距。

推广到双线性插值,
是假设某个点的值,应该为一个矩形的四个角点,作为离散的参考点的值的线性叠加。加权程度,以该点分别距离四个参考点的X方向和Y方向的远近来衡量。归一化尺度为X方向或者Y方向的参考点的距离,即边长。
双线性插值,是一个二维操作,可以先降维到一维,再计算单线性插值。
例如,对于一个矩形,有四个角点R00,R10,R01,R11,
对于点A,X方向归一化坐标为v,Y方向归一化坐标为u,
首先,降维,用Y方向的归一化坐标u,计算两组参考点的等效参考点的值。

Ver0 = Vr00 * (1-u) + Vr01 * u 
Ver1 = Vr10 * (1-u) +Vr11 * u

然后,在X方向上,用等效参考点的值,计算A点的值。

Va = Ver0 * (1-v) + Ver1 * v 
     = Vr00 * (1-u)*(1-v) + vr01 * u * (1-v) + Vr10 * (1-u) * v + Vr11 * u * v 

可以看出,双线性插值,加权系数,是X方向和Y方向的补距积。

马赛克效应,
最简单的应用下,每个区块,用自己的区块直方均衡表进行均衡,
但是这样的问题是,不同的区块,就会有相当明显的差别,
所以,需要相邻的区块之间,使用双线性插值。
由四个相邻的区块的直方均衡表一起使用。

这就涉及到,区块内有多个像素,那么区块的直方表,具体对应到区块的哪个位置?
显然,区块直方表,应该对应到区块的中心点上。即,中心绑定或者中心映射。

在使用了中心映射的时候,就存在一个现象,区块内的像素,相对于中心参考点的距离,有正有负,如何处理?
这就需要将区块均分为四象限。每个象限,成为一个更细的区块,这里称为象限区块。
每个区块的一个象限,会和其他三个相邻的区块的一个象限,共同组成一个新的虚拟区块。
现在的问题是,边界上的象限区块,不能组成完整的虚拟区块,如何处理?
这就需要虚拟参考点,假设在边界外,存在一圈虚拟的区块,这些虚拟的区块,复制了边界区块的参考点的值。

在算法设计时,还有一种思路,就是参考点散列,将区块中心点的参考点,按照规则散列到各个象限区块的角点上去。
这样,后续就可以直接以象限区块为单位,以象限区块的角点为参考点,进行双线性插值处理了。
这也是本例中选择的设计路线。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
视频程源:http://xilinx.eetop.cn/category-83 目录 1 从软件工程师的角度来看 FPGA 架构 3 2 Vivado HLS的工作机制 5 3-4 HLS设计流程基本概念 9 5 任意精度数据类型 15 5.1 C语言支持的数据类型 15 5.2 sizeof()函数使用 16 5.3 设置Visual Studio支持任意精度数据类型 17 6 数组类型转换 17 6.1 变量的定义和初始化 17 6.2 定点数据类型 18 6.3 浮点数据类型的定义和初始化 19 6.4 隐式数据类型转换 19 6.5 显示数据类型转换 19 7 Vivado HLS中的复合数据类型 20 7.1 结构体 20 7.2 枚举类型 22 8 Vivado HLS中的C++基本运算 23 9 测试平台的基本架构 25 9.1 Test Bench 25 9.2 C Test Bench 26 10 测试激励 28 11 测试输出检测与格式控制 28 11.1 Scoreboard 28 11.2 输出格式控制 30 12 接口综合基本介绍 33 12.1 接口综合概述 33 12.2 block-level interface protocol和port-level interface protocol 34 13 接口综合之数组 35 14 接口综合案例演示 37 14.1 添加寄存器 37 14.2 添加时钟使能信号 38 14.3 指令优化 38 15 for循环优化-基本性能指标 40 15.1 基本衡量指标 40 15.2 for循环pipeline 41 15.3 for循环UNROLL展开 41 15.4 for循环变量i 42 16 for循环优化-循环合并 42 17 for循环优化-数据流 46 18 for循环优化-嵌套的for循环优化 54 18.1 循环嵌套类型 54 18.2 Perfect loop nest示例 55 18.3 Imperfect loop nest示例 56 19 for循环优化-其他优化方法 59 19.1 for循环的并行性 59 19.2 for循环pipeline时的rewind选项 61 19.3 for循环的循环边界是变量时处理方法 64 20 数组优化-数组分割 67 20.1 数组接口 67 20.2 数组分割 67 21 数组优化-数组映射和重组 69 21.1 数组的映射 69 21.2 数组的重组 72 21.3 综合对比 72 22 数组优化-其他优化方法 72 22.1 定义ROM 72 22.2 数组的初始化 74 23 函数层面优化 75 23.1 代码风格 75 23.2 Inline 75 23.3 Allocation 75 23.3 Dataflow 75 24 总结分析 77 24.1 改善吞吐率(Throughput) 77 24.2 改善时延(Latency) 78 24.3 改善资源(Area) 79

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值