作者:饿狼传说 来源:FPGA的现今未
作为FPGA的初学阶段,都做过一个题,就是写一个同步FIFO,本意只是让初学者更好的理解FIFO的功能,一般当时写的,也不会真正的用于项目。
在早期,使用FIFO和RAM的时候,一般都是直接例化供应商的IP core,因为简单可靠,在仿真的时候编译下基本库,跑工程的时候添加下网表文件就搞定了。但是缺点也很明显,功能不方便扩展,不同位宽和深度的FIFO和RAM,需要重新例化,生成的文件也比较多,在工程里管理的IP core非常的多,比较乱。
随着项目的需要和团队经验的积累,就开始自己写FIFO和RAM,一般可以配置的参数会比较多,比如深度、宽度、供应商、整包计数等等。这个好处是不言而喻的,这些FIFO和RAM都是统一管理,参数可配置,定制化的开发非常适合自己和团队。但是对经验要求比较高,别看FIFO功能简单,但是做到稳定可靠,并不是一个简单的事情,各种使用场景都要考虑清楚。
还有一种方式就是采用源语封装,早期的源语封装也是一件比较复杂的事情,信号非常的多,也没有很好的文档支撑,一般用这种方式的少(当然不排除有大牛采用这种方式)。近几年,xilinx推出了XPM,个人理解就是对源语比较高层点的封装,作者这几年一直用这种方法,非常不错,该方式集成了上述2种方案的优点,同时又规避了缺点,比如,位宽、深度、类型等等都参数化,基本功能在源语中已经写好,接口信号和功能有专门的文档说明。工程管理上就是一个RTL代码,没有任何的IP core,用户界面也非常友好。intel也有类似的源语,使用相对也方便了。个人在使用的时候,可以根据项目特点,再做一层封装,如下所示:
module syn_fifo #( parameter FIFO_MEMORY_TYPE = "block" , parameter ECC_MODE = "no_ecc" , parameter READ_MODE = "fwft" , parameter FIFO_READ_LATENCY = 0 , parameter FIFO_WIDTH = 32 , parameter FIFO_DEPTH = 512 , parameter PKT_CNT_WIDTH = $clog2(FIFO_DEPTH), parameter PROG_EMPTY_THRESH = 32 , parameter PROG_FULL_THRESH = 480 ) ( input clk_sys , input rst , input wen , input [FIFO_WIDTH-1:0] din , input din_cnt_en , output afull , input ren , output [FIFO_WIDTH-1:0] dout , input dout_cnt_en , output empty , output [PKT_CNT_WIDTH:0] pkt_cnt , output reg [3:0] fifo_err );
那究竟什么样的方式适合自己或者团队呢?对于初创型公司,人员少,项目复杂度不高,经验也不是很丰富的情况下,直接采用例化IP core的方式,简单省事,除此以外,都可以考虑利用上面的方案三,即源语的方式,根据项目和团队特点,做一层封装后,再统一使用。
在FIFO和RAM的工程实践中,如果你有更好的方案,欢迎后台留言讨论。