国外大学生都用FPGA做什么项目(二十)

FPGA技术江湖 2026-07-25 09:00

小引:

今天我们去看看常春藤盟校Cornell University 康奈尔大学开设的FPGA项目课程,大部分课程是有源码的,而且和国内使用习惯类似都是Verilog开发,还是很有借鉴意义的。

项目链接

https://people.ece.cornell.edu/land/courses/ece5760/FinalProjects/

项目介绍

2024年春季 开发板:CycloneV DE1-SoC

基于FPGA的超标量乱序NPU设计

项目介绍

在这个项目中,构建了一个基于 DE1-SoC 的 NPU(神经处理单元),该 NPU 采用 VLIW 超标量乱序处理架构,用于加速矩阵计算。

神经网络处理器(NPU)是一种专门设计的硬件加速器,用于加速神经网络的计算。与可以处理各种任务的通用CPU甚至GPU不同,NPU针对神经网络工作负载的特定计算模式和数据流进行了优化。

设计的NPU(本质上是一个处理器)包含矩阵加法、矩阵点运算、矩阵卷积、ReLU激活函数等特殊功能单元。这些功能单元的目标是最终以更高的计算速度运行完整的CNN模型。借助128位指令集,项目旨在通过独特的指令码值来区分各个功能单元的不同操作。处理器是一个超标量、乱序执行的处理器,包含六个阶段:取指、译码、指令发出、执行、提交和写回。

该处理器采用单取单发射架构,同时支持乱序执行和乱序提交。写回阶段会更新状态标志。处理器中的各个功能单元并行工作。处理器使用了12个SRAM,实现了分块存储子系统的功能。这些SRAM将作为数据存储器使用。此外,还有一个额外的SRAM模块用作指令RAM。整个处理器设计具有可扩展性,因为可以不断添加功能单元模块来增强其性能。

在该系统中,ARM CPU 主要负责控制 NPU 执行计算。它还负责读取输入图像数据和由 PyTorch 训练的权重,并将数据写入 SRAM 供 NPU 使用。

高层设计

高级系统架构

神经处理单元(NPU)的高层设计是一种超标量乱序处理器架构,包含多个功能单元,用于执行卷积神经网络(CNN)运算。NPU的结构由指令集架构、功能单元、内存子系统、处理器流水线和时钟域组成。

该指令集架构 (ISA) 采用 128 位宽的指令格式。ISA 还详细说明了源地址、目标地址以及矩阵维度等参数的编码方式。每条指令的计算都由一个专用的功能单元处理。这些功能单元包括用于不同通道的卷积单元(Conv1、Conv2、Conv3)、矩阵加法、矩阵减法、逐元素乘法、矩阵乘法、ReLU 激活函数和最大池化单元。每个单元都是独立的,可以同时处理多条指令。

国外大学生都用FPGA做什么项目(二十)图1
系统架构概览

鉴于矩阵涉及大量数据,计算架构设计为 SIMD(单指令多数据流)。这种方法允许使用单条指令同时处理多个数据点,因此对于涉及大型矩阵的运算非常高效。

国外大学生都用FPGA做什么项目(二十)图2
SIMD框图

指令被发送到片上SRAM,该SRAM配置为指令RAM。处理器的取指阶段主要从片上SRAM获取指令。处理器的译码阶段为源指令和目标指令分配内存地址。使用12个SRAM作为数据存储器,每个SRAM的数据宽度为16位,内存容量为32,768字节。每个SRAM能够存储一个128 x 128像素的图像,每个像素为16位。这些SRAM将像一个分片存储子系统一样工作,并支持乱序执行。

设计中共有 9 个功能单元,每个功能单元都可以按照说明访问所有 SRAM。这得益于复杂的路由网络。为此,我们使用了多路复用器(用于连接 SRAM 和功能单元之间的信号)。发出阶段会分配源地址和目标地址,并为相应的功能模块提供起始信号。这些起始地址由解码阶段传递而来。

使用乱序提交,因为假设指令之间没有依赖关系。如果处理依赖关系,控制逻辑可能会变得很复杂。构建重排序缓冲区可以解决这个问题。提交缓冲区用于处理两条指令同时提交的情况。由于存在提交缓冲区,我们的处理器能够同时处理多个提交操作。但在写回阶段,我们使用轮询仲裁机制将完成信号写入SRAM,以便ARM知道指令已完全执行完毕。我们有一个完成SRAM,并根据函数完成情况以乱序的方式向其写入1。

所有功能单元都采用标准接口(包括所有必需的信号,如源地址、目标地址、行和列参数等),以便与处理器设计轻松兼容,并允许我们始终根据要求将任何新的功能单元连接到处理器。

SRAM 的运行频率为 250 MHz,因此在功能单元中,我们可以将其视为组合逻辑单元。此外,需要注意的是,由于功能单元彼此完全独立,它们能够同时对片上 SRAM 进行读取、必要的计算和写回操作。

使用 PyTorch 进行所有模型的训练,并使用 MNIST 数据集进行训练。训练后,5 个 epoch 的准确率达到了 96%。我们可以从 PyTorch 获取图像,并让它完成所有必要的归一化和预处理操作。

为了在NPU上进行推理,实现了一个C程序,其中包括一个编译器和一个驱动程序,它可以将所需的操作编译成指令并发送给NPU进行推理。该程序可以读取由PyTorch训练的权重,将权重写入NPU,然后由NPU执行计算。最后,C程序可以从NPU读取计算结果。需要注意的是,在指令执行期间,在前一条指令执行完毕之前,其他指令不应使用同一块SRAM。

背景知识

浮点数:使用了 FP16 浮点数,而不是 FP32。选择浮点数而不是定点数,是因为浮点数精度更高。16 位浮点数被称为半精度浮点数。FP16 包含一个符号位(1 位),用于确定数字的符号(0 表示正数,1 表示负数)。指数位(5 位),用于存储数字的指数。尾数位(10 位),也称为有效数字或小数,用于存储数字的有效数字。FP16 是一种二进制浮点数格式,在计算机内存中占用 16 位。指数位存储时带有 15 的偏移量,实际的指数值是通过从存储的指数值中减去 15 得到的。指数的范围从 -14 到 +15。特殊情况的表示如下:零,表示所有位都设置为零。无穷大用指数 31(所有位都设置为 1)和尾数 0 表示。NaN(非数字)用指数 31 和非零尾数表示。精度由尾数的位数决定。在本例中,FP16 的精度约为 3 位十进制有效数字。

