Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correct some mistakes of the documents #77

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion 00-Preface.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

本书将着重介绍如何对算法进行高层次综合(HLS),并以此完成一些比较具体、细分的FPGA应用。我们的目的是让读者认识到用HLS创造并优化硬件设计的好处。当然,FPGA的并行编程肯定是有别于在多核处理器、GPU上实行的并行编程,但是一些最关键的概念是相似的。例如,设计者必须充分理解内存层级和带宽、空间局部性与时间局部性、并行结构和计算与存储之间的取舍与平衡。

本书将更多的作为一个实际应用的向导,为那些对于研发FPGA系统有兴趣的读者提供帮助。对于大学教育来说,这本书将更适用于高阶的本科课程或研究生课程,同时也对应用系统设计师和嵌入式程序员有所帮助。我们不会对C/C++方面的知识做过多的阐述,而会以提供很多的代码的方式作为示范。另外,读者需要对基本的计算机架构有所熟悉,例如流水线(pipeline),加速,阿姆达尔定律(Amdahl's Law)。以寄存器传输级(RTL)为基础FPGA设计知识并不是必需的,但会对理解本书有所帮助。
本书将更多的作为一个实际应用的向导,为那些对于研发FPGA系统有兴趣的读者提供帮助。对于大学教育来说,这本书将更适用于高阶的本科课程或研究生课程,同时也对应用系统设计师和嵌入式程序员有所帮助。我们不会对C/C++方面的知识做过多的阐述,而会以提供很多的代码的方式作为示范。另外,读者需要对基本的计算机架构有所熟悉,例如流水线(pipeline),加速,阿姆达尔定律(Amdahl's Law)。以寄存器传输级(RTL为基础FPGA设计知识并不是必需的,但会对理解本书有所帮助。

本书囊括了很多对教学很有帮助的内容。每个章节均有一些小问题留给读者,这些问题将有助于加深对于材料的理解。在加州大学圣地亚哥分校(UCSD)的CSE 237C这门课里也有很多用HLS开发的项目,如果有出于教育目的的需要,我们可以对提出申请的读者分享这些课程项目的文件。这些HLS项目主要是与数字信号分析相关,并重点研究无线交流系统的开发。每个单独的项目都或多或少与书中的某一章节有所关联。这些项目以赛灵思大学计划(Xilinx University Program)使用的FPGA开发板为基础而开发,设计基础参考 <http://www.xilinx.com/support/university.html> 。赛灵思也同时提供这些开发板的商业订单。同时我们鼓励读者在 <http://xilinx.com> 申请Vivado HLS的试用许可。

Expand Down
16 changes: 8 additions & 8 deletions 03-CORDIC.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ CORDIC算法是1950年由Jack Volder发明,它最开始是作为数字解决
​ CORDIC核心思想是在一个二维平面上高效地执行一组矢量旋转。在这些旋转运算的基础上增加一些简单控制,我们就可以实现各种基础操作,例如,三角函数,双曲函数,对数函数,实乘和复乘,以及矩阵分解和因式分解。CORDIC已经广泛应用于信号处理、机器人技术、通信和许多科学计算领域。由于CORDIC占用资源少,所以常用在FPGA设计中。

​ 在下文中,我们将介绍CORDIC如何执行给定输入角θ的正弦和余弦的过程。这是使用一系列矢量旋转来完成的,这些简单的操作在硬件中使用非常有效。在高层次,算法使用一系列旋转来工作,目标是达到目标输入角θ。实现这种效率的关键创新是旋转可以以需要最少计算的方式完成。尤其我们使用乘以2的常数幂来执行旋转。这意味着简单地在硬件中移动位是非常有效的,因为它不需要任何逻辑。

​ 图3.1提供了用于计算cosφ和sinφ的CORDIC程序的高级概述。在这种情况下,我们在x轴上开始初始旋转矢量,即0°角。然后,我们执行一系列迭代旋转;在这个例子中,我们只执行四次旋转,但通常这是40次旋转。每个后续旋转使用越来越小的角度,这意味着每次迭代都会为输出值增加更多精度。在每次迭代中,我们决定以较小的角度进行正向或负向旋转。我们旋转的角度值是先验固定的;因此,我们可以轻松地将它们的值存储在一个小内存中,并保持我们到目前为止已经旋转的累积角度的运行总和。如果该累积角度大于我们的目标角度φ,则我们执行负旋转。如果它更小,那么旋转就是正的。一旦我们完成了足够数量的旋转,我们就可以通过直接读取最终旋转矢量的x和y值来确定cosφ和sinφ。如果我们的最终向量的幅度为1,则x =cosφ且y =sinφ。

我们从一些术语开始,目的是重新定义你的一些基本的三角函数和矢量概念。 如果熟悉的话就不必看了。 但请记住,创建高效硬件设计最重要的一个方面是真正理解应用程序; 只有这样,设计师才能有效地利用优化指令并执行代码重构,这是获得最有效设计所必需的。
Expand Down Expand Up @@ -130,7 +130,6 @@ $$

计算旋转操作数值,我们可以得到


$$
x_i = x_{i-1} \cos 45^\circ - y_{i-1} \sin 45^\circ = x_{i-1} \cdot \sqrt 2/2 - y_{i-1} \cdot \sqrt 2/2 \quad(3.10)
$$
Expand Down Expand Up @@ -348,7 +347,6 @@ cordic(THETA_TYPE theta, COS_SIN_TYPE &s, COS_SIN_TYPE &c)

图3.3:CORDIC代码实现计算给定角度的正弦和余弦值。


![图3.4:图中显示了一个二维平面和一个同时用笛卡尔形式(x,y)和极坐标形式(r,θ)表示的矢量,通过该矢量说明这两个坐标系之间的关系。](images/rotation.jpg)

​ 此代码接近于“软件”版本。它有多种方式来提高其性能并减小资源面积。我们将在本章后面讨论如何优化这段代码。
Expand Down Expand Up @@ -402,7 +400,6 @@ $$

​ cordic函数使用当前常用的变量类型。例如,变量sigma被定义为int类型,其他变量使用自定义数据类型(例如,THETA_TYPE和COS_SIN_TYPE)。在许多情况下,HLS工具能够进一步优化这些值,来实现硬件资源优化。举个例子,在图3.3中,变量sigma的值为1或−1,即使已经将变量声明为至少包含32位位宽的int类型,在不改变程序功能的情况下,可以用更少的位数用来实现。在其它情况下,特别是变量经常出现在函数输入、存储和递归中,HLS工具不能针对变量进行自动优化。在这些情况下,修改代码使用较小的数据类型是避免资源浪费的重要优化手段。


​ 虽然减少变量位宽通常是一个好的优化方法,但是这种优化方法可能改变程序的行为。一个小位宽数据类型不能像大位宽数据类型那样表征大量信息,而且无限的二进制数可以表示所有无限精度的实数。幸运的是,作为设计人员,我们可以选择符合特定需求的精度,并且可以在精度、资源占用和性能之间进行权衡。

​ 在进一步讨论cordic函数数值表示方法之前,我们首先介绍数值表示的背景知识。我们提供基础知识,因为这在理解Vivado HLS所提供的数据类型进行数值表示时非常重要。下一节将从数值表示的基本背景开始讲解,然后讨论Vivado HLS中可用的任意精度变量。
Expand Down Expand Up @@ -556,12 +553,14 @@ float p2 = 0xB4p-4;//Initialize p2 to "11.25"
| ------ | ----- | ----- | -------- | -------- | -------- | -------- | --------------- |
| 0 | 1 | 1 | 0 | 1 | 0 | 0 | =3.25 |

​ 同样,当一个数值不能通过给定小数位数来精确地表示时,需要采用*舍入*来解决这个问题。同样,这有几种常见的方法来对数值进行舍入。最简单的方法就是去掉额外不能表示的小数位数,但这样就会使数值变的更负。这种舍入的方法通常被称为向下舍入或向负无穷舍入。当向下舍入到最近的整数时,它就能与floot()函数对应起来,尽管它也可能舍入到其他小数位。
​ 同样,当一个数值不能通过给定小数位数来精确地表示时,需要采用*舍入*来解决这个问题。同样,这有几种常见的方法来对数值进行舍入。最简单的方法就是去掉额外不能表示的小数位数,但这样就会使数值变的更负。这种舍入的方法通常被称为向下舍入或向负无穷舍入。当向下舍入到最近的整数时,它就能与float()函数对应起来,尽管它也可能舍入到其他小数位。

![](images/3.5.3_1.png)

​ 同样,也可以使用类似的方法使舍入后数值变得更大(这种称为向上舍入或向正无穷舍入与ceil()函数相对应),向绝对值较小的方向舍入(称为向零舍入与trunc()函数相对应),或向绝对值更大的值舍入(称为远离零舍入或向无穷舍入和round()函数相对应)。然而,这些操作中没有一个总可以最小化舍入误差。一种更好的方法叫做向最接近偶数舍入、收敛舍入或四舍六入五成双,它在lrint()函数中实现。正如你所期望的,这种舍入方式总是选择最近可表示数字。另外,如果有两个误差相等的数,则取偶数。如果最后一个数值是零,那么任意精度数是一个偶数。这种方法是处理IEEE浮点数舍入的默认方法,因为它不仅可以最小化舍入误差,而且还可以确保计算随机数舍入误差之和趋于零。

![](images/3.5.3_2.png)

### 3.5.4 二进制运算

​ 二进制加法与十进制加法相似,可以简单地对齐每位二进制数并进行加法运算,注意正确处理从一列到下一列的进位操作。注意,两个N位数值加或减的结果通常需要N+1位的数值来正确表示才不能溢出。对于小数,总是在符号位进行增加位宽
Expand Down Expand Up @@ -662,7 +661,7 @@ uint32_t p = ((uint32_t) a*(uint32_t) b) >> 12;

{% endhint %}

​ 由于这些原因,最好使用c++和Vivado HLS模板类ap_int<>, ap_uint<>, ap_ fixed<>, ap_ufixed<>来表示任意精度数据。ap_int<>和ap_uint<>模板类需要整数参数来定义他们的位宽。计算函数通常产生结果的位宽足够宽,可以表示正确结果,参照3.5.4节中的规则。只有当结果分配给较小的位宽时,才会发生溢出或下溢。
​ 由于这些原因,最好使用c++和Vivado HLS模板类ap_int<>, ap_uint<>, ap_fixed<>, ap_ufixed<>来表示任意精度数据。ap_int<>和ap_uint<>模板类需要整数参数来定义他们的位宽。计算函数通常产生结果的位宽足够宽,可以表示正确结果,参照3.5.4节中的规则。只有当结果分配给较小的位宽时,才会发生溢出或下溢。

```C++
#include "ap_int.h"
Expand Down Expand Up @@ -707,10 +706,10 @@ ap_ufixed<18,12> p = a*b;
​ 在本节中,我们将对优化CORDIC函数的方法提出一些简要的想法和建议。在权衡吞吐量、精度和资源的同时,我们关注不同优化方式如何对处理结果精度产生影响。

在实现CORDIC功能时,选择合适的数据类型来表示角度和结果十分重要。尽管源代码在浮点数和定点数下都能运行,CORDIC通常都会使用定点数数据类型使得乘法器数量减少。而当高效的乘法实现可行时,其它计算三角函数的方法就会被采用。回看图3.3中的源代码,包含了几个跟sigma和factor变量有关的乘法器。通过限制代码只工作在定点数下,我们可以用移位和加法操作来替代。代码如图3.6所示。

```C++
// The file cordic.h holds definitions for the data types and constant values
#include cordic.h
#include "cordic.h"

// The cordic phase array holds the angle for the current rotation
// cordic_phase[0] =˜ 0.785
Expand Down Expand Up @@ -752,6 +751,7 @@ void cordic(THETA_TYPE theta, COS_SIN_TYPE &s, COS_SIN_TYPE &c)
c = current_cos;
}
```

Figure 3.6 定点数CORDIC代码实现给定角度sin和cos值

​ 最后,CORDIC运算得到一个近似值。随着迭代次数的增加,近似值的误差通常会减小。这对应于我们在cordic函数中执行**for**循环的次数,该循环次数由NUM_ITERATION进行设置。即使我们执行了大量迭代,我们仍然可能得到一个近似值。这样做的一个原因是我们可以接近,但从来没有得到完全匹配的目标角度。然而,我们可以通过选择执行更大或更小的迭代角度来调整精度。上述算法中需要更改的地方都可以通过修改NUM_ITERATIONS的值来实现。NUM_ITERATIONS数值依赖于使用这个CORDIC算法应用程序所需精度。
Expand Down
Loading