|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
3 }7 t, H0 V P3 n3 @4 [一、Verilog 编码风格
, R! I+ D# q. |! l; g& K6 X7 q4 |2 j* h9 N) F( ]
1.1 使用“`include编译器指令”
" f/ L2 v9 Z7 g0 E0 I! x
# Z. N0 H" X4 h 文件包含“`include编译器指令”用于在合成过程中将源文件的全部内容插入到另一个文件中。它通常用于包括全局项目定义,而无需在多个文件中重复相同的代码。另一个用例是将代码的一部分插入模块,如以下示例所示:
6 t" R- {6 s- `4 H, O' s" M" Y# P$ S2 d' i1 d" n" d( u
// file test_bench_top.v2 K& x8 e) E. i
// top-level simulation testbench
2 S& X7 r5 P) ?. ?4 j" M/ e) t; K4 M3 t" xmodule test_bench_top;! C- D# n h2 @0 |
`include “test_case.v”
( S- P+ j. g* ?: L! rendmodule: J e: g" ]+ d; o7 W1 i3 _
// file test_case.v
( g& P4 ]' S$ S' Oinitial begin% w8 i7 J. V5 Q5 B
//…
* a0 F' T2 H' T+ J [" z% E3 mend: }7 B p7 k! H* |4 M0 U
task my_task;' {% x: E! S% W! @+ h- C) @
//…; W; Z/ U0 o$ M# u2 [
endtask
2 J" {0 E9 P4 F0 {! n2 c( E5 r7 K5 u! _
> include编译器指令的语法定义为:`include <filename> N1 T. o, e" b5 C+ J3 g, f2 G
. p' f3 P" H) z T( d
<filename>可以是文件名,还可以包含绝对或相对路径名:. W" B9 F) e. ?8 I$ a
% W& x; V: ?# K`include “test_case.v”. m9 i8 X/ ]& L, ^+ s
`include “../../includes/test_case.v”
$ B+ R; H4 {7 t/ Y) j! }`include “/home/myprojects/test/includes/test_case.v”; @' y7 k- P) g% E
8 d0 U3 [% j; D. F5 y
建议仅在include中使用文件名,而不要使用绝对或相对路径名。这将使代码位置独立,因此更加可移植。另一个建议是保持包含文件简单而不使用嵌套的include指令。 B+ b) F$ G. i E- K1 j6 Q
' S6 x& |6 c* }1 P' }
1.2 使用`define编译器指令,parameter和localparam
# i$ N" D+ @. j2 m! {
9 k( p& C1 E: L5 N* H `define是文本宏替换编译器指令。它定义为:`define <text macro>3 {( ~/ ?: L& |- q; S' }
! H: x1 [0 N: |- {- m
<text macro>可以包含带有可选参数列表的单行或多行文本。
1 U# b3 H: M( ^2 }% v( [4 t! W# E7 r5 K5 g
`define具有全局范围。一旦定义了文本宏名称,就可以在项目中的任何地方使用它。文本宏通常是用于定义状态名称,常量或字符串的简单标识符。4 E: k" f& ~3 D! ?' _1 L/ q
+ o( q& q1 D y: w7 u
parameter关键字定义模块特定的参数,该参数在特定模块实例的范围生效。参数用于为模块实例提供不同的自定义,例如,输入或输出端口的宽度。以下是使用parameter关键字的示例:
( H1 ]! u. \% I3 ?) ]
; \. R9 ~" @. I; d9 s6 O" C: Dmodule adder #(parameter WIDTH = 8) (
% t( @3 |! M( r# t+ {input [WIDTH-1:0] a,b, output [WIDTH-1:0] sum );( ]! ]+ I B$ d( K
assign sum = a + b;8 m f4 E# C0 h) h' A2 `
endmodule // adder
0 }, K: X) o( V! D% a- L- ~// an instance of adder module* g( d; }: P2 o6 g/ E, q/ I' b
adder # (16) adder1 (.a(a[15:0]),.b(b[15:0]),.sum(sum[15:0]));
5 e: U9 m8 q2 R, x8 f3 `3 O z( N i0 S7 m, A- _
localparam关键字与parameter相似。它被分配了一个常量表达式,并在特定模块内具有作用域。它定义为:" |8 X% I3 p( |* B9 {; t
1 l0 d) Q) k$ }" Z0 f% ]$ a1.3 使用函数: `' e, l1 p6 H2 O* r1 z4 }) w/ \
, I5 M, j: Z, J! C# N 以下是执行XOR操作的Verilog函数的简单示例:
; w* g+ d D! n" J! ]3 t/ ?: s
# m/ r* i4 h0 r% u' X2 Pmodule function_example( input a,b, output func_out);$ H' {3 n. M% R9 q
function func_xor;4 n1 r L" R f$ L& V) z" N$ T/ Y
input a, b;
7 a! z: @( B z J begin
1 N0 a2 C6 m% v8 s7 o! z6 q func_xor = a ^ b;/ a6 a8 X9 q& D; H
end% Q" C5 @/ @( ~/ q' K: _7 f# S4 U
endfunction! O1 ?5 i/ b7 Y1 g1 E% N3 N
assign func_out = func_xor(a,b);5 i! d: P* M9 }0 C* j# m+ |
endmodule // function_example3 [% O. C% h; a/ t3 f4 L
; ]) {+ r( V0 M; a" u+ X 建议使用Verilog函数来实现组合逻辑和其他不需要非阻塞分配的操作,例如同步逻辑。使用函数可以编写更紧凑和模块化的代码。所有综合工具均支持Verilog函数。! O' m/ {* m% F) \& g
" n1 \, \% m$ j& W% }- ]' Q1.4 使用 generate 块
8 h; ?8 I2 s& F1 M S# E- g7 d9 e6 r; ^6 R; x
在Verilog-2001中引入了generate块,以使对同一模块,函数,变量,网络和连续分配的多个实例的实例化变得容易。以下是使用generate的两个示例:
: _) Y# P& m) v1 ]
' \' l3 k, b. S8 {* e// a conditional instantiation of modules
* i1 a! A- z4 [+ r4 Hparameter COND1 = 1;
, I1 `% y, ]! {& z3 Ogenerate
; @: y, ]1 A) r) }, @0 v2 c9 dif (COND1) begin : my_module1_inst
* l; m/ y! }% l5 E5 v1 @: emy_module1 inst (.clk(clk), .di(di), .do(do));! @% e; M2 P4 }
end
8 u5 R3 ^ L5 W+ ^( Y1 @5 Velse begin : my_module2_inst
* S- F$ X2 i: z- F0 m/ qmy_module2 inst (.clk(clk), .di(di), .do(do));
2 `% ?! k. d4 L/ G" C" [end
. ]" g8 l9 R$ k- z9 Fendgenerate
6 N' O/ g2 Z3 Z% x# v! e. T+ K9 s/ @7 C- n/ l5 C
// using for loop in generate block
\. }& H, ?9 T2 t- ngenvar ii;0 y" A* ?8 z# |1 Y! E( B& a
generate$ j+ {" _; ]8 v; }" L& H" {% Q
for (ii = 0; ii < 32; ii = ii+1) begin: for_loop3 s+ E; \; t8 z" l0 @& U9 ]; w" O: O
my_module1 inst (.clk(clk), .di(di[ii]), .do(do[ii]));) Z9 W. _9 Z, x
end7 c+ X# H2 R9 j$ |1 j+ r [
end7 H! T( m; J! ]% [- x
endgenerate6 g% |; w2 o# ` G
* X3 t, {9 I0 N1.5 开发简单的代码
2 y1 S9 t4 p( y: v( L
) d& K) K, d U 始终努力开发简单的代码。与每种编程语言一样,Verilog允许编写详细的语句,从功能的角度来看,这些语句很优美,但可读性不高。下面的简单示例说明了这一点:: I9 o/ s: a9 W' {5 _9 }
- C: `, U' e' }) q$ `6 i4 U
reg [5:0] sel;$ ~2 _+ h' c9 N; H$ R" p# _
reg [3:0] result1,result2,a,b;
2 l" c/ i3 G3 W7 s( H# [8 dalways @(*) begin
% @- B+ e8 h5 C* uresult1 = sel[0] ? a + b : sel[1] ? a - b :) {1 K) x8 R6 X# M; F
sel[2] ? a & b : sel[3] ? a ^ b :# ?# q. D* ?- C, W. I
sel[4] ? ~a : ~ b;* {+ t6 a" M+ {* K* t1 \
if(~|sel)
% V' D1 S+ [3 f" eresult1 = 4'b0;; \3 P3 b2 W0 L' \5 j' t
end // always
! L6 N. d! x7 N& Q
6 M$ Q4 F. U* g' s! @) |reg [5:0] sel;
9 ~+ c* P2 n0 B5 ~reg [3:0] result1,result2,a,b;+ E2 c* x( D5 Q6 E! v" o6 K
always @(*) begin
4 G1 E9 u8 S: V$ U- C casex(sel)
0 R+ p- z+ Y$ A 6'bxxxxx1: result2 = a + b;7 D/ [; A) @0 ~+ v# {8 D" ` ^
6'bxxxx10: result2 = a - b;
: I& ]# h- Y7 z 6'bxxx100: result2 = a & b;
. \$ f5 z' G$ n7 {; @ 6'bxx1000: result2 = a ^+ b;8 T# |$ y5 _( b+ j0 I Y2 g. h$ d
6'bx10000: result2 = ~a;. f% d: N' m' M8 n/ g' `
6'b100000: result2 = ~b;
g& u. b7 _' S; v# `3 v% X default: result2 = 4'b0;- C9 D/ J" V- _
endcase; l# p2 M N8 V
end // always
, r. }$ g1 A, {7 b) y
5 v- B( a# t( k! o 实现result1和result2的逻辑在功能上是等效的。但是,在result1中使用嵌套三元运算符和两个赋值语句不太透明,并且与result2逻辑的更清晰的case语句相比,需要花更多的精力来理解。5 E# Y3 e3 {! N8 f' G7 d
0 r) C9 q% o6 K" N/ f 通常,代码清晰度高容易实现高效率。同一段代码能在其生命周期内被多个开发人员读取。编写更清晰的代码更容易调试,并且一般不容易包含错误。 + S( q! W& q1 W( W6 a+ `
- o. @' e' E2 ` a二、为FPGA编写可综合的代码; J5 h$ j* O$ `5 e
& }6 H) a" P- U' i$ p* \4 b2.1 考虑资源1 i5 j" X+ x) `0 \9 J! X6 P
$ U6 |4 g4 E7 x' I) v# } o; Z Verilog语言参考手册(LRM)提供了丰富的功能来描述硬件。但是,只有一部分语言可以为FPGA综和。即使有些特定的语言结构是可综合的,也不能保证该代码能在特定FPGA上实现物理电路。考虑以下示例:
2 n e* Q& b3 ?, R6 u7 m# h; N( ]% u/ B! }5 F$ X
reg [7:0] memory[1:2**22];
; D t% [- U1 Z7 s! b$ W# Sinitial begin1 j( Z) ?9 p" c# Y. t/ O" @
memory[1] = 8’h1;
% R: T! `) T$ J9 Wmemory[2] = 8’h2;
! g) ]( U& M0 c) pend
) g1 V+ m$ m, Q2 r& ^ a, D
4 r% E1 ~- x9 E5 P+ q u# P 该示例能正确模拟出来,但会导致FPGA物理实现失败。该代码需要4 MB的内存,这是一些FPGA所没有的。此外,综合工具将忽略初始块,该块将初始化内存的最低两个字节。; Y0 z/ a3 o! h
5 A9 s# h2 b) a* }/ i 该技巧提供了一些指导方针和建议,以帮助编写用于FPGA的可综合代码。4 \6 C/ K; h; s+ v% r
- i: S9 \ `4 t7 m& s
2.2 遵循同步设计原则5 W* Q2 U+ V3 W
4 B( |5 e- I5 }: l4 z/ {. K 建议开发人员遵守FPGA同步设计的原则,其中包括以下内容:
- m2 v9 M' ]+ ^' J+ N, o
* J* q r6 l" e! {6 X$ Q {1、使用同步复位。后续会详细讨论,同步,异步复位的问题
6 W/ \; e5 u8 M' t
$ \( _; I0 M0 K, Q2、避免使用锁存
2 S; Q! _+ |) _, q2 y) X# B: m) [! Y! c7 m
3、避免使用门控,派生或分频时钟. q( c* [, a5 C; O; h: e4 y
1 ~% u2 |( y, D9 M/ u
4、使用时钟使能而不是多个时钟
/ t/ w( W" N: U' T$ ^' ~( V3 ?0 A5 I: E3 E+ {7 e
5、对所有异步信号实行正确同步 |
|