fifo知识整理
说明:均为网上资源整理,如有版权问题请联系删除
同步FIFO
21.跨时钟域
时钟的时序特性包括时钟周期、时钟占空比、时钟转换时间、时钟延迟、时钟偏斜和时钟抖动。
21.1格雷码与二进制转换
格雷码转二进制:
n位的二进制:Bn, Bn-1, Bn-2 … B2, B1, B0;
n位的格雷码:Gn, Gn-1, Gn-2 … G2, G1, G0;
转换公式: Bn =Gn;
Bi-1 = Bi ^ Gi-1;( i=0,1,2,n-1;
产生任意深度的 FIFO:
(1)格雷码是对称的,去掉中间几位格雷码,可以达到首尾相差 1 位,深度为偶数。
(2)深度为一般值,自行设计逻辑电路,或查找表。格雷码的 bus skew 不能超过一个周期,否则格雷码多位数据跳变失去作用。格雷码是对称的,去掉中间的或者去掉两头可以产生循环码。
Gray = Bin^(Bin>>1);错位异或的结果,(二进制转格雷码)
错位异或^ 异或:相同为0,相异为1
21.2同步fifo
同步fifo:同一时钟域下的,同时用于写入和读取操作。同步FIFO用于临时存储数据,此时写人和读取操作可以同时发生,也可发生在不同时刻。
clk : 该时钟为同步FIFO读写操作的工作时钟。
rst_n : 该信号为同步FIFO的复位信号,低电平有效。
wren : 该信号为同步FIFO的写使能。
rden : 该信号为同步FIFO的读使能。
wdata : 该总线为写数据总线。
rdata : 该总线为读数据总线。
full : 该信号为FIFO已满标志。如果 FIFO 为满状态,则禁止再写数据。
empty : 该信号为FIFO已空标志。如果 FIFO 为空状态,则禁止再读数据
同步FIFO结构:memory、写控制逻辑、读控制逻辑、空满标志判断
在 FIFO 中常用的RAM包括单口RAM、简单双口RAM、真双口RAM、单口ROM、双口ROM这5种类型的RAM,也可以使用寄存器来实现FIFO的存储器。
wclk : 该时钟为双口RAM写操作的工作时钟。
wren : 该信号为双口RAM的写使能。
waddr : 该总线为双口RAM的写地址总线。
wdata : 该总线为双口RAM的写数据总线。
rclk : 该时钟为双口RAM读操作的工作时钟。
rden : 该信号为双口RAM的读使能。
raddr : 该总线为双口RAM的读地址总线。
rdata: 该总线为双口RAM的读数据总线。
写控制逻辑----------------------------------------------------------------------------
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
waddr <= {
ADDR_WIDTH{
1'b0}};
else if (wren && !wfull) //只有写使能并且没写满的时候才能操作写地址
begin
if(waddr == DEPTH - 1) //如果快要计数到最大,则清零重新给读地址。
waddr <= {
ADDR_WIDTH{
1'b0}};
else
waddr <= waddr + 1'd1;
end
else
waddr <= waddr;
end
读控制逻辑----------------------------------------------------------------------------
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
raddr <= {
ADDR_WIDTH{
1'b0}};
else if (rden && !rempty)//只有读使能并且没读空的时候才能操作读地址
begin
if(raddr == DEPTH - 1)//如果快要计数到最大,则清零重新给读地址。
raddr <= {
ADDR_WIDTH{
1'b0}};
else
raddr <= raddr + 1'd1;
end
else
raddr <= raddr;
end
空满标志产生逻辑(两种解法)----------------------------------------------------------------------------
一:用地址计数器addr_cnt,在执行一次写操作时addr_cnt加1,执行一次读操作时addr_cnt减1。
1.如何操作地址计数器
always@(posedge clk or negedge rst_n)
begin
if(!rst_n) //复位时为0
addr_cout <= {
(ADDR_WIDTH){
1'b0}};
else if(wren && !rden && !wfull && (addr_cout < DEPTH-1))
//条件为写使能,不读使能,并且没写满时,地址<深度减一
防止在读写使能同时有效,FIFO已经存满时读使能无效,造成地址计数器向上溢出,产生错误的读空标志。
addr_cout <= addr_cout + 1'd1;
else if(!wren&& rden && !rempty && (addr_cout > 0))
//条件不写使能,读使能,并且没读空时,地址>0
防止在读写使能同时有效,FIFO只有1位时(此时地址计数器值为0),写使能无效,造成地址计数器向下溢出,产生错误的写满标志.
addr_cout <= addr_cout - 1'd1;
else
addr_cout <= addr_cout;
End
2.写满标志位产生
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
wfull <= 1'b0;
else
wfull <= ((!rinc) && (((addr_cout== DEPTH-2)&&winc)||(addr_cout== DEPTH-1)));
end
当读使能无效,地址计数器计数值等于FIFO的深度减1时,写满标志有效。
当读使能无效,地址计数器计数值等于FIFO的深度减2,写使能有效时。写满标志有效。
3.读空标志位产生
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
rempty <= 1'b1;
else
rempty <= ((!winc) && (((addr_cout== 1)&&rinc)||(addr_cout==0)));
end
当写使能无效,地址计数器计数值等于0时,读空标志有效。
当写使能无效,地址计数器计数值等于1时,读使能有效时。读空标志有效。
module sync_fifo#(
parameter WIDTH = 8,
parameter DEPTH = 16
)(
input clk , //读写时钟
input rst_n , //异步复位
input wren , //写使能
input rden , //读使能
input [WIDTH-1:0] wdata , //写数据
output wire wfull , //写满信号
output wire rempty , //读空信号
output wire [WIDTH-1:0] rdata //读数据
);
/********************** 内部信号声明 **********************/
localparam ADDR_WIDTH = $clog2(DEPTH); //地址位宽
wire wenc ; //双端口RAM写使能
wire renc ; //双端口RAM读使能
reg [ADDR_WIDTH:0] waddr ; //写地址寄存器
reg [ADDR_WIDTH:0] raddr ; //读地址寄存器
/*************************功能定义*************************/
assign wenc = wren && !wfull;
assign renc = rden && !rempty;
//双端口RAM
dual_port_RAM #(.DEPTH(DEPTH),.WIDTH(WIDTH))
dual_port_RAM_U1
(
.wclk (clk ), //写数据时钟
.wenc (wenc ), //写使能
.