题目:同步FIFO
FIFO:同步FIFO是读时钟与写时钟为同一时钟,在时钟上升沿同时发生读写操作。异步FIFO是读写时钟为不同时钟,读写时钟彼此相互独立。
module Ip_Fifo
#(
parameter DATA_WIDTH = 8,
parameter DATA_DEPTH = 8,
parameter RAM_DEPTH = (1 << DATA_DEPTH)
)
(
input wire clk ,
input wire rst_n ,
input wire [DATA_WIDTH: 1] data_in ,
input wire wr_req ,
input wire rd_req ,
output reg [DATA_WIDTH: 1] data_out,
output wire empty ,
output wire full
);
reg [DATA_DEPTH-1: 0] data_cnt;
reg [DATA_DEPTH-1: 0] wr_addr, rd_addr;
wire wren_wire, rden_wire;
assign full = (data_cnt == RAM_DEPTH-1)? 1'b1: 1'b0;
assign empty = (data_cnt == 1'b0)?1'b1:1'b0;
assign wren_wire = (wr_req && !full);
assign rden_wire = (rd_req && !empty);
// 写计数
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
wr_addr <= 1'b0;
else if(wr_req && !full)
wr_addr <= wr_addr + 1'b1;
else
wr_addr <= wr_addr;
end
// 读计数
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
rd_addr <= 1'b0;
else if(rd_req && !empty)
rd_addr <= rd_addr + 1'b1;
else
rd_addr <= rd_addr;
end
// data_cnt 计数
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
data_cnt <= 1'b0;
else begin
case({rd_req, wr_req})
2'b00: data_cnt <= data_cnt;
2'b01: begin
if(!full)
data_cnt <= data_cnt + 1'b1;
else
data_cnt <= data_cnt;
end
2'b10: begin
if(!empty)
data_cnt <= dat_cnt - 1'b1;
else
data_cnt <= data_cnt;
end
2'b11: data_cnt <= data_cnt;
default: data_cnt <= data_cnt;
endcase
end
end
// -----------------Syn_Dual_Port_Ram----------------------//
Dual_Ram_Port Dual_Ram_Port_Init
(
.wr_data (data_in ),
.rd_data (data_out ),
.clk (clk ),
.rst (rst_n ),
.wren (wren_wire ),
.rden (rden_wire ),
.wraddr (wr_addr ),
.rdaddr (rd_addr )
);
endmodule
#仿真代码
`timescale 1ps/1ps
module Ip_Fifo_tb();
reg clk, rst_n;
reg [ 7: 0] data_in;
reg wr_req, rd_req;
wire [ 7: 0] data_out;
wire empty, full;
wire [ 7: 0] usedw;
initial clk = 0;
always #10 clk = ~clk;
integer i;
initial begin
rst_n = 0;
wr_req = 0;
rd_req = 0;
data_in = 'd0;
# 100
rst_n = 1;
for(i=0; i<255; i=i+1) begin
wr_req = 1;
data_in = i;
end
wr_req = 0;
for(i=0;i<255;i=i+1) begin
rd_req = 1;
#20;
end
rd_req = 0;
# 200
$stop;
end
Ip_Fifo Ip_Fifo_Init
(
.clk (clk ),
.rst_n (rst_n ),
.data_in (data_in ),
.wr_req (wr_reg ),
.rd_req (rd_req ),
.data_out (data_out ),
.empty (empty ),
.full (full )
);
endmodule
题目:设计异步FIFO
将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很容易出现问题,因为采用二进制计数器时所有位都可能同时变化,在同一个时钟沿同步多个信号的变化会产生亚稳态问题。而使用格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个二进制到gray码的转换电路,将地址值转换为相应的gray码,然后将该gray码同步到另一个时钟域进行对比,作为空满状态的检测。
module Ip_Fifo_k(
input wire Rclk ,
input wire Wclk ,
input wire rst_n ,
input wire wr_req ,
input wire rd_req ,
input wire [ 7: 0] data_in ,
output reg empty ,
output reg full ,
output reg [ 7: 0] data_out
);
// --内部寄存器声明-------------------------------- //
parameter ADDR_WIDTH = 10;
reg [ADDR_WIDTH-1: 0] wr_addr_bin;
reg [ADDR_WIDTH-1: 0] rd_addr_bin;
reg [ADDR_WIDTH-1: 0] wr_addr_gray;
reg [ADDR_WIDTH-1: 0] wr_addr_gray;
reg [ADDR_WIDTH-1: 0] wr_addr_bin_n;
reg [ADDR_WIDTH-1: 0] rd_addr_bin_n;
reg [ADDR_WIDTH-1: 0] wr_addr_gray_n;
reg [ADDR_WIDTH-1: 0] wr_addr_gray_n;
reg [ADDR_WIDTH-1: 0] wclk_raddr_gray_r0;
reg [ADDR_WIDTH-1: 0] wclk_raddr_gray_r1;
reg [ADDR_WIDTH-1: 0] rclk_waddr_gray_r0;
reg [ADDR_WIDTH-1: 0] rclk_waddr_gray_r1;
wire wren_wire, rden_wire;
// - 写地址更新(二进制和格雷码)
always @(posedge Wclk or negedge rst_n) begin
if(!rst_n)
wr_addr_bin <= 10'b0;
else if(wr_req && !full)
wr_addr_bin <= wr_addr_bin_n;
else
wr_addr_bin <= wr_addr_bin;
end
always @(posedge Wclk or negedge rst_n) begin
if(!rst_n)
wr_addr_gray <= 10'b0;
else if(wr_req && !full)
wr_addr_gray <= wr_addr_gray_n;
else
wr_addr_gray <= wr_addr_gray;
end
assign wr_addr_bin_n = wr_addr_bin + 1'b1;
// 二进制转格雷码相当于高位不变,其余每一位和左位异或。
assign wr_addr_gray_n = wr_addr_bin_n ^ {1'b0, wr_addr_bin_n[ADDR_WIDTH-1: 1]};
// - 读地址更新(二进制和格雷码)
always @(posedge Rclk or negedge rst_n) begin
if(!rst_n)
rd_addr_bin <= 10'b0;
else if(rd_req && !empty)
rd_addr_bin <= rd_addr_bin_n;
else
rd_addr_bin <= rd_addr_bin;
end
always @(posedge Rclk or negedge rst_n) begin
if(!rst_n)
rd_addr_gray <= 10'b0;
else if(rd_req && !empty)
rd_addr_gray <= rd_addr_gray_n;
else
rd_addr_gray <= rd_addr_gray;
end
assign rd_addr_bin_n = rd_addr_bin + 1'b1;
assign rd_addr_gray_n = rd_addr_bin_n ^ {1'b0, rd_addr_bin_n[ADDR_WIDTH-1: 1]};
// - 同步写地址到读时钟
always @(posedge Rclk) begin
rclk_waddr_gray_r0 <= wr_addr_gray;
rclk_waddr_gray_r1 <= rclk_waddr_gray_r0;
end
// - 同步读地址到写时钟
always @(posedge Wclk) begin
wclk_raddr_gray_r0 <= rd_addr_gray;
wclk_raddr_gray_r1 <= wclk_raddr_gray_r0;
end
always @(posedge Wclk or negedge rst_n) begin
if(!rst_n)
full <= 1'b0;
else if(wr_addr_gray_n == wclk_raddr_gray_r1 && wr_req)
full <= 1'b1;
else if(wr_addr_gray_n != wclk_raddr_gray_r1)
full <= 1'b0;
else
full <= full;
end
always @(posedge Rclk or negedge rst_n) begin
if(!rst_n)
empty <= 1'b0;
else if(rd_addr_gray_n == rclk_waddr_gray_r1 && rd_req)
empty <= 1'b1;
else if(rd_addr_gray_n != rclk_waddr_gray_r1)
empty <= 1'b0;
else
empty <= empty;
end
assign wren_wire = (full == 1'b1) ? 1'b1 : wr_req;
assign rden_wire = (empty == 1'b1) ? 1'b1: rd_req;
// -----------------Syn_Dual_Port_Ram----------------------//
Dual_Ram_Port Dual_Ram_Port_Init
(
.wr_data (data_in ),
.rd_data (data_out ),
.wclk (Wclk ),
.rclk (Rclk ),
.rst (rst_n ),
.wren (wren_wire ),
.rden (rden_wire ),
.wraddr (wr_addr ),
.rdaddr (rd_addr )
);
endmodule