static
限定符的存储器来实现。这不仅可确保 Vitis HLS 以 RTL 中的存储器来实现阵列,还允许使用“static”(静态)类型的默认初始化行为。在以下代码中,阵列利用一组值来进行初始化。每次执行函数时,都会为 coeff
阵列分配这些值。综合后,每次执行设计时,用于实现 coeff
的 RAM 都会随这些值一起加载。对于单端口 RAM,此操作耗时 8 个时钟周期。对于 1024 阵列,当然也就需要 1024 个时钟周期,在此期间无法执行任何依赖于 coeff
的运算。
int coeff[8] = {-2, 8, -4, 10, 14, 10, -4, 8, -2};
以下代码使用 static
限定符来定义 coeff
阵列。该阵列开始执行时会使用指定的值进行初始化。每次执行该函数时,coeff
阵列都会保留上次执行的值。静态阵列在 C/C++ 语言代码中的行为与 RTL 中的存储器行为相同。
static int coeff[8] = {-2, 8, -4, 10, 14, 10, -4, 8, -2};
此外,如果变量包含 static
限定符,那么 Vitis HLS 会对 RTL 设计和 FPGA 比特流中的变量进行初始化。因此,无需经历多个时钟周期来初始化存储器,并且可确保大型存储器初始化不会产生任何运算开销。请参阅 GitHub 上提供的 initialization_and_reset 示例,以获取更多示例。
RTL 配置命令 syn.rtl.reset
可指定在应用复位后,静态变量是否返回其初始状态。这并非默认设置。使用 syn.rtl.reset=state
或 all
时,会强制作为块 RAM 实现的所有阵列都在复位后返回初始化状态。这可能导致 RTL 设计中出现 2 个非常不利的条件:
- 不同于上电初始化(或上电复位),显式复位要求 RTL 设计对块 RAM 中的每个地址进行迭代以设置该值:如果 N 较大,这可能导致时钟周期数量显著增加,并增加实现复位所需的面积资源。
- 在设计中每个阵列中都添加复位。
为防止在每个此类 BRAM 上添加复位逻辑,并因复位 RAM 内所有元素而产生周期开销,请指定默认 syn.rtl.reset=control
复位模式,并使用 RESET 编译指示或指令来识别要复位的个别静态变量或全局变量。
或者,可使用 syn.rtl.reset=state
复位模式,并使用 RESET 指令 off
选项选择不执行复位的个别静态变量或全局变量。
最后,根据您所选的硬件器件或平台(UltraScale+ 或 Versal 等),块 RAM 和 URAM 的初始化和/或复位方式可能不尽相同。总体上,Vitis HLS 支持两类复位:一种类型是器件上电(也称为上电初始化或上电复位),第二种类型是在器件执行期间,硬件 RESET 信号断言有效。以下显示了不同存储器资源的行为差异:
- 初始化行为:适用于所有平台上的所有块 RAM,仅适用于 Versal URAM。这是上电初始化(或上电复位)期间的行为。
- 如果读取/写入阵列,则保留“initial value array”(初始值阵列)和“runtime array”(运行时阵列)。此行为适用于块 RAM 和 URAM,对应于器件执行期间的硬件 RESET 信号。
write thru
、read first
和 no change
。URAM 仅支持 no change
。如果 Vitis HLS 在 URAM 端口上无法将存储器操作调度为在同一个周期内执行,那么将发出以下警告消息:Usage of URAM can potentially cause worse II as Vitis HLS does not exploit
read-first mode for URAMs. Consider using block RAMs instead.