使用Verilog设计FSM并生成状态图

使用Verilo HDL设计FSM并在MacOS下仿真和生成状态图

Verilog课程大作业有一个题要求设计FSM并生成状态图。按要求是使用ModelSIM,但是一方面配置Win7并使用ModelSIM太烦了,另一方面由于设计中为了更好的可读性,没有使用ModelSIM要求的可综合的编程规范(我猜大概是这个原因),所以使用ModelSIM的FSM View并不能生成想要的状态图,于是在MacOS中探索了FSM仿真和状态图的生成。

FSM编写

使用FSM监测0110和1101序列,其中时钟下降沿触发,同步复位。代码如下

module seq_detect(output reg flag, input din, clk, rst_n);
    reg [8:0] state;
    parameter   IDLE = 9'b0_0000_0001,
                A0 = 9'b0_0000_0010, A1 = 9'b0_0000_0100,
                A2 = 9'b0_0000_1000, A3 = 9'b0_0001_0000,
                B0 = 9'b0_0010_0000, B1 = 9'b0_0100_0000,
                B2 = 9'b0_1000_0000, B3 = 9'b1_0000_0000;
    always @(negedge clk) begin
        if (!rst_n ) begin state <= IDLE;   flag <= 1'b0; end
        else begin
            case(state)
                IDLE:   if(din) begin state <= A0;  flag <= 1'b0;   end
                        else    begin state <= B0;  flag <= 1'b0;   end
                  A0:   if(din) begin state <= A1;  flag <= 1'b0;   end
                        else    begin state <= B0;  flag <= 1'b0;   end
                  A1:   if(din) begin state <= A1;  flag <= 1'b0;   end
                        else    begin state <= A2;  flag <= 1'b0;   end
                  A2:   if(din) begin state <= A3;  flag <= 1'b1;   end
                        else    begin state <= B0;  flag <= 1'b0;   end
                  A3:   if(din) begin state <= B2;  flag <= 1'b0;   end
                        else    begin state <= B0;  flag <= 1'b0;   end
                  B0:   if(din) begin state <= B1;  flag <= 1'b0;   end
                        else    begin state <= B0;  flag <= 1'b0;   end
                  B1:   if(din) begin state <= B2;  flag <= 1'b0;   end
                        else    begin state <= B0;  flag <= 1'b0;   end
                  B2:   if(din) begin state <= A1;  flag <= 1'b0;   end
                        else    begin state <= B3;  flag <= 1'b1;   end
                  B3:   if(din) begin state <= A3;  flag <= 1'b1;   end
                        else    begin state <= B0;  flag <= 1'b0;   end
                default:        begin state <= IDLE;flag <= 1'b0;   end
            endcase
        end
    end
endmodule

仿真

在macOS使用icarus-Verilog仿真器和Scansion查看波形。测试台模块如下:

`include "seq_detect.v"
`timescale 10ns/ 1ns
module tb_seq_detect();
    wire p_flag;
    reg p_din, p_clk, p_rst_n;

    reg [35:0] data = 36'b0000_1100_0010_0110_1001_1011_0010_0001_1101;

    seq_detect dec(.flag(p_flag), .clk(p_clk), .din(p_din), .rst_n(p_rst_n));

    initial begin
        p_clk = 1'b1;
        forever #5 p_clk = ~p_clk;
    end

    integer k;
    initial
    begin
        p_rst_n = 1'b0;
        p_din = 1'bx;
        #7 p_rst_n = 1'b1;
        for(k = 0; k < 36; k = k + 1)
        begin
            #10;
            p_din = data[35];
            data = data << 1;
        end
        #20 $finish;
    end

    initial begin
        $monitor($time, " rst = %b, din = %b, flag = %b",
                        p_rst_n, p_din, p_flag);
        $dumpfile("tb_seq_detect.vcd");
        $dumpvars(0, tb_seq_detect);
    end
endmodule

仿真步骤如下,可以得到Monitor输出和vcd波形文件

iverilog tb_seq_detect.v
vvp a.out
open -a Scansion tb_seq_detect.vcd

生成状态图

可以使用Graphviz工具根据dot文件生成状态图。

dot文件的基本格式如下,即定义图的边以及标签。

digraph fsm {
    "IDLE/flag=0"  -> "A0=1/flag = 0" [ label = "0" ]
    "IDLE/flag=0"  -> "B0=0/flag=0" [ label = "0" ]
    "A0=1/flag = 0" -> "A1=11/flag=0" [ label = "0" ]
    "A0=1/flag = 0" -> "B0=0/flag=0" [ label = "0" ]
    "A1=11/flag=0" -> "A1=11/flag=0" [ label = "0" ]
    "A1=11/flag=0" -> "A2=110/flag=0" [ label = "0" ]
    "A2=110/flag=0" -> "A3=1101/flag=1" [ label = "0" ]
    "A2=110/flag=0" -> "B0=0/flag=0" [ label = "0" ]
    "A3=1101/flag=1" -> "B2=011/flag=0" [ label = "0" ]
    "A3=1101/flag=1" -> "B0=0/flag=0" [ label = "0" ]
    "B0=0/flag=0" -> "B1=01/flag=0" [ label = "0" ]
    "B0=0/flag=0" -> "B0=0/flag=0" [ label = "0" ]
    "B1=01/flag=0" -> "B2=011/flag=0" [ label = "0" ]
    "B1=01/flag=0" -> "B0=0/flag=0" [ label = "0" ]
    "B2=011/flag=0" -> "A1=11/flag=0" [ label = "0" ]
    "B2=011/flag=0" -> "B3=0110/flag=1" [ label = "0" ]
    "B3=0110/flag=1" -> "A3=1101/flag=1" [ label = "0" ]
    "B3=0110/flag=1" -> "B0=0/flag=0" [ label = "0" ]
}

使用Graphviz生成文件具体命令如下

dot -Tpng fsm.dot > fsm.png
circo -Tpdf fsm.dot > fsm.pdf

需要注意Graphviz的输出默认输出到stdcout,因此需要重定向写入到文件。其中-T可以指定输出文件的格式。而dot和circo分别为不同的状态图风格,具体的使用方法可以参见man dot。

部分内容如下(详细内容自行查看):

NAME
       dot - filter for drawing directed graphs
       neato - filter for drawing undirected graphs
       twopi - filter for radial layouts of graphs
       circo - filter for circular layout of graphs
       fdp - filter for drawing undirected graphs
       sfdp - filter for drawing large undirected graphs
       patchwork - filter for squarified tree maps
       osage - filter for array-based layouts
       ...

Traditionally, Graphviz supports the following:
       -Tdot (Dot format containing layout information),
       -Txdot (Dot format containing complete layout information),
       -Tps (PostScript),
       -Tpdf (PDF),
       -Tsvg -Tsvgz (Structured Vector Graphics),
       -Tfig (XFIG graphics),
       -Tpng (png bitmap graphics),
       -Tgif (gif bitmap graphics),
       -Tjpg -Tjpeg (jpeg bitmap graphics),
       -Tjson (xdot information encoded in JSON),
       ...
Lei Yang
Lei Yang
PhD candidate

My research interests include visual speech recognition and semantics segmentation.