基本使用
首先导入头文件
#include "hls_stream.h"
使用 stream 读写的接口(方式):
- 写入流
hls::stream<int> my_stream; //声明一个流
int src_var = 42;
my_stream.write(src_var); // 使用write函数写
my_stream << src_var; // C++风格的写
- 从流里面取数据
hls::stream<int> my_stream;
int dst_var;
my_stream.read(dst_var);
int dst_var = my_stream.read();
my_stream >> dst_var;
流的特性
- 在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
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);
}
}
main.cpp
#include "stream_test.h"
#include <iostream>
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;
}
运行
C仿真结果:
C综合结果:
报错:
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的流
#pragma HLS STREAM variable=tmp_stream depth=16 dim=1
再次C综合
优化
问题
如果数据是10000个:添加Loop_tripcout
那么中间流的16的深度就不够用,需要设置为10000
运行方式的不合理:
首先向tmp_stream里面写10000个数据
然后再从里面取10000个数据
一共花费20000个周期
但是这是不合理的,中间的流很长,但是我们只需要每取两个数据,计算一次写一次数据
他是串行做的,但是这是可以并行的,这样运行周期只需要10000
C语言是串行的语法,所以使用dataflow
告诉工具,这是并行的
dataflow优化
给stream_test 加上dataflow优化,让工具自动推断并行性
C综合结果,运行周期为10000
就算去掉深度的约束,C综合结果也可以过
这是因为没有深度约束,深度默认为1,但是这是数据流风格的,所以可以写一个数据,取一个数据,模块依然可以正常工作。
这里的模块是等两个送一个,所以把之前的10000深度,设置为4也可以,不耗费啥资源
C/RTL 仿真验证
16个数据的test
打开波形图
part1的波形图,确实是读一个写一 个
part2就是读两个数据,写一个