video_to_axi_stream
一、video转stream设计
#include "frame_top.h"
void video_to_axi_stream(
pixel_t &in_data,
ap_uint<1> &vsync,
ap_uint<1> &hsync,
ap_uint<1> &de,
hls::stream<axis_pkt_t> &output_stream
) {
#pragma HLS INTERFACE ap_ctrl_none port=return
#pragma HLS INTERFACE ap_none port=in_data
#pragma HLS INTERFACE ap_none port=vsync
#pragma HLS INTERFACE ap_none port=hsync
#pragma HLS INTERFACE ap_none port=de
#pragma HLS INTERFACE axis register both port=output_stream
static bool vsync_d1 = true; // 上一拍 vsync
static bool de_d1 = false; // 上一拍 de
static ap_uint<12> pixel_cnt = 0; // 行内像素计数
static bool sof = false; // 帧起始标志
axis_pkt_t out_pkt;
// ❶ 边沿检测(在使用延迟值之后才更新延迟寄存器)
bool vsync_rise = (vsync_d1 == 0) && (vsync == 1);
bool de_rise = (de == 1) && (de_d1 == 0);
vsync_d1 = vsync; // 更新延迟值
de_d1 = de;
// ❷ vsync 上升沿:新的一帧开始,置位 sof 并复位行计数器
if (vsync_rise) {
sof = true;
pixel_cnt = 0;
}
// ❸ de 上升沿(新的一行有效数据开始):复位行计数器
if (de_rise) {
pixel_cnt = 0;
}
// ❹ 输出有效像素
if (de == 1) {
out_pkt.data = in_data;
out_pkt.user = sof && (pixel_cnt == 0); // 帧首第一个像素
out_pkt.last = (pixel_cnt == (H_ACTIVE - 1)); // 行尾最后一个像素
output_stream.write(out_pkt);
if (sof && pixel_cnt == 0) sof = false; // 帧首标志仅维持一拍
pixel_cnt++; // 计数
}
}
上述代码是存在问题的!
其不满足综合设计要求的三大条件
二、三大条件分析
三条条件是:
条件一:组合逻辑设计。
条件二:流水线设计且任务间隔为 1。
条件三:带有 array streaming 或 hls::stream 或 AXI4 stream 端口的 design。
条件三并不是说你的数组按照hls::stream AXI4 stream设计就行,这里默认了一个条件,就是数据流设计,
就是你的输入和输出次数是意义对应的!不是说你的接口数组设计为阻塞的流设计就行了,如果你这么理解,你
就太肤浅了哈。
void video_to_axi_stream(
pixel_t &in_data,
ap_uint<1> &vsync,
ap_uint<1> &hsync,
ap_uint<1> &de,
hls::stream<axis_pkt_t> &output_stream
);
这个接口虽然满足了条件三;
但是,内部设计需要满足output_stream这个流能够及时的将:
pixel_t &in_data,
ap_uint<1> &vsync,
ap_uint<1> &hsync,
ap_uint<1> &de,
这几个接口信号的数据处理掉才行,如果处理不掉,不好意思啊,出问题了。
因为上述的接口是free-run模式的参数更新,后续的hls::stream<axis_pkt_t> &output_stream
是阻塞的流,是否能够将free-run内部的数据处理掉,这个需要内部设计注意!!!!
Vivado HLS 2018.3 对这条规则的实际解释是:整个设计必须是一个纯粹的流处理流水线,即主要功能是从流中消费数据、处理、再产出流。你的设计有以下不符合“流处理范式”的特征:
三、分析代码
第一条:不是组合逻辑,因为有静态变量和状态机,所以不满足。
第二条:使用 ap_ctrl_none 时,函数像一个无限循环自由运行,无法定义任务间隔,HLS 不会将其综合为 II=1 的可流水线化任务(因为输入是 ap_none 且内部有状态,通常综合为顺序逻辑,但没有开始/完成握手,无法以任务模型衡量 II)。因此不满足。
第三条:虽然有 AXI4 stream 端口,但设计并非“流处理”模型。工具的检查可能要求输入也应该是流接口,并且整个设计是数据流驱动,从流中读取、处理、写入流。而这里输入是 ap_none 的非流信号,且内部依赖于外部自由运行的时钟状态机,不符合流处理范式。Vivado HLS 2018.3 对第三条的解释较严格:它要求设计是纯粹的流处理流水线,而不是简单的带有 stream 端口的任意控制逻辑。因此会被拒绝。
所以总结:不满足第三条,也不满足前两条。三条都不满足,所以报错。
规则一:纯组合逻辑设计
你的设计内部有 static 变量(vsync_d1、de_d1、pixel_cnt、sof),并且使用了状态机(边沿检测、计数器),这显然是时序逻辑,而非组合逻辑。
➜ 不满足
规则二:流水线设计且任务间隔为 1
当使用 ap_ctrl_none 时,工具将函数视为自由运行模块(类似一个无限循环的硬件块),没有明确的“任务”概念。虽然内部逻辑可以流水化,但因为没有 ap_start/ap_done 等握手信号,工具无法定义任务间隔(II)。更重要的是,该设计每次调用的行为依赖于静态变量的历史,不能简单地以一个确定的任务间隔来重复启动。因此它不能被归类为 II=1 的流水线任务。
➜ 不满足。
规则三:带有数组流 / hls::stream / AXI4‑Stream 端口的设计
表面上看,你的模块有 hls::stream<axis_pkt_t> 输出,似乎满足这条。但 Vivado HLS 2018.3 对这条规则的实际解释是:整个设计必须是一个纯粹的流处理流水线,即主要功能是从流中消费数据、处理、再产出流。你的设计有以下不符合“流处理范式”的特征:
输入不是流:所有输入(in_data, vsync, hsync, de)都是 ap_none 的非流信号,工具无法将它们建模为 FIFO 通道。
行为本质上是一个自由运行的时序转换器,并非“读取流 → 处理 → 写入流”的数据驱动模型。
内部关键控制依赖于 vsync 和 de 的边沿,以及计数器状态,这属于控制密集型逻辑,而不是数据流密集型。
因此,工具判定它不属于“具有流端口的流处理设计”,拒绝协同仿真。
➜ 不满足。
四、修改为ap_ctrl_hs或者ap_ctrl_chain是否可行
修改为ap_ctrl_hs,也是解决不了问题的,仿真可能还是出问题的,需要你认真分析代码设计的问题,解决了问题才行,并不是ap_Ctrl_none不行,你直接改为ap_Ctrl_hs就行,有时候你可以这么干,有时候不行,这个要具体问题具体分析!
