一、概述1.stable指令在HLS高层次优化设计中是很重要的其作用是用于dataflow优化中解决因为数据依赖问题导致的启动时间间隔从而提升设计的吞吐量。2.stable指令的核心作用是移除不必要的同步开销运行dataflow区域的设计内的子任务更加密集的进行并行设计3.dataflow中如果一个子任务只是读取而不修改某个数组或者全部变量HLS为了确保数据的安全性会强制要求所有依赖这个变量的子任务都要同步启动这样就会导致流水线出现气泡降低吞吐量。二、stable典型应用场景1.配置参数通过AXI-Lite总线配置的系数表整个处理过程中保持不变。2.查找表预先计算好并固化在内存中的常量表。3.初始数据仅用于启动时初始化的数据。三、举例说明#include ap_int.h#include hls_stream.hvoid proc_A(hls::streamap_uint8 in, ap_uint8 out[32]) {// ... 处理输入流产生输出到 out 数组}void proc_B(ap_uint8 coeff[32], hls::streamap_uint8 out) {// ... 读取 coeff 数组进行进一步处理}void top_function(hls::streamap_uint8 input, hls::streamap_uint8 output) {#pragma HLS INTERFACE axis portinput#pragma HLS INTERFACE axis portoutputap_uint8 coeff[32] {1,2,3,4,5,...}; // 固定的系数表#pragma HLS DATAFLOWproc_A(input, coeff);proc_B(coeff, output);}HLS 会谨慎地认为coeff数组可能会被proc_B之外的进程修改因此会插入同步逻辑强制proc_A必须等待proc_B完全启动并准备好读取数据后才能开始工作。这种保守策略虽然保证了功能的绝对正确但打破了 DATAFLOW 的理想流水线导致部分硬件资源闲置限制了吞吐量。#include ap_int.h#include hls_stream.hvoid proc_A(hls::streamap_uint8 in, ap_uint8 out[32]) {// ... 处理输入流}void proc_B(ap_uint8 coeff[32], hls::streamap_uint8 out) {// ... 读取 coeff 数组}void top_function(hls::streamap_uint8 input, hls::streamap_uint8 output) {#pragma HLS INTERFACE axis portinput#pragma HLS INTERFACE axis portoutputap_uint8 coeff[32] {1,2,3,4,5,...};#pragma HLS stable variablecoeff // --- 添加 STABLE 指令#pragma HLS DATAFLOWproc_A(input, coeff);proc_B(coeff, output);}添加 STABLE 指令后HLS 得到了明确的“承诺”coeff 数组是只读的。因此它会放心地移除 proc_A 和 proc_B 之间与 coeff 相关的同步逻辑-12。这样一来proc_A 一旦有数据准备好就可以立即启动而不必再等待 proc_BDATAFLOW 的流水线得以充分流动从而提高了整体的吞吐量。四、案例对比案例一未使用 STABLE —— 强制同步流水线有气泡#include hls_stream.h#include ap_int.h#define N 100// 任务A产生输出到 coeff 数组只写一次void task_A(hls::streamap_uint8 in, ap_uint8 coeff[N]) {for (int i 0; i N; i) {#pragma HLS PIPELINE II1coeff[i] in.read(); // 从输入流读取数据写入 coeff}}// 任务B只读 coeff 数组进行某种处理void task_B(ap_uint8 coeff[N], hls::streamap_uint8 out) {for (int i 0; i N; i) {#pragma HLS PIPELINE II1out.write(coeff[i] 1); // 读取 coeff处理后输出}}// 顶层函数void top_function(hls::streamap_uint8 input,hls::streamap_uint8 output) {#pragma HLS INTERFACE axis portinput#pragma HLS INTERFACE axis portoutput#pragma HLS INTERFACE s_axilite portreturnap_uint8 coeff[N];// 注意这里没有使用 #pragma HLS STABLE#pragma HLS DATAFLOWtask_A(input, coeff);task_B(coeff, output);}task_A 向 coeff 数组写入数据task_B 只读取该数组。HLS 无法确定 coeff 在 DATAFLOW 期间是否会被修改尽管代码中只有 task_A 写入但 task_B 只读理论上可以流水。为了安全HLS 采用保守策略强制 task_A 和 task_B 同步启动即 task_B 必须等待 task_A 完全执行完才能开始。结果流水线中出现气泡——task_B 完全空闲直到 task_A 完成全部 N 次迭代后才开始工作。总处理时间 ≈ 2N 周期吞吐量仅为 0.5 数据/周期。案例二使用 STABLE —— 消除同步流水线无气泡#include hls_stream.h#include ap_int.h#define N 100void task_A(hls::streamap_uint8 in, ap_uint8 coeff[N]) {for (int i 0; i N; i) {#pragma HLS PIPELINE II1coeff[i] in.read();}}void task_B(ap_uint8 coeff[N], hls::streamap_uint8 out) {for (int i 0; i N; i) {#pragma HLS PIPELINE II1out.write(coeff[i] 1);}}void top_function(hls::streamap_uint8 input,hls::streamap_uint8 output) {#pragma HLS INTERFACE axis portinput#pragma HLS INTERFACE axis portoutput#pragma HLS INTERFACE s_axilite portreturnap_uint8 coeff[N];#pragma HLS STABLE variablecoeff // 关键声明 coeff 在 DATAFLOW 期间稳定#pragma HLS DATAFLOWtask_A(input, coeff);task_B(coeff, output);}添加 #pragma HLS STABLE variablecoeff 后HLS 得到明确承诺coeff 在 DATAFLOW 区域执行期间不会改变即 task_B 只读task_A 虽然写入但 HLS 会理解为先写后读且写完成后不再修改。工具因此可以安全地移除同步逻辑允许 task_B 在 task_A 写出第一个有效数据后立即开始读取。结果task_A 和 task_B 形成真正的流水线。当 task_A 输出第一个 coeff[0] 后task_B 就可以开始处理同时 task_A 继续计算下一个 coeff[1]。理想情况下每周期输出一个结果总处理时间 ≈ N 启动延迟很小吞吐量接近 1 数据/周期。