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