人脸后续部分(快完结啦)

发布时间 2023-06-25 19:43:58作者: 李白的白

LeakyRelu模块实现

模块功能
  • LeakyRelu模块实现的主要功能用于对卷积神经网络的输出进行激活函数处理。LeakyRelu模块的功能是将输入的8个8位有符号整数,根据一个预设的斜率参数,进行非线性变换,输出8个8位有符号整数。

  • 激活函数的计算公式是:

    \[y = \begin{cases} x, & \text{if } x \geq 0 \\ a \times x, & \text{if } x < 0 \end{cases} \]

    其中,\(x\)是输入数据,\(y\)是输出数据,\(a\)是激活函数的参数。

    如果输入的整数大于等于零,输出不变;如果输入的整数小于零,输出等于输入乘以斜率参数。斜率参数是从一个64位的数据流stream_rx_data中读取的,每次读取8位,并存储在一个5位地址的RAM(leakyrelu_ram_ip)中。

  • LeakyRelu模块可以处理不同的数据类型(有符号或无符号)、不同的站点类型(中间站点或边缘站点)和不同的批次类型(单批次或多批次)。

模块结构
  • LeakyRelu模块由以下几个子模块组成:

    • main_ctrl:主控制模块,负责从AXI4-Lite接口读取寄存器的配置参数,如数据类型、卷积核大小、特征图尺寸、乘法因子、偏移量等,并根据这些参数控制其他模块的工作状态和读写地址。

    • stream_rx:流接收模块,负责从Axi4-Stream接口接收数据,并根据数据类型将其分为特征数据、权重数据、偏置数据和LeakyRelu系数数据,输出给相应的缓存模块。

    • bias_buffer:偏置缓存模块,负责存储8个通道的从流接收模块stream_rx接收到的偏置数据,每个通道32位有符号整数,共256位。并根据主控制模块main_ctrl的读地址,输出给卷积核模块的偏置值。

    • feature_buffer:特征图缓存模块,负责存储从流接收模块接收到的特征图数据,每个数据64位无符号整数,共512位。并根据卷积核模块的读使能信号,输出特征图数据。

    • weight_top_buffer:权重缓存模块,负责存储从流接收模块接收到的8个通道的3x3卷积核权重数据,每个权重72位无符号整数,共576位。并根据主控制模块的读地址和卷积核大小,输出对应通道和位置的权重值。

    • conv_kernel_top:卷积核模块,负责对输入的特征数据和权重数据进行3x3卷积运算,并加上偏置数据,输出8个通道的卷积结果,每个结果24位有符号整数,共192位。(即将特征图数据和权重数据进行逐元素相乘并累加,并加上偏置值,得到卷积输出结果)

    • quant_int8_8ch:量化模块,负责将卷积输出结果从32位有符号整数转换为8位无符号整数,即进行量化操作。量化操作需要用到主控制模块提供的乘法因子、偏移量和零点值,具体公式为:量化输出 = (卷积输出 * 乘法因子) >> 偏移量 + 零点值。量化操作可以减少数据位宽,节省存储空间和传输带宽。

    • leakyrelu_ram_ip:LeakyRelu系数存储模块,负责存储8个通道的LeakyRelu系数,并根据写地址和写使能信号进行写入操作。

    • leaky_relu:LeakyRelu计算模块,负责对量化模块的输出进行LeakyRelu变换,根据输入数据和LeakyRelu系数进行乘法和比较运算,输出8位整数结果。

      负责实现LeakyRelu激活函数,即对量化输出进行非线性变换。LeakyRelu激活函数的公式为:激活输出 = max(量化输出, 量化输出 * 负斜率)。其中,负斜率是从流接收模块接收到的激活函数参数之一。LeakyRelu激活函数可以增加网络的非线性能力,提高目标检测的准确度。

