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 ; // 缓存1 11 reg [7:0] buffer2 ; // 缓存2 12 reg wr_flag ; // 写标志,wr_flag=0,写buffer1,wr_flag=1,写buffer2 13 reg rd_flag ; // 读标志,rd_flag=0,读buffer2,rd_flag=1,读buffer1 14 reg state ; // 状态机,0:写1读2,1:写2读1,状态转移和输出分开编码 15 // ------------------------------------------------------ // 16 // 状态转移 17 always @ (posedge clk or negedge rst_n) 18 begin 19 if(rst_n == 1\'b0) 20 begin 21 state <= \'b0; 22 end 23 else 24 begin 25 state <= !state; 26 //case(state) 27 // 1\'b0 : state <= 1\'b0; // 写1读2->写2读1 28 // 1\'b1 : state <= 1\'b1; // 写2读1->写1读2 29 // default : state <= 1\'b0; 30 //endcase 31 end 32 end 33 // ------------------------------------------------------ // 34 // 状态输出 35 always @ (state) 36 begin 37 case(state) 38 1\'b0: 39 begin 40 wr_flag = 1\'b0; // 写1 41 rd_flag = 1\'b0; // 读2 42 end 43 1\'b1: 44 begin 45 wr_flag = 1\'b1; // 写2 46 rd_flag = 1\'b1; // 读1 47 end 48 default: 49 begin 50 wr_flag = 1\'b0; 51 rd_flag = 1\'b0; 52 end 53 endcase 54 end 55 // ------------------------------------------------------ // 56 // 写buffer数据 57 always @ (posedge clk or negedge rst_n) 58 begin 59 if(rst_n == 1\'b0) 60 begin 61 buffer1 <= 8\'b0; 62 buffer2 <= 8\'b0; 63 end 64 else 65 begin 66 case(wr_flag) 67 1\'b0 : buffer1 <= data_in; // wr_flag = 0,写buffer1 68 1\'b1 : buffer2 <= data_in; // wr_flag = 1,写buffer2 69 default : 70 begin 71 buffer1 <= 8\'b0; 72 buffer2 <= 8\'b0; 73 end 74 endcase 75 end 76 end 77 // ------------------------------------------------------ // 78 // 读buffer数据 79 always @ (posedge clk or negedge rst_n) 80 begin 81 if(rst_n == 1\'b0) 82 begin 83 data_out <= 8\'b0; 84 end 85 else 86 begin 87 case(rd_flag) 88 1\'b0 : data_out <= buffer2; // rd_flag=0,读buffer2 89 1\'b1 : data_out <= buffer1; // rd_flag=1,读buffer1 90 default : data_out <= 8\'b0; 91 endcase 92 end 93 end 94 // ------------------------------------------------------ // 95 endmodule
2.2 FPGA仿真代码
`timescale 1ns / 1ps module 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.仿真结果