您的位置:首页 > 其它

(原創) 如何設計電子鐘(I)? (SOC) (Verilog) (DE2)

2008-07-27 17:50 701 查看
Abstract

學會計數器除頻電路後,就能以這兩個電路為基礎,設計一個電子鐘,並可自行調整目前時間。

Introduction

使用環境:Quartus II 7.2 SP3 + DE2(Cyclone II EP2C35F627C6)


(筆記) 如何設計計數器? (SOC) (Verilog) (MegaCore)討論過計數器,接著在(原創) 如何設計除頻器? (SOC) (Verilog) (MegaCore)討論過萬用除頻器,利用這兩個基礎,就可以設計一個簡易的電子鐘,如同電子錶一樣顯示時間。

Specification



SW17enable
SW16reset
SW15load (設定時間)
SW[3:0]設定分的個位數
SW[6:4]設定分的十位數
SW[10:7]設定時的個位數
SW[13:11]設定時的十位數
HEX[2]顯示秒的個位數
HEX[3]顯示秒的十位數
HEX[4]顯示分的個位數
HEX[5]顯示分的十位數
HEX[6]顯示時的個位數
HEX[7]顯示時的十位數
Block Diagram





這是一個簡化的系統方塊圖,因為寬度的關係,我將input與output都與以省略,上圖的divn是個除頻器,負責將DE2提供的50MHz clock除頻程1 Hz,seg7_lut則是負責將數字顯示在7段顯示器上。clock的細部實現為下圖,由兩個60計數器負責,24計數器負責

digi_clock.v / Verilog

1 /*

2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3

4 Filename : digi_clock.v

5 Compiler : Quartus II 7.2 SP3 + ModelSim-Altera 6.1g

6 Description : Demo how to write digital clock top module

7 Release : 07/27/2008 1.0

8 */

9

10 module digi_clock (

11 input CLOCK_50,

12 input [17:0] SW,

13 input [3:0] KEY,

14 output [6:0] HEX2,

15 output [6:0] HEX3,

16 output [6:0] HEX4,

17 output [6:0] HEX5,

18 output [6:0] HEX6,

19 output [6:0] HEX7

20 );

21

22 wire clk_1;

23 wire [3:0] w_sq0;

24 wire [2:0] w_sq1;

25 wire [3:0] w_mq0;

26 wire [2:0] w_mq1;

27 wire [3:0] w_hq0;

28 wire [2:0] w_hq1;

29

30 // 1Hz clock

31 divn # (.WIDTH(26), .N(50000000))

32 u0 (

33 .clk(CLOCK_50),

34 .rst_n(KEY[0]),

35 .o_clk(clk_1)

36 );

37

