目录
1. 简介
为了让硬件运行得更快,HLS工具需要能够从一段普通的代码中找出哪些部分可以同时进行,也就是并行处理。这样的并行处理可以让硬件的性能大大提升。但是,要让工具自己识别出并行的可能性并不容易。
再者,好的软件设计往往依赖于一些固定的规则和方法,比如运行时的类型信息(RTTI)、递归操作,以及动态地分配内存空间。这些在软件中很常见的技巧,在硬件设计里却没有直接对应的方法,这就给HLS工具带来了挑战。因此,很多现成的软件并不能直接高效地转换成硬件形式。
至少,我们需要检查软件里是否有些部分是HLS工具处理不了的,如果有,就需要修改代码,让它变得可以被工具处理。即便软件能够自动转换成硬件,也需要了解一些在FPGA器件上编写高效代码的最佳实践,这样才能帮助工具更好地完成转换工作。这就像是给工具一个清晰的指导,让它知道怎么把软件变成硬件。
2. 解析
要为 FPGA 平台编写优秀软件,就需要了解这三个范例:生产者使用者、串流数据和流水打拍。
"范例"(Paradigm)指的是一种模式、例子或者是一种特定的编程方法论,用于解决特定类型的问题。在编程和软件设计领域,范例通常用来指导如何以某种理想的方式来组织和编写代码,以便解决一类问题。
上文提到的范例,其背后的基本并行处理原理和方法如下:
2.1 任务集合与通道
HLS 设计应被视为一组任务的集合,任务可以按照其激活机制被划分为两大类:
控制驱动型:其中任务在接收到特定信号之后才启动执行。
数据驱动型:对于这种类型,任务的执行是由通道上数据的存在直接触发的。
这些任务通过通信链路(亦称作通道,channel)相互传递消息。
2.2 任务的结构和构成特征
任务是由具有特定功能和资源的可执行单元组成的。这里的“可执行单元”指的是能够执行特定计算或处理任务的硬件组件。
每个可执行单元具备以下特性:
本地存储器:即每个可执行单元都有自己的内部存储资源,用于保存执行过程中产生的数据或状态信息。
- 这些数据是私有的,只有该单元可以直接读写这些数据。
- 对此存储器执行的访问称为本地数据访问,如 BRAM/URAM 中存储的数据。
- 此类访问十分快速。
输入/输出 (I/O) 端口集合:这表示每个可执行单元都拥有一组输入和输出接口,通过这些接口,它能够接收来自其他单元的数据(输入),并将处理结果发送给其他单元或输出到外部环境中(输出)。
- I/O端口是任务单元之间进行通信和数据交换的唯一接口。
- 通过通道发送或接收的数据称为非本地数据访问。
2.3 通道的可靠性
通道应确保可靠,并具有如下行为:
-
数据的顺序性:1) 如果通道被设计为 FIFO 类型,那么生产者写入通道的数据顺序应与消费者从通道读取数据的顺序相同。2) 如果通道被设计为 PIPO 类型,数据可以按任意顺序写入或读取,这提供了更大的灵活性,适用于不需要严格顺序处理的场景。
-
数据的完整性:通道应确保在数据传输过程中不丢失任何数据值。
PIPO (Ping-Pong buffer),能够按随机顺序写入/读取数据的原因是,它通过交替使用两个缓冲区来避免读写操作的冲突。当一个缓冲区被填满数据后,系统会切换到另一个缓冲区进行写入,而此时第一个缓冲区可以安全地被读取。这样,即使写入和读取操作的顺序不固定,也不会影响数据的完整性。
2.4 通道的阻塞/非阻塞语义
2.4.1 阻塞语义
阻塞读/写(Blocking Reads/Writes):在阻塞语义下,读和写操作是阻塞的,也就是说,如果一个任务尝试读取数据但数据尚未准备好,任务将被阻塞,直到数据可用为止。写操作也是类似的,如果缓冲区已满,写操作会阻塞,直到缓冲区有空间为止。
使用阻塞的结果:
- 死锁(Deadlocks):FIFO深度不足或速率不匹配引起。
- 确定性仿真(Deterministic simulations):仿真结果是确定的。
- 数据不会丢失(Task and/or Channel can never lose data)。
2.4.2 非阻塞语义
非阻塞读/写(Nonblocking Reads/Writes):在非阻塞语义下,读和写操作是非阻塞的,也就是说,任务不会因为数据未准备好或缓冲区已满而阻塞。任务可以立即继续执行,不论操作是否成功。
使用非阻塞的结果:
- 死锁(Deadlocks):FIFO深度不足。
- 非确定性仿真(Non-deterministic simulations):仿真结果是不确定的。
- 数据可能丢失(Task can lose data)。
3. 总结
使用阻塞语义时:
- 读取空通道会导致读取进程发生阻塞。
- 写入已满的通道会导致写入进程发生阻塞。
- 生成的进程/通道网络会展现出确定性行为,此类行为不依赖于计算的时序或通信延迟。
- 发生死锁:通道队列大小不足(前提是通道为 FIFO)和/或由于生产者与使用者之间的产率不同。
该模型适用于对嵌入式系统、高性能计算系统、信号处理系统、串流处理系统、数据流编程语言及其它计算任务进行建模。
使用非阻塞语义时:
- 读取空通道就会导致读取未初始化的数据,或者导致重新读取上一个数据项。
- 写入已满的队列则会导致数据丢失。
为避免此类数据丢失,设计必须首先检查队列状态,然后才能执行读写。但这将导致此类模型的仿真呈现不确定性,因为它所依赖的决策是根据通道的运行时状态来作出的。这将导致对此模型的结果进行验证变得极为困难。