EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
& i1 ?* m3 p6 @7 q+ p
" r/ e: Z0 @ \$ e学习FPGA,最关键的是学什么?有读者学了大半年时间的FPGA,学了串口就只懂串口的设计,学了spi就只懂SPI接口的设计。每个接口、每个功能,都只是学一个懂一个。换个功能需求,或者对接口做一个小小的改动,就无从下手了。 设计代码,从来都只是模仿,或者不断地调试修改,凑代码。设计出的代码也没有任何规律,相同的功能,今天设计和明天设计都不一样。这就如学功夫,今天学下少林,明天学下武当,后天又学下华山,在这样的情况下,能成长为高手,那就奇怪了。 在明德扬看来,FPGA设计应该有一套通用的设计方法。该方法能够应付所有的功能设计,无论功能怎么变,都可以用该方法来套用。明德扬发明的这套方法就是至简设计法。 至简设计法从宏观上,适应所有的功能设计需求。例如,无论是什么功能,我们都先将其转化成需求波形。然后在此基础上设计模块架构;在模块架构基础上设计信号。这步骤都是通用的、是固化的。 至简设计法在微观上,则制定得实用的规范。详细到,要不要添加信号;怎么添加信号;添加信号的名字规范等,我们都做了详细的规定。 下面我们用4个经典例子,讲述了至简设计法的使用技巧。其他复杂功能,无论怎么变,都是这4个经典案例的变种。读者只需要强化、巩固技巧,多训练,多应用,逐步成长为高手。 至简设计法经典案例1 案例1. 当收到en=1后,dout产生一个宽度为10个时钟周期的高电平脉冲。
& I' u% Q( l3 a1 i, Q5 d! C* U需要说明,根据看波形规则,在第3个时钟上沿的时候,看到en==1,根据功能要求,上升沿之后dout就会变为1。10个时钟周期后,dout将变为0。 从功能要求中,看到数字10,我们就知道要计数,并且是dout==1的次数为10个。所以我们计算的是dout==1的时钟次数,并且是10次。为此,补充一个计数器信号cnt,更新后的波形如下图。 计数器cnt要遵守如下原则。 初值一定为0。 除了最后一个,在时钟上升沿,看到dout==1,就将cnt值加1 在时钟上升沿时看到dout==1,并且是最后一个时,cnt值不加1,直接清零。 从功能要求和波形图,我们确认,计数器cnt是对dout==1进行计数,并且一共数10个。为此,在GVIM编辑器中输入“Jsq”并回车,将出现如下代码。 在第13行,输入dout==1,在第14行代码中,输入10-1,这样就完成了计数器设计。 代码解释:第1至第11行,是一个时序always的代码。该代码要描述的功能是: 在时钟clk上升沿或者复位rst_n的下降沿的时候,always就对cnt判断条件并变化一次。具体变化过程如下: 如果是rst_n==0,则将cnt变为0。 否则(即rst_n==1),如果add_cnt有效,也就是为1的时候。继续判断条件并执行。 如果end_cnt有效,即end_cnt==1,则将cnt变为0。 否则(即end_cnt==0),cnt就自加1。 否则(即rst_n==1且add_cnt==0的时钟),cnt保持不变。 上面代码中add_cnt表示计数器加1条件,end_cnt表示计数器数到最后一个。 上面代码描述过于复杂,其实概括起来,功能就是:时钟上升沿时,如果计数器加1条件有效,并且是数到最后一个,则计数器清零;如果计数器加1条件有效,但不是最后一个,则计数器就加1;其他时候,计数器就保持不变。 那么加1条件,即add_cnt是什么呢?在第13行进行了定义。该行代码表示,dout==1就是计数器的加1条件。 那么结束条件,即end_cnt是什么呢?在第14行进行了定义。该行代码表示,数到10个就结束。其中我们关注的是那个数字10,而-1是固定的格式。 add_cnt && cnt==10-1,含义是表示“数到第10个的时候”,add_cnt &&cnt==x-1表示“数到第x个的时候”。记住这个规则。end_cnt==1也表示数完了。 设计好计数器cnt后,我们就可以设计输出信号dout了。仔细分析dout,该信号有两个变化点:变1和变0。我们分析原因,dout变1是由于收到en==1;dout变0,则是数到了10个或者是数完了。所以综上所述,dout的代码是: 至此,我们完成了主体程序的设计,接下来补充module的其他部分。 将module的名称定义为my_ex1。并且我们已经知道该模块有4个信号:clk、rst_n、en和dout。为此,代码如下: 其中clk、rst_n和en是输入信号,dout是输出信号,并且4个信号都是1比特的,根据这些信息,我们补充输入输出端口定义。代码如下: 接下来定义信号类型。 cnt是用always产生的信号,因此类型为reg。cnt计数的最大值为9,需要用4根线表示,即位宽是4位。add_cnt和end_cnt都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下: dout是用always方式设计的,因此类型为reg。并且其值是0或者1,1根线表示即可。因此代码如下: 至此,整个代码的设计工作已经完成。整体代码如下: 1 ) ]% r3 M6 t. Q% ~+ Q$ W4 i
2 ' ~$ n; Y! m" p
3
4 m) r, m7 r+ z% p4 / o1 }' A4 c% o% ]
5
1 x: p, l# L% ]5 Q" w6 5 J9 A% y+ Z$ ?) T n2 I
7 * d# w# a. `2 l4 n
8
/ ?! K, Q' m" H1 v! B9
1 G: `" y& A% D10
5 y1 X2 f% F, |' J5 |& q11
) e- R9 p6 g# @+ p12
! a6 S: `" |1 o! _. z. c4 @13
4 u. u4 t, R% C& m0 }( c' i14 * E* p- t, @0 g0 `: J, Q
15 $ m) l6 l0 R2 {5 b
16
5 E* Y3 Z/ ~6 a! A# Q7 x17
. h2 e4 c9 i T- M/ _2 F18 " g" S! {( T( N* A- J0 ]; [# ?! T& d
19 $ V3 d+ w4 I4 V7 k3 ?$ Z
20
+ K( A, G j3 \+ h1 L21 7 p; S) p# M& [1 x" x- C
22 % `, X, E' R4 t
23
8 }+ ?$ E7 N' n! u# f24 `5 Z& L8 W } r3 @
25 0 a4 L5 U- l/ q
26 ' k" u" a u# l; j; h% m
27
2 y: `- h) h* l" }6 N9 z! x28 | module my_ex1( - [4 z3 e* }0 W
clk , ' D+ w8 M1 h# Y$ j+ |# {
rst_n , - r2 {" j8 K" \
en ,
! p$ t" k1 E9 v2 Y8 k dout
- P# Q7 n' c0 ?" o$ r); : u+ { U1 W, U" r
input clk ; K* W0 _. ]3 C: A. ~/ ]
input rst_n ; 0 e! n4 k# N: D. Z: C
input en ;
4 }6 B6 G6 }9 G+ \3 s2 E; z2 Coutput dout ;
- o+ K5 l, q& L, breg [ 3:0] cnt ;
! I4 R$ m8 h# qwire add_cnt ;
! n( `2 |) j* |; m& S0 e3 @8 Iwire end_cnt ; 9 M" ?% [& }& z: M
reg dout ;
: t* j7 Q* I4 n) Zalways @(posEDGE clk or negedge rst_n)begin y2 K& q+ I5 n9 N0 S
if(!rst_n)begin
) w' V5 j4 `1 u0 H: C* V7 D cnt <= 0; 9 ?7 j, S. x9 {
end
) v6 |9 R+ b6 i. e else if(add_cnt)begin / y. F$ G3 ]# |1 l
if(end_cnt) 5 K6 A* ?2 s% u3 X1 j2 J
cnt <= 0; % P) P4 L& P, W3 m
else ( H& o2 z s ~! M1 J
cnt <= cnt + 1;
% C z4 y4 M6 y, P) f4 b4 T1 E end : `. [: e: z4 A
end 5 t0 D* u3 `9 \" J; u- s
assign add_cnt = (dout==1); ( \+ [1 P2 ?6 D2 Q* n+ U$ I
assign end_cnt = add_cnt && cnt==10 -1 ; + }" [+ @4 s/ Q/ S& m2 Y5 ^
always @(posedge clk or negedge rst_n)begin
1 a7 B) n; |, G, F3 x if(rst_n==1'b0)begin & b. m s3 g: v! O. K+ {% Y
dout <= 0; 2 F0 g' J B- p5 ^# |/ L
end ' m2 P8 g. p% E
else if(en==1)begin
0 Y- R9 ~: r$ h0 O( E+ \' g dout <= 1; 3 Q6 z& w" I: Y0 ]
end 3 r% L( C4 A; [
else if(add_cnt && cnt==10-1)begin : A/ o' D5 e$ X4 E
dout <= 0; 8 e5 Q4 X0 U& L& I! e; b
end # k/ L: |0 C, y1 s6 s
end 9 S, w7 u2 ^4 v1 N
endmodule |
& p3 c4 ?4 N7 T0 S# |4 S* m* _
0 E0 {3 K" c, @) ]9 ^( {$ R& n6 O2 Y; _( U8 O% g3 O ?$ ^
5 A+ v# v, g6 E# } |