FPGA四大常用思想与技巧之一乒乓操作的代码实现分享

FPGA设计的四种常用思想与技巧:乒乓操作、串并转换、流水线操作、数据接口同步化

具体的理论分析请参考这篇大佬的博文:

Verilog基础知识1(FPGA 设计的四种常用思想与技巧之一 -- 乒乓操作)

这里想分享下我设计的一段代码,首先是源代码。

module pingpang
    (
    input            clk,
    input            rst_n,
    input      [7:0] data_in,   // 输入数据
    output reg [7:0] data_out   // 输出数据
    );
// ------------------------------------------------------ //
    reg [7:0] buffer1;  // 缓存1
    reg [7:0] buffer2;  // 缓存2
    reg       wr_flag;  // 写标志,wr_flag=0,写buffer1,wr_flag=1,写buffer2
    reg       rd_flag;  // 读标志,rd_flag=0,读buffer2,rd_flag=1,读buffer1
    reg       state;    // 状态机,0:写1读2,1:写2读1,状态转移和输出分开编码
// ------------------------------------------------------ //    
    // 状态转移
    always @ (posedge clk or negedge rst_n)
    begin
        if(rst_n == 1'b0)
        begin
            state <= 1'b0;
        end
        else
        begin
            case(state)
                1'b0    : state <= 1'b1;    // 写1读2->写2读1
                1'b1    : state <= 1'b0;    // 写2读1->写1读2
                default : state <= 1'b0;
            endcase
        end
    end
// ------------------------------------------------------ // 
    // 状态输出
    always @ (state)
    begin
        case(state)
            1'b0:
            begin
                wr_flag = 1'b0; // 写1
                rd_flag = 1'b0; // 读2
            end
            1'b1:
            begin
                wr_flag = 1'b1; // 写2
                rd_flag = 1'b1; // 读1
            end
            default:
            begin
                wr_flag = 1'b0;
                rd_flag = 1'b0;
            end
        endcase
    end
// ------------------------------------------------------ //  
    // 写buffer数据   
    always @ (posedge clk or negedge rst_n)
    begin
        if(rst_n == 1'b0)
        begin
            buffer1 <= 8'b0;
            buffer2 <= 8'b0;
        end
        else
        begin
            case(wr_flag)
                1'b0 : buffer1 <= data_in;  // wr_flag = 0,写buffer1
                1'b1 : buffer2 <= data_in;  // wr_flag = 1,写buffer2
                default:
                begin
                    buffer1 <= 8'b0;
                    buffer2 <= 8'b0;
                end
            endcase
        end
    end    
// ------------------------------------------------------ // 
    // 读buffer数据
    always @ (posedge clk or negedge rst_n)
    begin
        if(rst_n == 1'b0)
        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

其次是仿真测试代码:

module tb_pingpang();
// ------------------------------------------------------ //
    // Inputs
    reg       clk;
    reg       rst_n;
    reg [7:0] data_in;
    // Outputs
    wire [7:0] data_out;
// ------------------------------------------------------ //
pingpang pingpang_ins
    (
    .clk(clk),
    .rst_n(rst_n),
    .data_in(data_in),   // 输入数据
    .data_out(data_out)   // 输出数据
    );
// ------------------------------------------------------ //
    initial
    begin
        clk = 0;
        rst_n = 0;
        data_in = 0;
        #100;
        rst_n = 1;
    end
// ------------------------------------------------------ //
    always #10 clk = ~clk;
// ------------------------------------------------------ //
    always @ (posedge clk or negedge rst_n)
    begin
        if(rst_n == 1'b0)
        begin
            data_in <= 8'b0;
        end
        else
        begin
            data_in <= data_in + 1'b1;
        end
    end
// ------------------------------------------------------ //
endmodule

最后是仿真波形:

FPGA四大常用思想与技巧之一乒乓操作的代码实现分享

从仿真波形中可以看出按照0、1、2、3递增的方式输入数据,两个缓存区交替存储数据,最后依次输出数据,不过输出数据会有两个时钟的延迟。