UpSample模块实现

发布时间 2023-07-08 21:07:21作者: 李白的白

UpSample模块实现

UpSample模块的原理和功能:UpSample模块是用来将输入数据的尺寸放大的,它的原理是将每个数据复制四次,从而使得输出数据的尺寸变为输入数据的两倍。例如,如果输入数据是22的,那么输出数据就是44的。UpSample模块在神经网络中可以用来实现上采样的操作,提高图像的分辨率或者恢复图像的细节。

  • UpSample模块的作用是将输入数据的尺寸放大,例如将2x2的数据变成4x4的数据,本质上是对每个数据进行复制,一个变成四个

  • UpSample模块的实现原理是利用FPGA的PS端和PL端进行数据的传输和处理

  • UpSample模块的实现流程如下:

    • 第一步:PS端向PL端发送输入数据
      • 使用IP核中的FIFO缓存输入数据
      • FIFO位宽为64比特,深度为4096
      • 每次发送8个通道的数据,每个通道需要发送16次才能完成128个通道的传输
      • FIFO深度2704即可存储所有输入数据
    • 第二步:PL端执行UpSample操作
      • PL端从缓存FIFO中读取输入数据,每隔一个双周期读取一个数据,并对其进行缓存
      • 然后将一个数据复制成四个数据,并通过AXI接口发送到PS端,同时将第一行的数据写入另一个FIFO中进行缓存,该FIFO的位宽是64比特,深度是26
    • 第三步:PL端通过DMA返回(PS端)输出数据
      • PL端从另一个FIFO中读取第一行的数据,并通过AXI接口发送到PS端,完成第二行的输出,以此类推,直到所有输出数据都发送完毕,输出数据是26x26x128的矩阵
  • UpSample模块的实现优化如下:

    • 将第二步和第三步合并,边执行UpSample操作,边通过AXI接口发送数据到PS端,减少缓存时间和空间(不需要缓存结果)

    • 根据AXI接口的ready信号和value信号进行握手操作,控制数据的读取和发送时机,避免数据丢失或重复

UpSample的优化方法实现思路

image

  • 为了提高效率,可以将第二步和第三步合并,即边执行UpSample操作,边发送输出数据,不需要额外缓存输出结果
  • 为了实现这种优化方法,需要注意以下几点:
    • 在奇数行时,每隔一个时钟周期读取输入buffer的数据,并对其进行寄存
    • 在奇数行时,将UpSample后的结果写入另一个深度为26的FIFO进行缓存
    • 在偶数行时,不读取输入buffer的数据,而是将另一个FIFO的数据读出并发送
    • 利用两个计数器(col_cnt和row_cnt)控制读写时序和last信号
    • 在发送数据时,需要根据AXI Stream接口的valid和ready信号进行握手handshake,以及根据last信号判断是否发送完毕

UpSample在Vivado HLS中的实现时序

image

  • 在UpSample start信号来到后,状态机跳转到UpSample状态,并开始执行UpSample操作
  • 在奇数行时,每隔一个时钟周期读取输入缓存的数据,并对其进行缓存(打拍),同时将UpSample的结果写入另一个深度为26的FIFO进行缓存
  • 在偶数行时,不读取输入缓存的数据,而是将另一个FIFO的数据读出,并通过AXI Stream接口发送
  • 在发送数据时,需要根据value信号和ready信号进行握手操作(handshake),并根据row_cnt和col_cnt判断是否发送last信号
- 状态机跳转到UpSample状态时开始工作
- buffer_read_en信号控制输入buffer的读取,每隔一个双周期拉高一次
- buffer_read_data信号输出读取的数据,并寄存到reg_ie中
- reg_ie和buffer_read_data共同构成AXI Stream的tdata信号
- tvalid信号在UpSample状态时一直为高,直到last信号为高时拉低
- tready信号由外部控制,影响handshake信号和计数器的加法操作
- handshake信号等于tvalid和tready的与操作,表示握手成功
- col_cnt在handshake为高时加1,到达25时清零,并使row_cnt加1
- row_cnt在UpSample状态时加1,到达415时清零,并跳出UpSample状态
- fifo_write_en信号在奇数行且handshake为高时拉高一次,将UpSample结果写入第一个FIFO中
- fifo_read_en信号在偶数行且handshake为高时拉高一次,将第一个FIFO中的数据读出发送

