• FPGA HLS stream与dataflow


    基本使用

    首先导入头文件

    #include "hls_stream.h"
    
    • 1

    使用 stream 读写的接口(方式):

    • 写入流
    hls::stream<int> my_stream; //声明一个流
    int src_var = 42;
    my_stream.write(src_var);	// 使用write函数写
    my_stream << src_var;	// C++风格的写
    
    • 1
    • 2
    • 3
    • 4
    • 从流里面取数据
    hls::stream<int> my_stream;
    int dst_var;
    my_stream.read(dst_var);
    int dst_var = my_stream.read();
    my_stream >> dst_var;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    流的特性

    image-20221106102953097

    image-20221106102959950

    • 在C代码中,hls::stream<>的行为类似于无限深度的FIFO
    • stream是按顺序读取和写入的。 即从stream中读取数据后,将无法再次读取
    • 默认情况下,top-level接口上的hls::stream<>是用ap_fifo接口实现的
    • 设计内部的hls::流<>被实现为深度为1的FIFO, 优化指令STREAM被用来改变这个默认大小。

    使用例子

    stream_test.h

    #ifndef __STREAM_TEST_H__
    #define __STREAM_TEST_H__
    
    #include "ap_int.h"
    #include "hls_stream.h"
    
    typedef ap_uint<128> my_uint128_t;
    void stream_test_part1(
    		hls::stream<my_uint128_t> &stream_in,
    		hls::stream<my_uint128_t> &stream_out,
    		ap_uint<16> stream_len
    );
    void stream_test_part2(
    		hls::stream<my_uint128_t> &stream_in,
    		hls::stream<my_uint128_t> &stream_out,
    		ap_uint<16> stream_len
    );
    void stream_test(
    		hls::stream<my_uint128_t> &stream_in,
    		hls::stream<my_uint128_t> &stream_out,
    		ap_uint<16> stream_len
    );
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    stream_test.cpp

    #include "stream_test.h"
    
    // 联合part1和part2
    void stream_test(
    		hls::stream<my_uint128_t> &stream_in,
    		hls::stream<my_uint128_t> &stream_out,
    		ap_uint<16> stream_len
    )
    {
    	// 中间临时数据流
    	hls::stream<my_uint128_t> tmp_stream;
    	// 从输入流里取数据写到临时数据流里
    	stream_test_part1(
    			stream_in,
    			tmp_stream,
    			stream_len
    	);
    	// 从临时数据流里取数据写到输出数据流里
    	stream_test_part2(
    			tmp_stream,
    			stream_out,
    			stream_len
    	);
    }
    // 读一个数+1,写一个数
    void stream_test_part1(
    		hls::stream<my_uint128_t> &stream_in,
    		hls::stream<my_uint128_t> &stream_out,
    		ap_uint<16> stream_len
    )
    {
    	for(int i = 0; i < stream_len; ++ i)
    	{
    		my_uint128_t tmp = stream_in.read();
    		tmp+= 1;
    		stream_out.write(tmp);
    	}
    
    }
    
    // 读两个数,写一个数为这两个数的和
    void stream_test_part2(
    		hls::stream<my_uint128_t> &stream_in,
    		hls::stream<my_uint128_t> &stream_out,
    		ap_uint<16> stream_len
    )
    {
    	for(int i = 0; i < stream_len/2; ++ i)
    	{
    		my_uint128_t tmp = stream_in.read();
    		my_uint128_t tmp2 = stream_in.read();
    		stream_out.write(tmp + tmp2);
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    main.cpp

    #include "stream_test.h"
    #include 
    
    using namespace std;
    
    int main()
    {
    	hls::stream<my_uint128_t> stream_in;
    	hls::stream<my_uint128_t> stream_out;
    	ap_uint<16> stream_len = 16;
    
    	for(int i = 0; i < stream_len; ++ i)
    		stream_in.write(i);
    
    	stream_test(
    			stream_in,
    			stream_out,
    			stream_len
    	);
    	for(int i = 0; i < stream_len/2; ++ i)
    		cout << stream_out.read() << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    运行

    C仿真结果:

    image-20221106103646456

    C综合结果:

    报错:

    image-20221106104020797

    ERROR: [XFORM 203-733] An internal stream ‘tmp_stream.V.V’ (stream_test/stream_test.cpp:25) with default size is used in a non-dataflow region, which may result in deadlock. Please consider to resize the stream using the directive ‘set_directive_stream’ or the ‘HLS stream’ pragma.

    也就是说我们stream_tmp的长度不够,因为我们需要写两个数据,取一个数据

    但是内部的流被默认为深度为1,所以我们需要价格约束指令:

    给tmp一个深度16的流

    image-20221106104332008

    #pragma HLS STREAM variable=tmp_stream depth=16 dim=1
    
    • 1

    image-20221106104413285

    再次C综合

    image-20221106104842389

    优化

    问题

    如果数据是10000个:添加Loop_tripcout

    image-20221106105547938

    那么中间流的16的深度就不够用,需要设置为10000

    运行方式的不合理:

    首先向tmp_stream里面写10000个数据

    然后再从里面取10000个数据

    一共花费20000个周期

    image-20221106105620719

    但是这是不合理的,中间的流很长,但是我们只需要每取两个数据,计算一次写一次数据

    他是串行做的,但是这是可以并行的,这样运行周期只需要10000

    C语言是串行的语法,所以使用dataflow 告诉工具,这是并行的

    dataflow优化

    给stream_test 加上dataflow优化,让工具自动推断并行性

    image-20221106110018801

    image-20221106110039481

    C综合结果,运行周期为10000

    image-20221106110145888


    就算去掉深度的约束,C综合结果也可以过

    这是因为没有深度约束,深度默认为1,但是这是数据流风格的,所以可以写一个数据,取一个数据,模块依然可以正常工作。

    这里的模块是等两个送一个,所以把之前的10000深度,设置为4也可以,不耗费啥资源

    image-20221106110441549

    C/RTL 仿真验证

    16个数据的test

    image-20221106111743218

    打开波形图

    part1的波形图,确实是读一个写一 个

    image-20221106112335314

    part2就是读两个数据,写一个

    image-20221106112514650

  • 相关阅读:
    SaaSBase:什么是小裂变SCRM?
    【Java】之Java8新特性
    refusing to merge unrelated histories
    后缀表达式求值
    没错,又是一位月薪过万的测试小伙伴!
    路由懒加载
    Java注解(3):一个真实的Elasticsearch案例
    Java版本+企业电子招投标系统源代码+支持二开+招投标系统+中小型企业采购供应商招投标平台
    什么是数字化存在?数字化转型要先从数字化存在开始
    ATECLOUD智能云测试平台-测试测量/仪器程控/工业控制/上位机开发软件
  • 原文地址:https://blog.csdn.net/qq_45364953/article/details/127713581