找回密码
 注册
关于网站域名变更的通知
查看: 541|回复: 3
打印 上一主题 下一主题

学习FPGA的小Tips

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2021-6-28 09:21 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
4 \' o( c" K, B: b$ _! K
一、Verilog 编码风格$ }. E# ?+ f% _1 g3 d% r

, i0 A( P2 m& ^" T& N. R8 ^$ M1.1 使用“`include编译器指令”" s4 w9 K, O$ W0 C0 N# c

( O7 I4 c3 k5 c    文件包含“`include编译器指令”用于在合成过程中将源文件的全部内容插入到另一个文件中。它通常用于包括全局项目定义,而无需在多个文件中重复相同的代码。另一个用例是将代码的一部分插入模块,如以下示例所示:
5 `2 k" d0 X; A/ U1 j  `! B2 @* h+ ]' X
// file test_bench_top.v
- U8 {  J6 v; _7 ^( F// top-level simulation testbench' r& `5 g/ T' J, c2 d8 ^
module test_bench_top;
- s( a3 Q# ^  B4 G8 X9 _; _& p`include “test_case.v”& }# D6 S) q1 a  U& d9 q; ]4 ]
endmodule
. I3 G+ b" |% P# @// file test_case.v
# P1 k* }9 b+ |3 ?9 t4 sinitial begin
+ y+ C0 O* B0 j2 l- T& q$ [//…
: s* N" V; _: @$ c- l; M3 send0 }7 S+ W8 c1 p  ~% K$ e
task my_task;
; V) C$ o  g9 @; E% R1 G" Y//…
0 Q/ s. i, R8 k( @. ?" b2 g% U9 zendtask
  Z" {: N) \9 f- |
# h2 f7 C# t- K( ~- k3 M0 O> include编译器指令的语法定义为:`include <filename>
# e$ p* Z' f* K6 y( k; O: m  @( E3 m9 E; b; A  C" q$ l
<filename>可以是文件名,还可以包含绝对或相对路径名:
5 z' F/ h4 s5 ]8 k4 |" q- C% F
1 q- u5 Z, j, E" C- K+ M4 c& ``include “test_case.v”
- u9 }* q8 s5 _1 e7 w9 n`include “../../includes/test_case.v”% a3 b3 O. R- O) x) u9 [- m
`include “/home/myprojects/test/includes/test_case.v”* i6 m7 d2 I" u, q3 s0 M

9 y) G% S+ ~. `' f1 O    建议仅在include中使用文件名,而不要使用绝对或相对路径名。这将使代码位置独立,因此更加可移植。另一个建议是保持包含文件简单而不使用嵌套的include指令。
- @( i; N0 r" u( }6 E; j
# a+ |5 }2 r# z" d0 P# g  B1 x1.2 使用`define编译器指令,parameter和localparam# E8 Z- K4 J6 O- r
. i5 P# Z' _# k  R% e! n
    `define是文本宏替换编译器指令。它定义为:`define <text macro>
# o- c/ c5 o2 G9 u7 \* p2 M) p8 W  |' v
    <text macro>可以包含带有可选参数列表的单行或多行文本。  |6 ?8 C! n& V
; m& ]+ P1 ~$ h: g
    `define具有全局范围。一旦定义了文本宏名称,就可以在项目中的任何地方使用它。文本宏通常是用于定义状态名称,常量或字符串的简单标识符。8 T2 i& p3 _0 h# F2 r1 C
  ?* X  P3 P) t( {
    parameter关键字定义模块特定的参数,该参数在特定模块实例的范围生效。参数用于为模块实例提供不同的自定义,例如,输入或输出端口的宽度。以下是使用parameter关键字的示例:
6 |" v( i7 b) `$ g: n8 u0 L: e2 s" ?* {; w
module adder #(parameter WIDTH = 8) (
" U6 H% C. N* Q' l6 H% i, F6 Qinput [WIDTH-1:0] a,b, output [WIDTH-1:0] sum );" r# r- [6 T4 |. d
assign sum = a + b;
3 T3 u) ]2 D: j3 oendmodule // adder
- y5 B0 O' B. m; q4 {// an instance of adder module% N8 ^0 I0 ~5 S
adder # (16) adder1 (.a(a[15:0]),.b(b[15:0]),.sum(sum[15:0]));
9 S5 y0 E6 J1 D; A  P
! ?& j: r; T2 T/ g) m    localparam关键字与parameter相似。它被分配了一个常量表达式,并在特定模块内具有作用域。它定义为:
5 d" X  i  P# r6 |1 N7 q2 v3 |6 o
$ F8 r" Z8 W0 M$ Z1.3 使用函数
, `7 i' i* M" }( e( s. P7 ?; K2 u3 L* Y- H% d) z# M# D
    以下是执行XOR操作的Verilog函数的简单示例:
. T1 V. g  q! h+ e- E) ]  h2 s/ E0 r* l
module function_example( input a,b, output func_out);
5 d- Y4 X! W9 X9 @. a& w# ?8 h6 l7 dfunction func_xor;
9 R& w. t! }  |1 r" s" I  uinput a, b;
$ q1 R( ~- d$ L) Q5 J) i: P  begin
2 t1 R& T' v' g% l2 y2 w    func_xor = a ^ b;7 g3 q% v4 d, H2 R1 [, \; @
  end
) h1 j; |* I9 X2 J; v: uendfunction7 N0 q( `* H, M3 r) y" ~8 ~- X
   assign func_out = func_xor(a,b);; g  R6 W- Q/ Y  w3 w
