1.乒乓操作原理
乒乓操作是一个主要用于数据流控制的处理技巧,典型的乒乓操作如图所示:
外部输入数据流通过“输入数据选择控制”模块送入两个数据缓冲区中,数据缓冲模块可以为任何存储模块,比较常用的存储单元为双口RAM(Dual RAM),SRAM,SDRAM,FIFO等。
在第1个缓冲周期,将输入的数据流缓存到“数据缓冲1”模块,在第2个缓冲周期,“输入数据选择控制”模块将输入的数据流缓存到“数据缓冲2”模块的同时,“输出数据选择控制”模块将“数据缓冲1”模块第一个周期缓存的数据流送到“后续处理”,模块进行后续的数据处理,在第三个缓冲周期,在“输入数据选择控制”模块的再次切换后,输入的数据流缓存到“数据缓冲1”模块,与此同时,“输出数据选择控制”模块也做出切换,将“数据缓冲2”模块缓存的第二个周期的数据送到“后续处理模块”,如此循环。
这里正是利用了乒乓操作完成数据的无缝缓冲与处理,乒乓操作可以通过“输入数据选择控制”和“输出数据选择控制”按节拍,相互配合地进行来回切换,将经过缓冲的数据流没有停顿的送到“后续处理模块”。
比如将乒乓操作运用在液晶显示的控制模块上,如图所示。
对于外部接口传输的图像数据,以一帧图像为单位进行SDRAM的切换控制,当SDRAM1缓存图像数据时,液晶显示的是SDRAM2的数据图像;反之,当SDRAM2缓存图像数据时,液晶显示的是SDRAM1的数据图像,如此反复,这样出路的好处在于液晶显示图像切换瞬间完成,掩盖了可能比较缓慢的图像数据流变换过程。
2.FPGA乒乓操作代码
2.1 FPGA设计代码
1 module pingpang 2 ( 3 input clk , 4 input rst_n , 5 input [7:0] data_in , // 输入数据 6 output reg [7:0] data_out // 输出数据 7 ); 8 9 // ------------------------------------------------------ //10 reg [7:0] buffer1 ; // 缓存111 reg [7:0] buffer2 ; // 缓存212 reg wr_flag ; // 写标志,wr_flag=0,写buffer1,wr_flag=1,写buffer213 reg rd_flag ; // 读标志,rd_flag=0,读buffer2,rd_flag=1,读buffer114 reg state ; // 状态机,0:写1读2,1:写2读1,状态转移和输出分开编码15 // ------------------------------------------------------ // 16 // 状态转移17 always @ (posedge clk or negedge rst_n)18 begin19 if(rst_n == 1'b0)20 begin21 state <= 'b0;22 end23 else24 begin25 state <= !state;26 //case(state)27 // 1'b0 : state <= 1'b0; // 写1读2->写2读128 // 1'b1 : state <= 1'b1; // 写2读1->写1读229 // default : state <= 1'b0;30 //endcase31 end32 end33 // ------------------------------------------------------ // 34 // 状态输出35 always @ (state)36 begin37 case(state)38 1'b0:39 begin40 wr_flag = 1'b0; // 写141 rd_flag = 1'b0; // 读242 end43 1'b1:44 begin45 wr_flag = 1'b1; // 写246 rd_flag = 1'b1; // 读147 end48 default:49 begin50 wr_flag = 1'b0;51 rd_flag = 1'b0;52 end53 endcase54 end55 // ------------------------------------------------------ // 56 // 写buffer数据 57 always @ (posedge clk or negedge rst_n)58 begin59 if(rst_n == 1'b0)60 begin61 buffer1 <= 8'b0;62 buffer2 <= 8'b0;63 end64 else65 begin66 case(wr_flag)67 1'b0 : buffer1 <= data_in; // wr_flag = 0,写buffer168 1'b1 : buffer2 <= data_in; // wr_flag = 1,写buffer269 default :70 begin71 buffer1 <= 8'b0;72 buffer2 <= 8'b0;73 end74 endcase75 end76 end 77 // ------------------------------------------------------ // 78 // 读buffer数据79 always @ (posedge clk or negedge rst_n)80 begin81 if(rst_n == 1'b0)82 begin83 data_out <= 8'b0;84 end85 else86 begin87 case(rd_flag)88 1'b0 : data_out <= buffer2; // rd_flag=0,读buffer289 1'b1 : data_out <= buffer1; // rd_flag=1,读buffer190 default : data_out <= 8'b0;91 endcase92 end93 end94 // ------------------------------------------------------ // 95 endmodule
2.2 FPGA仿真代码
`timescale 1ns / 1psmodule pingpang_tb(); reg clk ; reg rst_n ; reg [7:0] data_in ; wire[7:0] data_out; always #10 clk = ~clk; initial begin rst_n <= 1'b0 ; clk <= 1'b0 ; #2010; rst_n <= 1'b1 ; end always @(posedge clk or negedge rst_n) begin if(!rst_n) data_in <= 'd0; else data_in <= data_in + 1'b1; end pingpang dut ( .clk (clk ), .rst_n (rst_n ), .data_in (data_in ), .data_out (data_out ) ); endmodule
3.仿真结果