【Verilog】乒乓操作

乒乓操作

乒乓操作简单介绍
  • 乒乓操作常用于数据流的控制处理,在流水处理中,完成数据的无缝缓冲和处理
  • 典型乒乓操作示例图
    乒乓操作
乒乓操作的处理流程
  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
波形文件

波形文件

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

优点1

  • 资源使用情况

    • 两个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/

优点2

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

  • 整体需要处理数据量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原理的具体操作不做过多说明)
    • 此处利用读写空间的时钟间隙与数据写入读出的方式,实现了一块大小的空间存储大量数据的功能,从而节约了缓冲区空间
用低速模块处理高速数据流

优点3

  • 资源使用情况
    • 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的数据量,并且时时刻刻都在读,无间断读
  • 从两侧吞吐量来看,单位时间内输入输出比特数相等,并且实现了低速处理高速数据情况
  • 12
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
DDR乒乓操作是指在Verilog中使用双数据率(DDR)技术进行乒乓操作。在给定的Verilog代码中,有两个RAM模块(RAM1和RAM2),它们的使能信号(en1和en2)交替为高。当RAM1的o_addr为0时,表示RAM1正在进行读操作,而o_addw为高时,表示RAM1正在进行写操作。\[1\] 在提供的测试台(testbench)代码中,定义了时钟信号(clk)、复位信号(rst_n)和数据输入信号(data_in),以及数据输出信号(data_out)。通过对时钟信号进行周期性翻转,产生20ns的时钟周期。在初始化阶段,将时钟和复位信号设置为初始值,并在一定时间后将复位信号拉高。然后,通过每10个时钟周期翻转一次时钟信号,产生输入激励。\[2\] RAM存储模块(DRAM)定义了时钟信号(clk)、复位信号(rst_n)、数据输入信号(data)、写使能信号(wr_en),以及数据输出信号(o_data)、地址输出信号(o_addr)和写地址输出信号(o_addw)。在时钟上升沿或复位信号下降沿触发的always块中,根据不同的情况对输出信号进行更新。当复位信号为低电平时,将地址输出信号和写地址输出信号设置为0。当写使能信号为高时,将写地址输出信号递增1,地址输出信号设置为0。当写使能信号为低时,将地址输出信号递增1,写地址输出信号设置为0。同时,使用一个数组aRAM来存储数据,并根据读写操作对数据进行更新。\[3\] 综上所述,DDR乒乓操作是通过交替使用两个RAM模块,并根据读写操作对数据进行更新,实现数据的双数据率操作。 #### 引用[.reference_title] - *1* *3* [verilog 实现乒乓操作(附代码)](https://blog.csdn.net/weixin_44413306/article/details/128153149)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [FPGA设计——verilog实现乒乓操作并modelsim仿真](https://blog.csdn.net/H19981118/article/details/125103589)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值