【Verilog】乒乓操作

文章目录

      • 乒乓操作
        • 乒乓操作简单介绍
          • 乒乓操作的处理流程
          • 代码参考
            • 功能代码
            • testbench
            • 波形文件
        • 乒乓操作应用场景
          • 何时考虑使用乒乓操作
          • 乒乓操作的三个优点
          • 具体实现分析
            • 不间断地处理数据,无缝缓冲与处理
            • 可以节约缓冲区空间
            • 用低速模块处理高速数据流

乒乓操作

乒乓操作简单介绍

  • 乒乓操作常用于数据流的控制处理,在流水处理中,完成数据的无缝缓冲和处理
  • 典型乒乓操作示例图
    【Verilog】乒乓操作_第1张图片
乒乓操作的处理流程
  1. 输入数据流通过“输入数据选择单元”将数据流等时分配到两个数据缓冲区,数据缓冲区可以为任何存储模块(双口RAM、单口RAM、FIFO等)
  2. 在第n个缓冲周期,将输入的数据流缓存到“数据缓冲模块1”
  3. 在第n+1个缓冲周期,通过对“输入数据选择单元”的切换,将输入的数据流缓存到“数据缓冲模块2”,同时将“数据缓冲模块1”缓存的第n个周期的数据通过“输出数据选择单元”的切换,送到“数据流运算处理模块”
  4. 在第n+2个缓冲周期,通过对“输入数据选择单元”的切换,将输入的数据流缓存到“数据缓冲模块1”,同时将“数据缓冲模块2”缓存的第n+1个周期的数据通过“输出数据选择单元”的切换,送到“数据流运算处理模块”
  5. 如此循环
代码参考
功能代码
// 乒乓操作
// 输入数据的存储区域分为buffer1和buffer2,分为两个状态,state = 0和state = 1
// state = 0, 读buffer2,写buffer1
// state = 1, 读buffer1,写buffer2

module pinpong (
  input            clk      ,
  input            rst_n    ,
  input      [7:0] data_in  ,
  output reg [7:0] data_out
  );

// 定义两块存储区域
reg [7:0] buffer1;
reg [7:0] buffer2;
reg       wr_flag; // 写标志 wr_flag=0 写buffer1;wr_flag=1 写buffer2
reg       rd_flag; // 读标志 rd_flag=0 读buffer2;rd_flag=1 读buffer1
reg       state;
 
// 时序逻辑描述状态转换
always @ (posedge clk or negedge rst_n) begin
  if (!rst_n) begin
    state = 1'b0;
  end else begin
    case (state)
      1'b0    : state <= 1'b1;
      1'b1    : state <= 1'b0;
      default : state <= 1'b0;
    endcase
  end
end

// 输出逻辑
always @(*) begin
  if (!rst_n) begin
    wr_flag <= 1'd0;
	rd_flag <= 1'd0;
  end else begin
    case (state)
			1'b0    : begin
			            wr_flag <= 1'd0;//写1读2
			            rd_flag <= 1'd0;
			          end
			1'b1    : begin
			            wr_flag <= 1'd1;//写2读1
			            rd_flag <= 1'd1;
			          end
			default : begin
			            wr_flag <= 1'd0;//写1读2
			            rd_flag <= 1'd0;
			          end
		endcase
	end
end

always @ (posedge clk or negedge rst_n) begin
  if (!rst_n) begin
    buffer1 <= 8'b0;
    buffer2 <= 8'b0;
  end else begin
    case (wr_flag)
      1'b0    : buffer1 <= data_in;   //wr_flag = 0 ,写buffer1
      2'b1    : buffer2 <= data_in;   //wr_flag = 1 ,写buffer2
      default : begin
                  buffer1 <= 8'b0;
                  buffer2 <= 8'b0;
                end
    endcase
  end 
end
      
always @ (posedge clk or negedge rst_n) begin
  if (!rst_n) begin
    data_out <= 8'b0;
  end else begin
    case ( rd_flag)
      1'b0    : data_out <= buffer2;   //rd_flag = 0,读buffer2
      1'b1    : data_out <= buffer1;   //rd_flag = 1,读buffer1
      default : data_out <= 8'b0   ;
    endcase
  end       
