HDLBits的Count clock问题总结

  • 模块化设计思想

这是HDLBits的电路->时序逻辑->计数器问题的压轴大题,如果没有从头开始刷起,直接刷12-hour clock对我这种初学者来说是非常吃力的。从这可以看出HDLBits网站出题人的用心良苦,他想告诉我们硬件初学者的是对于复杂的问题不要害怕,复杂的问题是由一个一个简单的小问题组成的,要有将复杂大问题(module)分解成一个一个简单小问题(sub-module)的能力。

对计数器这一章的8个问题从头到尾捋一遍,便于我们理解模块化设计思想。

Four-bit binary counter:

Build a 4-bit binary counter that counts from 0 through 15, inclusive, with a period of 16. The reset input is synchronous, and should reset the counter to 0.重点是从0计数到15,并且是同步复位。

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

    always@(posedge clk) begin
        if(reset)
            q<=0;
        else
            q<=q+1;
    end
endmodule

Decade counter:

Build a decade counter that counts from 0 through 9, inclusive, with a period of 10. The reset input is synchronous, and should reset the counter to 0.与上一个问题不同的是计数器是从0计数到9,是人们熟悉的10进制计数法。

module top_module(
    input clk,
    input reset,
    output reg [3:0] q);
    
    always @(posedge clk)
        if (reset || q == 9)    // Count to 10 requires rolling over 9->0 instead of the more natural 15->0
            q <= 0;
        else
            q <= q+1;
    
endmodule

Decade counter again:

Make a decade counter that counts 1 through 10, inclusive. The reset input is synchronous, and should reset the counter to 1.与上一个不同的是从1计数到10,更符合常理的计数。

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

        always@(posedge clk) begin
        if(reset | q == 10)
            q<=1;
        else
            q<=q+1;
    end
endmodule

Slow decade counter:

Build a decade counter that counts from 0 through 9, inclusive, with a period of 10. The reset input is synchronous, and should reset the counter to 0. We want to be able to pause the counter rather than always incrementing every clock cycle, so the slowena input indicates when the counter should increment.个人任务这个例题是这几个例题中最重要的,也充分展现了使能信号的妙用,学校里教的使我们感觉使能信号是一个很鸡肋的信号,这个例题以及后面的例题会告诉我们使能信号是整个计数时钟的连接枢纽,是整个时钟系统最重要的信号。使用使能信号可以改变计数的快慢,这点与时针分针秒针的计数快慢相联系。因为我们不可能去生成不同频率的时钟信号来实现计数的快慢——这样就不是同步电路了。使能信号就是控制信号,而控制信号才是电路的核心信号。

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

    always@(posedge clk) begin
        if(reset)
            q<=0;
        else if(slowena)
            if(q == 9) q<=0;
            else
                q<=q+1;
        else q<=q;
    end
endmodule

Counter 1-12:

The c_enable, c_load, and c_d outputs are the signals that go to the internal counter's enable, load, and d inputs, respectively. Their purpose is to allow these signals to be checked for correctness.该例题为ece241题,主要引入模块化设计思想和三个check信号:使能、装载(包含reset的功能)、和提示一轮计数完成的信号。我在实现Count Clock的时候并没有使用load和d去验证计数的正确性,这是可以改进的一个点。

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:1'b0;
    assign c_d = c_load?4'd1:Q;
    count4 the_counter (clk, c_enable, c_load, c_d, Q);

endmodule

module count4(
    input clk,
    input enable,
    input load,
    input [3:0] d,
    output reg [3:0] Q
);

Counter 1000:

将1000HZ通过三个级联的十计数器生成1HZ的信号,这个例题其实是很关键的,很类似Count Clock例题。完成该例题的关键就在于级联的enable信号的设置。

module top_module (
    input clk,
    input reset,
    output OneHertz,
    output [2:0] c_enable
); //

    reg [3:0] Q0,Q1,Q2;
    assign c_enable[0] = 1'b1;
    assign c_enable[1] = Q0 == 4'd9;
    assign c_enable[2] = Q1 == 4'd9 && Q0 == 4'd9;
    assign OneHertz = Q2 == 4'd9 && Q1 == 4'd9 && Q0 == 4'd9;
    bcdcount counter0 (clk, reset, c_enable[0],Q0);
    bcdcount counter1 (clk, reset, c_enable[1],Q1);
    bcdcount counter2 (clk, reset, c_enable[2],Q2);
endmodule

4-digit decimal counter:

Build a 4-digit BCD (binary-coded decimal) counter. Each decimal digit is encoded using 4 bits: q[3:0] is the ones digit, q[7:4] is the tens digit, etc. For digits [3:1], also output an enable signal indicating when each of the upper three digits should be incremented.该例题主要展示如何用十进制显示数字,为Count Clock铺垫。主要还是enable信号发挥了关键性的作用。