38 clock u1 (

39 .clk(clk_1),

40 .en(SW[17]), // input enable

41 .clr(SW[16]), // input clear

42 .load(SW[15]), // input load

43 .sd0(4'h0), // input second digit 0

44 .sd1(3'h0), // input second digit 1

45 .md0(SW[3:0]), // input minute digit 0

46 .md1(SW[6:4]), // input minute digit 1

47 .hd0(SW[10:7]), // input hour digit 0

48 .hd1(SW[13:11]), // input hour digit 1

49 .sq0(w_sq0), // output second digit 0

50 .sq1(w_sq1), // output second digit 1

51 .mq0(w_mq0), // output minute digit 0

52 .mq1(w_mq1), // output minute digit 1

53 .hq0(w_hq0), // output minute digit 0

54 .hq1(w_hq1) // output minute digit 1

55 );

56

57 // sec. dig0 to seg7

58 seg7_lut u2 (

59 .i_dig(w_sq0),

60 .o_seg(HEX2)

61 );

62

63 // sec. dig1 to seg7

64 seg7_lut u3 (

65 .i_dig({1'b0, w_sq1}),

66 .o_seg(HEX3)

67 );

68

69 // min. dig0 to seg7

70 seg7_lut u4 (

71 .i_dig(w_mq0),

72 .o_seg(HEX4)

73 );

74

75 // min. dig1 to seg7

76 seg7_lut u5 (

77 .i_dig({1'b0, w_mq1}),

78 .o_seg(HEX5)

79 );

80

81 // hour dig0 to seg7

82 seg7_lut u6 (

83 .i_dig(w_hq0),

84 .o_seg(HEX6)

85 );

86

87 // hour dig1 to seg7

88 seg7_lut u7 (

89 .i_dig({1'b0, w_hq1}),

90 .o_seg(HEX7)

91 );

92

93 endmodule

這是整個project的top module,為了簡化pin assignment的動作,所以port的命名方式和DE2_pin_assignments.csv相同,或許你跟我一樣不喜歡port用大寫的coding style,但為了pin assignment方便,只好在top module配合一下大寫,其他module的port一樣可以用小寫。

30行

// 1Hz clock

divn # (.WIDTH(26), .N(50000000))

u0 (

.clk(CLOCK_50),

.rst_n(KEY[0]),

.o_clk(clk_1)

);

divn為(原創) 如何設計除頻器? (SOC) (Verilog) (MegaCore)所寫過的萬用除頻器,由於DE2提供的clock是50MHz,但電子鐘只希望每秒變化一次,所以要除頻剩下1Hz,所以要將50MHz除50M,經過計算,這樣需26位才夠,所以傳進26與50000000。

divn.v / Verilog

1 /*

2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3

4 Filename : divn.v

5 Compiler : Quartus II 7.2 SP3 + ModelSim-Altera 6.1g

6 Description : Demo how to write frequency divider by n

7 Release : 07/16/2008 1.0

8 */

9

10 module divn (

11 input clk,

12 input rst_n,

13 output o_clk

14 );

15

16 parameter WIDTH = 3;

17 parameter N = 6;

18

19 reg [WIDTH-1:0] cnt_p;

20 reg [WIDTH-1:0] cnt_n;

21 reg clk_p;

22 reg clk_n;

23

24 assign o_clk = (N == 1) ? clk :

25 (N[0]) ? (clk_p | clk_n) : (clk_p);

26

27 always@(posedge clk or negedge rst_n) begin

28 if (!rst_n)

29 cnt_p <= 0;

30 else if (cnt_p == (N-1))

31 cnt_p <= 0;

32 else

33 cnt_p <= cnt_p + 1;

34 end

35

36 always@(posedge clk or negedge rst_n) begin

37 if (!rst_n)

38 clk_p <= 1;

39 else if (cnt_p < (N>>1))

40 clk_p = 1;

41 else

42 clk_p = 0;

43 end

44

45 always@(negedge clk or negedge rst_n) begin

46 if (!rst_n)

47 cnt_n <= 0;

48 else if (cnt_n == (N-1))

49 cnt_n <= 0;

50 else

51 cnt_n <= cnt_n + 1;

52 end

53

54 always@(negedge clk or negedge rst_n) begin

55 if (!rst_n)

56 clk_n <= 1;

57 else if (cnt_n < (N>>1))

58 clk_n = 1;

59 else

60 clk_n = 0;

61 end

62

63 endmodule

64

dinn.v請參考(原創) 如何設計除頻器? (SOC) (Verilog) (MegaCore)中的討論。

seg7_lut.v / Verilog

1 /*

2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3

4 Filename : seg7_lut.V

5 Compiler : Quartus II 7.2 SP3

6 Description : Demo how to use 8 bit 7 segment display decimal

7 Release : 07/20/2008 1.0

8 */

9 module seg7_lut (

10 input [3:0] i_dig,

11 output reg [6:0] o_seg

12 );

13

14 always@(i_dig) begin

15 case(i_dig)

16 4'h1: o_seg = 7'b111_1001; // ---t----

17 4'h2: o_seg = 7'b010_0100; // | |

18 4'h3: o_seg = 7'b011_0000; // lt rt

19 4'h4: o_seg = 7'b001_1001; // | |

20 4'h5: o_seg = 7'b001_0010; // ---m----

21 4'h6: o_seg = 7'b000_0010; // | |

22 4'h7: o_seg = 7'b111_1000; // lb rb

23 4'h8: o_seg = 7'b000_0000; // | |

24 4'h9: o_seg = 7'b001_1000; // ---b----

25 4'ha: o_seg = 7'b000_1000;

26 4'hb: o_seg = 7'b000_0011;

27 4'hc: o_seg = 7'b100_0110;

28 4'hd: o_seg = 7'b010_0001;

29 4'he: o_seg = 7'b000_0110;

30 4'hf: o_seg = 7'b000_1110;

31 4'h0: o_seg = 7'b100_0000;

32 endcase

33 end

34

35 endmodule

這是一個7段顯示器的lookup table,請參考(原創) 如何以16進位顯示8位數的七段顯示器? (SOC) (Verilog) (DE2)

clock.v / Verilog

1 /*

2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3

4 Filename : clock.v

5 Compiler : Quartus II 7.2 SP3 + ModelSim-Altera 6.1g

6 Description : Demo how to write clock counter

7 Release : 07/28/2008 1.0

8 */

9

10 module clock (

11 input clk,

12 input en,

13 input clr,

14 input load,

15 input [3:0] sd0,

16 input [2:0] sd1,

17 input [3:0] md0,

18 input [2:0] md1,

19 input [3:0] hd0,

20 input [2:0] hd1,

21 output [3:0] sq0,

22 output [2:0] sq1,

23 output [3:0] mq0,

24 output [2:0] mq1,

25 output [3:0] hq0,

26 output [1:0] hq1,

27 output co

28 );

29

30 wire w_clr;

31 wire [3:0] w_md0;

32 wire [2:0] w_md1;

33 reg [3:0] w_hd0;

34 reg [2:0] w_hd1;

35 wire w_sco; // second carry

36 wire w_mco; // minute carry

37 wire w_hco; // hour carry

38

39 counter60 sec (

40 .clk(clk),

41 .load(load),

42 .clr(w_clr),

43 .en(en),

44 .d0(sd0),

45 .d1(sd1),

46 .q0(sq0),

47 .q1(sq1),

48 .co(w_sco)

49 );

50

51 counter60 min (

52 .clk(clk),

53 .load(load),

54 .clr(w_clr),

55 .en(en & w_sco),

56 .d0(w_md0),

57 .d1(w_md1),

58 .q0(mq0),

59 .q1(mq1),

60 .co(w_mco)

61 );

62

63 counter24 hour (

64 .clk(clk),

65 .load(load),

66 .clr(w_clr),

67 .en(en & w_mco & w_sco),

68 .d0(w_hd0),

69 .d1(w_hd1),

70 .q0(hq0),

71 .q1(hq1),

72 .co(w_hco)

73 );

74

75 assign w_clr = clr | co;

76 assign co = w_hco & w_mco & w_sco;

77 assign w_md0 = (!load) ? 0 :

78 (md0 < 10) ? md0 : 9;

79 assign w_md1 = (!load) ? 0 :

80 (md1 < 6) ? md1 : 5;

81

82 always@(load or hd0 or hd1) begin

83 if (!load) begin

84 w_hd0 = 0;

85 w_hd1 = 0;

86 end

87 else begin

88 if (hd1 <= 1) begin // 0 1

89 w_hd1 = hd1;

90

91 if (hd0 < 10)

92 w_hd0 = hd0;

93 else

94 w_hd0 = 9;

95 end

96 else begin // >= 2

97 w_hd1 = 2;

98

99 if (hd0 < 4)

100 w_hd0 = hd0;

101 else

102 w_hd0 = 3;

103 end

104 end

105 end

106

107 endmodule

clock.v事實上已經是一個完整功能的電子鐘,可以單獨用testbench測試,但為了要和DE2周邊搭配,所以才又包了一個digi_clock.v。

clock.v主要有兩個功能:

1.例化時、分、秒3個instance。

2.對輸入做防呆的動作。

75行

assign w_clr = clr | co;

assign co = w_hco & w_mco & w_sco;

co為整個電子鐘的進位功能,也就是當時、分、秒皆有進位時才進位,也就是23:59:59時才進位。

w_clr為整個電子鐘歸0的連線,除了手動clear外,當co為1,也就是23:59:59時, 整個電子鐘也會歸0。

下面都是對輸入防呆的程式,也就是的輸入的的部分,最多只能輸入到59,的部分最多只能輸入到23。

77行

assign w_md0 = (!load) ? 0 :

(md0 < 10) ? md0 : 9;

assign w_md1 = (!load) ? 0 :

(md1 < 6) ? md1 : 5;

的部分比較單純,所以用 ?: 寫法即可,因為分最多只能到59分,個位數最多只能到9,超過9的部分一率只能輸入9。十位部分最多只能到5,超過5的部分一率只能輸入5。

81行

always@(load or hd0 or hd1) begin

if (!load) begin

w_hd0 = 0;

w_hd1 = 0;

end

else begin

if (hd1 <= 1) begin // 0 1

w_hd1 = hd1;

if (hd0 < 10)

w_hd0 = hd0;

else

w_hd0 = 9;

end

else begin // >= 2

w_hd1 = 2;

if (hd0 < 4)

w_hd0 = hd0;

else

w_hd0 = 3;

end

end

end

的部分比較複雜,所以用always block來寫。因為時最多只能到23,所以個位數能輸入的數字還要看當時的十位數而定。若十位數為0或1,則個位數做多到9;若十位數大於2,個位數最多只能到3,超過3的部分一率只能輸3。

counter60.v / Verilog

1 /*

2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3

4 Filename : counter60.v

5 Compiler : Quartus II 7.2 SP3 + ModelSim-Altera 6.1g

6 Description : Demo how to write 60 counter

7 Release : 07/27/2008 1.0

8 */

9

10 module counter60 (

11 input clk,

12 input clr,

13 input load,

14 input en,

15 input [3:0] d0,

16 input [3:0] d1,

17 output reg [3:0] q0,

18 output reg [2:0] q1,

19 output co

20 );

21

22 assign co = q1[2] & q1[0] & q0[3] & q0[0]; // 101 1001 = 59

23

24 always@(posedge clk) begin

25 if (clr) begin

26 q0 <= 0;

27 q1 <= 0;

28 end

29 else if (load) begin

30 q0 <= d0;

31 q1 <= d1;

32 end

33 else if (en) begin

34 if (q0 == 9) begin

35 q0 <= 0;

36

37 if (q1 == 5)

38 q1 <= 0;

39 else

40 q1 <= q1 + 1;

41 end

42 else

43 q0 <= q0 + 1;

44 end

45 else begin

46 q0 <= q0;

47 q1 <= q1;

48 end

49 end

50

51 endmodule

由於分和秒為60進制,所以需要一個60計數器,並且能進位,請參考(筆記) 如何設計計數器? (SOC) (Verilog) (MegaCore)

22行

assign co = q1[2] & q1[0] & q0[3] & q0[0]; // 101 1001 = 59

進位的地方較特別,當59時要送出進位carry,若用2進位表示就是101 1001,所以只要將q1[2] & q1[0] & q0[3] & q0[0]即可,這就是2進位好用的地方。

counter24.v / Verilog

1 /*

2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3

4 Filename : counter24.v

5 Compiler : Quartus II 7.2 SP3 + ModelSim-Altera 6.1g

6 Description : Demo how to write 24 counter

7 Release : 07/27/2008 1.0

8 */

9

10 module counter24 (

11 input clk,

12 input clr,

13 input load,

14 input en,

15 input [3:0] d0,

16 input [1:0] d1,

17 output reg [3:0] q0,

18 output reg [1:0] q1,

19 output co

20 );

21

22 assign co = q0[1] & q0[0] & q1[1]; // 010 11 = 23

23

24 always@(posedge clk) begin

25 if (clr) begin

26 q0 <= 0;

27 q1 <= 0;

28 end

29 else if (load) begin

30 q0 <= d0;

31 q1 <= d1;

32 end

33 else if (en) begin

34 if (q0 == 9) begin

35 q0 <= 0;

36 q1 <= q1 + 1;

37 end

38 else if (q1 == 2 & q0 == 3) begin // 23

39 q1 <= 0;

40 q0 <= 0;

41 end

42 else

43 q0 <= q0 + 1;

44 end

45 else begin

46 q0 <= q0;

47 q1 <= q1;

48 end

49 end

50

51 endmodule

原理和60計數器一樣,我就不在多做解釋。

完整程式碼下載

digi_clock.7z

Conclusion

原來每天帶的電子錶,就是以clock為基礎,搭配計數器做出來的,透過FPGA,我們也可以自己設計一個簡易的電子鐘。

See Also

(原創) 如何設計除頻器? (SOC) (Verilog) (MegaCore)

(原創) 如何以16進位顯示8位數的七段顯示器? (SOC) (Verilog) (DE2)

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

(原創) 如何設計電子鐘(II)? (SOC) (Verilog) (MegaCore) (DE2)

Reference

陸自強 2007,數位系統實習 Quartus II,儒林圖書公司
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