(筆記) 如何設計計數器? (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,儒林圖書公司

posted on 2008-07-13 23:33  真 OO无双  阅读(44759)  评论(3编辑  收藏  举报

导航