(筆記) 如何設計計數器? (SOC) (Verilog) (MegaCore)

Abstract
計數器是循序電路最基本的應用,將來可以用此設計除頻器

Introduction
使用環境:Quartus II 7.2 SP3 + ModelSim-Altera 6.1g

Method 1:
使用Verilog

counter10.v / Verilog

1  /*  
2  (C) OOMusou 2008 http://oomusou.cnblogs.com
3 
4  Filename    : counter10.v
5  Compiler    : Quartus II 7.2 SP3 + ModelSim-Altera 6.1g
6  Description : Demo how to write synchronous decimal counter
7  Release     : 07/13/2008 1.0
8  */
9 
10  module counter10 (
11    input            clk,
12    input            clr,
13    input            load,
14    input            en,
15    input      [ 3 : 0 ] data,
16    output reg [ 3 : 0 ] q,
17    output reg       cout
18  );
19 
20  always@(posedge clk) begin
21    if (clr ==   1 ' b1)
22      q <=   0 ;
23    else   if (load ==   1 ' b1)
24      q <= data;
25    else   if (en ==   1 ' b1) begin
26      if (q ==   9 )
27        q <=   0 ;
28      else
29        q <= q +   1 ;
30    end
31  end
32 
33  always@(q) begin
34    if (q ==   9 )
35      cout =   1 ;
36    else
37      cout =   0 ;
38  end
39 
40  endmodule


Method 2:
使用Megafunction

counter10_mf.v / Verilog

1  /*  
2  (C) OOMusou 2008 http://oomusou.cnblogs.com
3 
4  Filename    : counter10_mf.v
5  Compiler    : Quartus II 7.2 SP3 + ModelSim-Altera 6.1g
6  Description : Demo how to write synchronous decimal counter by lpm_counter
7  Release     : 07/13/2008 1.0
8  */
9  module counter10_mf (
10    input            clk,
11    input            clr,
12    input            load,
13    input            en,
14    input      [ 3 : 0 ] data,
15    output     [ 3 : 0 ] q,
16    output           cout
17  );
18 
19  wire [ 9 : 0 ] eq;
20 
21  lpm_counter # (
22    .lpm_width( 4 ),
23    .lpm_direction( " UP " ),
24    .lpm_modulus( 10 ))
25  u0 (
26    .clock(clk),
27    .data(data),
28    .sload(load),
29    .sclr(clr),
30    .cnt_en(en),
31    .q(q),
32    .eq(eq)
33  );
34 
35  assign cout = eq[ 9 ];
36 
37  endmodule


testbench
counter10_tb.v / Verilog

1  /*  
2  (C) OOMusou 2008 http://oomusou.cnblogs.com
3 
4  Filename    : counter10.v
5  Compiler    : Quartus II 7.2 SP3 + ModelSim-Altera 6.1g
6  Description : Demo how to write synchronous decimal counter testbench
7  Release     : 07/13/2008 1.0
8  */
9 
10  `timescale 1ns / 10ps
11  module counter10_tb;
12  reg clk;
13  reg clr;
14  reg load;
15  reg en;
16  reg [ 3 : 0 ] data;
17 
18  wire [ 3 : 0 ] q;
19  wire       cout;
20 
21  counter10_mf u0 (
22    .clk(clk),
23    .clr(clr),
24    .load(load),
25    .en(en),
26    .data(data),
27    .q(q),
28    .cout(cout)
29  );
30 
31  initial begin
32  clk =   0 ;
33  clr =   0 ;
34  load =   1 ;
35  en =   1 ;
36  data =   4 ' b0101; 
37  end
38 
39  always # 50 clk =   ~ clk;
40 
41  initial # 100 load =   1 ' b0;
42  initial # 400 load =   1 ' b1;
43  initial # 500 load =   1 ' b0;
44  initial # 300 clr  =   1 ' b1;
45  initial # 400 clr  =   1 ' b0;
46  initial # 1900 en   =   1 ' b0;
47 
48  endmodule


執行結果

counter00

Method 1用的是很正規的寫法,第一個always用的是循序電路,第二個always用的是組合電路,也是很好的coding style,這樣寫不僅清楚的代表實際的電路,也幫助合成器合出更好的電路,或許你會想將兩個always合成一個來寫,讓程式看起來更精簡。

counter10_2.v / Verilog (錯的寫法)

1  /*  
2  (C) OOMusou 2008 http://oomusou.cnblogs.com
3 
4  Filename    : counter10_2.v
5  Compiler    : Quartus II 7.2 SP3 + ModelSim-Altera 6.1g
6  Description : Demo how to write synchronous decimal counter
7  Release     : 07/13/2008 1.0
8  */
9 
10  module counter10_2 (
11    input            clk,
12    input            clr,
13    input            load,
14    input            en,
15    input      [ 3 : 0 ] data,
16    output reg [ 3 : 0 ] q,
17    output reg       cout
18  );
19 
20  always@(posedge clk) begin
21    if (clr ==   1 ' b1)
22      q <=   0 ;
23    else   if (load ==   1 ' b1)
24      q <= data;
25    else   if (en ==   1 ' b1) begin
26      if (q ==   9 ) begin
27        q <=   0 ;
28        cout <=   1 ;
29      end
30      else begin
31        q <= q +   1 ;
32        cout <=   0 ;
33      end
34    end
35  end
36 
37  endmodule


乍看之下都很完美吧,但一模擬之後,發現cout會慢一個clock出現!!

counter01


為什麼會這樣呢?這實際上這是Verilog初學者常犯的錯,我也深受其害,哈,我們來看一下26行最關鍵的幾行程式碼

if (q ==   9 ) begin
  q
<=   0 ;
  cout
<=   1 ;
end
else begin
  q
<= q +   1 ;
  cout
<=   0 ;
end


若以邏輯的角度來看,非常完美,當q == 9時,q再度變成0,且cout <= 1,完全合乎我們的『演算法』!!

但在循序電路內,就有兩個要考慮:
1.使用的是non-blocking,所以q <= 0和cout <= 1一起執行。
2.因為是循序電路,所以q <= 0和 cout <= 1並不會馬上有結果,必須等到下一個clock才出現。

所以在ModelSim的模擬,我們看到『q等於0和cout等於1』同時在下一個clock時出現,所以若要cout『馬上』等於1,就不能用循序電路,而要改用組合電路,這樣就會『馬上』等於1,所以用另外一個always block來表示這個組合電路的結果才正確。

Conclusion
這個小例子,再次證明Verilog只是語法跟C靠攏,但觀念卻是硬體電路的概念,所以Verilog的優點是讓你不用再花時間去學習一個新的語言(如VHDL),但缺點是很容易用C的思維去寫Verilog而不自知,因為code實在太像了!!記得大學時,教授曾說:『C太厲害的Verilog會學不好!!』,現在深切領悟,哈~~。


See Also
(原創) 如何設計除頻器? (SOC) (Verilog) (MegaCore)
(原創) 哪一個計數器才會出現9呢? (SOC) (Verilog)
(原創) 如何設計電子鐘? (SOC) (Verilog) (DE2)
(原創) 如何設計電子鐘(II)? (SOC) (Verilog) (MegaCore) (DE2)   
Reference
陸自強 2007,數位系統實習 Quartus II,儒林圖書公司

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值