skyxjh 发表于 2013-6-23 23:16:14

开源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

skyxjh 发表于 2013-6-23 23:16:44

skyxjh 发表于 2013-6-23 23:17:01

skyxjh 发表于 2013-6-23 23:20:53

gwj221 发表于 2013-6-24 00:08:01

make xiexie

sky_prince 发表于 2013-6-24 08:42:46

学习了,谢谢。

Eric2013 发表于 2013-6-24 08:48:06

感谢分享,收藏。

guer 发表于 2013-6-24 08:54:17

感谢楼主无私奉献,收藏了

xiangzhi28 发表于 2013-6-24 12:25:48

感谢楼主无私奉献,收藏FIFO模块

skyxjh 发表于 2013-6-24 12:41:47

一楼是同步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 发表于 2013-6-24 12:43:51

skyxjh 发表于 2013-6-24 12:45:04

skyxjh 发表于 2013-6-24 12:50:54

lyl520719 发表于 2013-6-24 15:29:10

感谢skyxjh 。

STM32_Study 发表于 2013-6-24 16:14:59

{:lol:}感谢楼主无私奉献

skyxjh 发表于 2013-6-24 21:00:14

大家多指教!

jxcylxh 发表于 2013-6-28 20:13:01

楼主很厉害,谢谢。

skyxjh 发表于 2013-6-28 23:13:34

jxcylxh 发表于 2013-6-28 20:13 static/image/common/back.gif
楼主很厉害,谢谢。

使用中有什么问题可以互相交流。

sky_prince 发表于 2013-7-11 16:12:08

