verilog实现状态机

  • 背景
  • 问题:计数状态机的实现
  • Verilog实现此状态机
  • 1. 代码如下
  • 2. 代码的说明
  • 3. 代码的仿真


背景

在学习spinalHDL的时候对他的状态机的实现有很多不懂的地方,特地使用了verilog编写了一个相同的状态机来学习其中的时序。

问题:计数状态机的实现

状态转移图如下图所示

状态机设计与实现java 状态机编写_verilog

  1. stateA:直接跳转到stateB,同时赋值cnt = 2
  2. stateB:判断cnt的值是否为6,若是则跳转到stateC,没有则继续执行stateB,同时cnt <= cnt + 1’b1
  3. stateC:什么也不做直接跳转到stateA

Verilog实现此状态机

1. 代码如下

这里的非阻塞赋值后加了 #1,方便后续的仿真

`timescale 1ns/1ns
module test(
  input clk,
  input rst,
  output reg signal_out
);
parameter stateA = 2'b01, stateB = 2'b10, stateC = 2'b11;
reg [1:0] state;
reg [2:0] cnt;
always @(posedge clk or posedge rst) begin
  if(rst) begin
    state <= #1 stateA;
    cnt <= #1 'h0;
  end else begin
    case(state)
      stateA: begin
        state <= #1 stateB;
        cnt <= #1 'h2;
      end
      stateB: begin
        cnt <= #1 cnt + 1'b1;
        if(cnt == 'h6) begin
          state <= #1 stateC;
        end else begin
          state <= #1 stateB;
        end
      end
      stateC: state <= #1 stateA;
      default: begin
        state <= #1 stateA;
        cnt <= #1 'h0;
      end
    endcase
  end
end

always @(*) begin
  if(state == stateB && cnt == 'h6) begin
    signal_out = 'h1;
  end else begin
    signal_out = 'h0;
  end
end
endmodule

2. 代码的说明

对上面的代码进行一个解释说明:
首先状态机有三个状态分别是stataA, stateB, stateC。

  1. 代码13、14行对信号进行复位操作此时state的状态为stateA,cnt值为0。
  2. 复位操作完成之后在时钟的上升沿进行代码16行的状态判断,由于之前的复位状态state为stateA,所以执行分支stateA里的代码块即18,19行代码,将状态转移到stateB, cnt赋值为2,如图所示。==需要注意的是虽然这里的cnt <= 2是在stateA里面的但是由于非阻塞赋值的并行性,state <= stateB语句和cnt <= 2语句都在同一个时钟上升沿作用,所以在图中可以看到状态为stateB时cnt的值才为2。
  3. 状态机设计与实现java 状态机编写_嵌入式硬件_02

  4. 在下一个时钟上升沿的时候判断执行stateB分支中的代码块,22行代码执行cnt加1操作, 同时判断cnt是否等于6,注意这里判断cnt是否等于6中的cnt其实是还没有加1之前的cnt。如果cnt的值不等于6则继续执行stateB中的代码,直到cnt等于6。从图中我们可以看出当在时钟上升沿的时候判断出了cnt的值等于6了,那为什么在stateC状态下cnt还可以继续加1呢,这是因为cnt和state的赋值是并行的,当state从stateB状态跳转到stateC时,cnt的赋值也是同步进行的即cnt <= cnt + 1也是要执行的,所以才有了cnt = 7
  5. 状态机设计与实现java 状态机编写_状态机_03

  6. 状态跳转到了stateC时, 其中的代码块直接跳转到了stateA, 此时cnt的值保持不变还为7。
  7. 状态机设计与实现java 状态机编写_状态机设计与实现java_04

  8. 图二、

3. 代码的仿真

testbench如下所示

`timescale 1ns/1ns
`define clock_period 20
module test_tb;

reg clk       ;
reg reset      ;
wire io_signal_out;

test u_test(
    .clk       ( clk       ),
    .rst       ( reset       ),
    .signal_out  ( io_signal_out  )
);
initial begin
    clk = 1;
    forever begin
        #(`clock_period/2) clk = ~clk;
    end
end

initial begin
    reset = 1;
        #(`clock_period*10-1);
    reset = 0;
    #(`clock_period*30);
    $stop;
end
endmodule

仿真波形如下图所示

状态机设计与实现java 状态机编写_状态机设计与实现java_05