LeakyRelu模块实现的仿真过程
  • 编写测试平台(testbench)
    • 生成时钟信号、复位信号、寄存器信号和流数据信号
    • 监测LeakyRelu模块的输出信号
  • 绘制测试计划(test plan)
    • 设计不同的测试场景(test case)
      • 覆盖不同的数据类型、批处理类型、特征选择、权重选择、偏置选择等组合
      • 覆盖不同的输入数据范围和分布
  • 运行仿真工具(ModelSim)
    • 观察波形图(waveform)
      • 检查LeakyRelu模块的输出是否符合预期
      • 检查是否存在时序问题或逻辑错误
  • 分析代码覆盖率(Coverity)
    • 检查测试平台覆盖了多少LeakyRelu模块的代码
    • 检查是否存在未覆盖或冗余的代码
  • LeakyRelu模块的实现主要包含以下几个部分:
    • wr_addr: 写地址寄存器,用于存储当前写入LeakyRelu参数RAM的地址。每当接收到一个有效的LeakyRelu参数时,写地址加一;否则复位为零。
    • data_arr: 数据到达寄存器,用于延迟一个时钟周期后输出输入数据的有效信号。这是为了保证输入数据和输出数据在同一个时钟周期内有效。
    • ch_data_vld_o: 输出数据的有效信号,由数据到达寄存器的最高位决定。
    • U0_leakyrelu_ram_ip: LeakyRelu参数RAM,用于存储64个LeakyRelu参数,每个参数64位无符号整数。根据写地址和写使能信号写入数据,根据读地址输出数据。
    • ch0_data_o ~ ch7_data_o: 输出数据,由输入数据和LeakyRelu参数RAM的输出数据进行运算得到。如果输入数据大于等于零,则输出不变;如果输入数据小于零,则输出等于输入数据乘以LeakyRelu参数RAM的输出数据的低8位,并右移56位。
  • 模块仿真时需要观察以下信号:
    • s_axis_s2mm_tdata:Axi4-Stream接口的数据信号,用于输出LeakyRelu模块的结果数据。
    • s_axis_s2mm_tkeep:Axi4-Stream接口的有效位信号,用于指示数据信号的有效字节。
    • s_axis_s2mm_tvalid:Axi4-Stream接口的有效信号,用于指示数据信号是否有效。
    • s_axis_s2mm_tready:Axi4-Stream接口的就绪信号,用于反馈外部设备是否准备好接收数据信号。
    • s_axis_s2mm_tlast:Axi4-Stream接口的结束信号,用于指示数据信号是否为一帧的最后一个字。
四舍五入功能
  • 四舍五入功能是在量化模块中实现的,它是将卷积核模块输出的32位有符号整数转换为8位整数的一部分过程。
  • 四舍五入功能的原理是:
    • 首先,根据乘法因子和移位因子对32位有符号整数进行放缩,得到一个中间结果。乘法因子是一个15位无符号整数,移位因子是一个8位无符号整数,中间结果是一个38位有符号整数。
    • 然后,根据零点对中间结果进行偏移,得到一个偏移结果。零点是一个8位无符号整数,偏移结果是一个39位有符号整数。
    • 最后,对偏移结果进行四舍五入,得到一个8位整数。四舍五入的方法是:
      • 如果偏移结果的第31位(从右往左数)是0,表示偏移结果是正数或零,则直接截取偏移结果的[30:23]作为8位整数输出。
      • 如果偏移结果的第31位是1,表示偏移结果是负数,则先对偏移结果取反加一得到其补码形式,然后判断其[22:0]是否全为0。如果全为0,则直接截取其补码形式的[30:23]作为8位整数输出;如果不全为0,则先将其补码形式的[30:23]加一得到一个临时结果,然后截取临时结果的[7:0]作为8位整数输出。

四舍五入功能是指将一个实数按照一定的规则转换为一个整数,通常是将小数部分舍去或加1。在本模块中,并没有直接使用四舍五入功能,而是使用了一个quant_int8_8ch模块来实现对24位有符号整数数据的量化。这个模块使用了乘法器、移位器和零点偏移来将输入数据转换为8位整数数据。具体的计算公式是:

\[y = \text{clamp}(\frac{x \times mult}{2^{shift}} + zero\_point, 0, 255) \]

其中,\(x\)是输入数据,\(y\)是输出数据,\(mult\)是乘法因子,\(shift\)是移位量,\(zero\_point\)是零点偏移量。\(\text{clamp}(x, a, b)\)表示将\(x\)限制在\(a\)\(b\)之间,如果\(x < a\)则返回\(a\),如果\(x > b\)则返回\(b\)。这个模块相当于对输入数据进行了放缩和偏移,并且将超出范围的值截断为0或255。这种量化方式可以减少数据的位宽和存储空间,但也会引入一定的误差。如果要实现四舍五入功能,可以将上式中的\(\text{clamp}\)函数替换为\(\text{round}\)函数,即将\(x\)四舍五入到最接近的整数。

reg     [ 4:0]                  wr_addr                         ;       

reg     [ 1:0]                  data_arr                        ;       
//=============================================================================
//**************    Main Code   **************
//=============================================================================
always  @(posedge sclk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
                wr_addr <=      'd0;
        else if(stream_leakyrelu_vld == 1'b1)
                wr_addr <=      wr_addr + 1'b1;
        else
                wr_addr <=      'd0;
end

always  @(posedge sclk) begin
        data_arr        <=      {data_arr[0], ch_data_vld_i};
end

assign  ch_data_vld_o   =       data_arr[1];

Max_Pool模块实现


DMA数据发送缓存模块实现


DMA数据发送模块实现


UpSample模块实现


UpSample模块仿真


对已有YOLO加速模块进行Layer2仿真


完善1x1卷积方案


搭建YOLO加速模块上板验证工程


PS端上板验证Layer0计算结果