{addrbits{1'b1}}是什么意思啊?

stdio 发表于 2013-7-11 16:29:09

本帖最后由 stdio 于 2013-7-11 16:30 编辑

异步FIFO这样做有问题。

skyxjh 发表于 2013-7-11 20:57:09

sky_prince 发表于 2013-7-11 16:12 static/image/common/back.gif
{addrbits{1'b1}}是什么意思啊?

{addrbits{1'b1}}是addrbits位1的意思

skyxjh 发表于 2013-7-11 20:57:30

stdio 发表于 2013-7-11 16:29 static/image/common/back.gif
异步FIFO这样做有问题。

有什么问题?

jzczzcy 发表于 2013-7-11 20:59:34

谢谢楼主分享

hy2515131 发表于 2013-7-11 22:36:42

好像很不错啊,LZ有FPGA采集编码器的代码不?急需啊

skyxjh 发表于 2013-7-11 22:49:44

hy2515131 发表于 2013-7-11 22:36 static/image/common/back.gif
好像很不错啊,LZ有FPGA采集编码器的代码不?急需啊

编码器的输出波形很简单,解码不难,搞清楚正反转的编码规律,设定一个计数器初值,采集到一个正转编码计数器值加一,反之则减一,你可以自己写一个。

sky_prince 发表于 2013-7-12 08:48:16

skyxjh 发表于 2013-7-11 20:57 static/image/common/back.gif
{addrbits{1'b1}}是addrbits位1的意思

我以前常见变量的位拼接,常量也可以啊?

skyxjh 发表于 2013-7-12 12:31:59

sky_prince 发表于 2013-7-12 08:48 static/image/common/back.gif
我以前常见变量的位拼接,常量也可以啊?

常量当然可以拼接了。

sky_prince 发表于 2013-7-12 13:20:15

skyxjh 发表于 2013-7-12 12:31 static/image/common/back.gif
常量当然可以拼接了。

理解了,相当于1111。

wwwjjj-1 发表于 2013-7-12 14:04:58

delt = wraddr - rdaddr;当wraddr<rdaddr会如何???

86793 发表于 2013-7-12 16:20:59

skyxjh 发表于 2013-7-12 12:31 static/image/common/back.gif
常量当然可以拼接了。

这个iclk和oclk不能相等是吧?只能ilclk比oclk快?

skyxjh 发表于 2013-7-13 08:33:11

wwwjjj-1 发表于 2013-7-12 14:04 static/image/common/back.gif
delt = wraddr - rdaddr;当wraddr

无符号数减法,4‘d1-4'd2=4'd15

skyxjh 发表于 2013-7-13 08:34:43

86793 发表于 2013-7-12 16:20 static/image/common/back.gif
这个iclk和oclk不能相等是吧?只能ilclk比oclk快?

可以相等,也可以输入比输出慢,这样FIFO就不会满。

loyoan 发表于 2013-7-18 18:26:19

本帖最后由 loyoan 于 2013-7-18 19:08 编辑

谢谢LZ的分享~
请教一个低级的问题,我刚学没多久,大神见笑:
如果是3位地址的FIFO,delt也是3位的,
assign empty = delt == 1'b1 ? true : false; //fifo空信号
这句不会出问题吗?多位与1位的数能相等吗?

现在明白了。。。好代码,LZ给力

skyxjh 发表于 2013-7-20 22:25:07

loyoan 发表于 2013-7-18 18:26 static/image/common/back.gif
谢谢LZ的分享~
请教一个低级的问题,我刚学没多久,大神见笑:
如果是3位地址的FIFO,delt也是3位的,


当然不会出问题,比较器是以最长的变量来对齐的,高位补0。

Yoran 发表于 2013-8-1 13:55:01

谢谢楼主奉献

Julius20110 发表于 2013-8-1 23:35:58

非常的厉害非常的佩服楼主

lantian1991 发表于 2013-8-2 14:13:34

向楼主学习啊{:lol:}

zlpvch 发表于 2013-8-2 15:11:41

谢谢分享

gginhouse 发表于 2013-8-9 16:24:19

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的错误。有人说是后仿真约束设置的不对,可这些选项我根本就没设置啊

gginhouse 发表于 2013-8-9 17:48:57

太耗资源了,如果数据太多,根本不能做别的。请教一下lz:我想通过arm向单片机传4KB的数据。我用quartus里面提供的scfifo,时钟直接接20M,单片机写信号周期是100ns,arm写周期是70-80ns,单片机读出来的数据都是0。怎么加外围电路怎么让写信号或读信号里面只有一个时钟沿呢?如果用dcfifo由如何实现

skyxjh 发表于 2013-8-10 10:51:47

gginhouse 发表于 2013-8-9 17:48 static/image/common/back.gif
太耗资源了,如果数据太多,根本不能做别的。请教一下lz:我想通过arm向单片机传4KB的数据。我用quartus里 ...

通过arm向单片机传4KB的数据与QUARTUS有什么关系?{:sweat:}

gginhouse 发表于 2013-8-10 15:02:03

你没明白我的意思,我说quartus和modelsim联合仿真的时候我哪里设置错了,另外不知道异步两个时钟怎么接。原来LPM_FIFO读写是上升沿触发不是高电平触发,只要写的时钟够小就行,我读到数据的问题可能是别的引起的。你的程序写的很好,用modelsim独立仿真也没问题,如果设置深度太大太消耗LE。

skyxjh 发表于 2013-8-11 10:14:05

gginhouse 发表于 2013-8-10 15:02 static/image/common/back.gif
你没明白我的意思,我说quartus和modelsim联合仿真的时候我哪里设置错了,另外不知道异步两个时钟怎么接。 ...

我写的这个异步FIFO的两个时钟随便接,输入时钟接ien,输出时钟接oen就可以了,时钟下降沿置数和读数。

sczh0001 发表于 2013-8-11 12:16:59

学习了 楼主好人另外同步FIFO你在你的器件上能跑多快呢? 器件型号也报一下

skyxjh 发表于 2013-8-11 17:40:25

sczh0001 发表于 2013-8-11 12:16 static/image/common/back.gif
学习了 楼主好人另外同步FIFO你在你的器件上能跑多快呢? 器件型号也报一下 ...

极限速度我没测试过,你可以测试一下,顺便给大家一个参考。

mcupro 发表于 2013-8-18 10:27:08

`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;完全可以省掉,但是没有影响,所以就没有修改。

gginhouse 发表于 2013-8-18 12:12:43

其实,用现成的IP核,会仿真弄懂各种模式的时序(或熟读手册),还是挺简单实用的。FIFO和双口RAM折腾了一周,终于搞定了。

skyxjh 发表于 2013-8-18 22:39:47

自己写代码实现可以学到更多东西。

crydhw 发表于 2013-8-21 11:31:31

楼主:
你好,编译的时候提示:found 3 instances of uninferred ram logic
这个是什么问题呢?谢谢

skyxjh 发表于 2013-8-21 19:08:14

crydhw 发表于 2013-8-21 11:31 static/image/common/back.gif
楼主:
你好,编译的时候提示:found 3 instances of uninferred ram logic
这个是什么问题呢?谢谢 ...

找到3个uninferred ram逻辑实例,有问题吗?

FreshHope 发表于 2013-8-28 17:16:29

刚刚开始学习fpga,需要用到fifo功能,谢谢楼主的无私奉献

quanquan902222 发表于 2013-9-15 13:32:43

请教楼主一个问题。
使用altera的dcfifo,读出的时候依靠half full信号,一旦半满就读出8个。
现在问题是,在前端写数据结束后,fifo中还会残留接近fifo大小的一半的数据,这些数据要怎样才能读出来呢?

Fourier00 发表于 2013-9-15 13:59:00

quanquan902222 发表于 2013-9-15 13:32 static/image/common/back.gif
请教楼主一个问题。
使用altera的dcfifo,读出的时候依靠half full信号,一旦半满就读出8个。
现在问题是, ...

那位什么要用不半满读呢,直接用非空就读就可以了

skyxjh 发表于 2013-9-15 14:55:38

quanquan902222 发表于 2013-9-15 13:32 static/image/common/back.gif
请教楼主一个问题。
使用altera的dcfifo,读出的时候依靠half full信号,一旦半满就读出8个。
现在问题是, ...

半满开始读,读到空为止就行了。

quanquan902222 发表于 2013-9-15 16:20:51

嗯,感谢楼上两位的思路,我去试试。

河Elvis 发表于 2013-9-17 16:32:14

感谢楼主分享

河Elvis 发表于 2013-9-20 16:54:39

感谢楼主分享

kongethan 发表于 2013-9-22 10:53:39

学习中~~

stdio 发表于 2013-11-20 03:54:04

问题在于:
wire delt = wraddr - rdaddr;
wraddr是由ien驱动的,而rdaddr是由oen驱动的,它们属于不同的时钟域,这样直接运算,存在亚稳态问题。
要是异步FIFO就是这么简单的话,也不会有人长篇大论写了好些篇论文了。
建议楼主去搜搜异步FIFO的论文,有个洋鬼子写了两篇经典的。

Feco 发表于 2013-11-20 08:10:30


谢谢楼主奉献

yinian 发表于 2013-11-20 13:46:00

嗯,谢谢楼主分享

skyxjh 发表于 2013-11-20 20:02:19

stdio 发表于 2013-11-20 03:54 static/image/common/back.gif
问题在于:
wire delt = wraddr - rdaddr;
wraddr是由ien驱动的,而rdaddr是由oen驱动的,它 ...

我这个异步FIFO没有考虑亚稳态,也是一个抛砖引玉的方案,这么多人看了贴子,只有你指出问题,说明你是认真研究了。

skyxjh 发表于 2013-11-20 20:19:56

异步FIFO设计
http://wenku.baidu.com/link?url=abPjjMmprLrjQEbOTNesADxBA3izp26tFPaYhrDyXRba48eTrTBcamVX2u4BE0KFvadGSy3W0GUY_Csvuhgi2es1x4pFmTOyNRXaVonuufO

skyxjh 发表于 2013-11-20 20:47:51

异步FIFO及verilog原码
http://wenku.baidu.com/view/210f6dbef121dd36a32d8228.html

xmu234 发表于 2013-11-28 13:06:35

谢谢分享好资源

ycwjl728 发表于 2013-11-28 13:08:50

感谢分享,学习一下

Fourier00 发表于 2013-11-28 22:17:14

其实这个fifo有点小问题

cxhy 发表于 2013-12-1 11:05:39

楼主大神啊,好厉害

loves6036 发表于 2013-12-14 10:46:01

谢谢楼主分享!!!

此人较为厉害 发表于 2013-12-17 19:48:26

学习了,谢谢

orange-208 发表于 2013-12-19 21:54:02

顶一个!!      

yuntian 发表于 2013-12-19 22:47:44

mark ,以后用得上

margguo 发表于 2014-2-8 16:53:37

楼主好走位!!!1

DOER 发表于 2014-2-8 20:16:06

为何不用现成的IP核?我一直用ALTERA的IP,很简单也很好用呀!

skyxjh 发表于 2014-2-10 18:38:33

用IP核占用资源多

WITSOFT 发表于 2014-2-23 17:09:23

谢谢分享!

qianniao29 发表于 2015-3-24 22:33:47

收藏了,想用cpld采集AD数据,写个深度大点的fifo不知是否可行

NJ8888 发表于 2015-3-24 22:44:17

本帖最后由 NJ8888 于 2015-3-24 22:45 编辑

qianniao29 发表于 2015-3-24 22:33
收藏了,想用cpld采集AD数据,写个深度大点的fifo不知是否可行

CPLD没多少资源,想FIFO要么换FPGA要么外加SRAM 用CPLD做控制逻辑(不过也是资源紧张啊,还是用FPGA好),FPGA带FIFO的核,不用白不用,不占逻辑资源,不过深度上受芯片型号影响大小不一

skyxjh 发表于 2015-3-24 23:20:03

qianniao29 发表于 2015-3-24 22:33
收藏了,想用cpld采集AD数据,写个深度大点的fifo不知是否可行

CPLD没有RAM,是用逻辑单元来做FIFO的,很占用资源,一般也就写个几十字节的FIFO。

uestc19 发表于 2015-3-28 17:04:31

请教一个问题:我现在有2个16位,20MSPS的AD,后级有一个USB3.0将数据传输至PC,通过FPGA控制AD采样及采样数据传入USB芯片(16位数据总线),那么我如何通过FIFO将这两路AD的数据缓存呢?{:loveliness:}

skyxjh 发表于 2015-3-28 21:27:24

每一路AD接一个16位宽的FIFO,再用一个调度模块读取两个FIFO,打包通过USB上传到PC。

wang110 发表于 2015-3-29 21:30:55

楼主程序存在读、写时钟的跨时钟域问题,比如读写地址由两个时钟分别作用下产生,两地址相减的值会不确定。
我也在寻找问题解决方法,未果。楼主如有方法,望不要保留。
所以,直到现在,我还是使用ISE自带的FIFO核。
页: [1]
查看完整版本: 开源Verilog HDL原创FIFO模块