FPGA之FIFO详解,读写位宽不同

      这篇博客里,通过两个练习来总结在FPGA设计中FIFO读写位宽不同的情况下,我们应该去如何设计时序逻辑,因为在现实工程中FIFO读写位宽不同也是经常出现的情况。

练习1

         设计一个模块包含读写位宽是32bit、读写深度是64的异步时钟FIFO,其中输入数据信号din和输入 数据指示信号din_vld是属于clk_in时钟域的,上游模块会负责写数据到本模块的FIFO中,当本模块FIFO写端口已写入了60个数据,也就是FIFO快要写满溢出的时候,这时上游模块仍有数据要写入FIFO则直接丢弃该数据。下游模块负责从本模块的FIFO中读数据,当下游模块输入rdy信号时表示下游模块已准备好可以接收FIFO中的数据了,这时如果FIFO中有数据,那么就把FIFO中的数据赋值给输出数据信号dout送至下游模块并同时拉高dout_vld输出数据指示信号,其中输出数据信号dout和输出数据指示信号dout_vld则是属于clk_out时钟域的,上游模块din信号的写位宽是32bit,下游模块dout信号的读位宽是8bit,表1为练习1的信号列表。

信号列表

信号名

I/O

位宽

clk_in

I

1

rst_n

I

1

rdy

I

1

din

I

32

din_vld

I

1

clk_out

I

1

dout

O

8

dout_vld

O

1

fifo_empty

O

1

表1 FIFO IP练习1设计中的信号列表

         我们来思考下练习1功能模块的代码设计,其实不同的地方主要在于上游模块的写位宽din是32bit,而下游模块的读位宽dout是8bit,两者位宽是不一致的,同样的方法我们也在Vivado下初始化一个读写位宽是32bit、读写深度是64的异步时钟FIFO,因为FIFO读写位宽都是32bit,所以上游模块直接写入FIFO即可,这里和练习1中的方法完全一样,但是在读取FIFO的时候,又因为下游模块的读位宽是8bit,而一次性从FIFO中读出的数据是32bit,我们就需要用一个cnt计数器去计数拆分32bit的数据,dout再依次去输出从32bit数据拆分来的数据,同时也拉高dout_vld指示信号,如图1所示FIFO IP练习1的代码设计,具体细节笔者就不再赘述了。

       对于FIFO IP练习1的Testbench,其实和FIFO读写位宽相同的设计也大同小异,先模拟上游模块向本模块的写入数据,再模拟输入下游模块准备就绪的rdy信号,这里只补充说明一点,因为FIFO写端口的输入时钟是50Mhz,而读端口的输出时钟是100Mhz,其中上游模块的写位宽din是32bit,下游模块的读位宽dout是8bit,所以给出120个时钟的rdy高电平,大家可以简单计算下,写端口属于clk_in时钟域,120个clk_in应该是120*20ns=2400ns。而读端口属于clk_out时钟域,2400ns就是对应写端口的2400ns/10ns=240个时钟周期,因为下游模块的读位宽是8bit,所以说要输出完32bit*60的数据,需要32bit*60/8bit也就是240个时钟周期,如图2是FIFO IP练习1的输入信号激励设计。

       大家打开Vivado环境,添加好功能文件和测试文件后,启动Modelsim进行仿真,如图3是FIFO IP练习1的仿真结果,可以清楚地观察到当fifo_empty为高时,即FIFO当前已经没有数据了,此时恰好dout输出FIFO中的最后一个数据,且dout_vld被拉高,和我们预期的设计效果完全相符。

图1 FIFO IP练习1的代码设计

图2 FIFO IP练习1的输入信号激励设计

图3 FIFO IP练习1的仿真结果

练习2

      设计一个模块包含读写位宽是32bit、读写深度是64的异步时钟FIFO,其中输入数据信号din和输入数据指示信号din_vld是属于clk_in时钟域的,上游模块会负责写数据到本模块的FIFO中,当本模块FIFO写端口已写入了60个数据,也就是FIFO快要写满溢出的时候,这时上游模块仍有数据要写入FIFO则直接丢弃该数据。下游模块负责从本模块的FIFO中读数据,当下游模块输入rdy信号时表示下游模块已准备好可以接收FIFO中的数据了,这时如果FIFO中有数据,那么就把FIFO中的数据赋值给输出数据信号dout送至下游模块并同时拉高dout_vld输出数据指示信号,其中输出数据信号dout和输出数据指示信号dout_vld则是属于clk_out时钟域的,上游模块din信号的写位宽是8bit,下游模块dout信号的读位宽是32bit,表2为练习2的信号列表。

信号列表

信号名

I/O

位宽

clk_in

I

1

rst_n

I

1

rdy

I

1

din

I

8

din_vld

I

1

clk_out

I

1

dout

O

32

dout_vld

O

1

fifo_empty

O

1

表2 FIFO IP练习2设计中的信号列表

        练习2和练习1一样都是在FIFO相同读写位宽的基础上的延伸扩展,当然Vivado环境下完全支持异步时钟FIFO配置成位宽不同的读写端口,实际工作中也有不少FPGA工程师习惯性去把异步时钟FIFO读写端口根据需求人为配置成位宽不同的,但是笔者还是推荐大家把读写位宽都配置成相同的,这样在程序设计上也更简便不需要再去考虑一些其他因素影响,这里练习1和练习2其实把现实工作中上游模块输入信号din和下游模块输出信号dout的两种可能情况都包括在内了,所以大家实践过这两个练习,在实际项目中完全可以灵活应用,就可以达到二次开发的效果,练习2是输入信号din位宽是32bit,输出信号dout位宽是8bit,所以我们需要对FIFO的输出进行拆分,而练习2相反的是输入信号din位宽是8bit,输出信号dout位宽是32bit,所以我们需要对FIFO的输入进行拼接,练习1和练习2中包含了对数据拼接和拆分的一般性方法,有很强的代表性,如图4所示FIFO IP练习2的代码设计。

        对于FIFO IP练习2的Testbench,其实类似练习1,在练习2中因为FIFO写端口的输入时钟是50Mhz,读端口的输出时钟是100Mhz,这里上游模块的写位宽din是8bit,下游模块的读位宽dout是32bit,所以也给出了240个时钟周期的din_vld信号用以模拟上游模块向本模块写入240个8bit的数据,之后再去给出30个clk_in时钟周期的rdy高电平模拟下游模块读数据,这里30个clk_in是30*20ns=600ns,读端口在clk_out时钟域,600ns就对应读端口的600ns/10ns=60个时钟周期,因为下游模块的读位宽是32bit,所以说要输出完32bit*60的数据,需要60个时钟周期,如图5是FIFO IP练习2的输入信号激励设计。如图6 FIFO IP练习2的仿真结果,大家代入Modelsim下可以观察到和预期的设计效果完全一致。

 

图4 FIFO IP练习2的代码设计

图5 FIFO IP练习2的输入信号激励设计

图6 FIFO IP练习2的仿真结果

源工程代码下载链接:

链接:https://pan.baidu.com/s/1XF1unOQV_bYXAzbFoMstKQ 
提取码:g2io 
 

  • 3
    点赞
  • 19
    收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:数字20 设计师:CSDN官方博客 返回首页
评论 5

打赏作者

青青豌豆

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值