目录
1. 简介
2. 代码解析
2.1 pointer_stream_bad
2.2 pointer_stream_better
2.3 pointer_stream_good
3. 总结
1. 简介
本文将探讨在 Vitis HLS 中正确处理多重访问指针重要性以及其对功能的影响。通过分析示例代码,我们将讨论在如何正确处理指针访问,以及如何避免由于指针多次访问而导致的问题。从错误示例到更好的解决方案,我们将探讨使用 volatile 限定符、串流数据类型以及显式访问指针元素等方法来确保 RTL 设计的正确性。
2. 代码解析
2.1 pointer_stream_bad
在这个错误示例中,指针 d_i 被读取了 4 次,而指针 d_o 则被写入了 2 次,导致指针多次被访问。
void pointer_stream_bad ( dout_t *d_o, din_t *d_i) {
din_t acc = 0;
acc += *d_i;
acc += *d_i;
*d_o = acc;
acc += *d_i;
acc += *d_i;
*d_o = acc;
}
然而,在综合后的 RTL 设计中,该代码只会读取 1 次输入端口并写入 1 次输出端口。
原因:与标准 C/C++ 编译器一样,Vitis HLS 会优化掉多余的指针访问。
2.2 pointer_stream_better
要实现上述代码的功能,即对 d_i 读取 4 次,对 d_o 写入 2 次,必须将指针指定为 volatile:
void pointer_stream_better(volatile int *d_o, volatile int *d_i) {
#pragma HLS INTERFACE mode=ap_hs port=d_o //通过ap_hs接口
#pragma HLS INTERFACE mode=ap_hs port=d_i //通过ap_hs接口// #pragma HLS INTERFACE mode=ap_fifo depth=2 port=d_o //通过ap_fifo接口
// #pragma HLS INTERFACE mode=ap_fifo depth=4 port=d_i //通过ap_fifo接口int acc = 0;acc += *d_i;acc += *d_i;*d_o = acc;acc += *d_i;acc += *d_i;*d_o = acc;
}
volatile 限定符作用:
- 阻止指针访问最优化
- 生成的 RTL 设计执行流程如同 “C语言” 一样,会按照周期进行,对输入端口 d_i 执行 4 次读取,对输出端口 d_o 执行 2 次写入。
输入指针 d_i 和输出指针 d_o 在 RTL 中是作为 hs(或 fifo)接口实现的,目的是为了确保:
- 每次在 RTL 端口 d_i 上执行读取时,上游生产者模块都可提供新数据。
- 每次写入 RTL 端口 d_o 时,下游使用者模块都接受新数据。
可以通过查看综合报告加以确认:
================================================================
== HW Interfaces
================================================================
* REGISTER
+-----------+-------+----------+
| Interface | Mode | Bitwidth |
+-----------+-------+----------+
| d_i | ap_hs | 32 |
| d_o | ap_hs | 32 |
+-----------+-------+----------+* SW-to-HW Mapping
+----------+--------------+---------+
| Argument | HW Interface | HW Type |
+----------+--------------+---------+
| d_o | d_o | port |
| d_o | d_o_ap_vld | port |
| d_o | d_o_ap_ack | port |
| d_i | d_i | port |
| d_i | d_i_ap_vld | port |
| d_i | d_i_ap_ack | port |
+----------+--------------+---------+
上述报告是按照 ap_hs 接口进行综合的,可以看到输出 IP 包含了相应的硬件接口。
2.3 pointer_stream_good
不同于软件,硬件系统的并发性质使其能够充分利用串流数据。数据可持续不断提供给 HLS IP,HLS IP 可持续不断输出数据。HLS IP 可在完成处理现有数据之前接受新数据。
有多种方法可以优化上述代码:
- 添加 volatile 限定符,测试激励文件不会对专用读写进行建模,使用原 C/C++ 语言测试激励文件的 RTL 仿真可能失败,但通过查看追踪文件波形可以发现执行的读写操作正确。
- 修改代码以对显式专用读写进行建模。请参阅以下示例。
- 修改代码以使用串流数据类型。串流数据类型支持对使用串流数据的硬件进行准确建模。
void pointer_stream_better(volatile int *d_o, volatile int *d_i) {#pragma HLS INTERFACE mode=ap_fifo depth=2 port=d_o#pragma HLS INTERFACE mode=ap_fifo depth=4 port=d_iint acc = 0;acc += *d_i;acc += *(d_i + 1);*d_o = acc;acc += *(d_i + 2);acc += *(d_i + 3);*(d_o + 1) = acc;
}
以上代码,通过数组(指针)索引的方式显式地访问指针 d_i 和 d_o 的每个元素。由于指针访问按顺序进行并从位置 0 开始,因此综合期间可使用串流接口类型。
3. 总结
虽然 Vitis HLS 在指针上支持多重访问指针,但强烈建议使用 hls::stream 类替代多重访问指针来实现所需的行为,以避免陷入困难。如果设计使用接口上的顶层函数的实参列表中的指针,那么在使用指针执行多重访问时需考量一些特殊注意事项。当任一指针以相同方式多次执行读取或写入时,就会发生多重访问。