同步FIFO设计思路
- 方法1:使用计数器记录FIFO有效数据,从而产生空满信号
- 方法2:指针空间扩大一倍,读写指针最高位相同为空,最高位不同,剩下数据位相同为满
异步FIFO设计
- 读写指针分别在各自的时钟域进行维护
- 读空的时候需要在读时钟域进行判断,写满的时候需要在写时钟域进行判断,所以需要将读写指针同步给对方
- 读指针同步到写时钟域,写指针同步到读时钟域?读写指针都是多bit信号,所以不能直接通过直接打两拍的方式进行同步,使用格雷码进行实现
- 不使用格雷码,有些bit同步快,有些bit同步慢,所以会产生中间状态,影响电路功能
使用格雷码进行转换
- 相邻两个数之间格雷码只有1bit发生变化
- 格雷码产生:gray_code = din ^ (din >> 1),原数据异或右移1位的数据
- 用指针的格雷码判断空满情况:如果写指针和读指针最高两位不等,其余位全相等表示满,如果读指针和写指针的全相等表示空
RTL
fifo.v
wr_full.v
rd_empty.v
sync_2_stage.v
fifo_mem.v
fifo.v
定义参数
- ADDRSIZE - 地址位宽(和FIFO的深度有关)
- DATASIZE - 数据位宽
- SD_SLAVE_FIFO_DEPTH - FIFO深度
- ahb_soft_rst_n - 是同步复位
- ahb_fifo_rst_n和sd_fifo_rst_n是异步复位信号
- blk_len输入的作用:当前fifo是1k(256深度*数据位宽32bit)的容量,sd host传输数据一个block length的容量是512byte,也就是半满状态就可以认为是fifo满了,所以需要block length产生判断半满的信号
- bist测试 - 因为使用的是双端口的RAM,所以BIST逻辑分为a,b两端
中间信号根据coding过程进行书写
sync_two_stage.v
- 两级同步器,将读写指针转变为格雷码表示之后,经过两级同步器给到读时钟域和写时钟域
module sync_two_stage
#(
parameter UDLY = 1,
parameter ADDRSIZE = 8,
parameter DATASIZE = 32,
parameter SD_SLAVE_FIFO_DEPTH = 256
)
(
input wire clk ,
input wire rst_n ,
input wire clr ,
input wire [ADDRSIZE:0] ptr_reg ,
output reg [ADDRSIZE:0] ptr_s2_reg
);
reg [ADDRSIZE:0] ptr_s1_reg;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
ptr_s1_reg <= #UDLY 0;
ptr_s2_reg <= #UDLY 0;
end
else if(clr) begin
ptr_s1_reg <= #UDLY 0;
ptr_s2_reg <= #UDLY 0;
end
else begin
ptr_s1_reg <= #UDLY ptr_reg;
ptr_s2_reg <= #UDLY ptr_s1_reg;
end
end
endmodule
- 在sd host项目中,因为fifo模块两端都是可以进行读写操作的,所以需要四个同步器,例化四个两级同步器,同步读写指针信号
- clr信号就是复位信号,由fifo.v模块产生之后输入
- 需要同步的指针由rd_empty.v模块产生
rd_empty.v
- 在读时钟域下产生读空信号,并且维护读指针给到RAM进行读取数据
- 当读使能有效的时候并且没有读空的时候,读指针加1
- 将读指针的二进制数转成格雷码的形式,将格雷码形式的读地址(读指针)输出
- 空信号判断:转变为格雷码之后的读指针和输入的写指针地址是否一致,一致表示空,否则表示满
module rd_empty
#(
parameter UDLY = 1,
parameter ADDRSIZE = 8,
parameter DATASIZE = 32,
parameter SD_SLAVE_FIFO_DEPTH = 256
)
(
input wire clk ,
input wire rst_n ,
input wire clr ,
input wire rinc ,
input wire [ADDRSIZE:0] wptr_s2_reg ,
output wire [ADDRSIZE-1:0] raddr ,
output reg [ADDRSIZE:0] rptr_reg ,
output reg rd_empty
);
reg [ADDRSIZE:0] rptr_bin;
wire empty_val;
wire [ADDRSIZE:0] rptr_bin_nxt; // binary pointer
wire [ADDRSIZE:0] rptr_gray_nxt; // gray code pointer
wire [ADDRSIZE:0] rptr_gray_temp_nxt;
// 产生二进制读指针
assign rptr_bin_nxt = rptr_bin_nxt + {8'd0,(rinc && !rd_empty)};
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
rptr_bin <= 0;
else if(clr)
rptr_bin <= 0;
else
rptr_bin <= rptr_bin_nxt;
end
assign rptr_gray_nxt = (rptr_bin_nxt >> 1) ^ rptr_bin_nxt;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
rptr_reg <= 0;
else if(clr)
rptr_reg <= 0;
else
rptr_reg <= rptr_gray_nxt;
end
// 读地址
assign raddr = rptr_bin[ADDRSIZE-1:0];
assign empty_val = (rptr_gray_nxt == wptr_s2_reg);
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
rd_empty <= 0;
else if(clr)
rd_empty <= 0;
else
rd_empty <= empty_val;
end
endmodule
wr_full.v
module wr_full
#(
parameter UDLY = 1,
parameter ADDRSIZE = 8,
parameter DATASIZE = 32,
parameter SD_SLAVE_FIFO_DEPTH = 256
)
(
input wire clk ,
input wire rst_n ,
input wire clr ,
input wire winc ,
input wire [ADDRSIZE:0] rptr_s2_reg ,
input wire [ADDRSIZE:0] blk_len , // wfull半满即为满
output wire [ADDRSIZE-1:0] waddr ,
output reg [ADDRSIZE:0] wptr_reg ,
output reg wr_full
);
reg [ADDRSIZE:0] wptr_bin;
wire full_val;
wire [ADDRSIZE:0] wptr_bin_nxt; // binary pointer
wire [ADDRSIZE:0] wptr_gray_nxt; // gray code pointer
wire [ADDRSIZE:0] wptr_gray_temp_nxt;
wire [ADDRSIZE:0] wptr_temp;
// 产生二进制读指针
assign wptr_bin_nxt = wptr_bin_nxt + {8'd0,(winc && !wr_full)};
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
wptr_bin <= 0;
else if(clr)
wptr_bin <= 0;
else
wptr_bin <= wptr_bin_nxt;
end
// fifo深度 - block length
assign wptr_temp = wptr_bin_nxt + (SD_SLAVE_FIFO_DEPTH - blk_len);
// wptr_gray_nxt用于产生写指针,发送给另一个时钟域
assign wptr_gray_nxt = (wptr_bin_nxt >> 1) ^ wptr_bin_nxt;
// wptr_gray_temp_nxt - 用于判断是否写满
assign wptr_gray_temp_nxt = (wptr_temp >>1) ^ wptr_temp; //wptr_gray_nxt
// 输出写指针同步到读时钟域
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
wptr_reg <= 0;
else if(clr)
wptr_reg <= 0;
else
wptr_reg <= wptr_gray_nxt;
end
// 输出写地址给到RAM
assign waddr = wptr_bin[ADDRSIZE-1:0];
// 判断满信号,并输出出去
assign full_val = (wptr_gray_temp_nxt == {~rptr_s2_reg[ADDRSIZE:ADDRSIZE-1],rptr_s2_reg[ADDRSIZE-2:0]});
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
wr_full <= 1'b0;
else if(clr)
wr_full <= 1'b0;
else
wr_full <= full_val;
end
endmodule
fifo_mem.v
- fifo模块中例化存储体,将时钟复位\读写使能\读写数据的端口连接起来