一、ap_ctrl_none经典场景
#include "hls_stream.h"
void task_a(hls::stream<int> &in_fifo, hls::stream<int> &out_fifo) {
#pragma HLS inline off
int val = in_fifo.read(); // 阻塞读
out_fifo.write(val * 2); // 阻塞写
}
void task_b(hls::stream<int> &in_fifo, hls::stream<int> &out_fifo) {
#pragma HLS inline off
int val = in_fifo.read();
out_fifo.write(val + 5);
}
void top_function(hls::stream<int> &in_stream, hls::stream<int> &out_stream) {
// 1. 顶层禁用控制信号 (ap_ctrl_none)
#pragma HLS interface ap_ctrl_none port=return
// 2. 内部使用 Dataflow
#pragma HLS dataflow
// 内部连接流
hls::stream<int> internal_fifo;
#pragma HLS stream variable=internal_fifo depth=2
task_a(in_stream, internal_fifo);
task_b(internal_fifo, out_stream);
}
二、ap_ctrl_none使用注意问题
1.必须有无阻塞或阻塞型 I/O:由于没有 ap_start,如果输入接口不是流式的(例如是 AXI4 memory mapped 的 m_axi),设计可能会在启动时死锁,因为没有信号告诉它何时开始读取内存。
2.死锁风险:如果 Dataflow 内部的任务之间形成环路,且没有使用正确的 PIPO 或 FIFO 深度,很容易死锁。
三、ap_ctrl_none应用场景
1.ap_ctrl_none 适用于纯数据流驱动、无需外部启动/完成握手的连续处理场景;
2.主要包括四类:
--Vitis 自由运行内核(free-running kernel)
void increment(hls::stream<int> &input, hls::stream<int> &output) {
// 内核一上电即运行,无 ap_start/ap_done
#pragma HLS interface ap_ctrl_none port=return
#pragma HLS interface axis port=input
#pragma HLS interface axis port=output
int val = input.read(); // 阻塞读,无数据时停顿
output.write(val + 1);
}
--连续流式流水线(infinite loop + stream I/O)
void top(hls::stream<ap_axiu<32>> &in, hls::stream<ap_axiu<32>> &out) {
#pragma HLS interface ap_ctrl_none port=return
#pragma HLS interface axis port=in
#pragma HLS interface axis port=out
#pragma HLS pipeline II=1
while (1) {
ap_axiu<32> v = in.read();
v.data = v.data * 2;
out.write(v);
}
}
--纯组合逻辑块
--以及 Dataflow 区域内仅通过 FIFO 通信的子区域
void top(hls::stream<int> &in, hls::stream<int> &out) {
#pragma HLS interface ap_ctrl_none port=return // 顶层
#pragma HLS dataflow
hls::stream<int> s1, s2a, s2b;
producer(in, s1); // 快速分发
worker_a(s1, s2a); // 两个慢速消费
worker_b(s1, s2b);
merger(s2a, s2b, out);
}