OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 1062|回复: 0

[国产FPGA] 《ATK-DFPGL22G 之FPGA开发指南》第二十章 IP核之FIFO实验

[复制链接]

1117

主题

1128

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4666
金钱
4666
注册时间
2019-5-8
在线时间
1224 小时
发表于 2023-11-18 16:40:20 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2023-11-18 16:40 编辑

第二十章 IP核之FIFO实验


1)实验平台:正点原子 ATK-DFPGL22G开发板

2) 章节摘自【正点原子】ATK-DFPGL22G之FPGA开发指南_V1.0


4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz-PGL22G.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)FPGA技术交流QQ群:435699340

155537c2odj87vz1z9vj6l.jpg

155537nfqovl2gg9faaol9.png

FIFO的英文全称是First In First Out,即先进先出。FPGA使用的FIFO一般指的是对数据的存储具有先进先出特性的一个缓存器,常被用于数据的缓存,或者高速异步数据的交互也即所谓的跨时钟域信号传递。它与FPGA内部的RAM和ROM的区别是没有外部读写地址线,采取顺序写入数据,顺序读出数据的方式,使用起来简单方便,由此带来的缺点就是不能像RAM和ROM那样可以由地址线决定读取或写入某个指定的地址。本章我们将对PDS软件生成的FIFO IP核进行读写测试,来向大家介绍Pango FIFO IP核的使用方法。
本章包括以下几个部分:      
1.1          FIFO IP核简介
1.2          实验任务
1.3          硬件设计
1.4          程序设计
1.5          下载验证

1.1 FIFO IP核简介
根据FIFO工作的时钟域,可以将FIFO分为同步FIFO和异步FIFO。同步FIFO是指读时钟和写时钟为同一个时钟,在时钟沿来临时同时发生读写操作。异步FIFO是指读写时钟不一致,读写时钟是互相独立的。Pango的FIFO IP核可以被配置为同步FIFO或异步FIFO,其信号框图如下图所示。从图中可以了解到,当被配置为同步FIFO时,只使用wr_clk,所有的输入输出信号都同步于wr_clk信号。而当被配置为异步FIFO时,写端口和读端口分别有独立的时钟,所有与写相关的信号都是同步于写时钟wr_clk,所有与读相关的信号都是同步于读时钟rd_clk。
image001.png
图20.1.1 Pango的FIFO IP核的信号框图
对于FIFO需要了解一些常见参数:
FIFO的宽度:FIFO一次读写操作的数据位N。
FIFO的深度:FIFO可以存储多少个宽度为N位的数据。
将空标志:almost_empty。FIFO即将被读空。
空标志:rd_empty。FIFO已空时由FIFO的状态电路送出的一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出。
将满标志:almost_full。FIFO即将被写满。
满标志:wr_full。FIFO已满或将要写满时由FIFO的状态电路送出的一个信号,以阻止FIFO的写操作继续向FIFO中写数据而造成溢出。
读时钟:读FIFO时所遵循的时钟,在每个时钟的上升沿触发。
写时钟:写FIFO时所遵循的时钟,在每个时钟的上升沿触发。
这里请注意,“almost_empty”和“almost_full”这两个信号分别被看作“empty”和“full”的警告信号,他们相对于真正的空(empty)和满(full)都会提前一个时钟周期拉高。

对于FIFO的基本知识先了解这些就足够了,可能有人会好奇为什么会有同步FIFO和异步FIFO,它们各自的用途是什么。之所以有同步FIFO和异步FIFO是因为各自的作用不同。同步FIFO常用于同步时钟的数据缓存,异步FIFO常用于跨时钟域的数据信号的传递,例如时钟域A下的数据data1传递给异步时钟域B,当data1为连续变化信号时,如果直接传递给时钟域B则可能会导致收非所送的情况,即在采集过程中会出现包括亚稳态问题在内的一系列问题,使用异步FIFO能够将不同时钟域中的数据同步到所需的时钟域中。

1.2 实验任务
本节的实验任务是使用Pango生成FIFO IP核,并实现以下功能:当FIFO为空时,向FIFO中写入数据,写入的数据量和FIFO深度一致,即FIFO被写满;然后从FIFO中读出数据,直到FIFO被读空为止,以此向大家详细介绍一下FIFO IP核的使用方法。