module top_module (
    input clk,
    input reset,   // Synchronous active-high reset
    output [3:1] ena,
    output [15:0] q);
    
    assign ena[1] = q[3:0] == 9;
    assign ena[2] = q[3:0] == 9 && q[7:4] == 9;
    assign ena[3] = q[3:0] == 9 && q[7:4] == 9 && q[11:8] == 9;

    decade_counters d1(clk,1,reset,q[3:0]);
    decade_counters d2(clk,ena[1],reset,q[7:4]);
    decade_counters d3(clk,ena[2],reset,q[11:8]);
    decade_counters d4(clk,ena[3],reset,q[15:12]);
endmodule

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

    always@(posedge clk) begin
        if(reset)
            q<=0;
        else if(slowena)
            if(q == 9) q<=0;
            else
                q<=q+1;
        else q<=q;
    end
endmodule

Count clock:

Create a set of counters suitable for use as a 12-hour clock (with am/pm indicator). Your counters are clocked by a fast-running clk, with a pulse on ena whenever your clock should increment (i.e., once per second).

reset resets the clock to 12:00 AM. pm is 0 for AM and 1 for PM. hh, mm, and ss are two BCD (Binary-Coded Decimal) digits each for hours (01-12), minutes (00-59), and seconds (00-59). Reset has higher priority than enable, and can occur even when not enabled.

The following timing diagram shows the rollover behaviour from 11:59:59 AM to 12:00:00 PM and the synchronous reset and enable behaviour.

module top_module(
    input clk,
    input reset,
    input ena,
    output pm,
    output [7:0] hh,
    output [7:0] mm,
    output [7:0] ss); 
    
    wire ena_m,ena_h;
    reg pm_reg = 0;
    assign pm = pm_reg;
    
    assign ena_m = (ss[3:0] == 4'd9 && ss[7:4] == 4'd5);
    assign ena_h = (mm[3:0] == 4'd9 && mm[7:4] == 4'd5) && (ss[3:0] == 4'd9 && ss[7:4] == 4'd5);
    
    always@(posedge clk) begin
        if((hh[3:0] == 1 && hh[7:4] == 1)&&(mm[3:0] == 9 && mm[7:4] == 5) && (ss[3:0] == 9 && ss[7:4] == 5))
            pm_reg = ~pm_reg;
        else
            pm_reg = pm_reg;
    end
    
    ms_counters sc(clk,ena,reset,ss);
    ms_counters mc(clk,ena_m,reset,mm);
    cnt_counters_h hc(clk,ena_h,reset,hh);

endmodule


module ms_counters (
    input clk,
    input ena,
    input reset,
    output [7:0] q);
    
    wire ena_g;
    
    assign ena_g = q[3:0] == 4'd9 & ena;
    
    cnt_counters d1(clk,ena,reset,4'd9,q[3:0]);
    cnt_counters d2(clk,ena_g,reset,4'd5,q[7:4]);
endmodule

module cnt_counters (
    input clk,
    input ena,
    input reset,
    input [3:0] cnt,
    output [3:0] q);

    reg [3:0] q_reg;
    assign q = q_reg;

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

module cnt_counters_h (
    input clk,
    input ena,
    input reset,
    output [7:0] q_out);

    reg [7:0] q;
    assign q_out = q;

    always@(posedge clk) begin
        if(reset) begin
            q[3:0]<=4'd2;
            q[7:4]<=4'd1;
        end
        else if(ena) begin
            if(q[3:0] == 4'd2 && q[7:4] == 4'd1) begin 
                q[3:0]<=4'd1;
                q[7:4]<=4'd0;
            end 
            else if(q[3:0] == 4'd9 && q[7:4] == 4'd0) begin 
                q[3:0]<=4'd0;
                q[7:4]<=4'd1;
            end
            else begin
                q[3:0]<=q[3:0]+4'd1;
                q[7:4]<=q[7:4];
            end
        end
        else begin 
            q[3:0]<=q[3:0];
            q[7:4]<=q[7:4];
        end
    end
endmodule

注意以上代码的39行,一开始是assign ena_g = q[3:0] == 4'd9;会导致如下的波形错误:

test bench代码:

`timescale 1ns / 1ns

module top_tb;
parameter PERIOD  = 10;


// top_module Inputs
reg   clk                                  = 0 ;
reg   reset                                = 1 ;
reg   ena                                  = 1 ;

// top_module Outputs
wire  pm                                   ;    
wire  [7:0]  hh                            ;    
wire  [7:0]  mm                            ;    
wire  [7:0]  ss                            ;    


initial
begin
    forever #(PERIOD/2)  clk=~clk;
end

initial
begin
    #(PERIOD*2) reset  =  0;
    #(PERIOD*4) reset  =  1;
    #(PERIOD*2) reset  =  0;
end

top_module  u_top_module (
    .clk                     ( clk          ),  
    .reset                   ( reset        ),  
    .ena                     ( ena          ),  

    .pm                      ( pm           ),  
    .hh                      ( hh     [7:0] ),  
    .mm                      ( mm     [7:0] ),  
    .ss                      ( ss     [7:0] )   
);

// initial
// begin

//     $finish;
// end

endmodule

总结:

模块化为子模块单个解决的思想方法。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值