代码清单:

`include "debug_ctrl.h"

module  upsample(
        // system signals
        input                   sclk                    ,       
        input                   s_rst_n                 ,       
        //
        input           [ 5:0]  state                   ,       
        output  wire            buffer_rd_en            ,       
        input           [63:0]  buffer_rd_data          ,       
        // 
        output  wire    [63:0]  axis_tdata              ,       
        output  wire            axis_tvalid             ,       
        input                   axis_tready             ,       
        output  reg             axis_tlast              ,
        //
        output  wire            upsample_finish         
);

//========================================================================\
// =========== Define Parameter and Internal signals =========== 
//========================================================================/
localparam      COL_END         =       'd26                    ;
`ifndef SIM
localparam      ROW_END         =       'd416                   ;
`else
localparam      ROW_END         =       'd26                    ;
`endif 


reg     [ 4:0]                  col_cnt                         ;       
reg     [ 8:0]                  row_cnt                         ;       

reg     [63:0]                  buffer_rd_data_r1               ;       

wire                            hand_shake                      ;       
wire                            fifo_wr_en                      ;       
wire                            fifo_rd_en                      ;       
wire    [63:0]                  fifo_rd_data                    ;       

//=============================================================================
//**************    Main Code   **************
//=============================================================================

assign  buffer_rd_en    =       (col_cnt[0] == 1'b0 && row_cnt[0] == 1'b0) ? hand_shake : 1'b0;
assign  axis_tdata      =       (row_cnt[0] == 1'b0) ? ((buffer_rd_en == 1'b1) ? buffer_rd_data : buffer_rd_data_r1) : fifo_rd_data;
assign  axis_tvalid     =       state[4];
assign  hand_shake      =       axis_tvalid & axis_tready;
assign  fifo_wr_en      =       (row_cnt[0] == 1'b0) ? hand_shake : 1'b0;
assign  fifo_rd_en      =       (row_cnt[0] == 1'b1) ? hand_shake : 1'b0;

assign  upsample_finish =       axis_tlast;


always  @(posedge sclk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
                col_cnt <=      'd0;
        else if(col_cnt == COL_END-1 && hand_shake == 1'b1)
                col_cnt <=      'd0;
        else if(hand_shake == 1'b1)
                col_cnt <=      col_cnt + 1'b1;
end

always  @(posedge sclk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
                row_cnt <=      'd0;
        else if(state[4] == 1'b0)
                row_cnt <=      'd0;
        else if(col_cnt == COL_END-1 && hand_shake == 1'b1)
                row_cnt <=      row_cnt + 1'b1;
end

always  @(posedge sclk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
                buffer_rd_data_r1       <=      64'h0;
        else if(buffer_rd_en == 1'b1)
                buffer_rd_data_r1       <=      buffer_rd_data;
end

always  @(posedge sclk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
                axis_tlast      <=      1'b0;
        else if(col_cnt == COL_END-2 && row_cnt == (ROW_END-1) && hand_shake == 1'b1)
                axis_tlast      <=      1'b1;
        else
                axis_tlast      <=      1'b0;
end


up_fifo_ip up_fifo_ip_inst (
        .clk                    (sclk                   ),      // input wire clk
        .srst                   (~s_rst_n               ),      // input wire srst
        .din                    (axis_tdata             ),      // input wire [63 : 0] din
        .wr_en                  (fifo_wr_en             ),      // input wire wr_en
        .rd_en                  (fifo_rd_en             ),      // input wire rd_en
        .dout                   (fifo_rd_data           ),      // output wire [63 : 0] dout
        .full                   (                       ),      // output wire full
        .empty                  (                       ),      // output wire empty
        .data_count             (                       )// output wire [5 : 0] data_count
);


endmodule