1.3 硬件设计
本章实验只用到了输入的时钟信号和按键复位信号,没有用到其它硬件外设。
本实验中,各端口信号的管脚分配如下表所示。
QQ截图20231118163652.png
表 20.3.1 IP实验管脚分配

对应的FDC约束语句如下所示:
  1. define_attribute{p:sys_clk} {PAP_IO_DIRECTION} {INPUT}
  2. define_attribute{p:sys_clk} {PAP_IO_LOC} {B5}
  3. define_attribute{p:sys_clk} {PAP_IO_VCCIO} {3.3}
  4. define_attribute{p:sys_clk} {PAP_IO_STANDARD} {LVCMOS12}
  5. define_attribute{p:sys_clk} {PAP_IO_NONE} {TRUE}
  6. define_attribute{p:sys_rst_n} {PAP_IO_DIRECTION} {INPUT}
  7. define_attribute{p:sys_rst_n} {PAP_IO_LOC} {G5}
  8. define_attribute{p:sys_rst_n} {PAP_IO_VCCIO} {1.5}
  9. define_attribute{p:sys_rst_n} {PAP_IO_STANDARD} {LVCMOS12}
  10. define_attribute {p:sys_rst_n}{PAP_IO_NONE} {TRUE}
复制代码

1.4 程序设计
根据实验任务要求和模块化设计的思想,我们需要如下4个模块:fifo IP核、写fifo模块、读fifo模块以及顶层例化模块实现前三个模块的信号交互。由于FIFO多用于跨时钟域信号的处理,所以本实验我们使用异步FIFO来向大家详细介绍双时钟FIFO IP核的创建和使用。为了方便大家理解,这里我们将读/写时钟都用系统时钟来驱动。系统的功能框图如下图所示:
image004.png
图 20.4.1 系统框图
首先在PDS软件中创建一个名为ip_fifo的工程,工程创建完成后,在PDS软件的上方的菜单栏中“Tools”栏中单击“IP Compiler”按钮后弹出的“IP Compiler”窗口如图 20.4.3所示。
image005.png
图 20.4.2 点击“IPCompiler”
image007.png
图20.4.3 “IP Compiler”窗口
本实验使用的IP核路径是Module→Memory→DRM→DRM Base FIFO,如下图所示。
image009.png
图 20.4.4 DRM Base FIFO IP
点击上图中红框中的IP后如下图所示:
image011.png
图 20.4.5 DRM Base FIFO详情页面
Pathname:新建IP核在工程所在路径,这里保持默认即可。
InstanceName:在这里给新建的FIFO IP命名,我们这里命名为“fifo_generator_0”。
接下来的IP框里面的内容是该IP的基础信息,例如名称、版本号与所属公司(Pango)。Part框中的信息为工程所使用的芯片的详细信息“PGL22G-6CMBG324”。
点击上图中的“Customize”按钮进入“Customize IP”窗口对FIFO IP的参数进行配置,“Customize IP”窗口界面如下图所示:图中的“Add IP”弹窗点击“Yes”后进入“CustomizeIP”界面进行异步FIFO的参数配置,“Customize IP”界面如图 20.4.7所示。
image013.png
图 20.4.6 “Add IP”弹窗
image015.png
图20.4.7 "CustomizeIP"界面
本实验我们主要进行如下配置:
image017.png
图 20.4.8 DRM Base FIFO参数配置界
Customize IP界面主要包括如下选项:
DRM Resource Usage:选项主要说明DRM资源使用情况。该选项下的“DRM Resource Type”是DRM资源类型,可以设置“AUTO”、“DRM9K”、“DRM18K”三种模式。三种模式可以根据你需要地址宽度与数据宽度来来设置,一般我们设置为“AUTO”模式。
Write/Read Port Use Same Data Widt:配置读写端口是否使用混合位宽模式(注:混合位宽时,关闭Address Strobe功能),本实验勾选该选项,使用混合位宽模式,只需要配置写入端口的地址位宽与数据位宽,读端口的地址位宽与数据位宽会与写入端口保持一致。
FIFO Type: FIFO可以配置的类型有两种,"ASYN_FIFO"异步FIFO和"SYNC_FIFO"同步FIFO,本实验配置的是异步FIFO类型。
Enable Byte Write:配置是否使能Byte Write功能,其含义是Byte写使能,也就是以字节为单位写入数据;Byte Write使能时,同时需要配置字节(Byte)的位宽(Byte Size)与字节个数(Byte Numbers),Byte Size可选择8或者9;Byte Numbers即配置所使用的Byte个数(注:Byte Write使能时,关闭Address Strobe功能。)举例说明:输入数据为32-bit,字节的位宽(Byte Size)设置为8bit,那么就需要4-bit Byte写使能信号,这个使能信号与输入数据各位的对应关系如下图所示。从图中不难看出,当we[3]有效时,只会将输入数据的高8-bit写入到目标地址;当we[0]有效时,只会将输入数据的低8-bit写入到目标地址。其实,也就是根据写使能来更新指定地址上原始数据的某些位,本实验不需要使能Byte Write功能即不需要勾选该配置。
QQ截图20231118163716.png
图 20.4.9 Byte写使能与输入数据的对应关系