国外大学生都用FPGA做什么项目(二十)图3
浮点格式

卷积:卷积运算是指将一个卷积核应用于输入矩阵以生成输出矩阵的数学过程。该运算有助于检测局部模式。输入矩阵代表待处理的图像或数据。我们考虑使用方阵进行卷积运算。卷积核是一个较小的矩阵,它在输入矩阵上滑动。卷积核与输入矩阵的重叠区域进行逐元素相乘,并将结果相加得到输出矩阵中的一个值。输出矩阵是卷积运算的结果,其大小取决于输入矩阵的大小和卷积核的大小。卷积核最初位于输入矩阵的左上角。进行逐元素相乘后,将结果放置在输出矩阵的相应位置。卷积核在输入矩阵上移动,每次移动的像素数称为步长。卷积运算针对每个新位置执行。一旦整个输入矩阵都被卷积核覆盖,该过程即停止。

国外大学生都用FPGA做什么项目(二十)图4
卷积运算

ReLU: ReLU(修正线性单元)激活函数因其简单有效,能够为模型引入非线性,而被广泛应用于神经网络中。ReLU 的主要目的是使神经网络能够通过逼近非线性函数来学习复杂的模式。从数学角度来看,ReLU 定义为 f(x)=max(0,x),这意味着如果输入为正,则直接输出该输入;否则,输出为零。这种非线性变换有助于解决梯度消失问题。梯度消失问题是指深度网络中用于更新权重的梯度变得非常小,从而阻碍学习过程。ReLU 通过为正输入提供恒定的梯度来缓解这个问题,从而促进高效快速的训练。此外,通过将负值设置为零,ReLU 为网络引入了稀疏性,这可以提高计算效率,并通过降低网络对输入数据微小变化的敏感性来减少过拟合的风险。

国外大学生都用FPGA做什么项目(二十)图5
ReLU 操作
国外大学生都用FPGA做什么项目(二十)图6
ReLU 操作

最大池化:最大池化是一种常用的下采样操作,常用于卷积神经网络(CNN)中,用于降低输入矩阵的空间维度,同时保留最重要的特征。最大池化的主要目的是降低网络的计算复杂度、提高效率并提供空间变异性。在最大池化中,一个特定大小的滤波器(或窗口)在输入矩阵上滑动,并在每个位置输出该窗口内的最大值。此操作通过概括最显著的特征有效地压缩输入,有助于降低过拟合的风险。通过关注最强的响应,最大池化还提供了一种平移不变性,确保输入中的微小偏移或平移不会显著影响池化后的输出。这一特性对于图像识别等任务至关重要,因为不同图像中特征的确切位置可能略有不同。

国外大学生都用FPGA做什么项目(二十)图7
最大池化操作

全连接层:全连接层通过整合先前层学习到的特征来形成最终输出,发挥着至关重要的作用。全连接层中的每个神经元都与前一层的所有神经元相连。该层本质上接收一个输入数据,并输出一个 N 维向量,其中 N 是程序试图分类的类别数量。例如,在人脸识别场景中,该输出向量可以表示模型训练识别的不同个体的面孔。全连接层通过学习到的权重将提取的特征映射到最终的输出类别,这使其成为卷积神经网络 (CNN) 中模式识别任务的关键所在。

国外大学生都用FPGA做什么项目(二十)图8
完全互联运营

处理器架构:超标量处理器的工作原理是每个时钟周期执行多条指令,这需要掌握指令级并行性以及如何利用多个单元的知识。乱序执行技术允许指令在其操作数可用时立即执行,而不是按照程序顺序执行。

权衡取舍

CPU 与 NPU: CPU 提供通用计算功能,能够处理更广泛的任务,而 NPU 则为特定功能单元提供专门的高速计算任务。在本系统中,NPU 负责矩阵计算,而 CPU 负责控制。

性能与精度:使用 16 位浮点精度可以在计算速度和准确性之间取得平衡,但与 32 位浮点相比,精度有所降低。

复杂性与时钟速度:处理器核心运行频率为 100 MHz,功能单元运行频率为 25 MHz,这会增加时钟域同步的复杂性,但可以实现更高的整体吞吐量。

内存带宽与复杂性:分块 SRAM 架构允许并行矩阵运算所需的高内存带宽,但会增加内存管理和布线的复杂性。

软件设计

128 位指令集设计

128 位指令集架构旨在处理 NPU 处理器支持的各种矩阵运算,每条指令都以结构化格式编码。该架构包括加法 (ADD)、卷积 (CONV、CONV2、CONV3)、池化 (POOL)、激活函数 (RELU)、减法 (SUB)、逐元素乘法 (MULT) 和矩阵乘法 (DOT) 指令。

对于大多数指令,字段包括操作码、源数组地址、目标数组地址和数组大小。这种一致的格式确保指令易于解码和执行,从而提高处理效率。

然而,DOT 指令的设计与其他指令不同,以满足矩阵乘法的特定需求。与其他指令不同,DOT 指令的低 28 位采用不同的编码方式。这是因为在矩阵点积运算中,第一个矩阵的列数等于第二个矩阵的行数,因此只需要三个参数。这种设计使得指令集能够更有效地利用有限的位空间,从而支持更大的矩阵。

指令集如下表所示:

国外大学生都用FPGA做什么项目(二十)图9

编译器设计

实现了一个简单的 C 编译器函数,该函数可以根据用户输入生成 128 位指令。该函数接受操作码、源地址 1 和 2、目标地址以及矩阵的行和列信息作为输入,并返回一条 128 位指令。

在代码中,定义了一个名为“uint128_t”的结构体,它包含两个64位无符号长整型变量“low”和“high”,分别表示128位指令的低64位和高64位。函数“gen_inst”初始化一个名为“result”的“uint128_t”变量,用于存储生成的指令。

在指令生成过程中,该函数使用 strcmp 函数比较输入的操作码 op,并将相应的二进制编码写入 result.high 的高 4 位。例如,如果操作码为“ADD”,则将 0x1ULL 左移 60 位,并通过按位或运算将其存储在 result.high 中。其他操作码,例如“CONV”和“POOL”,也以类似方式处理。源地址 1 和 2 以及目标地址的信息通过按位运算写入 result.high 和 result.low。此外,矩阵的行和列信息也写入 result.low 的不同位置。需要注意的是,“DOT”操作码的处理方式较为特殊,因为它的指令格式与其他操作码略有不同。最后,该函数将指令存储在指向指令 SRAM 的指令指针 inst_ptr 中。

驱动设计

在这个系统中,运行在 ARM CPU 上的 C 驱动程序用于控制 NPU 的运行。该驱动程序可以通过寻址访问安装在 AXI 总线上的 SRAM。NPU 可以对这些 SRAM 进行读写操作,从而实现 ARM 和 NPU 之间的通信。

在 C 驱动程序中,使用 mmap 获取指向物理 SRAM 的虚拟地址,从而可以通过 Linux 管理访问 SRAM。SRAM 的地址在 Qsys 中配置。此处必须使用相同的地址进行内存访问。C 驱动程序可以向“指令 SRAM”写入指令,从“数据 SRAM”读取和写入数据,并读取“指令完成 SRAM”以检查指令是否已完成。

需要注意的是,数据SRAM是16位的,而内存寻址是面向字节的。因此,当通过指针访问内存时,指针变量加一意味着内存地址指向前方两个字节的位置。

PyTorch中的CNN训练

神经网络架构

使用 PyTorch 实现了一个简单的卷积神经网络。该网络只有一个卷积层。该网络的架构如下:

国外大学生都用FPGA做什么项目(二十)图10
CNN架构

  1. 卷积层:输入通道数为 1,输出通道数为 3,卷积核大小为 3x3。

    1. ReLU 激活。

    1. 池化层:最大池化层。池化核大小为 2*2。步长为 2。

    1. 展平层:用于将多维输入展平为一维向量。

    1. 全连接层:输入尺寸为 13x13x3,输出尺寸为 10。

    在前向传播过程中,输入首先经过卷积层,然后经过 ReLU 激活函数,再经过最大池化层。接下来,数据被展平并经过全连接层,最终产生输出。

    神经网络训练

    使用的数据集是 MNIST 手写数字数据集,预处理步骤如下:


    1. 转换为张量 (ToTensor):将图像数据转换为 PyTorch 张量。

    1. 归一化(Normalize):将图像数据归一化,使其均值为 0.5,标准差为 0.5。

    预处理后,将数据集加载到训练和测试数据加载器中,批次大小为 64。

    该模型使用交叉熵损失函数和 Adam 优化器进行训练。模型训练 5 个周期,每个周期对训练数据集进行一次迭代。

    训练完成后,在测试集上对模型进行评估,计算准确率并输出结果。该模型经过 5 个 epache 的训练后,准确率达到 96%。

    国外大学生都用FPGA做什么项目(二十)图11
    CNN准确率

    训练好的权重存储在 csv 文件中,可用于使用 NPU 进行推理。

    基于 NPU 的推理

    为了在NPU上进行推理,考虑到归一化的复杂性,首先在PyTorch中对MNIST图像数据进行归一化,并将其输出到CSV文件。归一化后的数据可以直接供NPU使用。或者,也可以在C程序中实现归一化函数,这样就可以将原始图像数据直接读入C程序进行归一化,然后再传递给NPU进行推理。

    为了在NPU上进行推理,首先需要从CSV文件中读取数据。这些数据包括PyTorch训练好的权重参数和待推理的图像数据。读取数据后,必须将其写入NPU的SRAM。AXI总线接口用于高效地处理ARM CPU和SRAM之间的数据传输。

    写入数据后,可以调用编译器函数生成相应的指令。由于我们在 PyTorch 中训练的是单层网络,因此在推理过程中需要控制 NPU 执行相同的计算。NPU 包含三个卷积模块,允许同时运行三个卷积通道。卷积运算完成后,三个通道的结果会进行 ReLU 激活。ReLU 激活之后,进行池化操作,然后执行一个全连接层。最后,C 程序从 SRAM 中读取全连接层输出的矩阵数据。值最高的条目即为推理结果。

    硬件设计

    硬件架构

    国外大学生都用FPGA做什么项目(二十)图12
    硬件架构

    在系统中,硬件主要由三个组件构成:ARM CPU、FPGA 上的 NPU 以及用于 ARM 和 FPGA 之间通信的 SRAM。ARM CPU 主要负责发送指令和读写 SRAM,而 FPGA 上的 NPU 则负责处理计算。SRAM 作为指令和数据的存储介质,用于支持 ARM CPU 和 NPU 之间的通信。

    处理器架构

    国外大学生都用FPGA做什么项目(二十)图13
    处理器流水线

    该处理器是一款超标量乱序处理器,支持 128 位超长指令字。它包含九个功能单元,每个单元都设计用于处理特定类型的指令。这些单元能够执行卷积、池化、ReLU 激活以及常见的矩阵运算等操作,从而支持广泛的计算任务。该处理器的架构采用六级流水线:取指、译码、指令发出、执行、提交和写回。如上图所示。

    在取指阶段,处理器使用单次按序取指机制从内存中检索指令,并检查每条指令的有效性。有效指令会使指令指针递增。然后,指令被流水线传输到译码阶段,在那里根据指令集架构的规范进行译码。译码过程生成控制信号,这些信号管理功能单元的运行以及多路复用器网络,从而实现单元与内存之间的通信。

    在发出阶段,控制信号被发送到各个功能单元。控制信号包括:矩阵的起始地址、矩阵的大小、多路复用器选择信号(由路由网络使用)和起始信号。在执行阶段,功能单元在接收到“起始”信号后开始计算。计算完成后,每个单元发送一个“完成”信号,该信号以乱序方式返回。提交缓冲区用于处理同一周期内出现多个完成信号的情况。提交缓冲区可以成功捕获提交信息。在写回阶段,提交信息被写回SRAM。

    在回写阶段,指令的执行结果会被写入“指令完成SRAM”。处理器会使用轮询策略持续检查提交缓冲区。如果检测到需要回写指令,处理器会将指令完成信息写入“指令完成SRAM”。同时,ARM CPU 会不断轮询“指令完成SRAM”。如果发现某条指令已完成,ARM 处理器就可以使用 SRAM 中的结果进行后续操作。

    需要注意的是,该处理器运行频率为 100 MHz。然而,由于使用了浮点运算,所有功能单元的运行频率均为 25 MHz,因此起始信号和完成信号都需要跨越时钟域。例如,在指令发出阶段,“起始”信号的频率从 100 MHz 降至 50 MHz。该处理器采用握手机制来管理起始信号和完成信号的时钟域跨越。起始信号保持高电平,直到完成信号变为低电平,此时起始信号才能恢复为低电平。

    系统中连接功能单元和SRAM的路由网络由多路复用器组成。地址、写入数据和写入使能信号均由功能单元输出并由SRAM接收。因此,这些信号使用同一种多路复用器。在本处理器中,我们使用25对1多路复用器来处理这些信号。然而,从SRAM到功能单元的读取数据流需要不同类型的多路复用器。对于这种数据流,我们在本处理器中使用16对1多路复用器。

    内存系统

    鉴于该处理器是 SIMD(单指令多数据流)处理器,这意味着一条指令可以处理多条数据,因此其内存设计相当独特。为了处理矩阵数据,每个功能单元都必须能够访问内存。然而,在 Altera 的片上 SRAM 中,一个 SRAM 最多只能有两个端口,其中一个端口必须连接到 ARM 的 AXI 总线。如果只使用一个 SRAM,那么在一个时钟周期内只能进行一次 SRAM 访问,这将不可避免地导致严重的性能损失,并阻碍处理器进行并行处理。

    因此,需要将SRAM分割成总共12个块,每个SRAM块能够存储128x128个16位数据元素。这种分割方式增加了SRAM端口的数量,允许最多12个并发内存访问,从而显著提升性能。

    所有 SRAM 都安装在 AXI 总线上,并且地址连续。这种设置简化了通过 C 程序对内存的访问。此外,SRAM 的工作频率为 250 MHz。因此,处理器和功能单元可以将内存视为组合逻辑内存,从而大大简化设计。Qsys 设计如下图所示。

    除了上文提到的数据SRAM之外,Qsys系统中还包含“指令SRAM”和“指令完成SRAM”。“指令SRAM”用于存储ARM CPU发送的指令,数据宽度为128位,深度为512。“指令完成SRAM”用于指示指令已完全处理完成,数据宽度为8位,深度为512。这种内存功能的分离优化了系统内数据和指令的流动与管理。

    国外大学生都用FPGA做什么项目(二十)图14
    Qsys设计
    国外大学生都用FPGA做什么项目(二十)图15
    Qsys设计

    半精度浮点模块

    使用 IEEE 754 半精度浮点格式 (fp16) 进行算术运算,该格式使用 16 位:1 位符号位、5 位指数位和 10 位小数位,如下图所示。利用 fp16 的算术规则可以减少内存使用和计算开销,这在资源受限的环境中至关重要。虽然 fp16 的精度低于 fp32,但它在精度和效率之间取得了平衡。

    国外大学生都用FPGA做什么项目(二十)图16
    FP16

    参考了一些开源的浮点模块实现。复用了参考代码中的一些代码,并实现了半精度浮点运算的硬件模块,包括加法、乘法和比较运算。

    加法:在半精度浮点加法中,两个数首先根据它们的指数进行对齐。指数较小的数向右移动一位,使其与指数较大的数的指数相同。然后将它们的小数部分相加。加法运算完成后,结果会被归一化,以满足 IEEE754 的要求。在此过程中会考虑溢出和下溢的情况,并对结果超出可表示范围的情况进行特殊处理。

    乘法:对于半精度浮点数的乘法运算,首先将两个数的指数相加。然后判断结果指数是否存在溢出或下溢。之后,将小数部分相乘,并将结果四舍五入并归一化,使其符合 10 位小数表示的限制。

    比较:在对两个 FP16 浮点数进行二进制比较时,首先比较符号位。正数大于负数。对于符号相同的数,接下来比较指数。指数越大,数值越大。如果指数相同,则继续比较尾数,尾数较大的数即为较大数。

    卷积模块

    卷积Verilog模块旨在执行卷积运算。该模块通过与外部SRAM接口来获取输入数据并存储输出结果。它处理三个内存接口:一个用于输入矩阵,一个用于卷积核,第三个用于输出矩阵。这些接口通过指定的地址和数据来管理各自的读写操作。时钟、复位、启动等控制信号控制着运算流程。输入矩阵和卷积核的大小经过设定,以支持不同的卷积规模。基本上对所有功能单元使用了相同的接口,这样更容易将所有功能单元集成到处理器中。

    国外大学生都用FPGA做什么项目(二十)图17
    卷积模块状态机

    该模块内部,一个状态机通过六个状态控制卷积过程,如上图所示。初始状态下IDLE,模块处于等待模式,直到start信号被激活。信号激活后,模块转换到读取READ_KERNEL状态,从SRAM读取内核数据并将其存储到内部寄存器中。内核完全加载后,模块进入执行CALC状态。

    在CALC卷积模块的当前状态下,卷积计算通过将输入矩阵中的数据与卷积核值相乘并累加结果来完成,同时调整输入矩阵中的寻址以使其与卷积核尺寸对齐。根据计算进度,模块可以继续CALC处理其他矩阵段,或者转换到其他状态WRITE将结果存储到 SRAM 输出矩阵中。如果需要处理更多段,它会转换到其他状态SLIDE以调整卷积窗口在输入矩阵上的起始位置,从而实现连续运算。该过程最终在DONE模块通过done指示器发出完成信号、重置内部寄存器并返回到其他IDLE状态以准备新操作周期的状态结束。该状态机确保卷积运算高效进行,并实现精确的数据处理和系统化的存储。

    ReLU模块

    ReLU(修正线性单元)函数常用于神经网络中以引入非线性,它将任何负输入值设置为零,同时保持正值不变。

    国外大学生都用FPGA做什么项目(二十)图18
    ReLU 模块状态机

    功能单元中使用的 ReLU 激活函数采用有限状态机 (FSM) 实现。ReLU 函数是神经网络中常用的非线性激活函数,它将负输入值置零,而保留正值。FSM 初始处于空闲状态(状态 2),等待启动信号。接收到启动信号后,FSM 进入初始化状态(状态 0),设置输入矩阵的读取地址和输出矩阵的写入地址,并将行和列的内部计数器重置为零。随后,FSM 进入处理状态(状态 1),遍历输入矩阵的每个元素。对于每个元素,模块从输入矩阵(源)读取数据,检查其值是否小于零,如果小于零,则将对应的输出值(目标)置零;否则,保留输入值。处理后的值被写入输出矩阵的当前目标地址,并置位写入使能信号以执行写入操作。有限状态机(FSM)通过更新地址和计数器来遍历整个矩阵,从而继续执行此过程。它递增行计数器,当到达行尾时,重置行计数器并递增列计数器。此循环持续进行,直到处理完整个矩阵。一旦所有元素都被处理完毕,FSM 便转换回空闲状态(状态 2),并设置“完成”信号以指示 ReLU 操作已完成。FSM 保持此空闲状态,直到接收到新的开始信号,此时它将重复上述过程。这种系统化的方法确保输入矩阵的每个元素都被评估并应用 ReLU 函数,从而生成一个输出矩阵,其中所有负值都被替换为零,而正值保持不变。

    池化模块

    最大池化是卷积神经网络中常用的一种操作,用于减少输入矩阵的空间维度(宽度和高度),同时保留最关键的信息。

    国外大学生都用FPGA做什么项目(二十)图19
    最大池化模块状态机

    功能单元中使用的最大池化逻辑采用有限状态机(FSM)实现,它系统地处理输入矩阵的各个块,以确定每个池化窗口内的最大值,并将该值写入输出矩阵的相应位置。该过程从空闲状态(状态 3)开始,模块在此状态等待“开始”信号。一旦收到“开始”信号,FSM 便转换到初始化状态(状态 0),设置从输入矩阵读取数据和写入输出矩阵的初始地址,并将 max_pool 变量的值重置为零。然后,FSM 进入处理状态(状态 1),在此状态中,它遍历当前池化窗口的元素。在此迭代过程中,每当遇到大于当前 max_pool 值的值时,模块都会更新 max_pool 变量。这包括递增行计数器和列计数器,调整读取下一个元素的地址,并将读取的数据与当前的 max_pool 值进行比较。一旦整个池化窗口被处理完毕,FSM 便转换到写入状态(状态 2)。在此状态下,模块将 max_pool 值写入输出矩阵,更新下一次写入操作的目标地址,并重置行计数器和列计数器。如果所有块都已处理完毕(由计数器变量 'val' 达到池化窗口总数表示),则有限状态机 (FSM) 转换回空闲状态(状态 3),并将 'done' 信号设置为 1,表示最大池化操作已完成。否则,它返回处理状态(状态 1)以继续处理下一个块。该逻辑确保模块从输入矩阵读取数据,计算池化窗口内的最大值,并将该值迭代地写入输出矩阵,直到处理完整个输入矩阵。

    矩阵乘法模块

    该模块使用与卷积模块相同的接口。但矩阵乘法要求第一个矩阵的列数与第二个矩阵的行数相同。因此,该模块只需配置三个输入:第一个矩阵的行数、第一个矩阵的列数(应与第二个矩阵的行数相同)以及第二个矩阵的列数。这三个参数确保矩阵能够正确相乘。

    该 Verilog 模块实现了一个用于矩阵乘法的状态机,包含四个主要状态:空闲 (IDLE)、计算 (CALC)、写入 (WRITE) 和完成 (DONE)。在空闲状态下,模块等待起始信号激活,此时所有地址和计数器都会被初始化,并且完成信号会被设置为 1,表示当前没有正在进行的计算。接收到起始信号后,模块会转换到计算状态,并开始增量式地执行矩阵乘法。这里使用内部累加器来计算结果。每完成一行的计算后,状态机会进入写入状态,并将结果写入目标地址。在写入状态下,地址指针会被更新,以便开始处理下一行,为下一个计算周期做好准备。如果所有数据都已处理完毕,状态机将进入完成状态,此时所有计数器和地址都会被重置,并且完成信号会被设置为 1,表示计算已完成。然后,状态机返回到空闲状态,等待下一个计算命令。整个过程通过精确的状态转换进行精心控制,以确保高效、准确地执行矩阵乘法,使其适用于高效矩阵运算的硬件级实现。

    国外大学生都用FPGA做什么项目(二十)图20
    矩阵乘法模块状态机

    在神经网络中,矩阵乘法是执行全连接层运算的基础。在卷积神经网络(CNN)模型中,权重矩阵为 507×10,输入向量(展平后)为 507×1。当这两个矩阵相乘时,得到的矩阵为 10×1。这是因为结果向量的每个元素都是权重矩阵和输入向量中对应元素乘积之和。每个和代表网络中单个神经元的总加权输入。这种乘法有效地将所有输入与其各自的权重结合起来,这正是全连接神经网络层所需的计算,其中每个输入都通过这些权重连接到每个输出。矩阵乘法是处理全连接层内计算的高效数学运算。

    矩阵元素运算模块

    矩阵元素运算模块包括矩阵加法、矩阵减法和矩阵逐元素乘法。对于矩阵元素运算,两个输入矩阵的大小必须相同。因此,这些模块可以使用相同的接口和相同的状态机。这些模块之间的唯一区别在于它们在计算时使用的运算模块不同。

    让我们以矩阵加法为例来解释状态机,如下图所示。

    国外大学生都用FPGA做什么项目(二十)图21
    矩阵加法模块状态机

    初始状态下,模块处于空闲状态(IDLE),等待启动信号。接收到启动信号后,模块转换到设置状态(SETUP),初始化源矩阵和目标矩阵的地址。此设置过程为逐元素加法的核心操作做好准备。设置完成后,机器进入计算与写入状态(CALC&WRITE),这是执行矩阵加法的主要状态。在此状态下,机器迭代处理矩阵的每个元素,并更新相应的地址和计数器。floatAdd 子模块计算源矩阵中对应元素的和,然后将结果写入目标矩阵。此状态会持续检查当前行的所有元素是否已处理完毕,然后再移动到下一行,以确保矩阵的每个部分都得到准确处理。整个矩阵处理完毕后,模块会转换回空闲状态(IDLE)。在此状态下,机器重置所有参数并等待另一个启动信号,表示已准备好执行新的操作。

    结果

    测试结果

    首先需要测试每个功能单元和浮点模块。为此,编写了一个单元测试平台来测试所有模块。测试结果如下。

    以下是FP16加法和乘法模块的测试结果。

    国外大学生都用FPGA做什么项目(二十)图22
    FP16 添加波形
    国外大学生都用FPGA做什么项目(二十)图23
    FP16 乘法波形

    这是卷积模块的测试结果,输入矩阵大小为 2828,卷积核大小为 33。

    国外大学生都用FPGA做什么项目(二十)图24
    卷积波形

    这是 ReLU 模块的测试结果,输入矩阵大小为 26*26。

    国外大学生都用FPGA做什么项目(二十)图25
    ReLU波形

    这是最大池化模块的测试结果,输入矩阵大小为 2626,内核大小为 22。

    国外大学生都用FPGA做什么项目(二十)图26
    最大池化波形

    这是矩阵乘法模块的测试结果,输入矩阵为 50710 和 5071,输出大小为 10*1。

    国外大学生都用FPGA做什么项目(二十)图27
    矩阵乘法(全连接)波形

    这是处理器的测试结果。从波形图中可以看出,三个功能单元(卷积、ReLU 和点运算)正在并行运行。这证明超标量乱序处理器工作正常。

    国外大学生都用FPGA做什么项目(二十)图28
    处理器中的并行计算

    以下是使用 NPU 进行 CNN 推理的结果。上面的矩阵输出是经 PyTorch 归一化并由 C 程序读取的 MNIST 图像数据,可以识别为数字 7。下面的输出显示了推理结果,具体来说是全连接层的输出。结果表明,位置 7 的数据最大,这表明推理结果为数字 7。

    国外大学生都用FPGA做什么项目(二十)图29
    使用NPU进行推理

    执行速度

    搭建了测试平台,并使用与训练好的神经网络相同规模的网络进行了测试。根据上图所示的测试平台波形,计算时间如下表所示。实际硬件的时钟频率为25MHz。

    国外大学生都用FPGA做什么项目(二十)图30

    准确率

    经过 5 个训练周期后,训练后的 CNN 模型准确率达到 96.6%。

    国外大学生都用FPGA做什么项目(二十)图31
    CNN准确率

    可用性

    整个系统非常易于使用,只需具备神经网络的基本知识即可。用户只需调用 C 程序中的函数即可生成必要的指令,无需了解算法实现的具体细节。通过调用这些函数生成相应的指令,即可利用 NPU 加速计算。

    结论

    这款NPU的开发取得了良好成效,符合设计预期。通过集成专用的矩阵运算、卷积和激活函数功能单元,该NPU能够高效地执行CNN模型,并拥有极高的计算速度。其架构的乱序处理能力,结合强大的内存子系统和完善的指令集,确保了最佳的性能和灵活性。尽管在浮点运算方面遇到了一些初期挑战,但该NPU展现出了令人瞩目的成果,在MNIST数据集上取得了很高的准确率。

    项目链接

    https://people.ece.cornell.edu/land/courses/ece5760/FinalProjects/s2024/yg585_kg534_sj778/yg585_kg534_sj778/yg585_kg534_sj778.html

    代码链接

    https://github.com/YqGe585/Neural-Processing-Unit-on-FPGA.git

    视频链接

    https://www.youtube.com/watch?v=esBwo9wiOaI

    扮演上帝——供求多智能体模拟

    无代码,有兴趣可自行研读。

    核心架构:制作了一个供求模拟器。商品价格上涨时,需求下降,卖家更愿意出售;价格下跌时,需求上升,卖家更不愿意出售。这个项目的目标是模拟一个由理性参与者组成的经济体,并观察供求原理的实际运作。

    项目链接

    https://people.ece.cornell.edu/land/courses/ece5760/FinalProjects/s2024/yc938_ljk74_ir93/web_submit/website.html

    代码链接

    视频链接

    https://youtu.be/JeDBy0oQDiI?si=mhNgSB9Aif34fHoX

    基于FPGA的CNN在DE1-SoC上的数字识别


    引言

    通过在 Verilog 中定制硬件实现卷积神经网络 (CNN),可以实现手写数字识别,从而提高实时应用的效率和速度。

    在这个项目中,致力于利用卷积神经网络(CNN)的强大功能来加速手写数字识别,并在FPGA中实现整个架构。

    首先使用 Verilog 编写网络的每一层代码,并在 ModelSim 中进行严格测试。在确保时序正确后,使用 Python 脚本验证了其功能。然后,将这些层集成到一个统一的系统中,并构建了一个数据流机制,使用户图像能够通过所有这些层最终输出。此外,使用 PyTorch 在 MNIST 数据集上训练了一个 CNN 模型,并提取了卷积层和全连接层的权重。这些权重被加载到我们的硬件系统中,使其能够执行识别任务。使用 MNIST 数据集中的数字和真实的手写样本对系统的准确性进行了广泛的测试。

    通过将计算从软件转移到专用硬件,旨在显著提高性能和效率,从而实现快速准确的数字识别。

    高层设计

    背景数学

    本节将讨论卷积神经网络(CNN)架构背后的基本数学概念。这些概念包括卷积、池化、ReLU激活函数和全连接层。

    a. 卷积

    卷积是一种用于从输入图像中提取特征的数学运算。它涉及将一个滤波器(或卷积核)在输入图像上滑动,并执行逐元素乘法和求和。结果是一个特征图,它突出显示了图像中特定模式的存在。从数学角度来看,如果 𝐼 是输入图像,𝐾 是卷积核,则在位置 (𝑖, 𝑗) 处的卷积运算 𝐼 ∗ 𝐾 可表示为:

    国外大学生都用FPGA做什么项目(二十)图32

    b. 合并

    池化是一种下采样操作,它能在保留重要特征的同时降低特征图的维度。最常见的池化类型是最大池化,它从特征图的每个区域中选取最大值。这种操作可以降低计算复杂度,并有助于实现空间不变性。例如,对特征图进行 2x2 最大池化操作,会从每个 2x2 区域中选取最大值。

    c. ReLU激活

    ReLU(修正线性单元)是一种用于在模型中引入非线性的激活函数。它的定义如下:

    国外大学生都用FPGA做什么项目(二十)图33

    该函数将特征图中的所有负值置零,从而使网络能够学习复杂的模式。ReLU 激活函数可以加速收敛并缓解梯度消失问题。

    d. 全连接层

    全连接层 (FC) 用于 CNN 架构的末端,以执行分类任务。FC 层中的每个神经元都与前一层中的每个神经元相连。前几层的输出被展平为一个向量,并乘以一个权重矩阵,然后加上一个偏置项。这个过程可以用数学公式描述:

    国外大学生都用FPGA做什么项目(二十)图34

    其中,𝑥 为输入向量,𝑊 为权重矩阵,𝑏 为偏置向量,𝑦 为输出向量。最后一层使用 softmax 函数将输出转换为每个类别的概率。

    e. 数学表示

    全连接层的输出,记为𝑓𝑐,可以表示如下:

    国外大学生都用FPGA做什么项目(二十)图35

    其中 𝑓𝑐 是全连接层的输出,mp是最大池化层的输出,10 × 507 矩阵是权重矩阵。

    这些数学原理构成了我们用于手写数字识别硬件实现的卷积神经网络(CNN)的基础。通过理解这些概念,我们可以有效地设计和优化网络的每一层,从而实现高精度和高性能。

    逻辑结构

    针对此多分类任务提出了一种卷积神经网络(CNN)架构,该架构包含卷积层、ReLU激活函数、最大池化层和全连接层。如图1所示,输入图像为28×28像素的8位灰度图像。由于输入图像只有一个通道,我们在卷积层中使用了三个3×3×1的卷积核。卷积层没有填充,步长为1。卷积层的输出是一个26×26×3的特征图,该特征图随后被送入ReLU激活函数。最大池化层的卷积核大小为2×2,步长为2,也没有填充。最大池化后,得到的特征图尺寸为13×13×3。在全连接层中,13×13×3的特征图首先被展平,然后与相应的权重相乘,得到最终的输出。

    国外大学生都用FPGA做什么项目(二十)图36
    图 1:本项目 CNN 结构

    在硬件设计(图 2)中,集成了三个神经网络模块和三个 SRAM 单元。SRAM 单元对于数据交换、存储和传输至关重要。SRAM1 存储图像数据,SRAM2 存储卷积层和全连接层的权重数据。SRAM3 存储卷积结果,作为最大池化层的输入。最大池化层的输出随后存储到 SRAM1 中,并作为全连接层的输入。最终结果存储在 SRAM3 中。由于 SRAM1 和 SRAM3 被双重使用,并且神经网络模块需要同时进行读写操作,因此必须采用多路复用技术。由于 SRAM2 存储两个层的权重,因此也需要多路复用才能供两个模块同时访问。

    国外大学生都用FPGA做什么项目(二十)图37
    图 2:各层之间的数据流

    程序/硬件设计

    硬件设计

    a. 卷积层

    在卷积层设计中,每个卷积单元使用一个乘法器,这是执行卷积运算所需的最小组件。如图 3 所示,完成一次卷积运算至少需要九个时钟周期。该过程从卷积核顶部对齐的像素开始。在每个后续周期中,运算移动到右侧的下一个像素,执行乘法运算,并将结果与前一个值累加。此过程重复进行,直到计算完成并输出累加值。状态机控制此过程(图 4)。下表显示了每个状态下的操作,图 3 中的数字表示每个像素的当前状态。考虑到空闲状态和数据存储,完成一次卷积计算需要 14 个时钟周期,因为每个结果都必须单独存储在 SRAM 中。

    表 1:卷积单元状态机的详细信息
    国外大学生都用FPGA做什么项目(二十)图38
    国外大学生都用FPGA做什么项目(二十)图39
    图 3:卷积单元
    国外大学生都用FPGA做什么项目(二十)图40
    图 4:卷积单元的状态机传输

    在卷积层中,从图像的上边缘开始执行卷积运算。完成一个卷积单元后,卷积核移动到图像中的下一个 3x3 区域执行下一次运算。这个过程重复进行,直到卷积核到达图像的右下角。此时,卷积层的输出被存储在指定的 SRAM 中,如图 6 所示。为此过程设置了三个并行通道。状态机控制卷积层内的操作。图 5 展示了状态机的转换过程,表 2 详细列出了每个状态下执行的操作。

    国外大学生都用FPGA做什么项目(二十)图41
    图 5:卷积层的状态机传输
    国外大学生都用FPGA做什么项目(二十)图42
    图 6:卷积层的算术过程
    表 2:卷积层状态机的详细信息
    国外大学生都用FPGA做什么项目(二十)图43

    b. 最大池化层

    在最大池化层模块设计中,镜像了卷积层的控制流程。然而,由于最大化操作所需的逻辑复杂度较低,其内部状态机更为简洁。在空闲状态(IDLE)下,模块等待开始信号,该信号同时也是来自卷积层的结束信号。接收到该信号后,模块转换到计算状态(CALC)。在该状态下,模块遍历输入矩阵,找到每个2x2子区域中的最大值。随后,模块转换到写入状态(WRITE),将最大值存储到指定的SRAM地址。最后,模块到达完成状态(DONE),准备处理下一个子区域。

    c. 全连接层

    全连接层将最大池化层的 507 个输出转换为 10 个输出值。其中最大的输出值即为识别出的数字。由于权重矩阵的大小为 507×507,无法一次性将其上传到 SRAM。解决方案是上传矩阵并计算两次输出。最终 CNN 识别出的数字将显示在数字显示屏上。

    全连接层包含 5 个过程:


    1. 将第一个上半部分权重矩阵上传到 SRAM

    1. 计算前 5 个结果

    1. 指示HPS将剩余的全连接层矩阵上传到SRAM。

    1. 计算其余 5 个结果

    1. 找出输出识别数字中的最大值
    国外大学生都用FPGA做什么项目(二十)图44
    图 7:全连接层的计算流程

    软件设计

    HPS设计的主要问题在于如何在不超出SRAM存储容量的情况下,将图像、卷积权重矩阵和全连接权重矩阵加载到三个SRAM中。设置了两个PIO端口来控制加载进度。

    load_fc 是 HPS 的“输入”,表示最大池化过程已完成。fc_start_sign 是 HPS 的“输出”,它允许全连接层在加载完所有权重矩阵数据后开始计算。

    首先,HPS 将图像和卷积层权重矩阵加载到 SRAM 中。FPGA 完成最大池化操作后,HPS 将全连接 (fc) 权重矩阵加载到 FPGA 内部的两个独立 SRAM 中。全连接权重矩阵加载完成后,FPGA 会向 HPS 发送信号,表明加载完成。此过程确保 FPGA 拥有执行神经网络计算所需的所有数据。该过程如图 8 所示。

    国外大学生都用FPGA做什么项目(二十)图45
    图 8:用于神经网络计算的 HPS-FPGA 通信序列
    国外大学生都用FPGA做什么项目(二十)图46
    图 9:Quartus Prime FPGA 组件配置

    结果

    部分结果已在最前面的视频演示中展示。最高位代表输出结果:

    国外大学生都用FPGA做什么项目(二十)图47
    图 10:3 号样本的测试结果
    国外大学生都用FPGA做什么项目(二十)图48
    图 11:7 号样本的测试结果
    表3. FPGA识别准确率,每个数字测试10次
    国外大学生都用FPGA做什么项目(二十)图49

    结论

    我们的设计加速了CNN识别算法的计算过程。基于FPGA的CNN平均准确率达到了91%。通过添加实时摄像头、将固定点数改为浮点数以及在PyTorch中训练更复杂的模型以获得更优的权重矩阵,可以进一步提升准确率。

    项目链接

    https://people.ece.cornell.edu/land/courses/ece5760/FinalProjects/s2024/jy874_yz2952_ac2839/jy874_yz2952_ac2839/5760_Project_Web/5760_final.html

    代码链接

    https://github.com/Yiyang-Zhao/MNIST_Recognition

    视频链接

    https://youtu.be/4z4a9h6hijE?si=Xs8b9q0JMmekzfml

    总结

    本文整理了康奈尔大学 2024 年春季 ECE5760 课程基于 CycloneV DE1-SoC 开发板的 2 个 FPGA 实战项目,全部采用 Verilog 开发,具体项目如下:

    第一个项目是一个 基于 FPGA 的超标量乱序 NPU(神经处理单元)设计,面向 CNN 矩阵计算加速。系统采用 128-bit 指令集和 VLIW/乱序执行架构,内部包含矩阵加法、卷积、点乘、ReLU 等多个专用功能单元,并通过多 SRAM 分块存储实现高吞吐数据访问。ARM(HPS)负责加载输入数据与权重并控制 NPU 执行,FPGA 负责并行计算核心处理单元。整体架构支持单发射、多功能单元并行执行与乱序提交,使其能够高效加速深度学习计算,同时具备良好的可扩展性,可通过增加功能单元进一步提升性能。

    第二个项目该项目在 DE1-SoC 平台上实现了一套完整的卷积神经网络(CNN)手写数字识别系统,将卷积、ReLU、最大池化和全连接层全部采用 Verilog 实现,并利用 PyTorch 在 MNIST 数据集上训练模型,将得到的卷积核和全连接层权重加载到 FPGA 中进行硬件推理。整个系统采用 HPS+FPGA 协同架构,其中 HPS 负责图像及网络权重加载,FPGA 完成神经网络计算,最终识别结果通过数码管输出。

    在硬件实现方面,设计采用 三块 SRAM 作为各神经网络层之间的数据缓冲区,通过多路复用实现图像数据、卷积结果和权重数据的共享访问,并针对卷积层、最大池化层和全连接层分别设计了独立的状态机控制计算流程。其中卷积层采用 三个并行卷积通道 提高吞吐率,全连接层则通过分两次加载权重矩阵解决 SRAM 容量受限的问题。最终系统在 MNIST 测试集上的平均识别准确率达到 91%,验证了 FPGA 实现 CNN 推理的可行性,同时也展示了 FPGA 在神经网络硬件加速、数据流设计和异构计算方面的应用潜力。

    END


    往期精选 

    国外大学生都用FPGA做什么项目(二十)图50 
    国外大学生都用FPGA做什么项目(二十)图51 


    国外大学生都用FPGA做什么项目(二十)图52
    国外大学生都用FPGA做什么项目(二十)图53

    FPGA技术江湖广发江湖帖

    无广告纯净模式,给技术交流一片净土,从初学小白到行业精英业界大佬等,从军工领域到民用企业等,从通信、图像处理到人工智能等各个方向应有尽有,QQ微信双选,FPGA技术江湖打造最纯净最专业的技术交流学习平台。


    FPGA技术江湖微信交流群

    国外大学生都用FPGA做什么项目(二十)图54

    加群主微信,备注姓名+公司/学校+岗位/专业进群


    FPGA技术江湖QQ交流群

    国外大学生都用FPGA做什么项目(二十)图55

    备注姓名+公司/学校+岗位/专业进群

    国外大学生都用FPGA做什么项目(二十)图56

    声明:内容取材于网络,仅代表作者观点,如有内容违规问题,请联系处理。 
    FPGA
    more
    AMD一颗“低端FPGA”,把国产厂商的路直接堵死了?
    国产FPGA闯出新速度,HME-PV为AI视觉注入新动能
    新左移:以FPGA原型验证为“决策透镜”,加速RISC-V IP精准选型
    国外大学生都用FPGA做什么项目(十八)
    国外大学生都用FPGA做什么项目(十九)
    广东FPGA龙头,获数亿元Pre-IPO轮融资
    AMD 嵌入式计算峰会--自适应SoC|FPGA|边缘 AI|NPU|DSP|x86 嵌入式 等
    基于FPGA的分布式视频处理平台
    诚征男友!FPGA测试小姐姐,99年,喜欢打csgo,想找个对象一起电竞。
    22nm+23K LUTs混合架构,高云半导体FPGA实现创新引领
    Copyright © 2025 成都区角科技有限公司
    蜀ICP备2025143415号-1
      
    川公网安备51015602001305号