UpSample模块实现及仿真

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

UpSample模块仿真

回顾UpSample模块的工作流程

  • PS端发送数据给PL端
  • PS端向PL端发送UpSample的start指令
  • PL端接收到指令后,执行UpSample操作,并将结果通过AXI接口返回给PS端

仿真UpSample模块的结构和步骤

  • 仿真结构与工作流程相对应,包括以下模块:

    • testbench:用于发送数据和指令,以及接收结果
    • UpSample_top:顶层模块,包含main_control和UpSample两个子模块
    • main_control:主控制模块,用于解析指令和控制状态机
    • UpSample:UpSample操作模块,包含FIFO和stream_tx两个子模块
    • FIFO:缓存模块,用于存储输入数据
    • stream_tx:输出模块,用于将UpSample结果发送给PS端
  • 仿真步骤如下:

    • testbench发送13*13的单通道数据给UpSample_top,然后复制8份拼接成8通道数据
    • testbench发送write_data指令给main_control,让其将数据写入FIFO
    • testbench发送up_sample_start指令给main_control,让其执行UpSample操作
    • main_control根据指令控制状态机的转换,以及读写使能信号的产生
    • UpSample根据读写使能信号从FIFO读取数据,并进行UpSample操作,将结果通过stream_tx发送给testbench
    • testbench接收到UpSample结果,并进行比较和验证
  • 仿真结构与layer 0的数据仿真结构类似,但为了方便,只发送单个通道的13*13的测试数据
  • 发送数据后,发送write和UpSample的指令,然后等待数据返回
  • 仿真模块包括test bench,top,main control,receive data,UpSample等

修改test bench的代码

  • 修改状态机,只保留发送数据和执行UpSample两个状态
  • 修改发送数据的模块,让它产生13*13个递增数,并复制8份作为8个通道的数据
  • 修改发送指令的模块,只发送write_data和up_sample_start两个指令
  • 修改testbench中的ready信号,使其在UpSample状态中有一段时间为低电平,以模拟断续传输的情况
  • 修改main_control中的状态机,使其在接收到up_sample_finish信号后转换到idle状态

修改UpSample模块的代码

  • 修改last信号的判断条件,根据输入数据的行数来确定

    使其在输出最后一个数据时为高电平

  • 修改UpSample finish信号的产生方式,直接用last信号来表示

  • 修改ready信号的产生方式,让它在UpSample状态中有一段时间为低,模拟中断过程

  • 修改UpSample_top中的row_cnt信号,使其在输出\(13*13*8\)个数据后停止计数

仿真结果分析

  • 通过观察波形图,可以看到仿真结果符合预期,具体表现如下:
    • testbench成功地将13138个数据发送给了UpSample_top,并且在ready信号为低电平时暂停发送
    • testbench成功地将write_data和up_sample_start两个指令发送给了main_control,并且在接收到UpSample结果后进行了比较和验证
    • main_control成功地解析了指令,并根据指令控制了状态机的转换,以及读写使能信号的产生
    • UpSample成功地从FIFO读取了数据,并进行了UpSample操作,将结果通过stream_tx发送给了testbench
    • UpSample在输出最后一个数据时将last信号拉高,并将up_sample_finish信号传递给了main_control
    • main_control在接收到up_sample_finish信号后转换到了idle状态,结束仿真

image

image

posted @ 2023-07-08 21:03  李白的白  阅读(638)  评论(0编辑  收藏  举报