Write Port:对写入端口进行配置。AddressWidth配置地址位宽,Data Width配置数据位宽。地址位宽合法配置范围为5~20,数据位宽合法配置范围为1~1152,但是所占用的DRM9K或者DRM18K个数必须小于总资源个数。本实验我们配置的地址位宽为5,数据位宽为8位。
Read Port:对读出端口进行配置。Address Width配置地址位宽,Data Width配置数据位宽。地址位宽合法配置范围为5~20,数据位宽合法配置范围为1~1152,但是所占用的DRM9K或者DRM18K个数必须小于总资源个数。本实验我们配置使用了混合位宽模式,所以读端口的地址位宽与数据位宽会直接与写端口的配置保持一致。
Enable Almost Full Water Level:配置是否使能wr_water_level信号,使能wr_water_level信号可以对写端口数据进行计数,本实验配置使能写端口计数。
Enable Almost Empty Water Level:配置是否使能wr_empty_level信号,使能wr_empty_level信号可以对读端口数据进行计数,本实验配置使能读端口计数。
Almost Full Numbers:配置FIFO Almost Full个数,设置为该选项后面的允许的最大值。
Almost Empty Numbers:配置FIFO Almost Empty个数,设置为该选项后面的允许的最小值。
Enablerd_oce Signal:配置是否使能rd_oce(输出寄存器选项)信号,输出寄存使能信号为高时对应地址有效,读数据会寄存输出,若输出寄存使能信号为低时对应地址无效,读数据保持。并且使能rd_oce信号时,默认且必须使能读端口输出寄存“Enable Output Register”,本实验不需要使能读信号,所以不勾选该选项。
Enable Output Register:输出寄存器选项。如果勾选了“Enable rd_oce Signal”信号,输出寄存器默认是选中状态,作用是打开DRM内部位于输出数据总线之后的输出流水线寄存器,虽然在一般设计中为了改善时序性能会保持此选项的默认勾选状态,但是这会使得BRM输出的数据延迟一拍,这不利于我们在调试窗口中直观清晰地观察信号;而且在本实验中我们仅仅是把BRM的数据输出总线连接到了调试的探针端口上来进行观察,除此之外数据输出总线没有别的负载,不会带来难以满足的时序路径,因此这里取消勾选。
Enable Clock Polarity Invert for OutputRegister:配置是否使能读端口输出时钟极性反向,使能读端口输出时钟极性反向时,默认且必须使能读端口输出寄存(Enable Output Register),这里不需要使能读端口输出时钟极即不需要勾选该配置。
Enable Low Power Mode:配置是否使能低功耗模式,本实验不需要配置成低功耗模式,即不勾选使能低功耗模式。
Reset Type:配置复位方式:"ASYNC"异步复位,"SYNC"同步复位,"Sync_Internally "异步复位同步释放,本实验选择异步复位(低有效)。
至此本实验所需要的异步FIFO IP已配置完成,接下来点击“Customize IP”窗口左上角的“Generate”在弹出的“Question”对话框选择“OK”即可,如下图所示。
image019.png
图 20.4.10 点击“OK”
点击“OK”后打印如下左图信息,至此FIFO IP核配置成功,并生成fifo_generator_0_tmpl.v文件。例化FIFO IP时可以使用下图中fifo_generator_0_tmpl.v文件红框中的代码。
image021.png
图 20.4.11 FIFO IP创建成功
然后如下图所示,关闭“Customize IP”页面与“IP Compiler”页面。
image023.png
图 20.4.12 关闭创建IP的窗口
之后我们就可以在“Sources”窗口的“Designs”一栏中出现了该IP核“fifo_generator_0”如下图所示。
image025.png
图 20.4.13 FIFO IP核
我们创建一个verilog源文件,其名称为ip_fifo.v,作为顶层模块,其代码如下:
  1. 1 module ip_fifo(
  2. 2      input    sys_clk ,  // 时钟信号
  3. 3      input   sys_rst_n  // 复位信号
  4. 4 );
  5. 5
  6. 6 //wire define
  7. 7 wire         fifo_wr_en         ;    // FIFO写使能信号
  8. 8 wire         fifo_rd_en         ;    // FIFO读使能信号
  9. 9 wire  [7:0  fifo_din           ;    // 写入到FIFO的数据
  10. 10 wire  [7:0 fifo_dout          ;    // 从FIFO读出的数据
  11. 11 wire        almost_full        ;    // FIFO将满信号
  12. 12 wire        almost_empty       ;    // FIFO将空信号
  13. 13 wire        fifo_full          ;    // FIFO满信号
  14. 14 wire        fifo_empty         ;    // FIFO空信号
  15. 15 wire  [7:0 fifo_wr_data_count ;    //FIFO写时钟域的数据计数
  16. 16 wire  [7:0 fifo_rd_data_count ;    //FIFO读时钟域的数据计数
  17. 17
  18. 18 //*****************************************************
  19. 19 //**                   main code
  20. 20 //*****************************************************
  21. 21
  22. 22 reg         almost_empty_d0  ;    //almost_empty 延迟一拍
  23. 23 reg         almost_empty_d1  ;    //almost_empty 延迟两拍
  24. 24 reg         almost_empty_syn ;    //almost_empty延迟三拍
  25. 25                                   
  26. 26 reg         almost_full_d0   ;    //almost_full 延迟一拍
  27. 27 reg         almost_full_d1   ;    //almost_full 延迟两拍
  28. 28 reg         almost_full_syn  ;    //almost_full 延迟三拍
  29. 29
  30. 30 //因为almost_empty 信号是属于FIFO读时钟域的
  31. 31 //所以要将其同步到写时钟域中
  32. 32 always@( posedge sys_clk ) begin
  33. 33 if( !sys_rst_n ) begin
  34. 34      almost_empty_d0  <= 1'b0 ;
  35. 35      almost_empty_syn <= 1'b0 ;
  36. 36      almost_empty_d1  <= 1'b0 ;
  37. 37 end
  38. 38 else begin
  39. 39      almost_empty_d0  <= almost_empty ;
  40. 40      almost_empty_d1  <= almost_empty_d0 ;
  41. 41      almost_empty_syn <= almost_empty_d1 ;
  42. 42 end
  43. 43 end
  44. 44
  45. 45 //因为almost_full 信号是属于FIFO读时钟域的
  46. 46 //所以要将其同步到写时钟域中
  47. 47 always@( posedge sys_clk ) begin
  48. 48 if( !sys_rst_n ) begin
  49. 49      almost_full_d0  <= 1'b0 ;
  50. 50      almost_full_syn <= 1'b0 ;
  51. 51      almost_full_d1  <= 1'b0 ;
  52. 52 end
  53. 53 else begin
  54. 54      almost_full_d0  <= almost_full ;
  55. 55      almost_full_d1  <= almost_full_d0 ;
  56. 56      almost_full_syn <= almost_full_d1 ;
  57. 57 end
  58. 58 end
  59. 59
  60. 60 fifo_generator_0u_fifo_generator_0 (
  61. 61     .wr_clk            (sys_clk          ),    //input
  62. 62     .wr_rst            (~sys_rst_n       ),    //input
  63. 63     .wr_en             (fifo_wr_en       ),    //input
  64. 64     .wr_data           (fifo_din         ),    //input [7:0]
  65. 65     .wr_full           (fifo_full        ),    //output
  66. 66     .wr_water_level    (fifo_wr_data_count),    // output [8:0]
  67. 67     .almost_full       (almost_full      ),    //output
  68. 68     .rd_clk            (sys_clk          ),    //input
  69. 69     .rd_rst            (~sys_rst_n       ),    //input
  70. 70     .rd_en             (fifo_rd_en       ),    //input
  71. 71     .rd_data           (fifo_dout        ),    //output [7:0]
  72. 72     .rd_empty          (fifo_empty       ),    //output
  73. 73     .rd_water_level    (fifo_rd_data_count),    // output [8:0]
  74. 74     .almost_empty      (almost_empty     )     //output
  75. 75 );
  76. 76
  77. 77 //例化写FIFO模块
  78. 78 fifo_wr  u_fifo_wr(
  79. 79     .clk            ( sys_clk         ),    //写时钟
  80. 80     .rst_n          ( sys_rst_n       ),    //复位信号
  81. 81
  82. 82     .fifo_wr_en     ( fifo_wr_en      ),    //fifo写请求
  83. 83     .fifo_wr_data   ( fifo_din        ),    //写入FIFO的数据
  84. 84     .almost_empty   ( almost_empty_syn ),    // fifo空信号
  85. 85     .almost_full    ( almost_full_syn )     //fifo满信号
  86. 86 );
  87. 87
  88. 88 //例化读FIFO模块
  89. 89 fifo_rd  u_fifo_rd(
  90. 90     .clk            ( sys_clk         ),    //读时钟
  91. 91     .rst_n          ( sys_rst_n       ),    //复位信号
  92. 92                                             
  93. 93     .fifo_rd_en     ( fifo_rd_en      ),    //fifo读请求
  94. 94     .fifo_dout      ( fifo_dout       ),    //从FIFO输出的数据
  95. 95     .almost_empty   ( almost_empty_syn ),    // fifo空信号
  96. 96     .almost_full    ( almost_full_syn )     //fifo满信号
  97. 97 );
  98. 98
  99. 99 endmodule
复制代码
顶层模块主要是对FIFOIP核、写FIFO模块、读FIFO模块进行例化。
写FIFO模块fifo_wr.v源文件的代码如下:
  1. 1 module fifo_wr(
  2. 2      //mudule clock
  3. 3      input                 clk         ,    // 时钟信号
  4. 4      input                 rst_n       ,    // 复位信号
  5. 5      //FIFO interface      
  6. 6      input                 almost_empty,    //FIFO将空信号
  7. 7      input                 almost_full ,    //FIFO将满信号
  8. 8      output    reg         fifo_wr_en  ,    // FIFO写使能
  9. 9      output    reg  [7:0  fifo_wr_data     // 写入FIFO的数据
  10. 10 );
  11. 11
  12. 12 //reg define
  13. 13 reg  [1:0  state      ; //动作状态
  14. 14 reg  [3:0  dly_cnt    ; //延迟计数器
  15. 15 //*****************************************************
  16. 16 //**                   main code
  17. 17 //*****************************************************
  18. 18 //向FIFO中写入数据
  19. 19 always @(posedge clk ) begin
  20. 20     if(!rst_n) begin
  21. 21         fifo_wr_en   <=1'b0;
  22. 22         fifo_wr_data <= 8'd0;
  23. 23         state        <= 2'd0;
  24. 24         dly_cnt      <= 4'd0;
  25. 25     end
  26. 26     else begin
  27. 27         case(state)
  28. 28             2'd0: begin
  29. 29                 if(almost_empty) begin  //如果检测到FIFO将被读空
  30. 30                     state <= 2'd1;        //就进入延时状态
  31. 31                 end
  32. 32                 else
  33. 33                     state <= state;
  34. 34             end
  35. 35          2'd1: begin
  36. 36              if(dly_cnt == 10) begin  //延时10拍
  37. 37                                         //原因是FIFO IP核内部状态信号的更新存在延时
  38. 38                                          //延迟10拍以等待状态信号更新完毕                  
  39. 39                     dly_cnt    <= 4'd0;
  40. 40                  state      <= 2'd2;     //开始写操作
  41. 41                  fifo_wr_en <= 1'b1;     //打开写使能               
  42. 42              end
  43. 43              else begin
  44. 44                  dly_cnt <= dly_cnt + 4'd1;              
  45. 45              end
  46. 46             end            
  47. 47          2'd2: begin
  48. 48                if(almost_full) begin        //等待FIFO将被写满
  49. 49                     fifo_wr_en   <=1'b0;  //关闭写使能
  50. 50                     fifo_wr_data <= 8'd0;
  51. 51                     state        <= 2'd0;  //回到第一个状态
  52. 52                 end
  53. 53                 else begin                 //如果FIFO没有被写满
  54. 54                     fifo_wr_en   <=1'b1;  //则持续打开写使能
  55. 55                     fifo_wr_data <= fifo_wr_data + 1'd1;  //且写数据值持续累加
  56. 56                 end
  57. 57             end
  58. 58          default : state <= 2'd0;
  59. 59         endcase
  60. 60     end
  61. 61 end
  62. 62
  63. 63 endmodule
复制代码
fifo_wr模块的核心部分是一个不断进行状态循环的小状态机,如果检测到FIFO为空,则先延时10拍,这里注意,由于FIFO的内部信号的更新比实际的数据读/写操作有所延时,所以延时10拍的目的是等待FIFO的空/满状态信号、数据计数信号等信号的更新完毕之后再进行FIFO写操作,如果写满,则回到状态0,即等待FIFO被读空,以进行下一轮的写操作。
读FIFO模块fifo_rd.v源文件的代码如下:
  1. 1 module fifo_rd(
  2. 2      //system clock
  3. 3      input              clk         ,    // 时钟信号
  4. 4      input              rst_n       ,    // 复位信号
  5. 5      //FIFO interface
  6. 6      input        [7:0 fifo_dout   ,    // 从FIFO读出的数据
  7. 7      input              almost_full ,    //FIFO将满信号
  8. 8      input              almost_empty,    //FIFO将空信号
  9. 9      output  reg        fifo_rd_en       // FIFO读使能
  10. 10 );
  11. 11
  12. 12 //reg define
  13. 13 reg  [1:0  state           ;    // 动作状态
  14. 14 reg        almost_full_d0  ;    // fifo_full 延迟一拍
  15. 15 reg        almost_full_syn ;    //fifo_full 延迟两拍
  16. 16 reg  [3:0  dly_cnt         ;    //延迟计数器
  17. 17
  18. 18 //*****************************************************
  19. 19 //**                   main code
  20. 20 //*****************************************************
  21. 21
  22. 22 //因为fifo_full 信号是属于FIFO写时钟域的
  23. 23 //所以要将其同步到读时钟域中
  24. 24 always@( posedge clk ) begin
  25. 25 if( !rst_n ) begin
  26. 26      almost_full_d0  <= 1'b0 ;
  27. 27      almost_full_syn <= 1'b0 ;
  28. 28 end
  29. 29 else begin
  30. 30      almost_full_d0  <= almost_full ;
  31. 31      almost_full_syn <= almost_full_d0 ;
  32. 32 end
  33. 33 end
  34. 34
  35. 35 //读出FIFO的数据
  36. 36 always @(posedge clk ) begin
  37. 37     if(!rst_n) begin
  38. 38         fifo_rd_en <= 1'b0;
  39. 39      state     <= 2'd0;
  40. 40      dly_cnt   <= 4'd0;
  41. 41     end
  42. 42     else begin
  43. 43         case(state)
  44. 44             2'd0: begin
  45. 45                 if(almost_full_syn)      //如果检测到FIFO将被写满
  46. 46                     state <= 2'd1;       //就进入延时状态
  47. 47                 else
  48. 48                     state <= state;
  49. 49             end
  50. 50          2'd1: begin
  51. 51                 if(dly_cnt == 4'd10) begin  //延时10拍
  52. 52                                          //原因是FIFO IP核内部状态信号的更新存在延时
  53. 53                                          //延迟10拍以等待状态信号更新完毕
  54. 54                     dly_cnt <= 4'd0;
  55. 55                  state   <=2'd2;        //开始读操作
  56. 56              end
  57. 57              else
  58. 58                  dly_cnt <= dly_cnt + 4'd1;
  59. 59             end
  60. 60          2'd2: begin
  61. 61                 if(almost_empty) begin     //等待FIFO将被读空
  62. 62                     fifo_rd_en <= 1'b0;    //关闭读使能
  63. 63                     state      <= 2'd0;    //回到第一个状态
  64. 64                 end
  65. 65                 else                      //如果FIFO没有被读空
  66. 66                    fifo_rd_en <= 1'b1;    //则持续打开读使能
  67. 67             end
  68. 68          default : state <= 2'd0;
  69. 69         endcase
  70. 70     end
  71. 71 end
  72. 72
  73. 73 endmodule
复制代码
读模块的代码结构与写模块几乎一样,也是使用一个不断进行状态循环的小的状态机来控制操作过程,读者参考着代码应该很容易能够理解,这里就不再赘述。
我们对代码进行仿真,TestBench中只要送出时钟的复位信号即可。TB文件如下:
  1. 1 module tb_ip_fifo;
  2. 2
  3. 3 reg  grs_n;
  4. 4 //GTP_GRS I_GTP_GRS(
  5. 5      GTP_GRS GRS_INST(
  6. 6      .GRS_N (grs_n)
  7. 7      );
  8. 8      
  9. 9      initial begin
  10. 10     grs_n = 1'b0;
  11. 11     #5000 grs_n = 1'b1;
  12. 12     end
  13. 13     
  14. 14     //Inputs
  15. 15     reg sys_clk;
  16. 16     reg sys_rst_n;
  17. 17     
  18. 18     //Instantiate the Unit Under Test (UUT)
  19. 19     ip_fifo u_ip_fifo (
  20. 20         .sys_clk         (sys_clk),
  21. 21         .sys_rst_n       (sys_rst_n)
  22. 22     );
  23. 23     
  24. 24     //Genaratethe clk
  25. 25     parameter PERIOD = 20;
  26. 26     always begin
  27. 27         sys_clk = 1'b0;
  28. 28         #(PERIOD/2) sys_clk = 1'b1;
  29. 29         #(PERIOD/2);
  30. 30     end   
  31. 31   
  32. 32     initial begin
  33. 33         // Initialize Inputs
  34. 34         sys_rst_n = 0;
  35. 35         // Wait 100 ns for global reset to finish
  36. 36         #100  ;
  37. 37         sys_rst_n = 1;
  38. 38         // Add stimulus here
  39. 39         
  40. 40     end
  41. 41
  42. 42 endmodule
复制代码
写满后转为读的仿真波形图如下图所示:
image027.png
图 20.4.14仿真波形1
由波形图可知,当写满255个数据后,fifo_full满信号就会拉高。经过延时之后,fifo_rd_en写使能信号拉高,经过一拍之后就开始将fifo中的数据送到fifo_dout端口上。
写满后转为读的仿真波形图如下图所示:
image029.png
图 20.4.15 仿真波形2
由波形图可知,当读完255个数据后,fifo_empty空信号就会拉高。经过延时之后,fifo_wr_en写使能信号拉高,经过一拍之后就开始向fifo中继续写入数据。

1.5 下载验证
新建DebugCore,将fifo_wr_en、fifo_rd_en、fifo_din、fifo_dout、almost_full、almost_empty、fifo_full、fifo_empty、fifo_wr_data_count和fifo_rd_data_count这十个信号添加至观察列表中,新建DebugCore核的方法这里不再赘述。
编译工程并生成比特流.sbit文件后,此时将下载器一端连接电脑,另一端与开发板上的JTAG下载口连接,连接电源线,并打开开发板的电源开关。
点击PDS工具栏的下载按钮,在弹出的Fabric Configuration界面中双击“Boundary Scan”,我们将生成好的sbit流文件下载到开发板中去。
image031.png
图 20.5.1 将探针信号添加到波形窗口中
同时我们在窗口将“fifo_rd_en”信号设置为上升沿触发。
单击左上角的触发按钮,如下图所示:
image033.png
图 20.5.2 触发按钮
在FabricDebugger中观察的波形如下图所示:
image035.png
图 20.5.3 捕获得到的波形图
从捕获得到的波形图中可以看出,其逻辑行为与仿真波形图中的一致,证明我们的代码正确地实现了预期的功能。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-22 17:46

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表