end

endmodule
testbench
`timescale  1ns / 1ps
`include "pingpong.v"

module tb_pinpong;     

// pinpong Parameters  
parameter PERIOD  = 10;


// pinpong Inputs
reg   clk                                  = 0 ;
reg   rst_n                                = 0 ;
reg   [7:0]  data_in                       = 0 ;

// pinpong Outputs
wire  [7:0]  data_out                      ;


initial
begin
    forever #(PERIOD/2)  clk=~clk;
end

initial
begin
    #(PERIOD) rst_n  =  1;
end

initial
begin
  forever #(PERIOD) data_in = data_in + 1'd1;
end

pinpong  u_pinpong (
    .clk                     ( clk             ),
    .rst_n                   ( rst_n           ),
    .data_in                 ( data_in   [7:0] ),

    .data_out                ( data_out  [7:0] )
);

initial
begin
    $dumpfile("./build/pingpong.vcd");
    $dumpvars;

    #1000
    $finish;
end

endmodule
波形文件

【Verilog】乒乓操作_第2张图片

乒乓操作应用场景

  • 学习乒乓操作的处理流程之后,会很明显地想到FIFO操作,因为类似的逻辑想法,从输入端观测,FIFO同样可以对输入数据进行一定的缓存,从输出端观测,从FIFO读出的数据需要按照当前指针按序“选择”读出的数据
  • 那在使用时,究竟如何对乒乓操作和FIFO进行选择
何时考虑使用乒乓操作
  1. 异步FIFO跨时钟域
  2. 效率第一的场景
  3. 非空即读的场景
  4. 不间断读、不间断写的场景
  5. 数据流接口
乒乓操作的三个优点
  1. 不间断地处理数据,无缝缓冲与处理
  2. 可以节约缓冲区空间
  3. 用低速模块处理高速数据流
具体实现分析
不间断地处理数据,无缝缓冲与处理

【Verilog】乒乓操作_第3张图片

  • 资源使用情况

    • 两个1K大小的FIFO1和FIFO2
    • 数据输入1K
  • 时序分析

    • 第n缓冲周期,data1写入FIFO1
    • 第n+1缓冲周期,data2写入FIFO2,并data1从FIFO1读出
    • 第n+2缓冲周期,data3写入FIFO1,并data2从FIFO2读出
    • 第n+3缓冲周期,data4写入FIFO2,并data3从FIFO1读出
  • 观测

    • 写侧:n~n+3缓冲周期,无间断写入数据
    • 读侧:n+1~n+3缓冲周期,无间断读出数据
  • 此时思考一个问题:为何在此处使用两个1K大小的FIFO进行乒乓操作,为何不直接使用1个2K大小的FIFO?

    • 若处于同步状态,不存在跨时钟域
      • 2个1K大小的FIFO进行乒乓操作,和使用1个2K大小FIFO是一样的
      • 因为当前为同步状态,同步状态的FIFO不存在任何安全风险,并且此时无论是缓存空间大小的使用,还是模块的使用,乒乓和FIFO没有本质上的区别
      • 这种情况下,可以使用2个1K大小的FIFO配合乒乓操作,同样可以直接使用1个2K大小的FIFO
    • 若处于异步状态,存在跨时钟域
      • 对于使用2个1K大小FIFO乒乓操作来讲,不存在同时对读写地址进行比较判断空满的情况,因为同一个时间段内,一个FIFO只进行写另一个FIFO只进行读,对于单个FIFO来讲,无需将读侧指针同步到写侧判满或将写侧指针同步到读侧判空,则不存在安全风险问题
      • 对于使用1个2K大小的异步FIFO操作来讲,存在同时对读写指针进行比较判断空满的情况,同一个时间段内,既要将读侧指针同步到写侧判满,又需要将写侧指针同步到读侧判空,就有概率出现误判,即误判空或误判满,此时就不能保证数据的无间断
      • 故使用1个2K的异步FIFO会出现对同时读写的地址跨时钟同步判断,会引入不可忽视的安全风险,此处2个1K大小FIFO的乒乓操作优于1个2K大小的异步FIFO
    • 此处也对应“何时考虑使用乒乓操作”的几种场景
