Verilog计数器

HDLBits链接


前言

今天更新一个小节内容:计数器。计数器可以说是我们接触数字电路以后用的最频繁的模块之一了,无论是项目、应聘还是将来的工作,计数器都无处不在。


题库

题目描述1:

构建一个从0到15的4位二进制计数器,周期为16。同步复位,复位应该将计数器重置为0。

1

Solution1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module top_module (
input clk,
input reset, // Synchronous active-high reset
output [3:0] q);

always @(posedge clk)begin
if(reset)begin
q<=4'b0;
end
else begin
q<=q+1'b1;
end
end

endmodule

题目描述2:

构建一个从0到9(包括9)的十进制计数器,其周期为10。同步复位,复位应该将计数器重置为0。

2

Solution2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module top_module (
input clk,
input reset, // Synchronous active-high reset
output [3:0] q);

always @(posedge clk)begin
if(reset || q >= 4'd9)begin
q<=4'b0;
end
else begin
q<=q+1'b1;
end
end

endmodule

题目描述3:

制作一个从1到10的10进制计数器。同步复位,复位应该将计数器复位为1。

3

Solution3:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module top_module (
input clk,
input reset,
output [3:0] q);

always @(posedge clk)begin
if(reset || q>=4'd10)begin
q<=4'b1;
end
else begin
q<=q+1'b1;
end
end

endmodule

题目描述4:

构建一个从0到9(包括9)的十进制计数器,其周期为10。同步复位,复位应该将计数器重置为0。我们希望能够暂停计数器,而不是总是在每个时钟周期中递增,因此slowena输入指示计数器应该何时递增。

4

Solution4:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
module top_module (
input clk,
input slowena,
input reset,
output [3:0] q);

always @(posedge clk)begin
if(reset)begin
q<=4'b0;
end
else if(slowena)begin
if(q==4'd9)begin
q<=4'b0;
end
else begin
q<=q+1'b1;
end
end
end

endmodule

题目描述4:

设计一个1-12计数器

Solution4:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module top_module (
input clk,
input reset,
input enable,
output [3:0] Q,
output c_enable,
output c_load,
output [3:0] c_d
);

assign c_enable = enable;
assign c_load = reset | ((Q == 4'd12) && (enable == 1'b1));
assign c_d = c_load ? 4'd1 : 4'd0;

count4 the_counter (clk, c_enable, c_load, c_d , Q);

endmodule

题目描述5:

例化BCD模块实现降频操作,1kHz->1Hz。

Solution5:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module top_module (
input clk,
input reset,
output OneHertz,
output [2:0] c_enable
); //
wire[3:0] one, ten, hundred;
assign c_enable = {one == 4'd9 && ten == 4'd9, one == 4'd9, 1'b1};
assign OneHertz = (one == 4'd9 && ten == 4'd9 && hundred == 4'd9);

bcdcount counter0 (clk, reset, c_enable[0], one);
bcdcount counter1 (clk, reset, c_enable[1], ten);
bcdcount counter2 (clk, reset, c_enable[2], hundred);

endmodule

题目描述6:

构建一个4位BCD(二进制编码的十进制)计数器。每个十进制数字使用4位进行编码:q[3:0]是个位,q[7:4]是十位,以此类推。各进制上的进位时也需输出一个使能信号,指示三位数字何时应该增加。

Solution6:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
module top_module (
input clk,
input reset, // Synchronous active-high reset
output [3:1] ena,
output [15:0] q);

reg [3:0] ones;
reg [3:0] tens;
reg [3:0] hundreds;
reg [3:0] thousands;

always @(posedge clk) begin
if(reset)begin
ones <= 4'b0;
end
else if(ones == 4'd9)begin
ones <=4'b0;
end
else begin
ones <= ones + 4'd1;
end
end

always @(posedge clk)begin
if(reset)begin
tens <= 4'b0;
end
else if(tens == 4'd9 && ones == 4'd9)begin
tens <= 4'b0;
end
else if(ones == 4'd9)begin
tens <= tens + 4'd1;
end
end

always @(posedge clk)begin
if(reset)begin
hundreds <= 4'b0;
end
else if(hundreds == 4'd9 && tens == 4'd9 && ones == 4'd9)begin
hundreds <= 4'b0;
end
else if(tens == 4'd9 && ones == 4'd9) begin
hundreds <= hundreds + 4'd1;
end
end

always @(posedge clk)begin
if(reset)begin
thousands <= 4'b0;
end
else if(thousands == 4'd9 && hundreds == 4'd9 && tens == 4'd9 && ones == 4'd9)begin
thousands <= 4'b0;
end
else if(hundreds == 4'd9 && tens == 4'd9 && ones == 4'd9)begin
thousands <= thousands + 4'd1;
end
end

assign q = {thousands,hundreds,tens,ones};
assign ena[1] = (ones == 4'd9) ? 1'b1 : 1'b0;
assign ena[2] = ((ones == 4'd9) && (tens == 4'd9)) ? 1'b1 : 1'b0;
assign ena[3] = ((ones == 4'd9) && (tens == 4'd9) && (hundreds == 4'd9)) ? 1'b1 : 1'b0;

endmodule

题目描述7:

创建一组适合作为12小时的时钟使用的计数器(带有am/pm指示器)。你的计数器是由一个快速运行的clk驱动,每次时钟增加时ena必须为1。reset将时钟重置到中午12点。上午时pm=0,下午时pm=0。hh,mm和ss分别是小时(01-12)、分钟(00-59)和秒(00-59)的两个BCD(二进制编码的十进制)数字。

Reset比enable具有更高的优先级,并且即使在没有启用时也会发生。

下面的时序图显示了从11:59:59 AM到12:00:00 PM的翻转行为以及同步的Reset和enable行为。

5

Solution7:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
module top_module(
input clk,
input reset,
input ena,
output pm,
output [7:0] hh,
output [7:0] mm,
output [7:0] ss);

reg pm_temp;
reg [3:0] ss_ones;
reg [3:0] ss_tens;
reg [3:0] mm_ones;
reg [3:0] mm_tens;
reg [3:0] hh_ones;
reg [3:0] hh_tens;

wire add_ss_ones;
wire end_ss_ones;
wire add_ss_tens;
wire end_ss_tens;
wire add_mm_ones;
wire end_mm_ones;
wire add_mm_tens;
wire end_mm_tens;
wire add_hh_ones;
wire end_hh_ones_0;
wire end_hh_ones_1;
wire add_hh_tens;
wire end_hh_tens_0;
wire end_hh_tens_1;
wire pm_ding;

assign add_ss_ones = ena;
assign end_ss_ones = add_ss_ones && (ss_ones == 4'd9);
always @(posedge clk)begin
if(reset)begin
ss_ones <= 4'b0;
end
else if(add_ss_ones)begin
if(end_ss_ones)begin
ss_ones <= 4'b0;
end
else begin
ss_ones <= ss_ones + 4'b1;
end
end
end

assign add_ss_tens = end_ss_ones;
assign end_ss_tens = add_ss_tens && (ss_tens == 4'd5);
always @(posedge clk)begin
if(reset)begin
ss_tens <= 4'b0;
end
else if(add_ss_tens)begin
if(end_ss_tens)begin
ss_tens <= 4'b0;
end
else begin
ss_tens <= ss_tens + 4'b1;
end
end
end

assign add_mm_ones = end_ss_tens;
assign end_mm_ones = add_mm_ones && (mm_ones == 4'd9);
always @(posedge clk)begin
if(reset)begin
mm_ones <= 4'b0;
end
else if(add_mm_ones)begin
if(end_mm_ones)begin
mm_ones <= 4'b0;
end
else begin
mm_ones <= mm_ones + 4'b1;
end
end
end

assign add_mm_tens = end_mm_ones;
assign end_mm_tens = add_mm_tens && (mm_tens == 4'd5);
always @(posedge clk)begin
if(reset)begin
mm_tens <= 4'b0;
end
else if(add_mm_tens)begin
if(end_mm_tens)begin
mm_tens <= 4'b0;
end
else begin
mm_tens <= mm_tens + 4'b1;
end
end
end

assign add_hh_ones = end_mm_tens;
assign end_hh_ones_0 = add_hh_ones && (hh_ones == 4'd9);
assign end_hh_ones_1 = add_hh_ones && ((hh_ones == 4'd2) && (hh_tens == 4'd1));
always @(posedge clk)begin
if(reset)begin
hh_ones <= 4'd2;
end
else if(add_hh_ones)begin
if(end_hh_ones_0)begin
hh_ones <= 4'b0;
end
else if(end_hh_ones_1)begin
hh_ones <= 4'b1;
end
else begin
hh_ones <= hh_ones+4'b1;
end
end
end

assign add_hh_tens = end_mm_tens;
assign end_hh_tens_0 = add_hh_tens && end_hh_ones_1;
assign end_hh_tens_1 = add_hh_tens && end_hh_ones_0;
always @(posedge clk)begin
if(reset)begin
hh_tens <= 4'b1;
end
else if(add_hh_tens)begin
if(end_hh_tens_0)begin
hh_tens <= 4'b0;
end
else if(end_hh_tens_1)begin
hh_tens <= hh_tens + 4'b1;
end
end
end

always@(posedge clk)begin
if(reset)begin
pm_temp <= 1'b0;
end
else if(pm_ding)begin
pm_temp <= ~pm_temp;
end
end

assign pm_ding = hh_tens == 4'd1 && hh_ones == 4'd1 && end_mm_tens;

assign ss = {ss_tens, ss_ones};
assign mm = {mm_tens, mm_ones};
assign hh = {hh_tens, hh_ones};
assign pm = pm_temp;

endmodule

总结

  • 熟悉了基本计数器的代码编写。
  • 时钟的进位条件应单独用assign列出,这样层次感更加清晰。