由于各个位数都是采用4位BCD码的编码方式,因此在这里考虑级联BCD计数器来实现。
由于模为60的BCD计数器可以通过模为6和模为10的计数器级联,模为12的可以考虑2*6,因此首先需要设计模为2,6,10的BCD计数器。
在这里参考
[如下博客] https://blog.csdn.net/step__forward/article/details/124499102
,应用带参数(parameter)的模块(module),通过修改参数来实现模不同的计数器,从而达到精简化代码的目的。代码如下:
module counter_4bit #( parameter c_number = 4'd10) ( input clk , input rst_n , input cin , output reg cout , output reg[3:0] q ); always@(posedge clk ) begin if(rst_n) q <= 4'd0 ; else if(q == c_number-1'b1 && cin == 1'b1) q <= 4'd0 ; else if(cin == 1'b1) q <= q+1'b1; else q <= q; end always@(*) begin if(q == c_number-1'b1 && cin == 1'b1) cout <= 1'b1; else cout <= 1'b0; end endmodule
如上所设计模块为带进位的BCD计数器。
在大多数需要级联的计数器的设计中一般都会设计进位Carry_in与Carry_out ,因为若不使用Carry_in只使用Carry_out与时钟连接会导致reset信号可能在第二级就失效,若都不使用而使用输出q则只适用于模为2的n次方的计数器。这也是我在第一次尝试中失败的原因。
注意 :该写法中进位的状态转移进程不能用上升沿触发,因为两个进程是并行的,所以会在0时才输出进位。但是该写法进位会产生毛刺,如图:
但是该毛刺并不影响级联时功能的实现,原因还没找出,可能是因为该毛刺滞后于上升沿。
上述计数器的应用主要有以下两种:
对时钟进行计数
设置cin为1即可实现。注意不是cin=clk,这会导致输出一直为0,如下图所示。
但是cin = ~clk 可以实现,只不过引入了更多的门,因此不推荐。
级联实现倍数计数
两个计数器连接到同一个高频系统时钟clk,再将低位的出位Carry_out连接到高位的Carry_out即可。
在设计中注意到 12的下一位为1:
Note that
11:59:59 PM
advances to12:00:00 AM
, and12:59:59 PM
advances to01:00:00 PM
. There is no 00:00:00.
因此需要设计一个模为11,从1计数到12,复位到1的同步计数器。
在这里首先考虑将通用计数器更改为复位可调的计数器,即引入新的parameter,再进行级联。但是个位并不是每次2都会复位到1,因此需要在通用模块中添加输入信号十位数字来判断,这增加了设计中无用的逻辑门数量。因此在这里直接更改通用计数器的结构来实现最方便。代码如下:
module counter12 #( parameter c_number = {1'b1,4'd2}) ( input clk , input rst_n , input cin , output reg cout , output reg[4:0] q // 改为5位 ); always@(posedge clk) begin if(rst_n) q <= c_number ; // 更改复位数字 else if(q == c_number && cin == 1'b1) q <= 5'd1 ; // 更改复位后的下一位 else if(q == 5'd9 && cin == 1'b1) q <= {1'b1,4'd0} ; // BCD,跳过0x0A~0x0F else if(cin == 1'b1) q <= q+1'b1; else q <= q; end always @(*) begin if(q == {1'b1,4'd1} && cin == 1'b1) // 计数器产生进位为最后一位,对应5,9 cout <= 1'b1; else cout <= 1'b0; end endmodule
在级联时,注意根据题意处理好reset和ena的优先级,如下备注所示。因此不可用进位直连下一级。
最后,可以考虑到,pm就像小时的下一位,因此再使用一个counter2输出即可。
module top_module(input clk, input reset, input ena, output pm, output [7:0] hh, output [7:0] mm, output [7:0] ss); wire carryOutSS0,carryOutMM0,carryOutHH0 ; wire carryOutSS,carryOutMM,carryOutHH ; //assign clk0=clk&&ena; //错误,因为reset有更高的优先级。上述写法会导致使能为低时的复位失效 assign carryOutSS = carryOutSS0 && (ena); assign carryOutMM = carryOutMM0 && (ena); assign carryOutHH = carryOutHH0 && (ena); counter60 counterSS (clk , reset , ena ,carryOutSS0, ss); counter60 counterMM (clk , reset , carryOutSS ,carryOutMM0, mm); counter12 countHH (clk , reset , carryOutMM, carryOutHH0 , hh); counter_4bit #(.c_number(2)) XM ( .clk (clk ), .rst_n (reset), .cin (carryOutHH ), .q (pm) ); endmodule
module top_module(input clk, input reset, input ena, output pm, output [7:0] hh, output [7:0] mm, output [7:0] ss); wire carryOutSS0,carryOutMM0,carryOutHH0 ; wire carryOutSS,carryOutMM,carryOutHH ; //assign clk0=clk&&ena; //错误,因为reset有更高的优先级。上述写法会导致使能为低时的复位失效 assign carryOutSS = carryOutSS0 && (ena); assign carryOutMM = carryOutMM0 && (ena); assign carryOutHH = carryOutHH0 && (ena); counter60 counterSS (clk , reset , ena ,carryOutSS0, ss); counter60 counterMM (clk , reset , carryOutSS ,carryOutMM0, mm); counter12 countHH (clk , reset , carryOutMM, carryOutHH0 , hh); counter_4bit #(.c_number(2)) XM ( .clk (clk ), .rst_n (reset), .cin (carryOutHH ), .q (pm) ); endmodule module counter_4bit #( parameter c_number = 4'd10) ( input clk , input rst_n , input cin , output reg cout , output reg[3:0] q ); always@(posedge clk ) begin if(rst_n) q <= 4'd0 ; else if(q == c_number-1'b1 && cin == 1'b1) q <= 4'd0 ; else if(cin == 1'b1) q <= q+1'b1; else q <= q; end always@(*) begin if(q == c_number-1'b1 && cin == 1'b1) cout <= 1'b1; else cout <= 1'b0; end endmodule module counter60( input clk , input rst_n, input cin , output cout , output [7:0] q ); wire m1; counter_4bit //对counter_4bit进行例化 #(.c_number(10)) u1 ( .clk (clk ), .rst_n (rst_n), .cin (cin ), .cout (m1 ), .q (q[3:0]) ); counter_4bit #(.c_number(6)) u2 ( .clk (clk ), .rst_n (rst_n), .cin (m1 ), .cout (cout ), .q (q[7:4]) ); endmodule module counter12 #( parameter c_number = {1'b1,4'd2}) ( input clk , input rst_n , input cin , output reg cout , output reg[4:0] q // 改为5位 ); always@(posedge clk) begin if(rst_n) q <= c_number ; // 更改复位数字 else if(q == c_number && cin == 1'b1) q <= 5'd1 ; // 更改复位后的下一位 else if(q == 5'd9 && cin == 1'b1) q <= {1'b1,4'd0} ; // BCD,跳过0x0A~0x0F else if(cin == 1'b1) q <= q+1'b1; else q <= q; end always @(*) begin if(q == {1'b1,4'd1} && cin == 1'b1) // 计数器产生进位为最后一位,对应5,9 cout <= 1'b1; else cout <= 1'b0; end endmodule
HDLbits Success截图:
2. Quartus 仿真结果: