FPGA HLS stream与dataflow

基本使用

首先导入头文件

#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;

流的特性

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

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仿真结果:

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

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

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值