LJgibbs · 2020年11月02日

HDLBits:在线学习 Verilog (三十一 · Problem 150-154)

转载自:知乎
作者:ljgibbs

首先附上传送门:

https://hdlbits.01xz.net/wiki/Exams/2013\_q2bfsm​hdlbits.01xz.net

Problem 150 Q2b Another FSM (Exams/2013 q2bfsm)

牛刀小试

想象你这会儿需要开发一个控制电机的状态机。FSM 有两个来自电机的输入信号 x 和 y,产生两个输出信号 f 和 g 控制电机,此外还有时钟信号 clk 以及低电平的复位信号 resetn。

开发电机的同事给了你状态机的需求,并告诉你今晚必须搞定(原文中并没有,由译者脑补^\_^)。状态机电路的工作原理如下:

  • 在复位信号有效的情况下,FSM 处于初始状态。
  • 在复位信号移除后,FSM 在下一个时钟沿输出 f = 1,持续一个时钟周期。
  • 接下来的,FSM 监视输入信号 x,当 x 在连续 3 个时钟周期内输出 3'b101 时,在下一周期将信号 g 置 1。
  • 在信号 g 置 1 期间,FSM 监视输入信号 y ,如果
  • 在接下来 2 个周期内,输入信号 y 跳变为 1'b1,那么 FSM 保持信号 g = 1 (直到复位信号到来)
  • 输入信号 y 在 2 个时钟周期内未跳变为 1'b1,那么 FSM 保持信号 g = 0 (直到复位信号到来)

解答与分析

作为一道状态机题目,我们首先分析需要设置哪些状态:

首先,设置初始状态 IDLE。对于复位信号移除后的事件和操作,设置状态 AFT\_RST,该周期中输出一个周期的高电平 f 信号,注意:此时还未触发任何其他功能。

接下来是对信号 x 的监控,需求可化简为序列检测电路,待检测的序列是 3'b101,设置相应的状态:STRT\_X\_MNT, X\_0, X\_1, X\_10, X\_101。

接下来是对信号 y 的监控,需求可化简为接下来 2 个周期中,y 是否均为低电平。同样可转换为一个检测 2'b00 的序列检测电路。为其设计一个中间状态 Y\_S0 ,表示第一周期 y 为低电平。

最后设计 2 个保持 g 输出的状态:G\_O0,G\_O1。

看起来不困难,根据需求,我们写完代码,跑完 tb,下班(希望如此)!

module top_module (
    input clk,
    input resetn,    // active-low synchronous reset
    input x,
    input y,
    output f,
    output g
); 
    localparam FSM_W  = 10;
    localparam FSM_W1 = FSM_W - 1'b1;

    reg [FSM_W1:0]   state;
    reg [FSM_W1:0]   nxt_state;

    localparam  IDLE      = 0;
    localparam  AFT_RST   = 1;
    localparam  STRT_X_MNT= 2;
    localparam  X_1       = 3;
    localparam  X_0       = 4;
    localparam  X_10      = 5;
    localparam  X_101     = 6;
    localparam  Y_S0      = 7;
    localparam  G_O0      = 8;
    localparam  G_O1      = 9;

    // State transition logic (combinational)
    always @(*) begin
        nxt_state[IDLE   ]          =   1'b0; // never reach for nxt_state
        nxt_state[AFT_RST]          =   (state[IDLE   ]);
        nxt_state[STRT_X_MNT]       =   (state[AFT_RST]);
        nxt_state[X_1    ]          =   (state[STRT_X_MNT] &&  x) || (state[X_1    ] &&  x) || (state[X_0    ] &&  x);
        nxt_state[X_0    ]          =   (state[STRT_X_MNT] && ~x) || (state[X_10   ] && ~x) || (state[X_0    ] && ~x);
        nxt_state[X_10   ]          =   (state[X_1    ] && ~x);
        nxt_state[X_101  ]          =   (state[X_10   ] &&  x);
        nxt_state[Y_S0   ]          =   (state[X_101  ] && ~y);
        nxt_state[G_O0   ]          =   (state[Y_S0   ] && ~y) || state[G_O0   ];
        nxt_state[G_O1   ]          =   (state[Y_S0   ] &&  y) || (state[X_101  ] &&  y) || state[G_O1   ];
    end

    // State flip-flops (sequential)
    always @(posedge clk) begin
        if(~resetn)
            state   <=  'b1; //IDLE
        else begin
            state   <=  nxt_state;
        end  
    end

    //output logic
    assign  f    =   state[AFT_RST];
    assign  g    =   (state[X_101] || state[G_O1] || state[Y_S0]) ? 1'b1 : 1'b0;
endmodule

提示:题目中说

then after the next clock edge the FSM has to set the output f to 1 for one clock cycle. Then, the FSM has to monitor the x input.

对于这里的 THEN ,我们可以理解为,在输出一个周期的高电平 f 信号后,在下个周期才开始监测 X 的值,输出高电平的周期不进行监控。

实不相瞒,笔者做此题时在 X 序列检测上也卡了一会,但当我在纸上画下状态转移图时,神奇的事情发生了:我一下子就发现了自己状态跳转中的错误。所以建议读者们画出状态转移图,即使是并不复杂。

到这里我们就完成了状态机的单元,撒花~


接下来 Building Larger Circuits 这一节中,我们将综合运用此前在计数器、状态机章节中掌握的技能,尝试构建一些规模稍大的数字电路。

Problem 151 Counter with period 1000

牛刀小试

本题需要我们构建一个周期为 1000 的计数器,从 0 计数至 999。设计一个同步复位信号 reset ,复位置起时,计数值清 0.

