你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32MP1嵌入式Linux驱动开发指南V1.7——IP核之FIFO实验

[复制链接]
STMCU小助手 发布时间:2022-9-27 12:02
IP核之FIFO实验3 S" [1 h6 f  c, T$ F
FIFO的英文全称是First In First Out,即先进先出。FPGA使用的FIFO一般指的是对数据的存储具有先进先出特性的一个缓存器,常被用于数据的缓存,或者高速异步数据的交互也即所谓的跨时钟域信号传递。它与FPGA内部的RAM和ROM的区别是没有外部读写地址线,采取顺序写入数据,顺序读出数据的方式,使用起来简单方便,由此带来的缺点就是不能像RAM和ROM那样可以由地址线决定读取或写入某个指定的地址。本章我们将对Vivado软件生成的FIFO IP核进行读写测试,来向大家介绍Xilinx FIFO IP核的使用方法。
5 O% |; d2 I2 M( z# ?/ }5 P2 _1 s0 p0 P) f( w
15.1 FIFO IP核简介
1 G& q; Y+ d! z根据FIFO工作的时钟域,可以将FIFO分为同步FIFO和异步FIFO。同步FIFO是指读时钟和写时钟为同一个时钟,在时钟沿来临时同时发生读写操作。异步FIFO是指读写时钟不一致,读写时钟是互相独立的。Xilinx的FIFO IP核可以被配置为同步FIFO或异步FIFO,其信号框图如下图所示。从图中可以了解到,当被配置为同步FIFO时,只使用wr_clk,所有的输入输出信号都同步于wr_clk信号。而当被配置为异步FIFO时,写端口和读端口分别有独立的时钟,所有与写相关的信号都是同步于写时钟wr_clk,所有与读相关的信号都是同步于读时钟rd_clk。
" x. q) g  S) `. A  G& _" \/ p$ [$ ]' {% p# D
ae5fd345c63b4174b7b822603aef3e03.png
# E1 t/ j/ s4 S9 h( @2 z9 A6 w0 e7 ~
. o2 M/ X7 C1 B/ }5 [图 7.5.13.1 Xilinx的FIFO IP核的信号框图2 J& O' I- }; }
对于FIFO需要了解一些常见参数:
+ K+ D/ r( N+ m; w- _* uFIFO的宽度:FIFO一次读写操作的数据位N。2 Q; i1 @; x* |
FIFO的深度:FIFO可以存储多少个宽度为N位的数据。
/ {/ d( f% k/ [8 Q- m/ b将空标志:almost_empty。FIFO即将被读空。4 I+ `% s( B8 n4 w5 t6 E1 T/ ~9 i
空标志:empty。FIFO已空时由FIFO的状态电路送出的一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出。
8 H: X, Z7 j% t2 j$ n. E" n将满标志:almost_full。FIFO即将被写满。. D5 n: q* T$ n  Q6 W- f9 a
满标志:full。FIFO已满或将要写满时由FIFO的状态电路送出的一个信号,以阻止FIFO的写操作继续向FIFO中写数据而造成溢出。8 F9 ~$ q; r  M8 @
读时钟:读FIFO时所遵循的时钟,在每个时钟的上升沿触发。. E) ]- O" b  ]1 ]$ L: h
写时钟:写FIFO时所遵循的时钟,在每个时钟的上升沿触发。
& f. ~7 z6 a4 ~- |这里请注意,“almost_empty”和“almost_full”这两个信号分别被看作“empty”和“full”的警告信号,他们相对于真正的空(empty)和满(full)都会提前一个时钟周期拉高。
) |* `7 m, c  ?2 U对于FIFO的基本知识先了解这些就足够了,可能有人会好奇为什么会有同步FIFO和异步FIFO,它们各自的用途是什么。之所以有同步FIFO和异步FIFO是因为各自的作用不同。同步FIFO常用于同步时钟的数据缓存,异步FIFO常用于跨时钟域的数据信号的传递,例如时钟域A下的数据data1传递给异步时钟域B,当data1为连续变化信号时,如果直接传递给时钟域B则可能会导致收非所送的情况,即在采集过程中会出现包括亚稳态问题在内的一系列问题,使用异步FIFO能够将不同时钟域中的数据同步到所需的时钟域中。4 I8 K7 V6 q9 |6 E* H- ~7 }
15.2 实验任务9 {9 K+ e- t3 Y; U4 ^: h/ w
本节的实验任务是使用Vivado生成FIFO IP核,并实现以下功能:当FIFO为空时,向FIFO中写入数据,写入的数据量和FIFO深度一致,即FIFO被写满;然后从FIFO中读出数据,直到FIFO被读空为止,以此向大家详细介绍一下FIFO IP核的使用方法。3 L* `, ~6 O' B( j
; F3 [% P  W  }% x
15.3 硬件设计% q- }' q& X! Z
本章实验只用到了输入的时钟信号和按键复位信号,没有用到其它硬件外设。
+ s* M- G) n( _* E本实验中,各端口信号的管脚分配如下表所示。, ~- u) T! Q# c! }
表 15.3.1 IP实验管脚分配
) L2 e! x3 Q& V) u5 W/ A2 z* `) e信号名 方向 管脚 端口说明 电平标准
- p7 H# c" @: B! d; Bsys_clk input U18 系统时钟,50Mhz LVCMOS337 c/ N: z$ R" ^
sys_rst_n input N16 系统复位,低有效 LVCMOS33
3 t' t* f3 y6 [1 P/ N对应的XDC约束语句如下所示:
, ]  e3 d6 D9 _2 A$ B0 d6 o4 N1 E5 a6 E' M8 l/ C
  1. create_clock -period 20.000 -name clk [get_ports sys_clk]
    1 F: J8 h6 ?: F+ m, ^/ X) G! o8 V
  2. set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports sys_clk]3 K) R5 C/ r) k) F1 |
  3. set_property -dict {PACKAGE_PIN N16 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
复制代码

2 ?) f, s. x1 B: }9 [15.4 程序设计
1 K4 c9 u1 T( Q4 m: ]根据实验任务要求和模块化设计的思想,我们需要如下4个模块:fifo IP核、写fifo模块、读fifo模块以及顶层例化模块实现前三个模块的信号交互。由于FIFO多用于跨时钟域信号的处理,所以本实验我们使用异步FIFO来向大家详细介绍双时钟FIFO IP核的创建和使用。为了方便大家理解,这里我们将读/写时钟都用系统时钟来驱动。系统的功能框图如下图所示:; L3 j! ~& N5 `# h6 T/ |5 t9 f
8 R( G" J  p: G4 A
7d28cd1b619b4938b821406654db921d.png
3 o( [  P% l8 h0 m% O- e$ j  d
' e$ l& M# W( ]2 a$ i图 7.5.13.1 系统框图, C, s  ?5 C8 F
首先创建一个名为ip_fifo的工程,接下来我们创建fifo IP核。在Vivado软件的左侧“Flow Navigator”栏中单击“IP Catalog”,“IP Catalog”按钮以及单击后弹出的“IP Catalog”窗口如下图所示。
, X( I$ a- F! j! v
6 w9 B  P$ {% ^. B. z fd980f6b917c4a7bb74df6dfcf8b100e.png
( S# K7 v  `7 E( W' |6 F
6 O6 B% G* [( |! h7 O图 7.5.13.2 “IP Catalog”按钮6 ^6 ?, c! Z( {# Z" i
7 r$ }; l/ Y' _+ \# u) d
03c136113b23493db527d24f13efac11.png
/ V) v$ o1 b$ v9 ]3 x- \
0 ~) J) W7 ~3 c& B' L图 7.5.13.3 “IP Catalog”窗口6 Q' G& w6 I: B4 l
在“IP Catalog”窗口中,在搜索栏中输入“fifo”关键字,这时Vivado会自动查找出与关键字匹配的IP核名称,我们双击“FIFO Generator”,如下图所示。
" J' f$ ~) d/ E8 I/ I0 K  y  p  N8 I3 |+ G0 v
d012d9f3f6404b46b9d3a068d851b84e.png 6 _7 D$ `+ E; w  _- R

" C& v0 C& d7 d3 D: e+ s+ ~9 c- ]( i图 7.5.13.4 搜索栏中输入关键字5 b) l4 h; e1 |' M6 A
弹出“Customize IP”窗口,如下图所示。
# E: ]& H! O, y4 u$ }0 c* w9 {- L3 f2 ]& |
fcbb85c85d604d0bbad8fdfe9147259d.png
/ U% L! K, i7 s* J8 b. d( c
2 F; e2 ^  q9 Y8 T图 7.5.13.5 “Customize IP”窗口
& {6 [8 H8 o3 J3 ^, E; g接下来就是配置IP核的时钟参数的过程。
& d( y0 b' N1 x4 C最上面的“Component Name”一栏设置该IP元件的名称,这里保持默认即可。在第一个“Basic”选项卡中,“Interface Type”选项用于选择FIFO接口的类型,这里我们选择默认的“Native”,即传统意义上的FIFO接口。“Fifo Implementation”选项用于选择我们想要实现的是同步FIFO还是异步FIFO以及使用哪种资源实现FIFO,这里我们选择“Independent Clocks Block RAM”,即使用块RAM来实现的异步FIFO。如下图所示。( `/ K& m6 D2 V

7 z1 `# v" ]$ U3 _7 M" ~: e. O/ | b1c794e44ac547658e8e0e7258012579.png
8 u; [% ~9 w! ?5 _
. C# f2 H3 D# e" _) u图 7.5.13.6 “Basic”选项卡
1 T/ e7 |/ z6 ^& V接下来是“Native Ports”选项卡,用于设置FIFO端口的参数。“Read Mode”选项用于设置读FIFO时的读模式,这里我们选择默认的“Standard FIFO”。“Data Port Parameters”一栏用于设置读写端口的数据总线的宽度以及FIFO的深度,写宽度“Write Width”我们设置为8位,写深度“Write Depth”我们设置为256,注意此时FIFO IP核所能实现的实际深度却是255;虽然读宽度“Read Width”能够设置成和写宽度不一样的位宽,且此时读深度“Read Depth”会根据上面三个参数被动地自动设置成相应的值;但是我们还是将读宽度“Read Width”设置成和写宽度“Write Width”一样的位宽,这也是在实际应用中最常用的情况。由于我们只是观察FIFO的读写,所以最下面的“Reset Pin”选项我们可以不使用,把它取消勾选。其他设置保持默认即可,如下图所示。
9 F/ p0 A! N# {, t# Y3 O+ N
7 P4 }9 Q4 ]6 K+ H: R 39b0ad25b7364aed8988b9f0f7a6239b.png ; E* v. z9 F8 F6 J

$ i4 Y3 v' }9 U图 7.5.13.7 “Native Ports”选项卡; E# v; ]$ K) P8 w( A  s; C
“Status Flags”选项卡,用于设置用户自定义接口或者用于设定专用的输入口。这里我们使用“即将写满”和“即将读空”这两个信号,所以我们把它们勾选上,其他保持默认即可,如下图所示。
! s- p4 }, L. H8 t# N4 _  l7 _! {3 u+ }1 l  B5 N7 Y1 ~
7ae0f417ed954987b904a9e11b93ce8b.png ) x/ k1 i1 I$ T. b4 `0 s

, x7 d% s) i0 h图 7.5.13.8 “Status Flags”选项卡6 [, B. y3 Q; B0 X- `  I) T
“Data Counts”选项卡用于设置FIFO内数据计数的输出信号,此信号表示当前在FIFO内存在多少个有效数据。为了更加方便地观察读/写过程,这里我们把读/写端口的数据计数都打开,且计数值总线的位宽设置为满位宽,即8位,如下图所示。7 r" H% E% D3 F! q
! [! _1 M: k; f5 M
e59f5941a7234754b0d70b6f6f2a6596.png
* Y; l+ P4 A# e
% `4 y: h% m. ~- U/ n8 `2 R图 7.5.13.9 “Data Counts”选项卡7 M9 L+ o: G7 k1 g
最后的“Summary”选项卡是对前面所有配置的一个总结,在这里我们直接点击“OK”按钮即可,如下图所示。; L& U. S( e2 o& F. q8 U
# X1 i+ Z  S- O& b5 O8 a  Z6 O
743f467f4cf94854a1d3fb191ae3eb33.png ) n$ W# h1 T. L- b
& C% L, F* H, N6 z* @* \9 z9 h+ j
图 7.5.13.10 “Summary”选项卡( W: A6 A) C/ s
接着就弹出了“Genarate Output Products”窗口,我们直接点击“Generate”即可,如下图所示。; ]* S' x, v" B

: ]; o! X5 x5 K9 |$ J" v3 n! H+ s! e, g cd448a6f093643b1a331cea257f4f26d.png 9 x- _6 b! C7 L% R- h5 M
' J$ t6 d. W1 y* Q- {! _2 Q
图 7.5.13.11 “Genarate Output Products”窗口1 k) z7 C' t& X+ w8 C" h
之后我们就可以在“Design Run”窗口的“Out-of-Context Module Runs”一栏中出现了该IP核对应的run“fifo_generator_0_synth_1”,其综合过程独立于顶层设计的综合,所以在我们可以看到其正在综合,如下图所示。" o6 J" \, X. P2 [; ?1 d. H$ B

; ^+ @' P$ @) n: E e0584a38383c4fb8866533650f64f531.png
/ k+ g+ @1 W! y, B: a- p; |$ _1 ]3 s3 V' t
图 7.5.13.12 “fifo_generator _0_synth_1”run* A% i# i7 T# b* g7 }
在其Out-of-Context综合的过程中,我们就可以进行RTL编码了。首先打开IP核的例化模板,在“Source”窗口中的“IP Sources”选项卡中,依次用鼠标单击展开“IP”-“fifo_generator _0”-“Instantitation Template”,我们可以看到“fifo_generator_0.veo”文件,它是由IP核自动生成的只读的verilog例化模板文件,双击就可以打开它,如下图所示。
- R( r' V5 e2 x9 A9 i0 l! i$ F& `" t& L# S
95a69cb0d8e14a2689b6c9fa8778d833.png
* N! [0 P# u2 G, V1 `6 x  I7 E1 ~6 l& Q# n& _2 r) d8 w
图 7.5.13.13 “fifo_generator_0.veo”文件! Q3 G+ n! q5 B, V( Z' C2 t
我们创建一个verilog源文件,其名称为ip_fifo.v,作为顶层模块,其代码如下:% W. N9 G" X& r6 o
7 h. s. @$ ^8 y. o6 E  u
  1. 1  module ip_fifo(( \( S/ L9 R- W; a* R
  2. 2      input    sys_clk ,  // 时钟信号- M5 h8 P  I3 E
  3. 3      input    sys_rst_n  // 复位信号
    : g/ H2 U; m# q
  4. 4  );
    : P: N8 I) ^" G( ^0 e) A
  5. 5  ) V- [0 k# Q5 W! \/ [! s2 ]1 b# p
  6. 6  //wire define
    7 }2 y4 i9 d) e2 ]9 v
  7. 7  wire         fifo_wr_en         ;  // FIFO写使能信号
    0 I) e3 u, f* d: _* r& D
  8. 8  wire         fifo_rd_en         ;  // FIFO读使能信号
    ( C1 y9 {7 c$ y/ C: k7 V6 A" O# W/ t; U
  9. 9  wire  [7:0]  fifo_din           ;  // 写入到FIFO的数据
    ( P* i/ l, h/ u8 c4 y* W4 l
  10. 10 wire  [7:0]  fifo_dout          ;  // 从FIFO读出的数据
    8 S. V8 H% |1 C; Z% l$ x" @- P
  11. 11 wire         almost_full        ;  // FIFO将满信号
    - a3 Z7 H  X1 ~: f' ?) [
  12. 12 wire         almost_empty       ;  // FIFO将空信号
    6 i) e2 d. _; z( l0 L( c8 |
  13. 13 wire         fifo_full          ;  // FIFO满信号
    ; f2 Q" G3 H& ^7 \+ v. `8 _
  14. 14 wire         fifo_empty         ;  // FIFO空信号( l1 r4 v. R4 e0 s$ D
  15. 15 wire  [7:0]  fifo_wr_data_count ;  // FIFO写时钟域的数据计数) L3 ]+ t9 B6 m) u, }
  16. 16 wire  [7:0]  fifo_rd_data_count ;  // FIFO读时钟域的数据计数
    9 N/ r9 T8 k! M1 L: J* z
  17. 17
    ) R- `7 A( a- }3 G/ m  c) |
  18. 18 //*****************************************************4 O, |! `; B, I2 y  b
  19. 19 //**                    main code
    # @  M, m  U: l  R- p; F
  20. 20 //*****************************************************
    3 h5 C" A7 L  P1 S% d
  21. 21 : R% w2 U7 G; {! `- \
  22. 22 //例化FIFO IP核; |# w4 X, @5 l0 T$ U
  23. 23 fifo_generator_0  fifo_generator_0 (. {1 a& G6 D) R6 t
  24. 24     .wr_clk        ( sys_clk            ),  // input wire wr_clk, ]/ @; C/ U( F0 p$ g+ F* n
  25. 25     .rd_clk        ( sys_clk            ),  // input wire rd_clk" ]' l. `  }& M- `$ \0 g; ]
  26. 26 , l6 P5 l8 u0 M) L# l8 g. f0 Q
  27. 27     .wr_en         ( fifo_wr_en         ),  // input wire wr_en
    * ~6 `" I) b! W; Z  Q& c3 O
  28. 28     .rd_en         ( fifo_rd_en         ),  // input wire rd_en
    - |* ^: u3 {9 G8 ]" M9 z
  29. 29 ) j% e2 O; _3 Y6 m
  30. 30     .din           ( fifo_din           ),  // input wire [7 : 0] din7 ~8 {  w6 f9 M7 c
  31. 31     .dout          ( fifo_dout          ),  // output wire [7 : 0] dout5 U6 o7 M" U" l4 d
  32. 32     * y+ ]; s2 R! ?
  33. 33     .almost_full   (almost_full         ),  // output wire almost_full
    9 P0 U9 }- e; f7 f. I% o
  34. 34     .almost_empty  (almost_empty        ),  // output wire almost_empty9 e6 H4 O+ f7 f" g/ i- F
  35. 35     .full          ( fifo_full          ),  // output wire full
    3 b+ Q% M& l/ E& n- T
  36. 36     .empty         ( fifo_empty         ),  // output wire empty
    - u# w. k, h3 P, M6 r6 p& K# b
  37. 37 ( t2 L  T1 p6 v1 \
  38. 38  .wr_data_count ( fifo_wr_data_count ),  // output wire [7 : 0] wr_data_count   
    ; V* K8 ^) v/ A* _4 v( M
  39. 39  .rd_data_count ( fifo_rd_data_count )   // output wire [7 : 0] rd_data_count
    ! ^) r! i8 ]- B; U
  40. 40 );& v: ?! _- Y! H. [+ I
  41. 41
    + b4 Y$ {' u+ [$ T+ b& ^
  42. 42 //例化写FIFO模块
    - E, R) ~# e5 J- I
  43. 43 fifo_wr  u_fifo_wr(
    % p$ Z" N1 E: w* B! L
  44. 44     .clk            ( sys_clk    ),   // 写时钟$ N0 `+ W& j: [0 b& }+ k! G+ O
  45. 45     .rst_n          ( sys_rst_n  ),   // 复位信号2 @2 B1 J/ k  y5 i! F, U% q
  46. 46 2 h6 M* w- X- `8 t. a( t# N
  47. 47     .fifo_wr_en     ( fifo_wr_en )  , // fifo写请求
    4 {. V  }2 k, J1 G* i
  48. 48     .fifo_wr_data   ( fifo_din    ) , // 写入FIFO的数据, Y; c( e% `2 d% Y( }
  49. 49     .almost_empty   ( almost_empty ), // fifo将空信号) p! P9 Y0 N6 t5 a& T$ J
  50. 50     .almost_full    ( almost_full  )  // fifo将满信号: P/ o" j$ [' r- k; {5 b( h
  51. 51 );% Q. K* q' Z, {0 R
  52. 52 ' T: w2 a9 j- d4 [5 w
  53. 53 //例化读FIFO模块3 d4 I) N1 ^5 ?7 K
  54. 54 fifo_rd  u_fifo_rd(4 ^) A% F; b3 }/ f0 p5 h7 U
  55. 55     .clk          ( sys_clk    ),      // 读时钟
    3 o0 N! C- Y( o' t. C
  56. 56     .rst_n        ( sys_rst_n  ),      // 复位信号" p" }8 ?7 X7 L! e5 t( j, @7 {
  57. 57 . ]- N7 P. i. J9 z5 D9 _) P7 s+ \
  58. 58     .fifo_rd_en   ( fifo_rd_en ),      // fifo读请求" @1 C4 Y- X2 ]# i2 x4 J( y$ u9 C# d
  59. 59     .fifo_dout    ( fifo_dout  ),      // 从FIFO输出的数据6 k( ]5 D4 J& o; E1 l% p% w2 r
  60. 60     .almost_empty ( almost_empty ),    // fifo将空信号
    " S: h$ d! N7 X+ o/ J. Y
  61. 61     .almost_full  ( almost_full  )     // fifo将满信号' J1 d9 S$ [; x
  62. 62 );
    9 P, j. B5 P, U2 b& l
  63. 63
    " F6 m8 R1 V9 x
  64. 64 //例化ILA IP核
    , q2 K# f1 P; p2 h
  65. 65 ila_0  ila_0 (
    ! J  v( r- a8 f! J
  66. 66  .clk    ( sys_clk            ), // input wire clk( H% }; `6 v& {: f! q) o
  67. 67
    " y. V: T; b; y* l6 D% v
  68. 68  .probe0 ( fifo_wr_en         ), // input wire [0:0]  probe0  
    1 Q. x" S3 n7 R9 W$ g
  69. 69  .probe1 ( fifo_rd_en         ), // input wire [0:0]  probe1 . O% O6 Z9 J2 ?$ I* [7 Y7 W
  70. 70  .probe2 ( fifo_din           ), // input wire [7:0]  probe2 4 I+ z7 t' `# b3 U
  71. 71  .probe3 ( fifo_dout          ), // input wire [7:0]  probe3 4 m; A# x- p' R: E
  72. 72  .probe4 ( fifo_empty         ), // input wire [0:0]  probe4 - u$ o( @# R5 q/ f' r1 E
  73. 73  .probe5 ( almost_empty       ), // input wire [0:0]  probe5
    9 s' {3 ?6 `3 A
  74. 74  .probe6 ( fifo_full          ), // input wire [0:0]  probe6
    " O5 r/ q( O8 Z- _' T( e
  75. 75  .probe7 ( almost_full        ), // input wire [0:0]  probe7
    9 f  c2 s8 f: t" \1 z
  76. 76  .probe8 ( fifo_wr_data_count ), // input wire [7:0]  probe8
    6 M! v+ l6 ^) k8 ?  }# S
  77. 77  .probe9( fifo_rd_data_count  )  // input wire [7:0]  probe9
    $ }5 W2 ~4 T1 y- G7 B2 d  L: r. u
  78. 78 );2 Q6 M* V5 h! j2 J
  79. 79
    ) c+ W" D8 a* u- E  G1 v4 X  M
  80. 80 endmodule
