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的矩阵
- 第一步:PS端向PL端发送输入数据
-
UpSample模块的实现优化如下:
-
将第二步和第三步合并,边执行UpSample操作,边通过AXI接口发送数据到PS端,减少缓存时间和空间(不需要缓存结果)
-
根据AXI接口的ready信号和value信号进行握手操作,控制数据的读取和发送时机,避免数据丢失或重复
-
UpSample的优化方法实现思路
- 为了提高效率,可以将第二步和第三步合并,即边执行UpSample操作,边发送输出数据,不需要额外缓存输出结果
- 为了实现这种优化方法,需要注意以下几点:
- 在奇数行时,每隔一个时钟周期读取输入buffer的数据,并对其进行寄存
- 在奇数行时,将UpSample后的结果写入另一个深度为26的FIFO进行缓存
- 在偶数行时,不读取输入buffer的数据,而是将另一个FIFO的数据读出并发送
- 利用两个计数器(col_cnt和row_cnt)控制读写时序和last信号
- 在发送数据时,需要根据AXI Stream接口的valid和ready信号进行握手handshake,以及根据last信号判断是否发送完毕
UpSample在Vivado HLS中的实现时序
- 在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状态,结束仿真