endmodule // function_example8 t' }6 R' c5 |7 ]$ z

/ z7 {$ w4 {" \* ~, ]' t* a  M- f    建议使用Verilog函数来实现组合逻辑和其他不需要非阻塞分配的操作,例如同步逻辑。使用函数可以编写更紧凑和模块化的代码。所有综合工具均支持Verilog函数。- J( F" t6 f  d# `
: M9 E2 t4 c' \  U1 u/ v$ O# I+ f
1.4 使用 generate 块
3 p  a3 `  o% \  A# j' p9 L3 I$ E5 D9 }; d/ L- _, T, [
    在Verilog-2001中引入了generate块,以使对同一模块,函数,变量,网络和连续分配的多个实例的实例化变得容易。以下是使用generate的两个示例:" a0 }' \! T* _& H

+ B0 M9 e9 G# ]$ R" `& `  u// a conditional instantiation of modules
5 T; D( {$ _" {1 r# [parameter COND1 = 1;
& \! V, z6 ^. @! ], C( [6 sgenerate2 F9 z0 j9 @# }* n: b* B6 j
if (COND1) begin : my_module1_inst
2 m3 f3 }6 k" g; {% o; ?my_module1 inst (.clk(clk), .di(di), .do(do));; x  l- j- k2 Q* \, X1 g# u' K0 D* q
end
8 a- s: M% S1 [) j) R6 j+ S2 F: f; xelse begin : my_module2_inst& @+ h5 S* ^+ g* A! r7 D
my_module2 inst (.clk(clk), .di(di), .do(do));
0 K. x2 U' k$ e- Aend( h0 j) n6 G* Z  n- v" B8 ^
endgenerate
; Z* x. n, m4 j" a2 V
' i2 r! T3 M, R" b// using for loop in generate block
7 ]; k; e$ j* |genvar ii;
% u0 ~  X- [7 \  X, p- ^& vgenerate
8 O5 `' z/ e: o    for (ii = 0; ii < 32; ii = ii+1) begin: for_loop5 H* L/ L4 t* _8 d
    my_module1 inst (.clk(clk), .di(di[ii]), .do(do[ii]));
& r( j3 O0 h& ^* g  h7 ~7 L    end" F; H( n7 l. U  e
end
9 e' K0 g  z( {. m5 x) rendgenerate
' S8 |" I  R% J4 \' m. W* T
& Q* k4 V% l9 j& ^1.5 开发简单的代码4 m: j1 h! Y0 r( u: P2 i8 m
# E+ V, J9 O( m6 F, g  C
    始终努力开发简单的代码。与每种编程语言一样,Verilog允许编写详细的语句,从功能的角度来看,这些语句很优美,但可读性不高。下面的简单示例说明了这一点:
2 Y4 z9 l  Q2 Q! y" `0 C
$ t9 X+ |  N2 u) v- b( k, u7 zreg [5:0] sel;
0 Z6 F; o; m; [7 |5 x* Jreg [3:0] result1,result2,a,b;
5 c6 F3 W7 }3 |+ U, H. balways @(*) begin
, _  H5 X0 x$ J2 b# nresult1 = sel[0] ? a + b : sel[1] ? a - b :
8 X3 Z9 [# ^: |3 ~sel[2] ? a & b : sel[3] ? a ^ b :
2 b1 n  y1 |6 v" ^sel[4] ? ~a : ~ b;( B; Y6 @! S& B- A
if(~|sel)
. G7 Q  N" O0 q3 N1 t5 _result1 = 4'b0;
. e3 Y0 V+ S) a* M0 v# hend // always& g9 O+ W4 b4 R. N' G

( {# |9 c2 n5 O0 V4 ~: G( oreg [5:0] sel;
1 `, B! F4 [6 T+ a( R" ?1 w% Treg [3:0] result1,result2,a,b;
/ k. A0 H0 f2 Z# Ualways @(*) begin2 F0 i( _4 k. c$ O0 k
casex(sel)
0 _! d, B2 D+ u    6'bxxxxx1: result2 = a + b;
7 S# R( Y4 T+ g6 x9 k' n* x5 i7 I    6'bxxxx10: result2 = a - b;
9 U, [" I- u1 U( e) V: ?    6'bxxx100: result2 = a & b;5 c" E2 L  D8 ]; D5 c
    6'bxx1000: result2 = a ^+ b;
( X" B+ @( a3 h0 l    6'bx10000: result2 = ~a;; G8 y# t1 _+ R3 C4 `: V$ J
    6'b100000: result2 = ~b;2 u' I' T* V% ^2 x" F- D5 [
    default: result2 = 4'b0;2 |# N  M0 p  M0 [
endcase+ I5 [$ H3 O  c. P0 B! V
end // always* U- ?3 W9 r8 b$ a6 r( t4 `. d
# P' e' I$ w0 `$ S1 E
    实现result1和result2的逻辑在功能上是等效的。但是,在result1中使用嵌套三元运算符和两个赋值语句不太透明,并且与result2逻辑的更清晰的case语句相比,需要花更多的精力来理解。
7 l% ?6 m* R& x  r6 |0 i" O1 o) A  M
    通常,代码清晰度高容易实现高效率。同一段代码能在其生命周期内被多个开发人员读取。编写更清晰的代码更容易调试,并且一般不容易包含错误。 # N2 ^' t2 N& J7 n  n% l
' R$ X- X; J, A( u  H
二、为FPGA编写可综合的代码
2 j( @9 p' ^' q9 e5 v' `% z7 n4 w; W  ]9 R9 A
2.1 考虑资源7 B% L, ^9 N8 P) W

" I$ A/ j7 ~$ J  Q# l' S    Verilog语言参考手册(LRM)提供了丰富的功能来描述硬件。但是,只有一部分语言可以为FPGA综和。即使有些特定的语言结构是可综合的,也不能保证该代码能在特定FPGA上实现物理电路。考虑以下示例:
. B. F2 g3 }7 N" y& W6 m$ E+ i5 Y1 Y1 L! {( V2 t
reg [7:0] memory[1:2**22];* V, m9 v7 v- J  k5 J1 h5 I
initial begin
# Z0 p$ U3 t% `5 kmemory[1] = 8’h1;" |+ a  L4 O( y. L1 Z3 U/ w0 Z! J6 _
memory[2] = 8’h2;( u( ]6 P, t' D2 m
end
) f6 r3 F6 O* r9 G% k
# t: H6 y. @, B- H  e    该示例能正确模拟出来,但会导致FPGA物理实现失败。该代码需要4 MB的内存,这是一些FPGA所没有的。此外,综合工具将忽略初始块,该块将初始化内存的最低两个字节。: ], Z4 h: P/ k4 z- B8 ]

1 v' g( C% \1 @! P# H! X    该技巧提供了一些指导方针和建议,以帮助编写用于FPGA的可综合代码。2 p; s9 y4 C2 r  Q

6 H4 a- P. n5 _+ T  N  ]; t2.2 遵循同步设计原则+ ]8 |6 X; L! G; ^) J- y6 f7 \7 {
1 E$ q$ I+ x5 B3 @+ B6 ]
    建议开发人员遵守FPGA同步设计的原则,其中包括以下内容:7 m# H; ]% s, p, w3 \: x
8 J9 H" N0 @, e+ m
1、使用同步复位。后续会详细讨论,同步,异步复位的问题1 n' H& x2 m4 H# [/ v1 u% i) m7 O

; u/ j4 R2 A! S2、避免使用锁存
4 _) r+ Y/ X0 Q5 B/ U5 m" s) E7 p% h. f% Z/ t
3、避免使用门控,派生或分频时钟; }) ~  p$ t- r* F
  ^3 L0 F$ n8 f+ i+ r8 d- O
