FPGA之乒乓操作

时间:2024-03-08 16:35:13
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.仿真结果