ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

verilog实现异步fifo

2020-03-02 18:01:04  阅读:279  来源: 互联网

标签:异步 wire addr fifo gaddr verilog rst input reg


首先要理解fifo的写满和读空,用多出来的一位来扩充地址

使用格雷码判断当,写指针追上读指针时候就是写满,为了方便判断当写满时候即,写地址地最高和次高位与读地址相反其他位相同时就是写满,
当读地址的所有位和写地址的所有位相等就是读空

代码根据图可以轻松得到:
下图是地址计数器的增加图

在这里插入图片描述
在这里插入图片描述

写控制器:
module	w_ctrl(
	input	wire		w_clk,//写时钟
	input	wire		rst_n,//复位
	input	wire		w_en,//写使能
	input	wire	[8:0]	r_gaddr,//读时钟域过来的格雷码读地址指针
	output	reg		w_full,//写满标志
	output	wire	[8:0]	w_addr,//256深度的FIFO写二进制地址
	output	wire	[8:0]	w_gaddr//写FIFO地址格雷码编码
);
reg	[8:0]	addr;
reg	[8:0]	gaddr;
wire	[8:0]	addr_wire;
wire	[8:0]	gaddr_wire;
reg	[8:0]	r_gaddr_d1,r_gaddr_d2;
wire		w_full_wire;
//打两拍进行时钟同步
always @(posedge w_clk or negedge rst_n)
	if(rst_n == 1'b0)
		{r_gaddr_d2,r_gaddr_d1} <= 18'd0;
	else 
		{r_gaddr_d2,r_gaddr_d1} <= {r_gaddr_d1,r_gaddr};
//产生些ram的地址指针二进制
assign w_addr = addr;
always @(posedge w_clk or negedge rst_n)
	if(rst_n == 1'b0)
		addr <= 9'd0;
	else 
		addr <= addr_wire;

assign addr_wire = addr + ((~w_full)&w_en);
//转换格雷码编码地址
assign gaddr_wire=(addr_wire>>1)^addr_wire;
always @(posedge w_clk or negedge rst_n)
	if(rst_n == 1'b0)
		gaddr<=9'd0;
	else gaddr <= gaddr_wire;
assign w_gaddr = gaddr;

//写满标志产生完成
always @(posedge w_clk or negedge rst_n)
	if(rst_n == 1'b0)
		w_full <= 1'b0;
	
	else if({~gaddr_wire[8:7],gaddr_wire[6:0]}==r_gaddr_d2)//根据仿真验证一下打两拍对空满标志的影响???
		w_full <=1'b1; //w_full_wire;
	else w_full <=1'b0;


endmodule

在这里插入代码片
读数据:

module	r_ctrl (
	input	wire		r_clk,//读时钟
	input	wire		rst_n,
	input	wire		r_en,//读使能
	input	wire	[8:0]	w_gaddr,//写时钟域中的写地址指针
	output	reg		r_empty,//读空标志
	output	wire	[8:0]	r_addr,//读地址二进制
	output	wire	[8:0]	r_gaddr//读格雷码地址
);
reg	[8:0]	addr;
reg	[8:0]	gaddr;
wire	[8:0]	addr_wire;
wire	[8:0]	gaddr_wire;
reg	[8:0]	w_gaddr_d1,w_gaddr_d2;
wire		r_empty_wire;
//打两拍进行时钟同步
always @(posedge r_clk or negedge rst_n)
	if(rst_n == 1'b0)
		{w_gaddr_d2,w_gaddr_d1} <= 18'd0;
	else 
		{w_gaddr_d2,w_gaddr_d1} <= {w_gaddr_d1,w_gaddr};
//二进制的读地址
assign r_addr = addr;
always @(posedge r_clk  or negedge rst_n)
	if(rst_n == 1'b0)
		addr <=9'd0;
	else 
		addr <= addr_wire;

assign addr_wire = addr + ((~r_empty)&r_en);
//格雷码的读地址
assign r_gaddr = gaddr;
assign	gaddr_wire = (addr_wire >>1 )^ addr_wire;

always @(posedge r_clk or negedge rst_n)
	if(rst_n == 1'b0)
		gaddr <= 9'd0;
	else 
		gaddr <= gaddr_wire;
	
//读空标志的产生
assign r_empty_wire =(gaddr_wire == w_gaddr_d2);
always @(posedge r_clk or negedge rst_n)
	if(rst_n == 1'b0)
		r_empty<=1'b1;
	else //if(gaddr_wire == w_gaddr_d2)//根据仿真验证一下打两拍对空满标志的影响???
		r_empty <= r_empty_wire;
	//else 
		//r_empty <= 1'b0;

endmodule



下面是fifo_mem实现:
module	fifomem(
	input	wire		w_clk,
	input	wire		r_clk,
	input	wire		w_en,//来自于FIFO的写控制模块
	input	wire		w_full,//来自于FIFO的写控制模块
	input	wire	[7:0]	w_data,//来自于外部数据源
	input	wire	[8:0]	w_addr,//来自于FIFO的写控制模块
	input	wire		r_empty,//来自于FIFO的读控制模块
	input	wire	[8:0]	r_addr,//来自于FIFO的读控制模块
	input  wire  	rst_n,
	output	reg	[7:0]	r_data//读数据是从内部ram中读取
);

reg  [7:0]data_mem[255:0];
	wire [7:0]w_addr_r;//定义写入地址
	wire [7:0]r_addr_r;//定义写入地址
	integer i;
	assign w_addr_r = w_addr[7:0];
	assign r_addr_r = r_addr[7:0];
	//写入数据
	always@(posedge w_clk or negedge rst_n)
	
	if(!rst_n)begin	
	
		for(i = 0;i < 256;i = i + 1)begin
			data_mem[i] <= 8'b0;
		end
	end
		
	else if(w_en &&(!w_full))//防止写溢出,如果已经满了禁止写数据
		data_mem[w_addr_r] <= w_data;
	//读出数据
always@(posedge r_clk or negedge rst_n)
	if(!rst_n)
		r_data <= 0;
	else
		r_data <=data_mem[r_addr_r];
endmodule

下面是顶层模块:
module	ex_fifo(
	input	wire		w_clk,
	input	wire		r_clk,
	input	wire		rst_n,
	input	wire		w_en,
	input	wire	[7:0]	w_data,
	output	wire		w_full,
	input	wire		r_en,
	output	wire	[7:0]	r_data,
	output	wire		r_empty
);
wire	[8:0]	r_gaddr;
wire	[8:0]	w_addr; 
wire	[8:0]	w_gaddr;
wire	[8:0]	r_addr;
w_ctrl	w_ctrl_inst(
	.w_clk		(w_clk),//写时钟
	.rst_n		(rst_n),//复位
	.w_en		(w_en),//写使能
	.r_gaddr	(r_gaddr),//读时钟域过来的格雷码读地址指针
	.w_full		(w_full),//写满标志
	.w_addr		(w_addr),//256深度的FIFO写二进制地址
	.w_gaddr	(w_gaddr)//写FIFO地址格雷码编码
);

fifomem	fifomem_inst(
	.w_clk		(w_clk),
	.r_clk		(r_clk),
	.w_en		(w_en),//来自于FIFO的写控制模块
	.w_full		(w_full),//来自于FIFO的写控制模块
	.w_data		(w_data),//来自于外部数据源
	.w_addr		(w_addr),//来自于FIFO的写控制模块
	.r_empty	(r_empty),//来自于FIFO的读控制模块
	.r_addr		(r_addr),//来自于FIFO的读控制模块
	.r_data		(r_data),//读数据是从内部ram中读取
	.rst_n(rst_n)
);

r_ctrl r_ctrl_inst(
	.r_clk		(r_clk),//读时钟
	.rst_n		(rst_n),
	.r_en		(r_en),//读使能
	.w_gaddr	(w_gaddr),//写时钟域中的写地址指针
	.r_empty	(r_empty),//读空标志
	.r_addr		(r_addr),//读地址二进制
	.r_gaddr	(r_gaddr)//读格雷码地址
);

endmodule

下面是testbench:

`timescale	1ns/1ns

module	tb_ex_fifo;
reg		r_clk,w_clk,rst_n;
reg		w_en;
reg	[7:0]	w_data;
reg		r_en;
wire		w_full;
wire		r_empty;
wire	[7:0]	r_data;

parameter	CLK_P=20;

initial begin
	rst_n<=0;
	r_clk<=0;
	w_clk<=0;
	
	#200
	rst_n=1;
end
//写的初始化模块
initial	begin
	w_en=0;
	w_data=0;
	#300
	write_data(256);
end
//读的初始化模块
initial begin
	r_en =0;
	@(posedge w_full);
	#40;
	read_data(256);
end

always #(CLK_P/2) r_clk =~r_clk;
always #(CLK_P/2) w_clk =~w_clk;

ex_fifo ex_fifo_inst(
	.w_clk		(w_clk),
	.r_clk		(r_clk),
	.rst_n		(rst_n),
	.w_en		(w_en),
	.w_data		(w_data),
	.w_full		(w_full),
	.r_en		(r_en),
	.r_data		(r_data),
	.r_empty	(r_empty)
);


task write_data(len);
	integer i,len;
	begin
		for (i=0;i<len;i=i+1)
		begin
			@(posedge w_clk);
			w_en=1'b1;
			w_data=i;
		end
		@(posedge w_clk);
		w_en = 1'b0;
		w_data =0;
	end
endtask

task read_data(len);
	integer i,len;
	begin
		for (i=0;i<len;i=i+1)
		begin
			@(posedge r_clk);
			r_en=1'b1;
		end
		@(posedge r_clk);
		r_en = 1'b0;
	end
endtask
endmodule


自己用modelsim仿真脚本如下:

quit -sim
.main clear

vlib work

vlog	./tb_ex_fifo.v
vlog	./../design/*.v


vsim	-voptargs=+acc	work.tb_ex_fifo

add wave tb_ex_fifo/*
add wave tb_ex_fifo/ex_fifo_inst/*
add wave -divider {w}
add wave tb_ex_fifo/ex_fifo_inst/w_ctrl_inst/*
add wave -divider {R}
add wave tb_ex_fifo/ex_fifo_inst/r_ctrl_inst/*

run 25us
qq_43210117 发布了4 篇原创文章 · 获赞 0 · 访问量 12 私信 关注

标签:异步,wire,addr,fifo,gaddr,verilog,rst,input,reg
来源: https://blog.csdn.net/qq_43210117/article/details/104616240

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有