4、使用时钟使能而不是多个时钟8 B$ V" O+ l2 s/ a* x2 S3 O5 C% q

+ A) \( ~$ X- U: e& j5、对所有异步信号实行正确同步

该用户从未签到

2#
发表于 2021-6-28 09:58 | 只看该作者
1、使用同步复位。后续会详细讨论,同步,异步复位的问题3 ^# u, S1 x5 m  o9 \$ y) u
  ]  {; }' O: {7 R5 b2 f
. `& r  j( g# M# q2、避免使用锁存
9 z4 T; `8 O. ~- e# @# \) l3 J5 K& n- }+ M
4 o: Y0 a( w/ ^5 P- M& I/ |3、避免使用门控,派生或分频时钟2 |" m3 X5 x9 t: B: Q
- m2 o4 L. I2 n# m8 N  X5 ^  x' h; u3 F- z
4、使用时钟使能而不是多个时钟2 F, a. `. b+ z& g/ `* F
3 t5 B2 P% ^) ?( Q% `  W! O0 d5 [  k. B" ^) R" Z+ L/ d# y9 `3 Y
5、对所有异步信号实行正确同步
0 q/ x5 i8 @/ @- ]# E% X4 P3 K+ G/ Z

* |) b% Q. O, b, j% p$ `1 ^

该用户从未签到

3#
发表于 2021-6-28 11:02 | 只看该作者
路过,围观学习& V# f% o# ^# |* L' F# V

该用户从未签到

4#
发表于 2021-6-28 11:12 | 只看该作者
不错的指导方针和建议1 ?' R9 F6 Y" C9 @- ]% u
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-8-12 05:48 , Processed in 0.109375 second(s), 23 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

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