解答与分析

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

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

endmodule

Problem 152 4-bit shift register and down counter

牛刀小试

接下来的五道题目,每题构建一个小型的电路,最终组装成为一个复杂的计数器电路。

本题设计一个 4bit 位宽的移位寄存器,并具有计数器功能,可向下计数(即计数值递减)。

当移位使能 shift\_ena 信号有效时,数据 data 移入移位寄存器,向高位移动(most-significant-bit),即左移。

当计数使能 count\_ena 信号有效时,寄存器中现有的数值递减,向下计数。

由于系统不会同时使用移位以及计数功能,因此不需要考虑两者使能信号皆有效的情况。

解答与分析

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

    always @(posedge clk ) begin
        if(shift_ena)
            q   <=  {q[2:0],data};
        else if(count_ena) begin
            q   <=  q - 10'd1;
        end  
    end
endmodule

Problem 153 FSM:1101 序列检测器

牛刀小试

本题实现复杂计数器的第二个组件,构建一个能从输入数据流中,识别 1101 序列的有限状态机电路。当检测到相应序列后,将信号 start\_shifting 置 1 ,保持该状态直到被复位为止。

最终状态机会进入一个不会跳转的状态(stuck),这是为了模拟后续状态机的其他状态。其他状态我们将在后面的几项练习中陆续构建,最终构成一个贼大的状态机。

解答与分析

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

    localparam FSM_W  = 6;
    localparam FSM_W1 = FSM_W - 1'b1;

    reg [FSM_W1:0]   state;
    reg [FSM_W1:0]   nxt_state;

    localparam  IDLE        = 0;
    localparam  S_0         = 1;
    localparam  S_1         = 2;
    localparam  S_11        = 3;
    localparam  S_110       = 4;
    localparam  S_1101      = 5;

    // State transition logic (combinational)
    always @(*) begin
        nxt_state[IDLE   ]          =   1'b0; // never reach for nxt_state
        nxt_state[S_0    ]          =   (state[IDLE   ] && ~data) || (state[S_1    ] && ~data) || (state[S_0    ] && ~data) || (state[S_110   ] && ~data);
        nxt_state[S_1    ]          =   (state[IDLE   ] &&  data) || (state[S_0    ] &&  data);
        nxt_state[S_11   ]          =   (state[S_1    ] &&  data) || (state[S_11   ] &&  data);
        nxt_state[S_110  ]          =   (state[S_11   ] && ~data);
        nxt_state[S_1101 ]          =   (state[S_110  ] &&  data) || state[S_1101  ];
    end

    // State flip-flops (sequential)
    always @(posedge clk) begin
        if(reset)
            state   <=  'b1; //IDLE
        else begin
            state   <=  nxt_state;
        end  
    end

    //output logic
    assign  start_shifting    =   state[S_1101];
endmodule

检查一个序列检测状态机是否完备,一个简单的方法是观察所有状态,是否均包括输入分别为 0/1 的情况下的跳转,比如 state[IDLE ] && ~datastate[IDLE ] && data 是否均存在于状态跳转条件中。

Problem 154 FSM:Enable shift register

牛刀小试

本题实现复杂计数器的第三个组件,用于控制我们我们先前构建的移位寄存器模块。整个控制电路在检测到某个序列后,使能移位寄存器 4 个周期。前一题中我们已经完成了序列检测的部分,本题中的状态机则来提供 4 个周期的使能信号。

电路复位信号移除后,将 shift\_ena 信号置 1 ,持续 4 个周期后置 0,直至电路被复位。

解答与分析

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

    localparam FSM_W  = 3;
    localparam FSM_W1 = FSM_W - 1'b1;

    reg [FSM_W1:0]   state;
    reg [FSM_W1:0]   nxt_state;

    localparam  IDLE        = 0;
    localparam  S_1         = 1;
    localparam  S_0         = 2;

    reg [1:0]       asrt_cntr;
    wire            asrt_cntr_add;  
    wire            asrt_cntr_clr;  

    //assert signal cntr
    always @(posedge clk) begin
        if(reset) begin
            asrt_cntr              <= 'b0;
        end else if(asrt_cntr_add)begin
            asrt_cntr              <= asrt_cntr + 1'b1;
        end else if(asrt_cntr_clr)begin
            asrt_cntr              <= 'b0;
        end
    end
    assign                  asrt_cntr_add  = state[S_1   ];
    assign                  asrt_cntr_clr  = 1'b0; // cntr clear itself


    // State transition logic (combinational)
    always @(*) begin
        nxt_state[IDLE   ]          =   1'b0; // never reach for nxt_state
        nxt_state[S_1    ]          =   state[IDLE   ] || (state[S_1] && ~(asrt_cntr == 2'd3));
        nxt_state[S_0    ]          =   (state[S_1] && asrt_cntr == 2'd3) || state[S_0   ];
    end

    // State flip-flops (sequential)
    always @(posedge clk) begin
        if(reset)
            state   <=  'b1; //IDLE
        else begin
            state   <=  nxt_state;
        end  
    end

    //output logic
    assign  shift_ena    =   nxt_state[S_1];
endmodule

注意:题目要求 reset 信号移除后,立即 输出使能信号,因此需要组合逻辑输出。

结语

久违的 HDLBits 中文导学专栏终于更新啦!在接下来一段时间,我们将尽快完成所有题目的解析文章,并将读者们留在评论区中的优秀解法添加到正文中!

推荐阅读

关注此系列,请关注专栏FPGA的逻辑
推荐阅读
关注数
10614
内容数
577
FPGA Logic 二三事
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息