开源Verilog HDL原创FIFO模块
本帖最后由 skyxjh 于 2013-6-23 23:43 编辑开源Verilog HDL原创FIFO模块,包含测试文件,请勿用于商业用途,用于出版或教程请联系本人。
module fifo(rst,iclk,ien,oclk,oen,idat,odat,full,empty);
parameter datbits = 8, addrbits = 4;
parameter low = 1'b0, hi = 1'b1, true = low, false = hi;
input rst,iclk,oclk,ien,oen; //复位,输入时钟、输出时钟,输入使能,输出使能
input idat; //输入数据
output odat; //输出数据
output full,empty; //fifo满,空
reg mem; //2^addrbits字节RAM块
reg rdaddr = {addrbits{1'b1}}; //fifo读地址
reg wraddr = {addrbits{1'b0}}; //fifo写地址
wire delt = wraddr - rdaddr; //有效数据数+1
assign full = delt == 1'b0 ? true : false; //fifo满信号
assign empty = delt == 1'b1 ? true : false; //fifo空信号
assign odat = oen == true ? mem : {datbits{1'bz}}; //输出控制
initial begin //初始化
wraddr = {addrbits{1'b0}}; //写地址复位
rdaddr = {addrbits{1'b1}}; //读地址复位
end //初始化
always @(negedge iclk or negedge rst) begin //push
if(rst == low) begin //复位
wraddr <= {addrbits{1'b0}}; //写地址复位
end
else begin //进栈
if(ien == true) begin //输入使能
mem <= idat; //进栈
wraddr <= wraddr + 1'b1; //写地址调整
end
end //进栈
end //push
always @(negedge oclk or negedge rst) begin //pop
if(rst == low) begin //复位
rdaddr <= {addrbits{1'b1}}; //读地址复位
end
else begin //出栈
if(oen == true) begin //输出使能
rdaddr <= rdaddr + 1'b1; //读地址调整
end
end //出栈
end //pop
endmodule
`timescale 1ps/1ps
module testbench();
parameter low = 1'b0, hi = 1'b1, true = low, false = hi;
wire odat;
wire ick,ock;
reg iclk,oclk;
assign ick = iclk;
assign ock = oclk;
wire iena,oena;
reg ien,oen;
assign iena = ien;
assign oena = oen;
wire full,empty;
wire idat;
reg dat;
assign idat = dat;
always #1 iclk <= ~iclk;
always #3 oclk <= ~oclk;
initial begin
iclk = hi;
oclk = hi;
dat = 8'd0;
end
always @(posedge iclk) begin
if(full == true) ien <= false;
else begin
ien <= true;
dat <= dat + 1'b1;
end
end
always @(posedge oclk) begin
if(empty == true) oen <= false;
else oen <= true;
end
fifo #(8,3) f1(hi,ick,iena,ock,oena,idat,odat,full,empty);
endmodule
make xiexie 学习了,谢谢。 感谢分享,收藏。 感谢楼主无私奉献,收藏了 感谢楼主无私奉献,收藏FIFO模块 一楼是同步FIFO模块,改写了一个异步FIFO模块,使用更方便。
module fifo(rst,ien,oen,idat,odat,full,empty);
parameter datbits = 8, addrbits = 4;
parameter low = 1'b0, hi = 1'b1, true = low, false = hi;
input rst,ien,oen; //复位,输入使能,输出使能
input idat; //输入数据
output odat; //输出数据
output full,empty; //fifo满,空
reg mem; //2^addrbits字节RAM块
reg rdaddr = {addrbits{1'b1}}; //fifo读地址
reg wraddr = {addrbits{1'b0}}; //fifo写地址
wire delt = wraddr - rdaddr; //有效数据数+1
assign full = delt == 1'b0 ? true : false; //fifo满信号
assign empty = delt == 1'b1 ? true : false; //fifo空信号
assign odat = oen == true ? mem : {datbits{1'bz}}; //输出控制
initial begin //初始化
wraddr = {addrbits{1'b0}}; //写地址复位
rdaddr = {addrbits{1'b1}}; //读地址复位
end //初始化
always @(negedge ien or negedge rst) begin //push
if(rst == low) begin //复位
wraddr <= {addrbits{1'b0}}; //写地址复位
end
else if(full == false) begin //进栈
mem <= idat; //进栈
wraddr <= wraddr + 1'b1; //写地址调整
end //进栈
end //push
always @(negedge oen or negedge rst) begin //pop
if(rst == low) begin //复位
rdaddr <= {addrbits{1'b1}}; //读地址复位
end
else if(empty == false) begin //出栈
rdaddr <= rdaddr + 1'b1; //读地址调整
end //出栈
end //pop
endmodule
`timescale 1ps/1ps
module testbench();
parameter low = 1'b0, hi = 1'b1, true = low, false = hi;
wire odat;
wire ick,ock;
reg iclk,oclk;
assign ick = iclk;
assign ock = oclk;
wire full,empty;
wire idat;
reg dat;
assign idat = dat;
always #3 iclk <= ~iclk;
always #1 oclk <= ~oclk;
initial begin
iclk = hi;
oclk = hi;
dat = 8'd0;
end
always @(posedge iclk) begin
dat <= dat + 1'b1;
end
fifo #(8,3) f1(hi,ick,ock,idat,odat,full,empty);
endmodule
感谢skyxjh 。 {:lol:}感谢楼主无私奉献 大家多指教! 楼主很厉害,谢谢。 jxcylxh 发表于 2013-6-28 20:13 static/image/common/back.gif
楼主很厉害,谢谢。
使用中有什么问题可以互相交流。 {addrbits{1'b1}}是什么意思啊? 本帖最后由 stdio 于 2013-7-11 16:30 编辑
异步FIFO这样做有问题。 sky_prince 发表于 2013-7-11 16:12 static/image/common/back.gif
{addrbits{1'b1}}是什么意思啊?
{addrbits{1'b1}}是addrbits位1的意思 stdio 发表于 2013-7-11 16:29 static/image/common/back.gif
异步FIFO这样做有问题。
有什么问题? 谢谢楼主分享 好像很不错啊,LZ有FPGA采集编码器的代码不?急需啊 hy2515131 发表于 2013-7-11 22:36 static/image/common/back.gif
好像很不错啊,LZ有FPGA采集编码器的代码不?急需啊
编码器的输出波形很简单,解码不难,搞清楚正反转的编码规律,设定一个计数器初值,采集到一个正转编码计数器值加一,反之则减一,你可以自己写一个。 skyxjh 发表于 2013-7-11 20:57 static/image/common/back.gif
{addrbits{1'b1}}是addrbits位1的意思
我以前常见变量的位拼接,常量也可以啊? sky_prince 发表于 2013-7-12 08:48 static/image/common/back.gif
我以前常见变量的位拼接,常量也可以啊?
常量当然可以拼接了。 skyxjh 发表于 2013-7-12 12:31 static/image/common/back.gif
常量当然可以拼接了。
理解了,相当于1111。 delt = wraddr - rdaddr;当wraddr<rdaddr会如何??? skyxjh 发表于 2013-7-12 12:31 static/image/common/back.gif
常量当然可以拼接了。
这个iclk和oclk不能相等是吧?只能ilclk比oclk快? wwwjjj-1 发表于 2013-7-12 14:04 static/image/common/back.gif
delt = wraddr - rdaddr;当wraddr
无符号数减法,4‘d1-4'd2=4'd15 86793 发表于 2013-7-12 16:20 static/image/common/back.gif
这个iclk和oclk不能相等是吧?只能ilclk比oclk快?
可以相等,也可以输入比输出慢,这样FIFO就不会满。 本帖最后由 loyoan 于 2013-7-18 19:08 编辑
谢谢LZ的分享~
请教一个低级的问题,我刚学没多久,大神见笑:
如果是3位地址的FIFO,delt也是3位的,
assign empty = delt == 1'b1 ? true : false; //fifo空信号
这句不会出问题吗?多位与1位的数能相等吗?
现在明白了。。。好代码,LZ给力 loyoan 发表于 2013-7-18 18:26 static/image/common/back.gif
谢谢LZ的分享~
请教一个低级的问题,我刚学没多久,大神见笑:
如果是3位地址的FIFO,delt也是3位的,
当然不会出问题,比较器是以最长的变量来对齐的,高位补0。 谢谢楼主奉献 非常的厉害非常的佩服楼主 向楼主学习啊{:lol:} 谢谢分享 Error: Fitter requires 344 LABs to implement the project, but the device contains only 288 LABs;
数据位设置为9位时,用EP2C5Q208时报的错误,占用资源太大。怎么我位数小的时候,用modelsim独立仿真的时候效果还好,用quartus和modelsim联合仿真时,出现Error: ModelSim Error: # ** Error: c:/altera/11.0/quartus/eda/sim_lib/cycloneii_atoms.v(5351): $hold( posedge clk &&& nosloadsclr:7769 ps, datain:7784的错误。有人说是后仿真约束设置的不对,可这些选项我根本就没设置啊 太耗资源了,如果数据太多,根本不能做别的。请教一下lz:我想通过arm向单片机传4KB的数据。我用quartus里面提供的scfifo,时钟直接接20M,单片机写信号周期是100ns,arm写周期是70-80ns,单片机读出来的数据都是0。怎么加外围电路怎么让写信号或读信号里面只有一个时钟沿呢?如果用dcfifo由如何实现 gginhouse 发表于 2013-8-9 17:48 static/image/common/back.gif
太耗资源了,如果数据太多,根本不能做别的。请教一下lz:我想通过arm向单片机传4KB的数据。我用quartus里 ...
通过arm向单片机传4KB的数据与QUARTUS有什么关系?{:sweat:} 你没明白我的意思,我说quartus和modelsim联合仿真的时候我哪里设置错了,另外不知道异步两个时钟怎么接。原来LPM_FIFO读写是上升沿触发不是高电平触发,只要写的时钟够小就行,我读到数据的问题可能是别的引起的。你的程序写的很好,用modelsim独立仿真也没问题,如果设置深度太大太消耗LE。 gginhouse 发表于 2013-8-10 15:02 static/image/common/back.gif
你没明白我的意思,我说quartus和modelsim联合仿真的时候我哪里设置错了,另外不知道异步两个时钟怎么接。 ...
我写的这个异步FIFO的两个时钟随便接,输入时钟接ien,输出时钟接oen就可以了,时钟下降沿置数和读数。 学习了 楼主好人另外同步FIFO你在你的器件上能跑多快呢? 器件型号也报一下 sczh0001 发表于 2013-8-11 12:16 static/image/common/back.gif
学习了 楼主好人另外同步FIFO你在你的器件上能跑多快呢? 器件型号也报一下 ...
极限速度我没测试过,你可以测试一下,顺便给大家一个参考。 `define BUF_WIDTH 3 // BUF_SIZE = 16 -> BUF_WIDTH = 4, no. of bits to be used in pointer
`define BUF_SIZE ( 1<<`BUF_WIDTH )
`define DATA_WIDTH 32
module fifo32( clk, rst, buf_in, buf_out, wr_en, rd_en, buf_empty, buf_full, fifo_counter );
input rst, clk, wr_en, rd_en;
// reset, system clock, write enable and read enable.
input [`DATA_WIDTH-1:0] buf_in;
// data input to be pushed to buffer
output[`DATA_WIDTH-1:0] buf_out;
// port to output the data using pop.
output buf_empty, buf_full;
// buffer empty and full indication
output[`BUF_WIDTH :0] fifo_counter;
// number of data pushed in to buffer
reg[`DATA_WIDTH-1:0] buf_out;
reg buf_empty, buf_full;
reg[`BUF_WIDTH :0] fifo_counter;
reg[`BUF_WIDTH -1:0]rd_ptr, wr_ptr; // pointer to read and write addresses
reg[`DATA_WIDTH-1:0] buf_mem[`BUF_SIZE -1 : 0]; //
always @(fifo_counter)
begin
buf_empty = (fifo_counter==0);
buf_full = (fifo_counter== `BUF_SIZE);
end
always @(posedge clk or posedge rst)
begin
if( rst )
fifo_counter <= 0;
else if( (!buf_full && wr_en) && ( !buf_empty && rd_en ) )
fifo_counter <= fifo_counter;
else if( !buf_full && wr_en )
fifo_counter <= fifo_counter + 1;
else if( !buf_empty && rd_en )
fifo_counter <= fifo_counter - 1;
else
fifo_counter <= fifo_counter;
end
always @( posedge clk or posedge rst)
begin
if( rst )
buf_out <= 0;
else
begin
if( rd_en && !buf_empty )
buf_out <= buf_mem;
else
buf_out <= buf_out;
end
end
always @(posedge clk)
begin
if( wr_en && !buf_full )
buf_mem[ wr_ptr ] <= buf_in;
else
buf_mem[ wr_ptr ] <= buf_mem[ wr_ptr ];
end
always@(posedge clk or posedge rst)
begin
if( rst )
begin
wr_ptr <= 0;
rd_ptr <= 0;
end
else
begin
if( !buf_full && wr_en ) wr_ptr <= wr_ptr + 1;
elsewr_ptr <= wr_ptr;
if( !buf_empty && rd_en ) rd_ptr <= rd_ptr + 1;
else rd_ptr <= rd_ptr;
end
end
endmodule
以上为同步FIFO 亲测可用。代码有点罗嗦,比方说else rd_ptr <= rd_ptr;完全可以省掉,但是没有影响,所以就没有修改。 其实,用现成的IP核,会仿真弄懂各种模式的时序(或熟读手册),还是挺简单实用的。FIFO和双口RAM折腾了一周,终于搞定了。 自己写代码实现可以学到更多东西。 楼主:
你好,编译的时候提示:found 3 instances of uninferred ram logic
这个是什么问题呢?谢谢 crydhw 发表于 2013-8-21 11:31 static/image/common/back.gif
楼主:
你好,编译的时候提示:found 3 instances of uninferred ram logic
这个是什么问题呢?谢谢 ...
找到3个uninferred ram逻辑实例,有问题吗? 刚刚开始学习fpga,需要用到fifo功能,谢谢楼主的无私奉献 请教楼主一个问题。
使用altera的dcfifo,读出的时候依靠half full信号,一旦半满就读出8个。
现在问题是,在前端写数据结束后,fifo中还会残留接近fifo大小的一半的数据,这些数据要怎样才能读出来呢? quanquan902222 发表于 2013-9-15 13:32 static/image/common/back.gif
请教楼主一个问题。
使用altera的dcfifo,读出的时候依靠half full信号,一旦半满就读出8个。
现在问题是, ...
那位什么要用不半满读呢,直接用非空就读就可以了 quanquan902222 发表于 2013-9-15 13:32 static/image/common/back.gif
请教楼主一个问题。
使用altera的dcfifo,读出的时候依靠half full信号,一旦半满就读出8个。
现在问题是, ...
半满开始读,读到空为止就行了。 嗯,感谢楼上两位的思路,我去试试。 感谢楼主分享 感谢楼主分享 学习中~~ 问题在于:
wire delt = wraddr - rdaddr;
wraddr是由ien驱动的,而rdaddr是由oen驱动的,它们属于不同的时钟域,这样直接运算,存在亚稳态问题。
要是异步FIFO就是这么简单的话,也不会有人长篇大论写了好些篇论文了。
建议楼主去搜搜异步FIFO的论文,有个洋鬼子写了两篇经典的。
谢谢楼主奉献 嗯,谢谢楼主分享 stdio 发表于 2013-11-20 03:54 static/image/common/back.gif
问题在于:
wire delt = wraddr - rdaddr;
wraddr是由ien驱动的,而rdaddr是由oen驱动的,它 ...
我这个异步FIFO没有考虑亚稳态,也是一个抛砖引玉的方案,这么多人看了贴子,只有你指出问题,说明你是认真研究了。 异步FIFO设计
http://wenku.baidu.com/link?url=abPjjMmprLrjQEbOTNesADxBA3izp26tFPaYhrDyXRba48eTrTBcamVX2u4BE0KFvadGSy3W0GUY_Csvuhgi2es1x4pFmTOyNRXaVonuufO 异步FIFO及verilog原码
http://wenku.baidu.com/view/210f6dbef121dd36a32d8228.html 谢谢分享好资源 感谢分享,学习一下 其实这个fifo有点小问题 楼主大神啊,好厉害 谢谢楼主分享!!! 学习了,谢谢 顶一个!! mark ,以后用得上 楼主好走位!!!1 为何不用现成的IP核?我一直用ALTERA的IP,很简单也很好用呀! 用IP核占用资源多 谢谢分享! 收藏了,想用cpld采集AD数据,写个深度大点的fifo不知是否可行 本帖最后由 NJ8888 于 2015-3-24 22:45 编辑
qianniao29 发表于 2015-3-24 22:33
收藏了,想用cpld采集AD数据,写个深度大点的fifo不知是否可行
CPLD没多少资源,想FIFO要么换FPGA要么外加SRAM 用CPLD做控制逻辑(不过也是资源紧张啊,还是用FPGA好),FPGA带FIFO的核,不用白不用,不占逻辑资源,不过深度上受芯片型号影响大小不一 qianniao29 发表于 2015-3-24 22:33
收藏了,想用cpld采集AD数据,写个深度大点的fifo不知是否可行
CPLD没有RAM,是用逻辑单元来做FIFO的,很占用资源,一般也就写个几十字节的FIFO。 请教一个问题:我现在有2个16位,20MSPS的AD,后级有一个USB3.0将数据传输至PC,通过FPGA控制AD采样及采样数据传入USB芯片(16位数据总线),那么我如何通过FIFO将这两路AD的数据缓存呢?{:loveliness:} 每一路AD接一个16位宽的FIFO,再用一个调度模块读取两个FIFO,打包通过USB上传到PC。 楼主程序存在读、写时钟的跨时钟域问题,比如读写地址由两个时钟分别作用下产生,两地址相减的值会不确定。
我也在寻找问题解决方法,未果。楼主如有方法,望不要保留。
所以,直到现在,我还是使用ISE自带的FIFO核。
页:
[1]