可以节约缓冲区空间
  • 节约缓冲区空间就是能对一片缓冲区空间进行循环利用
  • 这里我们借用OpenZR+中ofec中的操作,具体ofec流程在本篇中不做解释,有兴趣可以官网下载文档学习,关注章节7
    • https://www.openzrplus.org/documents/

【Verilog】乒乓操作_第4张图片

  • 简单说明此处数据操作就是:按照列优先写入数据,最终按照行优先读出数据

  • 整体需要处理数据量32 * 111bits

    • 输入端:32 * 111bits = 16 * 2 * 111bits,按照列优先将数据写入寄存器,一次写入222bits,总共需要16clk将整块数据写入完毕
    • 输出端:32 * 111bits = 16 * 2 * 111bits = 16 * 2 * (16 * 6+15)bits,按照行优先将数据从寄存器读出,一次读出222bits,总共需要16clk将整块数据读出完毕
  • 如何用到乒乓操作思想

    • 由于数据的写入与读出的结构差异,最先写入的数据并不能立马读出,因为对于要读出的结构来讲,数据并没有填充完毕,则基础操作方式可以为:开辟两片同等大小的缓冲区存储数据,当第一大块数据写入完毕,数据可以按序读出时,读出的同时新到来的数据写入第二片缓冲区,以此类推
    • 进一步地,我们可以按照结构重新审视缓冲空间的利用程度
      • 数据列优先地写入存储器,连续14clk之后,会发现当前首两行的数据已经填充完毕
      • 在第15clk,数据列优先地写入存储器的同时,可以按照行优先的方式读出首两行111bits数据,此时第一块32 * 111bits数据还未完全写入但已可以读出
      • 在第16clk,数据列优先地写入存储器的同时,可以按照行优先的方式读出首两行111bits数据,此时第一块数据32 * 111bits已完全写入,并已经读出两次数据
      • 从第17clk开始,新的整块数据到来时,无需重新开辟新的存储空间,只需将新到来数据写入已读出的位置中即可(不可直接写入,需要按照协议重新排列后写入,但此处关注乒乓操作,ofec原理的具体操作不做过多说明)
    • 此处利用读写空间的时钟间隙与数据写入读出的方式,实现了一块大小的空间存储大量数据的功能,从而节约了缓冲区空间
用低速模块处理高速数据流

【Verilog】乒乓操作_第5张图片

  • 资源使用情况
    • 4个1K大小的FIFO
  • 时序说明
    • 为方便计算与演示,输入端一次输入1K大小数据,周期为1S,即1Kbits/s
    • 输出端周期为2S
  • 时序分析
    • 使用有限的寄存器处理读侧写侧时钟频率不匹配的情况,可以考虑并行度的调整,让两侧吞吐量一致,2 * 1024bits = 1 * 2048bits
    • 由于读写频率差异,首先对并行度进行调整后,前后吞吐量一致,单位时间内数据的写入读出平衡,需要开辟4个1K大小的缓冲区空间
    • 分析
      • 1~2S内数据以1K大小写了两次,分别按顺序写入FIFO1和FIFO
      • 3~4S内数据以1K大写写了两次,分别按顺序写入FIFO3和FIFO4,与此同时数据从FIFO1与FIFO2取出拼成1个2K大小的数据读出
      • 5~6S内数据以1K大写写了两次,分别按顺序写入FIFO1和FIFO2,与此同时数据从FIFO3与FIFO4取出拼成1个2K大小的数据读出
  • 观测
    • 写侧:在4S的时间内进行4次写入,共写入4 * 1K的数据量,并且时时刻刻都在写,写无间断
    • 读侧:在4S的时间内进行2次读出,共读出2 * 2K的数据量,并且时时刻刻都在读,无间断读
  • 从两侧吞吐量来看,单位时间内输入输出比特数相等,并且实现了低速处理高速数据情况

你可能感兴趣的:(数字芯片研发,#,Verilog,fpga开发,数字芯片,乒乓操作,Verilog)