乒乓操作是FPGA设计中经常用到的设计思想,常用于需要提高数据效率的地方。其主要特点有:
1、 实现数据的无缝缓冲和处理;
2、 可节约缓冲区空间;
3、 可实现低速模块处理高速模块。
典型的乒乓操作原理如下图:
如图所示:
-
T1时刻,DATA_T1存入buffer 1;
-
T2时刻,buffer 1已被写满,DATA_T2存入buffer 2, 同时buffer 1将DATA_T1送至运算模块处理;
-
T3时刻,DATA_T3存入buffer A1,同时buffer 2将DATA_T2送至运算模块处理;
-
然后重复2、3阶段的操作。
从上图也可以看出乒乓操作的核心就是控制缓冲模块的读写,具体可以细化为输入数据控制模块、缓冲模块1、缓冲模块2以及输出数据控制模块,这里的缓冲模块可以是FIFO、BRAM等。下面举例具体说明:
-
输入不连续的数据字节,工作时钟要求是50Mhz;
-
输出要求每次16字节连续输出,并且有一个信号作为数据有效指示;
-
每次输出之间间隔时间可以是随机的,一般是至少不低于一个时钟周期的间隔;
思考:
1、 数据为8位且16个后输出,所以考虑用16*8的BRAM做缓冲模块。
2、 两个缓冲模块写控制考虑用5位计数器,其中最高位控制不同缓冲模块的写,低4位代表写地址
3、 缓冲模块的读控制需要判断缓冲模块是否写满,利用写计数器最高位的上升沿和下降沿判断:上升沿代表buffer 1写满,下降沿代表buffer 2写满。
4、 注意输出数据流单元数据与数据有效指示同步的关系(不同缓冲模块,数据有效指示做不同的同步处理)
基于上述考虑画出时序图,如下:
其对应的电路图如下:
对应的verilog描述如下:
`timescale 1ns/10ps
module pingpang_operation (
rst_n ,
clk_50m ,
data_in , // 输入数据
data_valid , // 输入数据有效信号
data_out , // 输出数据
data_out_vld
// 输出数据有效信号
);
input rst_n ;
input
clk_50m ;
input
[7:0] data_in ; // 输入数据
input data_valid ; // 输入数据有效信号
output [7:0]
data_out ; // 输出数据
output
data_out_vld ;
// 输出数据有效信号
reg [4:0] wr_cnt;
// ram A and ram B 的写地址
reg wr_cnt4_d;
// wr_cnt[4] 寄存一拍
wire wr_a_en; // ram A 的写使能
wire wr_b_en; //
ram B 的写使能
reg rd_a_en; // ram A 的读使能
reg rd_a_en_d1,
rd_a_en_d2; // ram A 的读使能寄存2拍
reg [3:0] rd_a_addr; // ram
A 的读地址
reg rd_b_en; // ram B 的读使能
reg rd_b_en_d1, rd_b_en_d2; // ram
B 的读使能寄存2拍
reg [3:0] rd_b_addr; // ram
B 的读地址
wire [7:0] rd_a_data; // ram
A 的读数据
wire [7:0] rd_b_data; // ram
B 的读数据
wire [7:0] data_out;
// 数据输出
wire data_out_vld; // 数据输出有效信号
//------------------------------------------------------
// ramA和ramB的写电路模块设计
//------------------------------------------------------
//设计ramA 和ramB的写地址
always
@(posedge clk_50m or negedge rst_n)
begin
if (!rst_n)
wr_cnt <= 5'h00;
else if(data_valid)
wr_cnt <= wr_cnt + 5'd1;
end
// 设计ram A 和 ram B的写使能
assign wr_a_en = ~wr_cnt[4] & data_valid;
assign wr_b_en = wr_cnt[4]
& data_valid;
//------------------------------------------------------
// ramA和ramB的读电路模块设计
//------------------------------------------------------
//ram_a或ram_b写完成后才能进行相应的读
always @(posedge clk_50m or negedge rst_n)
begin
if (!rst_n)
wr_cnt4_d <= 1'b0;
else
wr_cnt4_d
<= wr_cnt[4];
end
wire wr_cnt4_rise,wr_cnt4_down;
assign wr_cnt4_rise
= wr_cnt[4] & ~wr_cnt4_d;//ram_a 可读
assign wr_cnt4_down
= ~wr_cnt[4] & wr_cnt4_d;//ram_b 可读
//reg [3:0] cnt_rd;
//always @(posedge clk_50m or negedge
rst_n)
// begin
// if (!rst_n)
// cnt_rd <= 4'd0;
// else if(wr_cnt4_rise | wr_cnt4_down)
// cnt_rd <= cnt_rd + 4'd1;
//
end
//ramA的
读使能
always
@(posedge clk_50m or negedge rst_n)
begin
if (!rst_n)
rd_a_en <= 1'b0;
else if(wr_cnt4_rise)
rd_a_en <= 1'b1;
else if(rd_a_addr==4'hf)//else
if(cnt_cnt==4'd15)
rd_a_en <= 1'b0;
end
//ramA的 读地址
always
@(posedge clk_50m or negedge rst_n)
begin
if (!rst_n)
rd_a_addr <= 4'h0;
else if (rd_a_en)
rd_a_addr <= rd_a_addr +4'h1;
end
//ramB的 读使能
always
@(posedge clk_50m or negedge rst_n)
begin
if (!rst_n)
rd_b_en <= 1'b0;
else if(wr_cnt4_down)
rd_b_en <= 1'b1;
else if(rd_b_addr==4'hf)//else
if(cnt_cnt==4'd15)
rd_b_en <= 1'b0;
end
//ramB的 读地址
always
@(posedge clk_50m or negedge rst_n)
begin
if (!rst_n)
rd_b_addr <= 4'h0;
else if (rd_b_en)
rd_b_addr <= rd_b_addr +4'h1;
end
//------------------------------------------------------
// ramA和ramB的读数据输出电路
//------------------------------------------------------
//ramA的 读使能寄存2拍
always
@(posedge clk_50m or negedge rst_n)
begin
if (!rst_n)
begin
rd_a_en_d1 <= 1'b0;
rd_a_en_d2 <= 1'b0;
end
else
begin
rd_a_en_d1 <= rd_a_en;
rd_a_en_d2 <= rd_a_en_d1;
end
end
//ramB的 读使能寄存2拍
always
@(posedge clk_50m or negedge rst_n)
begin
if (!rst_n)
begin
rd_b_en_d1 <= 1'b0;
rd_b_en_d2 <= 1'b0;
end
else
begin
rd_b_en_d1 <= rd_b_en;
rd_b_en_d2 <= rd_b_en_d1;
end
end
//读数据输出
assign data_out =
rd_a_en_d2 ? rd_a_data : rd_b_data;
//读数据有效信号
assign data_out_vld = rd_a_en_d2
| rd_b_en_d2;
//------------------------------------------------------
// ramA和ramB
//------------------------------------------------------
//
ram A 16 X 8bits ,ram的读数据比读地址和读使能延迟2T
ram_ly
ram_a(
.clock (clk_50m), //时钟
.data (data_in), //写数据
.rdaddress(
rd_a_addr), //读地址
.rden (rd_a_en), //读使能
.wraddress(wr_cnt[3:0]
), //写地址
.wren (wr_a_en ), //写使能
.q (rd_a_data) //读数据
);
//
ram B 16 X 8bits
ram_ly
ram_b(
.clock (clk_50m ), //时钟
.data (data_in), //写数据
.rdaddress(rd_b_addr), //读地址
.rden (rd_b_en), //读使能
.wraddress(wr_cnt[3:0]), //写地址
.wren (wr_b_en ), //写使能
.q (rd_b_data) //读数据
);
endmodule
至此完成FPGA乒乓操作的设计。
本设计完整工程下载地址:链接:https://pan.baidu.com/s/19ZjRFEDLXUhPrV6gtn9uEA
提取码:1z44
另外,感谢恩师李煜老师。