复制代码
3 X. f  k# J1 F# P# }" w
顶层模块主要是对FIFO IP核、写FIFO模块、读FIFO模块进行例化,除此之外本实验还生成并例化了一个ILA IP核,用于对顶层模块信号的进行在线捕获观察。
5 F) b# b5 p1 V写FIFO模块fifo_wr.v源文件的代码如下:
) S3 d5 I  U% S. ~+ c. u
/ o6 f( M+ i. Z
  1. 1  module fifo_wr(
    " V3 u1 j) C* ~* ]6 v- w0 q
  2. 2      input                  clk    ,          // 时钟信号
    , _; \- O# R( i$ M, D3 ]- Y9 t
  3. 3      input                  rst_n  ,          // 复位信号
    1 `9 `. A3 l' X6 L  _
  4. 4      
    / \  y- _) o( A
  5. 5      input                  almost_empty,     // FIFO将空信号! A8 n5 Z4 J. D) C2 ~& \
  6. 6      input                  almost_full ,     // FIFO将满信号( n( V  P; y2 Q6 ~3 e
  7. 7      output    reg          fifo_wr_en ,      // FIFO写使能
    . E* y% m3 o) L2 i# J  V
  8. 8      output    reg  [7:0]   fifo_wr_data      // 写入FIFO的数据7 a) E7 Z4 y2 s
  9. 9  );3 e9 f- T& x/ D$ h' w9 m
  10. 10 5 A) s4 W: r2 V" h9 B& {  `1 P
  11. 11 //reg define* r3 t" S& F/ m$ @! z/ s
  12. 12 reg  [1:0]  state            ;  //动作状态+ A. w3 `8 \: J  Z, A" _
  13. 13 reg         almost_empty_d0  ;  //almost_empty 延迟一拍4 s8 f8 O$ [2 E+ x% C* k! y) b2 m( F+ s
  14. 14 reg         almost_empty_syn ;  //almost_empty 延迟两拍0 H, z6 P8 \5 `9 r5 w* I- r/ |
  15. 15 reg  [3:0]  dly_cnt          ;  //延迟计数器
    # E! r  D" \; ]7 T
  16. 16 //*****************************************************  p: x: M9 I2 l$ G
  17. 17 //**                    main code% d/ D* v/ }; W6 }
  18. 18 //*****************************************************
    * p0 b9 p' j6 n
  19. 19 % O5 `0 W- Q7 U" K
  20. 20 //因为 almost_empty 信号是属于FIFO读时钟域的
    , P, Y+ X" |3 C' p  X
  21. 21 //所以要将其同步到写时钟域中# g* O6 ~# m0 x( h) Q' l  n9 [$ h
  22. 22 always@( posedge clk ) begin8 w& g; i& d/ V4 ~
  23. 23  if( !rst_n ) begin
    2 p0 j& ]0 ]& U8 Z6 _6 p
  24. 24      almost_empty_d0  <= 1'b0 ;
    2 ~; D8 j; M0 z8 {1 u4 u% Q4 I, t+ V
  25. 25      almost_empty_syn <= 1'b0 ;
    # _. z) d( R9 |: I. f
  26. 26  end" ], |- n5 K+ l
  27. 27  else begin
    3 {  H1 B2 p  |
  28. 28      almost_empty_d0  <= almost_empty ;
    . w+ L& p. s. g8 R, l
  29. 29      almost_empty_syn <= almost_empty_d0 ;& u  [- s- c8 O% R+ X
  30. 30  end
    " H7 Z2 {3 l9 I' j6 R% @
  31. 31 end$ d$ C8 n' d9 e6 W
  32. 32 + p# O+ P- W6 P' l
  33. 33 //向FIFO中写入数据" L- |" r) d9 P- @. _9 g# V( O
  34. 34 always @(posedge clk ) begin
    ; P- U) i/ ]( r. t( S
  35. 35     if(!rst_n) begin
    0 G* K5 Q+ z' P. f
  36. 36         fifo_wr_en   <= 1'b0;* h: j, |$ _; l* W: n/ n
  37. 37         fifo_wr_data <= 8'd0;
    / l. k3 k4 H: K6 Y, w* b5 M/ ]+ }
  38. 38         state        <= 2'd0;; J8 n5 ?  a' x6 h4 I) C
  39. 39      dly_cnt      <= 4'd0;8 w9 H- q4 o: ]
  40. 40     end% ~/ `2 v% A# k4 R- _9 t3 d# ~# i
  41. 41     else begin
    ; S8 D& L4 y( G5 @) C
  42. 42         case(state)
    2 J+ I) @! [! k4 x1 U
  43. 43             2'd0: begin
    0 v/ h) E7 b' d3 V7 x( e1 `  L# F
  44. 44                 if(almost_empty_syn) begin  //如果检测到FIFO将被读空(下一拍就会空)* k4 L3 [5 Q0 t* B$ @6 x  C
  45. 45                     state <= 2'd1;          //就进入延时状态
    + x- D( v( M& ]+ e* T
  46. 46                 end
    7 t* b/ c4 u) v' b/ J
  47. 47                 else
    & \% H' o' S/ R
  48. 48                     state <= state;
    4 W" e0 V4 m5 c% J8 U
  49. 49             end
    : U0 n, ~. [9 f- ?8 ?* h  ~  {
  50. 50          2'd1: begin3 @( s$ o* {: W* N3 D( Z
  51. 51                 if(dly_cnt == 4'd10) begin  //延时10拍+ j% r" b  ^4 @4 c" O0 |
  52. 52                                             //原因是FIFO IP核内部状态信号的更新存在延时8 n& Q" |. a8 n9 m
  53. 53                                             //延迟10拍以等待状态信号更新完毕                   3 M0 c) p- [1 g5 k
  54. 54                     dly_cnt    <= 4'd0;     * Q; L7 V! q3 W0 Y, E7 @
  55. 55                  state      <= 2'd2;        //开始写操作4 y5 O8 Y) w1 U8 I$ ^% i3 V
  56. 56                  fifo_wr_en <= 1'b1;        //打开写使能) M- B. v9 V* _
  57. 57              end9 P. A# I* ?) o9 U2 c0 N6 I1 u
  58. 58              else
    & M/ \' X% S! z8 K* {0 Y6 P/ K
  59. 59                  dly_cnt <= dly_cnt + 4'd1;  |( M$ g9 O6 ^8 O* H! e
  60. 60             end            
    ' X/ N2 B) u3 O' |
  61. 61          2'd2: begin/ ~3 B1 `+ t0 \& @5 i6 Q+ ]
  62. 62                 if(almost_full) begin      //等待FIFO将被写满(下一拍就会满)
    9 ], V0 I& x2 i, h
  63. 63                     fifo_wr_en   <= 1'b0;  //关闭写使能5 t- O: U/ o, b+ r$ {" _
  64. 64                     fifo_wr_data <= 8'd0;3 w, z, y, q" D, Y( t
  65. 65                     state        <= 2'd0;  //回到第一个状态& s9 G' I( h/ l3 v$ s: ?$ V( a2 J2 I
  66. 66                 end9 F* q# V; @) V7 A& S/ `, u! _
  67. 67                 else begin                 //如果FIFO没有被写满. n; S9 d" _- D" E
  68. 68                     fifo_wr_en   <= 1'b1;  //则持续打开写使能
    6 J5 R0 B5 E" d; N, z7 d
  69. 69                     fifo_wr_data <= fifo_wr_data + 1'd1;  //且写数据值持续累加8 `+ Y& Y% N5 b/ h, |* Z
  70. 70                 end; E& ~  F0 ^$ G$ t% k
  71. 71             end
    . N, P! n- d/ t
  72. 72          default : state <= 2'd0;6 G/ T7 m7 d( ~+ u  k) `; [
  73. 73         endcase
    : Q& @6 W" ?# w
  74. 74     end+ ]$ e+ S) R3 I1 j/ b
  75. 75 end: N. t- [; l" `" w% X+ ]4 V
  76. 76 9 d# X7 F) E6 a$ [5 X0 L
  77. 77 endmodule" E, _( k5 Y6 V. [) X
复制代码

# u6 D1 u$ m' lfifo_wr模块的核心部分是一个不断进行状态循环的小状态机,如果检测到FIFO为空,则先延时10拍,这里注意,由于FIFO的内部信号的更新比实际的数据读/写操作有所延时,所以延时10拍的目的是等待FIFO的空/满状态信号、数据计数信号等信号的更新完毕之后再进行FIFO写操作,如果写满,则回到状态0,即等待FIFO被读空,以进行下一轮的写操作。
& B  ~. K6 u) O. d2 z9 h读FIFO模块fifo_rd.v源文件的代码如下:, G5 `- B4 e9 @+ r8 a) R. j
; g" @0 \3 X: R5 Z' A' d
  1. 1  module fifo_rd(
    ) T# }: p+ z4 ?' E& A
  2. 2      input               clk          ,   // 时钟信号2 S/ B8 N" N. S  \) ?. K6 X
  3. 3      input               rst_n        ,   // 复位信号
    & Q  C4 e7 D$ ~3 o* L9 j
  4. 4  , j+ q, H8 }) l6 e  R$ d
  5. 5      input        [7:0]  fifo_dout    ,   // 从FIFO读出的数据! t4 p2 q7 b) z( |
  6. 6      input               almost_full  ,   // FIFO将满信号3 V' H* n6 c4 W3 Q& t/ S  M
  7. 7      input               almost_empty ,   // FIFO将空信号
    9 s) }$ s6 T1 n$ D
  8. 8      output  reg         fifo_rd_en       // FIFO读使能" n/ a2 `% Q6 W! Y) s, s
  9. 9  );. i2 E8 k! o/ h- K
  10. 10 0 |0 ~9 L* J* K% P. P+ c* j
  11. 11 //reg define
    0 |7 v+ ~4 v( w) Q4 Z
  12. 12 reg  [1:0]  state           ;  //状态0 N+ X: V6 r& ~
  13. 13 reg         almost_full_d0  ;  //almost_full延迟一拍; H% f: a' _7 x# s& S- ?" c
  14. 14 reg         almost_full_syn ;  //almost_full延迟两拍8 n, g# X6 Q4 ]1 n% D
  15. 15 reg  [3:0]  dly_cnt         ;  //延迟计数器
    ; U! Z7 \5 D+ g
  16. 16
    * c  f5 S0 t7 W1 Y2 w0 H- C$ ?4 V
  17. 17 //*****************************************************. K0 e4 r% i7 s0 n0 H: l& G, ]6 z
  18. 18 //**                    main code4 W; K' Y6 \/ T" x, g+ h. F2 M
  19. 19 //*****************************************************6 k/ E& V6 i; P% B
  20. 20 , |- r* [- r' W/ _% Z9 F
  21. 21 //因为 fifo_full 信号是属于FIFO写时钟域的4 ^7 w& X3 r! W% {6 a
  22. 22 //所以要将其同步到读时钟域中
    - F- Y+ Y5 w# P8 _! a' N
  23. 23 always@( posedge clk ) begin
    " l- u$ c4 z4 Y1 W7 O. V
  24. 24  if( !rst_n ) begin
    . u/ c/ e# P/ Z9 |) K: N8 b2 l! _
  25. 25      almost_full_d0  <= 1'b0 ;
    ! c6 V. Q- P+ x9 N
  26. 26      almost_full_syn <= 1'b0 ;
    ! q" M0 }7 e- i: X! T5 ?! i
  27. 27  end
    2 s" b9 @- f/ I& C
  28. 28  else begin
    0 S3 k9 \4 j! ^* U0 k) e
  29. 29      almost_full_d0  <= almost_full ;
    % p8 j6 g1 `# I. a0 D
  30. 30      almost_full_syn <= almost_full_d0 ;2 ~# R( R* j0 I0 c  _, e& j; o
  31. 31  end
    " p& A; F- M5 B) ?
  32. 32 end$ E4 b$ M! K3 h
  33. 33 : ]7 u# T& o7 K* H
  34. 34 //读出FIFO的数据
    2 s. C# R* `+ T2 j. `) g$ |3 S
  35. 35 always @(posedge clk ) begin
    " U9 _8 p, S3 H
  36. 36     if(!rst_n) begin* N( [7 S2 j; N8 R4 L  H
  37. 37         fifo_rd_en <= 1'b0;
    $ W  z4 j& g# U0 O2 y2 P0 S' i0 i
  38. 38      state      <= 2'd0;  `7 X( ]' P, Z
  39. 39      dly_cnt    <= 4'd0;
    % P" s& D, b; Q3 x$ b/ k. G
  40. 40     end
    - L. {. P" \, K
  41. 41     else begin
    ! L( o; P+ f0 S: }6 ^
  42. 42         case(state)9 i8 K6 m! C$ j& n; J( E$ Y
  43. 43             2'd0: begin                     
    4 i% I& C6 |& e+ A
  44. 44                 if(almost_full_syn)        //如果检测到FIFO被写满% K# ]- E1 V- b. Z( w1 U/ l' W! G+ O
  45. 45                     state <= 2'd1;         //就进入延时状态
    - [) i# A' O$ y0 d
  46. 46                 else! E% s. S: @! R
  47. 47                     state <= state;6 O- J- b) ]5 m9 G1 s3 T/ s- Y
  48. 48             end
    & x; m: B( I0 E# N
  49. 49          2'd1: begin& m3 i1 \) C' t; J3 F3 R
  50. 50                 if(dly_cnt == 4'd10) begin //延时10拍) N& i9 C5 S! |: U4 ^( ^, a1 {
  51. 51                                            //原因是FIFO IP核内部状态信号的更新存在延时5 J# z  |* i# z4 |/ Z* x
  52. 52                                            //延迟10拍以等待状态信号更新完毕8 `8 q3 J9 q3 D2 V7 Q
  53. 53                     dly_cnt <= 4'd0;6 |& v* Z8 }2 x$ i8 I% z/ d
  54. 54                  state   <= 2'd2;          //开始读操作# }/ R- u. G5 c
  55. 55              end
    ( _* b9 k2 V& D+ Q5 u
  56. 56              else
    " q6 V& u- |( V1 z6 q1 {* G
  57. 57                  dly_cnt <= dly_cnt + 4'd1;
    9 K3 d6 B! u) K
  58. 58             end2 Q$ F8 g$ f% l1 V9 D
  59. 59          2'd2: begin& \3 q6 z( ]5 `! j+ @
  60. 60                 if(almost_empty) begin     //等待FIFO将被读空(下一拍就会空)
    4 D  l% X2 m% g; ?/ d$ O
  61. 61                     fifo_rd_en <= 1'b0;    //关闭读使能; a* C" e2 d5 R8 ^% q
  62. 62                     state      <= 2'd0;    //回到第一个状态
    . }3 B5 ]# H9 @$ q/ D- E  P, H
  63. 63                 end
    . Z' z2 u7 t( u6 n
  64. 64                 else                       //如果FIFO没有被读空
    % B& D: b; F& v- O, ^
  65. 65                     fifo_rd_en <= 1'b1;    //则持续打开读使能, M9 _* n( z. S  t+ B7 L) f
  66. 66             end ' {! \; J) H0 Z  W. h$ `" b! Y
  67. 67          default : state <= 2'd0;
    ! v7 I! C+ {# D
  68. 68         endcase8 P& [/ u% X3 }" R; u# b
  69. 69     end
    ( j# S+ K5 H' }2 K
  70. 70 end" w0 `4 s" \" d0 z/ w* i
  71. 71
    ( z8 f1 \0 d5 D3 z2 U
  72. 72 endmodule
复制代码
8 q, m8 G+ j! B6 o) a  @2 ^- O" ?
读模块的代码结构与写模块几乎一样,也是使用一个不断进行状态循环的小的状态机来控制操作过程,读者参考着代码应该很容易能够理解,这里就不再赘述。2 d' E# h4 h- [2 ^
我们对代码进行仿真,TestBench中只要送出时钟的复位信号即可。TB文件如下:
4 ?6 q, g( X. C, e
- S, z! B! G( V0 R
  1. 1  `timescale 1ns / 1ps$ y4 E6 E3 v7 ~
  2. 2  module tb_ip_fifo( );
    9 I$ ^) Q6 S0 W4 t5 M
  3. 3      // Inputs
    7 J7 h8 s) A" E
  4. 4      reg sys_clk;
    4 @! |: f- }6 `  ]5 ^& R7 j
  5. 5      reg sys_rst_n;7 V( j2 Q; p& \, F1 R7 B7 u
  6. 6      & k; K! D" w* B7 v2 _& I' L
  7. 7  // Instantiate the Unit Under Test (UUT)
    " P' ]! c& g/ r( M0 J2 V1 L* g
  8. 8  ip_fifo  u_ip_fifo (( w) l4 ], M  f3 I
  9. 9          .sys_clk         (sys_clk),
    9 H; C# ~+ W9 L5 i
  10. 10         .sys_rst_n       (sys_rst_n)4 }7 X. z- \, s& w9 D  |1 K
  11. 11     );8 i) o- w' L4 S# |4 o# X; f, Q
  12. 12     2 U3 N' i6 Z1 r, |
  13. 13 //Genarate the clk
      |( l% H7 v8 d3 b
  14. 14 parameter PERIOD = 20;
    6 n* M0 A* H9 U1 [: C  U3 |: U
  15. 15 always begin5 I; Z8 w( |9 q+ n5 O1 b6 b
  16. 16     sys_clk = 1'b0;2 D4 Y: i3 ~/ \8 b
  17. 17     #(PERIOD/2) sys_clk = 1'b1;! o4 e6 P& l1 M
  18. 18     #(PERIOD/2);
    " _3 a, |6 U. r/ ^) x/ {8 X! A
  19. 19 end   
    7 U- {2 d4 A0 g0 |" e& T: u6 N
  20. 20    $ k( _7 {* h- |# _- x& ?( o
  21. 21 initial begin
    9 r0 S$ w' B5 u
  22. 22     // Initialize Inputs
    . v- m  Y9 z3 E% J5 e3 d
  23. 23     sys_rst_n = 0;* W' B, P+ G( N3 F$ G+ m
  24. 24     // Wait 100 ns for global reset to finish0 j3 M6 e  v, `: R( l4 F! |
  25. 25     #100  ;
    . U5 `. v: ?  U' w7 x
  26. 26     sys_rst_n = 1;! E2 d! T  |" I, f$ y
  27. 27     // Add stimulus here
    3 @/ A3 [+ t; V0 C
  28. 28 end
    , c9 Z, n/ @! s' m8 _" I' x4 Y
  29. 29 ! T8 Z. [1 I" Z: e! I5 S
  30. 30 endmodule
复制代码
. E3 ?2 _8 Z0 e) ?) B" F4 a; }
写满后转为读的仿真波形图如下图所示:
1 X4 L1 U6 o# ]  V: U9 b% N3 [; S5 }) M+ X  T5 H
41ae55becb774f75908d2d7e1601e627.png 4 {  C, S/ R6 N% t

; o5 Q' _; v  K& {# @  H; b& [2 @' P图 7.5.13.14 Vivado仿真波形1, p' e0 I, d6 S( @  k8 Y
由波形图可知,当写满255个数据后,fifo_full满信号就会拉高。经过延时之后,fifo_rd_en写使能信号拉高,经过一拍之后就开始将fifo中的数据送到fifo_dout端口上。$ M. c" t6 h  |' Z, |3 _' p  f
写满后转为读的仿真波形图如下图所示:
8 I- m8 ^# d0 O1 n" C  M; ]
, z  T$ L% s4 U f1acfcc2daa440cd821e222a84d675cd.png
% n( i9 @5 b9 ~1 `8 ?. E' D! |
2 ^6 A! Y! x+ z+ P% @. U- ?图 7.5.13.15 Vivado仿真波形29 C) f$ D+ \5 [, ~: i7 ?
由波形图可知,当读完255个数据后,fifo_empty空信号就会拉高。经过延时之后,fifo_wr_en写使能信号拉高,经过一拍之后就开始向fifo中继续写入数据。0 Z. H: J# e0 J" ]: s

& h- X& [- ?1 v5 c15.5 下载验证
2 S- v: Q8 m6 a9 U+ b. Y6 x0 o编译工程并生成比特流.bit文件,将比特流.bit文件下载到Zynq中。
2 |/ r- P, _* h/ ]5 j4 Z9 {# G下载完成后,接下来在Vivado中会自动出现“hw_ila_1”Dashboard窗口。如下图所示:
, X$ G. e6 z; x/ Y# q2 A5 A+ c7 _1 Z7 f7 ]; m# c
79031191350c45ceac05d6f443afcaba.png
1 |9 J+ p5 z7 V$ Q/ W9 e/ R/ ~) l, G9 S$ s+ t" J) k, Q  r$ l
图 7.5.13.1 将探针信号添加到波形窗口中% p: |1 p3 L3 F; U" b
将有关探针信号添加到波形窗口中,这里我们已经完成信号的添加,方法是点击“hw_ila_1”Dashboard窗口左上角的“+”。同时我们在窗口右下角将“fifo_rd_en”信号添加到触发窗口中且设置为上升沿触发,
0 ~: T+ M  X/ B; j7 z' h8 i. L单击左上角的触发按钮,如下图所示:9 i4 h  S3 Q$ b0 {* \

7 e9 T8 _( r' B 4f510ade933b4ea78ded227c8609f5a4.png ' n: o& ?* v. ], L: p/ d) \

% h- ?7 f2 W- {% k6 v) V+ Q+ E+ i# M图 7.5.13.2 触发按钮
# x3 b/ ?( k: W$ m- i' I最后就看到了ILA捕获得到的数据,展开波形图如下图所示:+ k: G3 g) W* ]" |) N
5 k' M% c, [1 y0 U, K1 v3 P! r
ebb7edd59d2c43ce888faf767e434f50.png
( F7 C) d; _2 k; _1 |/ [. L  w$ o6 `7 @; u$ k( ^  z
图 7.5.13.3 捕获得到的波形图
  ]2 a! i2 c! j& E2 i2 N从捕获得到的波形图中可以看出,其逻辑行为与仿真波形图中的一致,证明我们的代码正确地实现了预期的功能.0 q: ?4 Y+ c! t. j2 \" K
————————————————6 v0 r  Y  Y6 e" g
版权声明:正点原子, d, T, n7 s) k$ G5 e; I; Y; z

8 U& [' J1 A/ l# A, j5 s3 _6 t3 {; s8 s( }  n1 q
赞 收藏 评论0 发布时间:2022-9-27 12:02

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版