浅谈FPGA的乒乓操作

时间:2024-03-22 18:22:07

乒乓操作是FPGA设计中经常用到的设计思想,常用于需要提高数据效率的地方。其主要特点有:

1、 实现数据的无缝缓冲和处理;

2、 可节约缓冲区空间;

3、 可实现低速模块处理高速模块。

典型的乒乓操作原理如下图:

浅谈FPGA的乒乓操作

如图所示:

  1. T1时刻,DATA_T1存入buffer 1;

  2. T2时刻,buffer 1已被写满,DATA_T2存入buffer 2, 同时buffer 1将DATA_T1送至运算模块处理;

  3. T3时刻,DATA_T3存入buffer A1,同时buffer 2将DATA_T2送至运算模块处理;

  4. 然后重复2、3阶段的操作。

从上图也可以看出乒乓操作的核心就是控制缓冲模块的读写,具体可以细化为输入数据控制模块、缓冲模块1、缓冲模块2以及输出数据控制模块,这里的缓冲模块可以是FIFO、BRAM等。下面举例具体说明:

  1. 输入不连续的数据字节,工作时钟要求是50Mhz;

  2. 输出要求每次16字节连续输出,并且有一个信号作为数据有效指示;

  3. 每次输出之间间隔时间可以是随机的,一般是至少不低于一个时钟周期的间隔;

思考:

1、 数据为8位且16个后输出,所以考虑用16*8的BRAM做缓冲模块。

2、 两个缓冲模块写控制考虑用5位计数器,其中最高位控制不同缓冲模块的写,低4位代表写地址

3、 缓冲模块的读控制需要判断缓冲模块是否写满,利用写计数器最高位的上升沿和下降沿判断:上升沿代表buffer 1写满,下降沿代表buffer 2写满。

4、 注意输出数据流单元数据与数据有效指示同步的关系(不同缓冲模块,数据有效指示做不同的同步处理)

基于上述考虑画出时序图,如下:

浅谈FPGA的乒乓操作

其对应的电路图如下:

浅谈FPGA的乒乓操作
对应的verilog描述如下:

`timescale 1ns/10ps
module pingpang_operation (
               rst_n                                 ,
               clk_50m                     ,
               data_in                      ,      // 输入数据
               data_valid           ,    // 输入数据有效信号              
               data_out                    ,      // 输出数据
               data_out_vld            
// 输出数据有效信号
              );
        
              input                                                                         rst_n                                   ;
              input
                                                           clk_50m                       ;

              input        
[7:0]                                            data_in                        ;      // 输入数据

              input                                                                         data_valid              ;    // 输入数据有效信号

              

              output  [7:0]   
                                    data_out               ;      // 输出数据

              output
                                                              data_out_vld  ;              
// 输出数据有效信号

 

              

              reg   [4:0]        wr_cnt;
                       //    ram A and ram B 的写地址

              reg                                           wr_cnt4_d; 
         //    wr_cnt[4] 寄存一拍

              

              wire                                 wr_a_en;   // ram A 的写使能 

              wire                                   wr_b_en;        //
ram B 的写使能

              

              reg                                           rd_a_en;        //    ram A 的读使能

              reg                                           rd_a_en_d1, 
rd_a_en_d2;          //       ram A 的读使能寄存2拍                        

              

              reg   [3:0]                  rd_a_addr;  //      ram
A 的读地址

              

              reg                                           rd_b_en;        //    ram B 的读使能

              reg                                           rd_b_en_d1,   rd_b_en_d2;          //       ram
B 的读使能寄存2拍

              

              reg   [3:0]                  rd_b_addr;  //      ram
B 的读地址

              

              wire  [7:0]                   rd_a_data;  //       ram
A 的读数据

              wire  [7:0]                   rd_b_data;  //      ram
B 的读数据

              

              wire [7:0]               data_out;
                   // 数据输出

              wire                                    data_out_vld;        // 数据输出有效信号

//------------------------------------------------------       

// ramA和ramB的写电路模块设计         

//------------------------------------------------------

       //设计ramA 和ramB的写地址

       always
@(posedge clk_50m or negedge rst_n) 

       begin

      if (!rst_n)

             wr_cnt <= 5'h00;

      else if(data_valid)   

             wr_cnt    <= wr_cnt + 5'd1; 

           

       end 

       

       

      

      

       

  // 设计ram A 和 ram B的写使能 

 
assign               wr_a_en  = ~wr_cnt[4] & data_valid;

  

 
assign               wr_b_en  =     wr_cnt[4]
& data_valid;

//------------------------------------------------------       

// ramA和ramB的读电路模块设计         

//------------------------------------------------------

//ram_a或ram_b写完成后才能进行相应的读

always @(posedge clk_50m or negedge rst_n) 
       begin
      if (!rst_n)
             wr_cnt4_d <= 1'b0;
      else 
                     wr_cnt4_d
<= wr_cnt[4];

  end

 

wire wr_cnt4_rise,wr_cnt4_down;

 

assign    wr_cnt4_rise
= wr_cnt[4] & ~wr_cnt4_d;//ram_a 可读

assign    wr_cnt4_down
= ~wr_cnt[4] & wr_cnt4_d;//ram_b 可读

 

//reg      [3:0] cnt_rd;

//always @(posedge clk_50m or negedge
rst_n)     

//    begin

//   if (!rst_n)

//          cnt_rd <= 4'd0;

//   else if(wr_cnt4_rise | wr_cnt4_down)

//          cnt_rd <= cnt_rd + 4'd1;

// 
end

       //ramA的
读使能 

       always
@(posedge clk_50m or negedge rst_n) 

       begin

      if (!rst_n)

             rd_a_en <= 1'b0;

      else if(wr_cnt4_rise)

             rd_a_en <= 1'b1;

      else if(rd_a_addr==4'hf)//else
if(cnt_cnt==4'd15)

             rd_a_en <= 1'b0;

  end

  

 
//ramA的 读地址

       always
@(posedge clk_50m or negedge rst_n) 

       begin

      if (!rst_n)

             rd_a_addr <= 4'h0;

      else if (rd_a_en)

             rd_a_addr <= rd_a_addr +4'h1;

  end

  

  

 
//ramB的 读使能 

       always
@(posedge clk_50m or negedge rst_n) 

       begin

      if (!rst_n)

             rd_b_en <= 1'b0;

      else if(wr_cnt4_down)

             rd_b_en <= 1'b1;

      else if(rd_b_addr==4'hf)//else
if(cnt_cnt==4'd15)

             rd_b_en <= 1'b0;

  end

  

 
//ramB的 读地址

       always
@(posedge clk_50m or negedge rst_n) 

       begin

      if (!rst_n)

             rd_b_addr <= 4'h0;

      else if (rd_b_en)

             rd_b_addr <= rd_b_addr +4'h1;

  end

  

//------------------------------------------------------       

// ramA和ramB的读数据输出电路

//------------------------------------------------------

  

 
//ramA的 读使能寄存2拍  

       always
@(posedge clk_50m or negedge rst_n) 

       begin

      if (!rst_n)

      begin

             rd_a_en_d1 <= 1'b0;

             rd_a_en_d2 <= 1'b0;

      end

      else 

      begin

             rd_a_en_d1    <= rd_a_en;

             rd_a_en_d2    <= rd_a_en_d1;

      end

  end

  

 
//ramB的 读使能寄存2拍 

       always
@(posedge clk_50m or negedge rst_n) 

       begin

      if (!rst_n)

      begin

             rd_b_en_d1 <= 1'b0;

             rd_b_en_d2 <= 1'b0;

      end

      else 

      begin

             rd_b_en_d1    <= rd_b_en;

             rd_b_en_d2    <= rd_b_en_d1;

      

      end

  end

  

  //读数据输出

 
assign               data_out =
rd_a_en_d2 ? rd_a_data : rd_b_data;

  

  //读数据有效信号

 
assign               data_out_vld  =     rd_a_en_d2
| rd_b_en_d2;

  

  

//------------------------------------------------------       

// ramA和ramB

//------------------------------------------------------  

       //
ram A 16 X 8bits ,ram的读数据比读地址和读使能延迟2T

       ram_ly
          ram_a(                

              .clock            (clk_50m),   //时钟     

              .data                    (data_in),   //写数据

              .rdaddress(
rd_a_addr),   //读地址

              .rden                    (rd_a_en),             //读使能

              .wraddress(wr_cnt[3:0]
),             //写地址

              .wren                    (wr_a_en ),            //写使能

              .q                         (rd_a_data)           //读数据

              );

       

       //
ram B 16 X 8bits

       ram_ly
          ram_b(                

              .clock            (clk_50m ),   //时钟      

              .data                    (data_in),   //写数据

              .rdaddress(rd_b_addr),   //读地址

              .rden                    (rd_b_en),             //读使能

              .wraddress(wr_cnt[3:0]),              //写地址

              .wren                    (wr_b_en ),            //写使能

              .q                         (rd_b_data)           //读数据

              );

 

     

endmodule

至此完成FPGA乒乓操作的设计。

本设计完整工程下载地址:链接:https://pan.baidu.com/s/19ZjRFEDLXUhPrV6gtn9uEA

提取码:1z44

另外,感谢恩师李煜老师。