首先,这个vivado的fifo和quartus有很大不同。
用BRAM来实现异步fifo。
vivado的fifo有复位,在时钟信号稳定后,复位至少三个时钟周期(读写端口的慢时钟),复位完成后30个时钟周期后再进行写操作(慢时钟)。
有两个模式:标准模式和预读模式。
标准模式,读出的数据会比读使能延后一个时钟周期,fifo的深度也会比配置的少一个。
预读模式,读出的数据会与读使能同步,深度会比配置的多一个。
犯下的错误:顶层模块,fifo的复位接到了系统复位上。
没有认真阅读正点原子开发指南,忽略了深度的问题。
模块框图:
时序图:
代码:
module fifo_wr_ctrl(
input wire sys_clk , // clk_wr // 50Mhz
input wire sys_rst_n ,
output wire rst_fifo ,
output wire wr_clk ,
output wire [7:0] wr_din ,
output reg wr_en
);
// parameter
parameter RST_FIFO_CNT = 3 ,
RST_WAIT_CNT = 30 ,
DATA_IN_CNT = 200 ; // 设置深度256,但实际深度只有255.写进数据0~254
// localparam
localparam RST = 4'b0001 ,
RST_WAIT = 4'b0010 ,
DATA_IN_S = 4'b0100 ,
FINISH_S = 4'b1000 ;
// reg signal define
reg [7:0] cnt_core ;
reg finish ;
reg [3:0] state ;
// wire signal define
wire rst_flag ;
wire wait_flag ;
wire data_over_flag ;
// reg [7:0] cnt_core ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_core <= 8'd0 ;
else if(rst_flag || wait_flag || data_over_flag || finish)
cnt_core <= 8'd0 ;
else
cnt_core <= cnt_core + 1'b1 ;
end
// reg finish ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
finish <= 1'b0 ;
else if(data_over_flag)
finish <= 1'b1 ;
else
finish <= finish ;
end
// reg [3:0] state ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
state <= 4'b0001 ;
else
case (state)
RST : if(rst_flag)
state <= RST_WAIT ;
else
state <= RST ;
RST_WAIT : if(wait_flag)
state <= DATA_IN_S ;
else
state <= RST_WAIT ;
DATA_IN_S : if(data_over_flag)
state <= FINISH_S ;
else
state <= DATA_IN_S ;
FINISH_S : state <= FINISH_S ;
default : state <= RST ;
endcase
end
// wire rst_flag ;
assign rst_flag = ((cnt_core == (RST_FIFO_CNT - 1)) && (state == RST)) ;
// wire wait_flag ;
assign wait_flag = ((cnt_core == (RST_WAIT_CNT - 1)) && (state == RST_WAIT)) ;
// wire data_over_flag ;
assign data_over_flag = ((cnt_core == (DATA_IN_CNT - 1)) && (state == DATA_IN_S)) ;
// output reg rst_fifo ,
// always @(posedge sys_clk or negedge sys_rst_n) begin
// if(~sys_rst_n)
// rst_fifo <= 1'b1 ;
// else if(state == RST && rst_flag)
// rst_fifo <= 1'b1 ;
// else if(state == RST)
// rst_fifo <= 1'b0 ;
// else
// rst_fifo <= 1'b1 ;
// end
assign rst_fifo = (state == RST) ? 1'b1 : 1'b0 ;
// output wire wr_clk ,
assign wr_clk = (sys_rst_n) ? sys_clk : 1'b0 ;
// output wire [7:0] wr_din ,
assign wr_din = (state == DATA_IN_S) ? cnt_core : 8'd0 ;
// output reg wr_en ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
wr_en <= 1'b0 ;
else if(wait_flag || data_over_flag)
wr_en <= ~wr_en ;
else
wr_en <= wr_en ;
end
endmodule
module fifo_rd_ctrl(
input wire sys_clk ,// clk_rd
input wire sys_rst_n ,
input wire wr_full ,
input wire almost_empty,// 将要读空
output reg rd_en ,
output wire rd_clk
);
assign rd_clk = (sys_rst_n) ? sys_clk : 1'b0 ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
rd_en <= 1'b0 ;
else if(almost_empty) // 将要读空后拉低读使能。因为是时序逻辑,应该正好读空fifo
rd_en <= 1'b0 ;
else if(wr_full) // 写满后拉高写使能
rd_en <= 1'b1 ;
else
rd_en <= rd_en ;
end
endmodule
module top(
input wire sys_clk ,
input wire sys_rst_n ,
output wire [7:0] data_out
);
// 例化间连线
wire clk_100Mhz ;
wire clk_50Mhz ;
wire locked ;
wire rst_n ;
wire rst_fifo ; // fifo的复位信号
wire wr_clk ; // 写端口相关信号
wire [7:0] wr_din ; // 写端口相关信号
wire wr_en ; // 写端口相关信号
wire [7:0] dout ;
wire full ;
wire almost_full ;
wire empty ;
wire almost_empty;
wire [7:0] rd_data_count;
wire [7:0] wr_data_count;
wire wr_rst_busy ;
wire rd_rst_busy ;
wire rd_en ;
wire rd_clk ;
mmcm_100M_50M mmcm_100M_50M_inst (
.resetn ( sys_rst_n ) ,
.clk_in1 ( sys_clk ) ,
.clk_out1 ( clk_100Mhz ) ,
.clk_out2 ( clk_50Mhz ) ,
.locked ( locked )
);
assign rst_n = sys_rst_n && locked ;
fifo_wr_ctrl fifo_wr_ctrl_inst(
.sys_clk ( clk_50Mhz ) , // clk_wr // 50Mhz
.sys_rst_n ( rst_n ) ,
.rst_fifo ( rst_fifo ) ,
.wr_clk ( wr_clk ) ,
.wr_din ( wr_din ) ,
.wr_en ( wr_en )
);
fifo_rd_ctrl fifo_rd_ctrl_inst(
.sys_clk ( clk_100Mhz ) ,// clk_rd
.sys_rst_n ( rst_n ) ,
.wr_full ( full ) ,
.almost_empty ( almost_empty ) ,// 将要读空
.rd_en ( rd_en ) ,
.rd_clk ( rd_clk )
);
fifo_256X8 fifo_256X8_inst(
.rst ( rst_fifo ) , // 在fpga配置完成后,fifo必须要进行复位操作�?�复位信号至少保�?3个时钟周期以慢时钟为准�?�复位完成后至少经过30个时钟周期后,才能进行数据写操作�?
.wr_clk ( wr_clk ) , // 写数据时�?50Mhz // 复位高有效�??
.rd_clk ( rd_clk ) , // 读数据时�?100Mhz
.din ( wr_din ) , // 写入数据
.wr_en ( wr_en ) , // 写使�?
.rd_en ( rd_en ) , // 读使�?
.dout ( data_out ) , // 输出数据
.full ( full ) , // 写满
.almost_full ( almost_full ) , // 将写�?
.empty ( empty ) , // 读空
.almost_empty ( almost_empty ) , // 将读�?
.rd_data_count ( rd_data_count ) , // 可读数据
.wr_data_count ( wr_data_count ) , // 已写数据
.wr_rst_busy ( wr_rst_busy ) , // 写复位忙�?
.rd_rst_busy ( rd_rst_busy ) // 读复位忙�?
);
endmodule
`timescale 1ns/1ns
module test_top();
reg sys_clk ;
reg sys_rst_n ;
wire [7:0] data_out ;
top top_inst(
.sys_clk ( sys_clk ) ,
.sys_rst_n ( sys_rst_n ) ,
.data_out ( data_out )
);
parameter CYCLE = 20 ;
initial begin
sys_clk = 1'b1 ;
sys_rst_n <= 1'b0 ;
#( CYCLE * 5) ;
sys_rst_n <= 1'b1 ;
#(3000*CYCLE) ;
$stop;
end
always #(CYCLE/2) sys_clk = ~sys_clk ;
endmodule