写在前面

全部答案汇总:刷完这套题,我才发现Verilog原来如此简单----HDLBits答案汇总

今天更新Circuits章节中Sequential Logic的1个小节:Counters


Four-bit binary counter

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

        个人思路:

                因为这个题目是4位宽的计数器且要求从0-15计数,那么可以不要设置清零条件(计数器到15后会溢出自动到0),所以只要在复位无效的情况下,计数器输出每周期递增一;

        复位有效时,计数器输出0。

module top_module (
    input clk,
    input reset,      // Synchronous active-high reset
    output [3:0] q);

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

Decade counter

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

        个人思路:

                这个题目是4位宽的计数器且要求从0-9计数,和上题不同的是,计数器到9后即要求恢复到0,所以不存在溢出的情况,需要手动清零。所以当计数器计数到9,计数器清零,其他

        时候计数器每周期递增一;复位有效时,计数器输出0。

module top_module (
    input clk,
    input reset,                
    output [3:0] q);
    
    always@(posedge clk)begin
        if(reset)
            q <= 4'd0;
        else if(q == 4'd9)      //计数到最大值清零
            q <= 4'd0;
        else
            q <= q + 1'b1;      //其他时候每周期+1
    end
endmodule

Decade counter again

        题目:构建一个从1到10的4位二进制计数器。同步复位,复位应该将计数器重置为1。

        个人思路:

                和上题基本相同;只需要把复位重置为1,且从1-10计数(计数最大值为10)。

module top_module (
    input clk,
    input reset,
    output [3:0] q);
    
    always@(posedge clk)begin
        if(reset)
            q <= 4'd1;
        else if(q == 4'd10)
            q <= 4'd1;
        else
            q <= q + 1'b1;
    end
endmodule

Slow decade counter

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

        个人思路:

                计数器在递增时应判断使能slowena是否有效,有效则递增或清零(计数到最大值),无效则保持。        

module top_module (
    input clk,
    input slowena,
    input reset,
    output [3:0] q);

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

Counter 1-12

题目:

使用以下条件设计一个1-12的计数器:

  • Reset:高电平有效的同步复位信号,复位到1
  • Enable:高电平有效的计数器计数使能信号
  • Clk:上升沿触发的时钟输入
  • Q[3:0]:计数器输出
  • c_enable, c_load, c_d[3:0] 控制信号到提供的4位计数器,因此可以验证正确的操作。

您需要准备以下组件:

  • 下面的4位二进制计数器(coun4),它具有使能和同步并行载入(载入优先级高于使能)。count4模块将提供给您。在你的电路中实例化它。
  • 逻辑门
module count4(
	input clk,
	input enable,
	input load,
	input [3:0] d,
	output reg [3:0] Q
);

c_enable、c_load和c_d输出分别是到内部计数器的enable、load和d输入的信号。它们的目的是为了检查这些信号的正确性。

个人思路:

        题目提供了一个4位的计数器(0-15):

                使能信号enable:直接把c_enable连到这里就可以

                载入信号load:结合c_load可以实现复位清零,以及最大值清零

                载入值c_load:同上

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'dx;                            //载入时载入值为1,其他时候不关心
    
    count4 the_counter (clk, c_enable, c_load, c_d , Q);

endmodule

Counter 1000

题目:

        从一个1000hz的时钟,派生一个1hz的信号,称为OneHertz,它可以用来驱动一组小时/分钟/秒计数器的启用信号,以创建一个数字挂钟。因为我们想让时钟每秒计数一次,所以一hz信号必须精确地保持每秒一个周期。建立分频器使用模10 (BCD)计数器和尽可能少的其他门。还输出您使用的每个BCD计数器的启用信号(c_enable[0]表示最快的计数器,c_enable[2]表示最慢的计数器)。

        以下BCD模块将提供给您。Enable是高电平有效的计数器使能信号。Reset是高电平有效同步复位信号,复位值为0。电路中的所有计数器必须直接使用相同的1000hz信号。

        module bcdcount (
	        input clk,
	        input reset,
	        input enable,
	        output reg [3:0] Q
        );

个人思路:

        从1000hz--1hz输出,实际上就是要设计一个周期为1000的计数器(每次计数到999就输出一个信号,这个信号周期就是1hz)。题目给出了一个4位的BCD模块,我们可以例化三次这个模块,第一次例化计数10次(每个时钟周期+1),第二次例化计数10次(在第一次例化每计数10次时加+1),第三次例化计数10次(在第二次例化每计数10次时加+1),这样就实现了每1000个时钟周期+1,对应的输出信号的频率也就变成了1hz。

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

4-digit decimal counter

题目:

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

个人思路:
         千、百、十、个一共4位(每位4位宽,共16位宽),分别计数即可,设计的时候注意好计数使能条件和清零条件就好。细节看代码注释吧。

module top_module (
    input 			clk,
    input 			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;
//个、十、百、千计数器使能信号
	wire	ones_ena;
	wire	tens_ena;
	wire	hundreds_ena;
	wire	thousands_ena;
//个、十、百、千计数器清零信号
	wire	ones_end;
	wire	tens_end;
	wire	hundreds_end;
	wire	thousands_end;	

//个、十、百、千计数器使能条件	
	assign	ones_ena = 1'b1;				//个计数器一直计数
	assign 	tens_ena = ones_end;			//十计数器在个计数器清零一次(计数到最大值)计数+1
	assign 	hundreds_ena = tens_end;		//百计数器在十计数器清零一次(计数到最大值)计数+1
	assign 	thousands_ena = hundreds_end;	//千计数器在百计数器清零一次(计数到最大值)计数+1	
//个、十、百、千计数器清零条件	
	assign 	ones_end = (ones == 4'd9);		//计数到最大值9清零
	assign 	tens_end = (tens == 4'd9 && ones == 4'd9);//计数到最大值99清零
	assign 	hundreds_end = (hundreds == 4'd9 && tens == 4'd9 && ones == 4'd9);//计数到最大值999清零
	assign 	thousands_end = (thousands == 4'd9 && hundreds == 4'd9 && tens == 4'd9 && ones == 4'd9);//计数到最大值9999清零	
//个计数器计数always块
    always@(posedge clk)begin
        if(reset)
            ones <= 4'd0;
        else if(ones_end)
            ones <= 4'd0;
        else if(ones_ena)
            ones <= ones + 1'b1;
    end
//十计数器计数always块    
    always@(posedge clk)begin
        if(reset)
            tens <= 4'd0;
        else if(tens_end)
            tens <= 4'd0;        
        else if(tens_ena) 
            tens <= tens + 1'b1;
    end
//百计数器计数always块    
    always@(posedge clk)begin
        if(reset)
            hundreds <= 4'd0;
        else if(hundreds_end)
            hundreds <= 4'd0;
        else if(hundreds_ena) 
            hundreds <= hundreds + 1'b1;
    end
//千计数器计数always块     
    always@(posedge clk)begin
        if(reset)begin
            thousands <= 4'd0;
        end
        else if(thousands_end)begin
            thousands <= 4'd0;
        end
        else if(thousands_ena) begin
            thousands <= thousands + 1'b1;
        end
    end
//输出   
    assign q = {thousands, hundreds, tens, ones};	//个、十、百、千拼接输出
    assign ena[1] = (ones_end) ? 1'b1 : 1'b0;
    assign ena[2] = (tens_end) ? 1'b1 : 1'b0;
    assign ena[3] = (hundreds_end) ? 1'b1 : 1'b0;
 
endmodule

12-hour clock

题目:

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

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

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

 个人思路:

        时、分、秒一共6位,分别计数即可,设计的时候注意好计数使能条件和清零条件就好。细节看代码注释吧。

module top_module(
    input 			clk,		//时钟
    input 			reset,		//复位
    input 			ena,		//计数使能信号
    output			pm,			//pm标志,1:PM;0:AM
    output [7:0] 	hh,			//时BCD
    output [7:0] 	mm,			//分BCD
    output [7:0] 	ss); 		//秒BCD	
    
    reg			pm_temp;		//pm寄存值
    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		ss_ones_ena;	//秒个位计数使能
    wire		ss_tens_ena;	//秒十位计数使能
    wire		mm_ones_ena;	//分个位计数使能
    wire		mm_tens_ena;	//分十位计数使能
    wire		hh_ones_ena;	//时个位计数使能
    wire		hh_tens_ena;	//时十位计数使能
	
    wire		ss_ones_end;	//秒个位计数完成
    wire		ss_tens_end;	//秒个位计数完成
    wire		mm_ones_end;    //分个位计数完成
    wire		mm_tens_end;    //分十位计数完成
    wire		hh_ones_0_end;	//时个位计数完成(十位为0)
    wire		hh_ones_1_end;	//时个位计数完成(十位为1)
    wire		hh_tens_0_end;	//时十位计数完成(十位为0)
    wire		hh_tens_1_end;  //时十位计数完成(十位为1)
	
    wire		pm_inv;			//PM跳转标志

//时、分、秒计数使能条件   
    assign ss_ones_ena = ena;								//使能信号有效秒钟各位开始计数
	assign ss_tens_ena = ss_ones_end;						//秒钟个位计数完成后,秒钟十位开始计数
    assign mm_ones_ena = ss_tens_end;						//秒钟个位计数完成后,分钟个位开始计数
    assign mm_tens_ena = mm_ones_end;						//分钟个位计数完成后,秒钟十位开始计数
    assign hh_ones_ena = mm_tens_end;						//分钟十位计数完成后,时钟个位开始计数
    assign hh_tens_ena = mm_tens_end;						//时钟个位计数完成后,时钟十位开始计数

//时、分、秒计数完成判断	
    assign ss_ones_end = ss_ones_ena && (ss_ones == 4'd9);	//秒钟个位计数使能信号有效,且秒钟个位计数到最大值9
    assign ss_tens_end = ss_tens_ena && (ss_tens == 4'd5);	//秒钟十位计数使能信号有效,且秒钟十位计数到最大值5
    assign mm_ones_end = mm_ones_ena && (mm_ones == 4'd9);	//时钟个位计数使能信号有效,且时钟个位计数到最大值9
    assign mm_tens_end = mm_tens_ena && (mm_tens == 4'd5);  //时钟十位计数使能信号有效,且时钟十位计数到最大值5
	//时钟个位计数使能信号有效,且时钟个位计数到最大值9(十位为0)
    assign hh_ones_0_end = hh_ones_ena && ((hh_ones == 4'd9) && (hh_tens == 4'd0));
	//时钟个位计数使能信号有效,且时钟个位计数到最大值2(十位为1)					
    assign hh_ones_1_end = hh_ones_ena && ((hh_ones == 4'd2) && (hh_tens == 4'd1));
    assign hh_tens_0_end = hh_tens_ena && hh_ones_1_end;	//时钟十位计数使能信号有效,且时钟个位计数完成(十位为0)
    assign hh_tens_1_end = hh_tens_ena && hh_ones_0_end;	//时钟十位计数使能信号有效,且时钟个位计数完成(十位为1)
//pm跳转条件判断	
	assign pm_inv = hh_tens == 4'd1 && hh_ones == 4'd1 && mm_tens_end;

//pm标志位输出   	
	assign pm = pm_temp;	
//时、分、秒输出
    assign ss = {ss_tens, ss_ones};		//十位、个位拼接
    assign mm = {mm_tens, mm_ones};		//十位、个位拼接
    assign hh = {hh_tens, hh_ones};		//十位、个位拼接	
//秒钟的个位计数	
    always @(posedge clk)begin
        if(reset)begin
            ss_ones <= 4'b0;				//复位清零
        end
        else if(ss_ones_ena)begin			//使能有效
            if(ss_ones_end)begin			//计数完成则清零
                ss_ones <= 4'b0;
            end
            else begin
                ss_ones <= ss_ones + 4'b1;	//计数递加1
            end
        end
    end
//秒钟的十位计数   
    always @(posedge clk)begin
        if(reset)begin
            ss_tens <= 4'b0;
        end
        else if(ss_tens_ena)begin
            if(ss_tens_end)begin
                ss_tens <= 4'b0;
            end
            else begin
                ss_tens <= ss_tens + 4'b1;
            end
        end
    end
//分钟的个位计数
    always @(posedge clk)begin
        if(reset)begin
            mm_ones <= 4'b0;
        end
        else if(mm_ones_ena)begin
            if(mm_ones_end)begin
                mm_ones <= 4'b0;
            end
            else begin
                mm_ones <= mm_ones + 4'b1;
            end
        end
    end   
//分钟的十位计数
	always @(posedge clk)begin
        if(reset)begin
            mm_tens <= 4'b0;
        end
        else if(mm_tens_ena)begin
            if(mm_tens_end)begin
                mm_tens <= 4'b0;
            end
            else begin
                mm_tens <= mm_tens + 4'b1;
            end
        end
    end
//时钟的个位计数    
    always @(posedge clk)begin
        if(reset)begin
            hh_ones <= 4'd2;
        end
        else if(hh_ones_ena)begin
            if(hh_ones_0_end)begin
                hh_ones <= 4'b0;
            end
            else if(hh_ones_1_end)begin
                hh_ones <= 4'b1;
            end
            else begin
                hh_ones <= hh_ones+4'b1;
            end
        end
    end
//时钟的十位计数
    always @(posedge clk)begin
        if(reset)begin
            hh_tens <= 4'b1;
        end
        else if(hh_tens_ena)begin
            if(hh_tens_0_end)begin
                hh_tens <= 4'b0;
            end
            else if(hh_tens_1_end)begin
                hh_tens <= hh_tens + 4'b1;
            end
        end
    end
//pm跳转   
    always@(posedge clk)begin
        if(reset)begin
            pm_temp <= 1'b0;
        end
        else if(pm_inv)begin		//满足跳转条件则翻转
            pm_temp <= ~pm_temp;
        end
    end
               
endmodule

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