循环索引变量请避免使用全局变量
一、循环变量索引的使用
1.循环变量的索引变量i一般是使用局部变量,这样保证了变量被综合工具充分优化
2.循环变量的索引如果使用全局变量,会妨碍综合工具进行优化
3.全局变量在HLS中,通常会被综合为寄存器或者存储器,会增加不必要的存储和跨周期依赖;
二、循环变量索引使用全部变量的问题
1.由于循环索引是一个单纯的计数变量,通常是只存在于循环体内作为局部变量
2.如果使用全局变量,那么HLS对这个循环索引的综合和优化策略是不一样的,使用全局变量
来定义循环变量的索引,会造成unroll问题,因为全局变量会被修改,会破环循环边界的编译
的不确定性;并且海汇影响pipeline的循环调度,因为每次循环迭代,都需要读写全部变量,
这个会引入读取和写入操作,并且带来了不必要的依赖和端口;
三、循环变量索引会影响
1.会影响pipeline
2.会影响unroll
3.会影响II
硬件综合工具会依赖变量的作用域,以及其生命周期来推断数据之间的依赖关系以及访问顺序。
全局变量打破了这种规则,引入罗列工具无法静态分析的外部可见状态,影响工具的判断。
四、全局变量索引的综合推断
1.有可能被综合为带状态的寄存器
当一个循环的索引变量是全局变量时,工具会认为:
该变量在每次迭代后必须写回到全局寄存器。
下一次迭代必须读出该全局寄存器的值,形成跨迭代的 RAW(读后写)依赖。
即使循环本身是简单的顺序递增,这种强制读写也会在数据流图中引入不必要的循环携带依赖(loop-carried dependency),造成所有迭代串行化。
2.破坏了循环边界的常量分析
当索引变量是全局变量时:
其初值可能不是当前代码块设置的,工具无法证明循环在进入时 i 一定为某个固定值。
即使在循环前刚赋值,由于全局变量可能被其他进程、中断或外部接口在任意时刻异步修改(工具保守假设),该变量被视为“可变的静态状态”。
结果:工具无法确定循环边界是常量,因而不能执行展开(unroll)、不能确定流水线深度、不能生成突发传输所需的确切地址序列,甚至可能无法流水化该循环。
3.强制引入不必要的接口和资源
全局变量综合为寄存器后,每当时钟上升沿,都需要从该寄存器读取索引值并写回更新后的值。这会导致:
额外的读/写端口竞争。如果循环内部还要访问别的数组,可能会与全局索引寄存器的读写产生资源冲突。
在循环流水线中,寄存器写回会限制 II 的降低,因为一次迭代的索引写回必须在启动下一次迭代前完成(RAW 依赖),即使工具试图重命名,全局变量无法重命名——它是唯一的物理寄存器。
若该全局变量同时也被其他模块访问,工具可能额外插入仲裁逻辑或多驱动冲突,导致设计变复杂且频率降低。
4.从编译器看差异
Vivado HLS 基于 LLVM,首先将 C 代码转换为中间表示(IR)。在 IR 中:
局部标量通过寄存器分配和 phi 节点表达循环感应变量,这些 phi 节点可被轻易识别并转化为硬件流水线中的控制步进。
全局变量被编译为 @global 的 IR 全局变量,对其的每次访问都是显式的 load/store 指令。这些 load/store 与其他内存访问混在一起,无法轻易消除。
HLS 的依赖分析模块需要处理这些 load/store,由于全局变量的别名分析困难(尤其有指针或函数调用时),工具会保守假设每次 load 都可能读到最新写入,无法重排或消除访问。
这最终导致优化失败。
