HDLBits答案(22)_基于有限状态机的计数器

基于有限状态机的计数器

HDLBits链接


前言

今天更新搭建更大的电路部分的习题,内容主要跟计数器和有限状态机有关。


题库

Counter with period 1000

构造一个0-999的计数器,同步高电平复位。

Solution

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

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

endmodule

4-bit shift register and down counter

构造一个4bit的移位寄存器,同时也可以做倒数的计数器使用。其中当shift_ena为1时数据data的高位先进到移位寄存器中;当count_ena为1时,计数器从寄存器中存储的数开始逐时钟递减;shift_enacount_ena没有重要级先后顺序,因为他们不会同时使能。

pic1.png

Solution:

module top_module (
    input clk,
    input shift_ena,
    input count_ena,
    input data,
    output [3:0] q);

    reg [3:0] q_temp;

    always @(posedge clk) begin
        if(shift_ena) begin
            q_temp <= {q_temp[2:0],data};
        end
        else if(count_ena) begin
            if(q_temp == 4'd0) begin
                q_temp <= 4'd15;
            end
            else begin
                q_temp <= q_temp - 1'b1;
            end
        end
    end

    assign q = q_temp;

endmodule

我以为计数器到0时停止计数,本来按这个逻辑写的,然后在线提交时出错。作者的意思是0后面下一个状态是15,这个大家做题时需注意。

FSM:Sequence 1101 recognizer

构造一个有限状态机检测data中的1101序列,如果检测到该序列,则将输出一直拉高直到同步复位信号为高。

pic2.png

Solution

module top_module (
    input clk,
    input reset,      // Synchronous reset
    input data,
    output start_shifting);

    parameter IDLE = 3'd0, S1 = 3'd1, S2 = 3'd2;
    parameter S3 = 3'd3, OUT = 3'd4;
    reg [2:0] current_state, next_state;

    always @(*) begin
        case(current_state)
            IDLE:       next_state = data ? S1 : IDLE;
            S1:         next_state = data ? S2 : IDLE;
            S2:         next_state = data ? S2 : S3;
            S3:         next_state = data ? OUT : IDLE;
            OUT:        next_state = OUT;
            default:    next_state = IDLE;
        endcase
    end

    always @(posedge clk) begin
        if(reset) begin
            current_state <= IDLE;
        end
        else begin
            current_state <= next_state;
        end
    end

    assign start_shifting = current_state == OUT;

endmodule

FSM:Enable shift register

当有限状态机被复位时,将shift_ena拉高4个周期,之后保持为0直到再次复位。

pic3.png

Solution

module top_module (
    input clk,
    input reset,      // Synchronous reset
    output shift_ena);

    parameter IDLE = 2'd0, ENA = 2'd1, STOP = 2'd2;
    reg [1:0] current_state, next_state;
    reg [2:0] counter;

    always @(*) begin
        case(current_state)
            IDLE:       next_state = ENA;
            ENA:        next_state = (counter == 3'd3) ? STOP : ENA;
            STOP:       next_state = STOP;
            default:    next_state = IDLE;
        endcase
    end

    always @(posedge clk) begin
        if(reset) begin
            current_state <= IDLE;
        end
        else begin
            current_state <= next_state;
        end
    end

    always @(posedge clk) begin
        if(reset) begin
            counter <= 3'd0;
        end
        else begin
            case(next_state)
                IDLE:       counter <= 3'd0;
                ENA:        counter <= counter + 1'b1;
                STOP:       counter <= 3'd0;
                default:    counter <= 3'd0;
            endcase
        end      
    end

    assign shift_ena = current_state == ENA | current_state == IDLE;

endmodule

需注意的是复位一直为高的时候输出也一直为高电平;

FSM:The complete FSM

pic4.png

官方提供的状态转移图

pic5.png

Solution

module top_module (
    input clk,
    input reset,      // Synchronous reset
    input data,
    output shift_ena,
    output counting,
    input done_counting,
    output done,
    input ack );

    parameter S = 4'd0, S1 = 4'd1, S11 = 4'd2, S110 = 4'd3;
    parameter B0 = 4'd4, B1 = 4'd5, B2 = 4'd6, B3 = 4'd7;
    parameter Count = 4'd8, Wait = 4'd9;
    reg [3:0] current_state, next_state;

    always @(*) begin
        case(current_state)
            S:          next_state = data ? S1 : S;
            S1:         next_state = data ? S11 : S;
            S11:        next_state = data ? S11 : S110;
            S110:       next_state = data ? B0 : S;
            B0:         next_state = B1;
            B1:         next_state = B2;
            B2:         next_state = B3;
            B3:         next_state = Count;
            Count:      next_state = done_counting ? Wait : Count;
            Wait:       next_state = ack ? S : Wait;
            default:    next_state = S;
        endcase
    end

    always @(posedge clk) begin
        if(reset) begin
            current_state <= S;
        end
        else begin
            current_state <= next_state;
        end
    end

    assign shift_ena = current_state == B0 | current_state == B1 | current_state == B2 | current_state == B3;
    assign counting = current_state == Count;
    assign done = current_state == Wait;

endmodule

标准的FSM格式,没啥说的,画出状态转移表写就行了。

The complete timer

该道状态机就是前面几道状态机的组合,大家需注意的是计数那边的部分;之前我将计数子的位宽设置不当,导致这题卡的挺久,希望大家做题分析时注意。

pic6.png

Solution

module top_module (
    input clk,
    input reset,      // Synchronous reset
    input data,
    output [3:0] count,
    output counting,
    output done,
    input ack );

    parameter IDLE = 4'd0, S1 = 4'd1, S2 = 4'd2, S3 = 4'd3;
    parameter C0 = 4'd4, C1 = 4'd5, C2 = 4'd6, C3 = 4'd7;
    parameter Count_1000 = 4'd8, Done = 4'd9;
    
    reg [3:0] current_state, next_state;
    reg [15:0] num;
    reg [3:0] delay;
    reg [3:0] already_count;

    wire count_state;
    assign count_state = (num == (delay + 1'b1)*1000) ? 1'b1 : 1'b0;

    always @(*) begin
        if(num <= 16'd1000) begin
            already_count = 4'd0;
        end
        else if(num > 16'd1000 && num <= 16'd2000) begin
            already_count = 4'd1;
        end
        else if(num > 16'd2000 && num <= 16'd3000) begin
            already_count = 4'd2;
        end
        else if(num > 16'd3000 && num <= 16'd4000) begin
            already_count = 4'd3;
        end
        else if(num > 16'd4000 && num <= 16'd5000) begin
            already_count = 4'd4;
        end
        else if(num > 16'd5000 && num <= 16'd6000) begin
            already_count = 4'd5;
        end
        else if(num > 16'd6000 && num <= 16'd7000) begin
            already_count = 4'd6;
        end
        else if(num > 16'd7000 && num <= 16'd8000) begin
            already_count = 4'd7;
        end
        else if(num > 16'd8000 && num <= 16'd9000) begin
            already_count = 4'd8;
        end
        else if(num > 16'd9000 && num <= 16'd10000) begin
            already_count = 4'd9;
        end
        else if(num > 16'd10000 && num <= 16'd11000) begin
            already_count = 4'd10;
        end
        else if(num > 16'd11000 && num <= 16'd12000) begin
            already_count = 4'd11;
        end
        else if(num > 16'd12000 && num <= 16'd13000) begin
            already_count = 4'd12;
        end
        else if(num > 16'd13000 && num <= 16'd14000) begin
            already_count = 4'd13;
        end
        else if(num > 16'd14000 && num <= 16'd15000) begin
            already_count = 4'd14;
        end
        else begin
            already_count = 4'd15;
        end
    end

    always @(posedge clk) begin
        if(reset) begin
            num <= 16'd0;
        end
        else if(next_state == Done) begin
            num <= 16'd0;
        end
        else if(next_state == Count_1000) begin
            num <= num + 16'd1;
        end
    end

    always @(*) begin
        case(current_state)
            IDLE:       next_state = data ? S1 : IDLE;
            S1:         next_state = data ? S2 : IDLE;
            S2:         next_state = data ? S2 : S3;
            S3:         next_state = data ? C0 : IDLE;
            C0:begin
                        next_state = C1;
                        delay[3] = data;
            end         
            C1:begin
                        next_state = C2;    
                        delay[2] = data;
            end         
            C2:begin
                        next_state = C3;    
                        delay[1] = data;
            end         
            C3:begin
                        next_state = Count_1000;    
                        delay[0] = data;
            end         
            Count_1000: next_state = count_state ? Done : Count_1000;
            Done:       next_state = ack ? IDLE : Done;
            default:    next_state = IDLE;
        endcase
    end

    always @(posedge clk) begin
        if(reset) begin
            current_state <= IDLE;
        end
        else begin
            current_state <= next_state;
        end
    end

    assign count = (current_state == Count_1000) ? (delay - already_count) : 4'd0;
    assign counting = (current_state == Count_1000);
    assign done = current_state == Done;

endmodule

FSM:One-hot logic equations

用one-hot编码的状态写出状态转移代码。

pic7.png

Solution

module top_module(
    input d,
    input done_counting,
    input ack,
    input [9:0] state,    // 10-bit one-hot current state
    output B3_next,
    output S_next,
    output S1_next,
    output Count_next,
    output Wait_next,
    output done,
    output counting,
    output shift_ena
); //

    // You may use these parameters to access state bits using e.g., state[B2] instead of state[6].
    parameter S=0, S1=1, S11=2, S110=3, B0=4, B1=5, B2=6, B3=7, Count=8, Wait=9;

    assign B3_next = state[B2];
    assign S_next = ~d & state[S] | ~d & state[S1] | ~d & state[S110] | ack & state[Wait];
    assign S1_next = d & state[S];
    assign Count_next = state[B3] | ~done_counting & state[Count];
    assign Wait_next = done_counting & state[Count] | ~ack & state[Wait];
    assign done = state[Wait];
    assign counting = state[Count];
    assign shift_ena = state[B0] | state[B1] | state[B2] | state[B3];

endmodule

结语

该小结就算更新结束了,下面剩的也不多了,争取在开学前全部刷完。如有不对的还望大家指正。

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
Verilog中,状态机计数器通常用于跟踪状态的持续时间或执行特定操作的次数。在状态机中,计数器的值可以用来控制状态转换的时机。 然而,在状态机中使用计数器时,有一些常见的问题需要注意。根据引用,在状态标志位跳变后,系统在下一个周期才会进行状态转换。这意味着在下一个周期开始时,系统仍然处于当前状态。如果在这个时候计数器的值已经被清零为0,那么系统可能会再次执行当前周期下的语句,导致计数器的值被重新递增。 引用提到了一种解决这个问题的方法,即将状态完成标志位在状态真正结束之前提前拉高,并在状态结束时再清零计数器。这样处理可以确保系统在我们希望的时刻进行状态跳转,并且不会再次执行当前周期的语句。 另外,引用中提到了一个具体的代码示例,其中在S_WAIT状态下,delay_done和time_count同时被清零。在下一个周期开始时,系统仍然处于S_WAIT状态一个周期,并且time_count的值被清空。因此,在S_WAIT状态的判断下,time_count会自增一次,导致在系统真正跳转到S_SEND状态时,time_count的值已经不是0,而是1,从而导致后续逻辑出现错误。 综上所述,在Verilog状态机中使用计数器时,需要注意状态跳转时机的正确处理,并确保计数器的值不会在不合适的时候被重新递增。这可以通过提前设置状态完成标志位和在状态结束时清零计数器的方式来解决。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [在利用Verilog状态机过程中遇到的一些小问题的总结](https://blog.csdn.net/weixin_44467597/article/details/108878714)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

日拱一卒_未来可期

若复习顺利望有闲钱的同学支持下

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值