verilog到FSM转换

verilog到FSM转换,verilog,fsm,system-verilog,Verilog,Fsm,System Verilog,我有一个用Verilog写的程序,我想把它自动转换成FSM。这是可能的(只是为了形象化) 代码如下: module pci(reset,clk,frame,irdy,trdy,devsel,idsel,ad,cbe,par,stop,inta,led_out); input reset; input clk; input frame; input irdy; output trdy; output devsel; input idsel;

我有一个用Verilog写的程序,我想把它自动转换成FSM。这是可能的(只是为了形象化)

代码如下:

module pci(reset,clk,frame,irdy,trdy,devsel,idsel,ad,cbe,par,stop,inta,led_out);
    input reset;
    input clk;
    input frame;
    input irdy;
    output trdy;
    output devsel;
    input idsel;
    inout [31:0] ad;
    input [3:0] cbe;
    inout par;
    output stop;
    output inta;
    output [3:0] led_out;

parameter DEVICE_ID = 16'h9500;
parameter VENDOR_ID = 16'h106d;     // Sequent!
parameter DEVICE_CLASS = 24'hFF0000;    // Misc
parameter DEVICE_REV = 8'h01;
parameter SUBSYSTEM_ID = 16'h0001;  // Card identifier
parameter SUBSYSTEM_VENDOR_ID = 16'hBEBE; // Card identifier
parameter DEVSEL_TIMING = 2'b00;    // Fast!

reg [2:0] state;
reg [31:0] data;

reg [1:0] enable;
parameter EN_NONE = 0;
parameter EN_RD = 1;
parameter EN_WR = 2;
parameter EN_TR = 3;

reg memen; // respond to baseaddr?
reg [7:0] baseaddr;
reg [5:0] address;

parameter ST_IDLE = 3'b000;
parameter ST_BUSY = 3'b010;
parameter ST_MEMREAD = 3'b100;
parameter ST_MEMWRITE = 3'b101;
parameter ST_CFGREAD = 3'b110;
parameter ST_CFGWRITE = 3'b111;

parameter MEMREAD = 4'b0110;
parameter MEMWRITE = 4'b0111;
parameter CFGREAD = 4'b1010;
parameter CFGWRITE = 4'b1011;

`define LED
`ifdef LED
reg [3:0] led;
`endif

`undef STATE_DEBUG_LED
`ifdef STATE_DEBUG_LED
assign led_out = ~state;
`else
`ifdef LED
assign led_out = ~led;  // board is wired for active low LEDs
`endif
`endif

assign ad = (enable == EN_RD) ? data : 32'bZ;
assign trdy = (enable == EN_NONE) ? 'bZ : (enable == EN_TR ? 1 : 0);
assign par = (enable == EN_RD) ? 0 : 'bZ;
reg devsel;

assign stop = 1'bZ;
assign inta = 1'bZ;

wire cfg_hit = ((cbe == CFGREAD || cbe == CFGWRITE) && idsel && ad[1:0] == 2'b00);
wire addr_hit = ((cbe == MEMREAD || cbe == MEMWRITE) && memen && ad[31:12] == {12'b0, baseaddr});
wire hit = cfg_hit | addr_hit;

always @(posedge clk)
begin
    if (~reset) begin
        state <= ST_IDLE;
        enable <= EN_NONE;
        baseaddr <= 0;
        devsel <= 'bZ;
        memen <= 0;
`ifdef LED
        led <= 0;
`endif
    end
    else    begin

    case (state)
        ST_IDLE: begin
            enable <= EN_NONE;
            devsel <= 'bZ;
            if (~frame) begin
                address <= ad[7:2];
                if (hit) begin
                    state <= {1'b1, cbe[3], cbe[0]};
                    devsel <= 0;
                    // pipeline the write enable
                    if (cbe[0])
                        enable <= EN_WR;
                end
                else begin
                    state <= ST_BUSY;
                    enable <= EN_NONE;
                end
            end
        end

        ST_BUSY: begin
            devsel <= 'bZ;
            enable <= EN_NONE;
            if (frame)
                state <= ST_IDLE;
        end

        ST_CFGREAD: begin
            enable <= EN_RD;
            if (~irdy || trdy) begin
                case (address)
                    0: data <= { DEVICE_ID, VENDOR_ID };
                    1: data <= { 5'b0, DEVSEL_TIMING, 9'b0,  14'b0, memen, 1'b0};
                    2: data <= { DEVICE_CLASS, DEVICE_REV };
                    4: data <= { 12'b0, baseaddr, 8'b0, 4'b0010 }; // baseaddr + request mem < 1Mbyte
                    11: data <= {SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID };
                    16: data <= { 24'b0, baseaddr };
                    default: data <= 'h00000000;
                endcase
                address <= address + 1;
            end
            if (frame && ~irdy && ~trdy) begin
                devsel <= 1;
                state <= ST_IDLE;
                enable <= EN_TR;
            end
        end

        ST_CFGWRITE: begin
            enable <= EN_WR;
            if (~irdy) begin
                case (address)
                    4: baseaddr <= ad[19:12];  // XXX examine cbe
                    1: memen <= ad[1];
                    default: ;
                endcase
                address <= address + 1;
                if (frame) begin
                    devsel <= 1;
                    state <= ST_IDLE;
                    enable <= EN_TR;
                end
            end
        end

        ST_MEMREAD: begin
            enable <= EN_RD;
            if (~irdy || trdy) begin
                case (address)
`ifdef LED
                    0: data <= { 28'b0, led };
`endif
                    default: data <= 'h00000000;
                endcase
                address <= address + 1;
            end
            if (frame && ~irdy && ~trdy) begin
                devsel <= 1;
                state <= ST_IDLE;
                enable <= EN_TR;
            end
        end

        ST_MEMWRITE: begin
            enable <= EN_WR;
            if (~irdy) begin
                case (address)
`ifdef LED
                    0: led <= ad[3:0];
`endif
                    default: ;
                endcase
                address <= address + 1;
                if (frame) begin
                    devsel <= 1;
                    state <= ST_IDLE;
                    enable <= EN_TR;
                end
            end
        end

    endcase
    end
end
endmodule
<代码>模块PCI(复位、CLK、帧、IrDy、TrDy、DEVEL、IDSEL、AD、CBE、PAR、STEP、ITA、LeDIOUT); 输入复位; 输入时钟; 输入帧; 输入irdy; 输出trdy; 输出devsel; 输入idsel; 伊努特[31:0]公元; 输入[3:0]cbe; 不合格; 输出停止; 输出inta; 输出[3:0]发光二极管输出; 参数装置ID=16'h9500; 参数VENDOR_ID=16'h106d;//接着! 参数设备_类=24'hFF0000;//杂项 参数装置_REV=8'h01; 参数子系统_ID=16'h0001;//卡片标识符 参数子系统供应商ID=16'hBEBE;//卡片标识符 参数DEVSEL_计时=2'b00;//快速的 reg[2:0]状态; reg[31:0]数据; reg[1:0]启用; 参数EN_NONE=0; 参数EN_RD=1; 参数EN_WR=2; 参数EN_TR=3; 注册会员;//回应baseaddr? reg[7:0]baseaddr; 注册[5:0]地址; 参数ST_IDLE=3'b000; 参数ST_BUSY=3'b010; 参数ST_MEMREAD=3'b100; 参数ST_MEMWRITE=3'b101; 参数ST_CFGREAD=3'b110; 参数ST_CFGWRITE=3'b111; 参数MEMREAD=4'b0110; 参数MEMWRITE=4'b0111; 参数CFGREAD=4'b1010; 参数CFGWRITE=4'b1011; `定义LED `ifdef发光二极管 reg[3:0]发光二极管; `恩迪夫 `未定义状态\u调试\u指示灯 `ifdef状态调试指示灯 分配led_out=~状态; `否则 `ifdef发光二极管 分配led_out=~led;//电路板接线用于有源低电平LED `恩迪夫 `恩迪夫 分配ad=(启用==EN\u RD)?数据:32'bZ; 分配trdy=(启用==无)?'bZ:(使能==EN_TR?1:0); 分配PAR =(启用= = EnRyd)?0:bZ; reg devsel; 指定停止=1'bZ; 分配inta=1'bZ; 导线cfg|u hit=((cbe==CFGREAD | | cbe==CFGWRITE)和&idsel&ad[1:0]==2'b00); wire addr|u hit=((cbe==MEMREAD | | cbe==MEMWRITE)&&memen&&ad[31:12]==12'b0,baseaddr}); 线击=cfg|u hit | addr|u hit; 始终@(posedge clk) 开始 如果(~reset)开始
状态更好、更昂贵的模拟器可以检测代码中的FSM并使其可视化。例如,Modelsim SE版本。这些可以很好地理解代码并检查Covage。
但是,制作自己的6态FSM并不难。

更好、更昂贵的模拟器可以检测代码中的FSM,并将其可视化。例如,Modelsim SE版本。这些可以很好地理解代码并检查Covage。
但是,自己绘制一个6态FSM并不难。

检查是否可以的方法是编写一个模拟,并检查行为是否符合您的要求。拿出一张气泡图,看看它是否与手绘图相符是没有意义的,因为你无法知道手绘图是否正确……

检查是否正确的方法是编写一个模拟,并检查行为是否符合你的要求。拿出一个气泡图并查看它是否与手绘图匹配是没有意义的,因为您无法知道手绘图是否正确……

有时编写代码并由此生成文档更容易。有时您在没有文档的情况下继承遗留代码,在这些情况下,尤其是对于语言新手来说,帮助可视化正在发生的事情的工具可能非常有用

使用cadence工具,您可以使用“代码覆盖率”运行代码,然后imc可以加载覆盖率数据并运行FSM分析

我在下面包含了一个简单的FSM,并显示了生成的状态图

module simple_fsm(); 
  //Inputs to FSM
  logic clk;
  logic rst_n;

  logic [1:0] state    ;
  logic [1:0] nextstate;
  logic       turn_on  ;
  logic       turn_off ;

  localparam  S_OFF    = 2'b00;
  localparam  S_GO_ON  = 2'b01;
  localparam  S_ON     = 2'b10;
  localparam  S_GO_OFF = 2'b11;

  // State FlipFlop
  always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
      state <= 2'b0;
    end
    else begin
      state <= nextstate;
    end
  end

  //Nextstate Logic
  always @* begin
    case (state)
      2'd0 : if (turn_on) begin
        nextstate = S_GO_ON;
      end
      2'd1 : nextstate = S_ON;
      2'd2 : if (turn_off) begin
        nextstate = S_GO_OFF ;
      end
      2'd3 : nextstate = S_OFF;
    endcase
  end    

//TB clk
initial begin
 #1ns;
 clk = 0;
 forever begin
  #20ns;
  clk = ~clk;
 end
end

//The Test
initial begin
  rst_n    = 1'b0;
  turn_on  = 1'b0;
  turn_off = 1'b0;
  @(posedge clk);
  @(posedge clk);
  rst_n = 1'b1 ;

  @(posedge clk);
  turn_on = 1'b1;
  @(posedge clk);
  turn_on = 1'b0;

  @(posedge clk);
  @(posedge clk);
  #100ms;

  $finish();
end
endmodule
在imc中加载cov_工作(通过上述模拟创建的文件夹),选择simple_fsm并选择fsm Analysis

imc也有助于可视化您的测试覆盖率。未命中的弧和状态显示为红色

我们已经看到,有一些工具可以可视化FSM,问题的另一部分是;专用FSM的语法是否适用于这些工具

@vermaete报告说Modelsim SE无法看到FSM。从imc我得到:


它似乎没有覆盖代码的复杂性,并且显示为只有两个可到达的状态,空闲和忙碌。如果OP正在使用工具进行可视化,我建议您采用更简单的(语法)FSM结构,以便工具能够更好地解析它。

有时编写代码和生成文档会更容易。有时您在没有文档的情况下继承遗留代码,在这些情况下,尤其是对于语言新手来说,帮助可视化正在发生的事情的工具可能非常有用

case(segmentRead)
            //-------------------
            SEGMENT0: begin
                READ_Ready_EEPROM <= 1'b0;
                READ_RDSR_Enable <= 1'b0;
                Read_Enable <= 1'b0;
                READ_RDSR_DATA_REG <= 8'b0;
//              READ_DATA_REG <= 8'b0;
            end 
            //-------------------
            SEGMENT2: begin          
                READ_RDSR_Enable <= 1'b1;
                READ_RDSR_DATA_REG <= 8'b0;
            end
//          //-------------------
            SEGMENT3: begin          
                READ_RDSR_Enable <= 1'b0;   
                READ_RDSR_DATA_REG <= RDSR_Data;
            end
            //-------------------
            SEGMENT4: begin
                Read_Enable <= 1'b1;
            end
            //-------------------
            SEGMENT5: begin
                Read_Enable <= 1'b0;
                READ_DATA_REG <= Read_Data;
            end
            //-------------------
            SEGMENT6: begin
                READ_Ready_EEPROM <= 1'b1;
            end
            //-------------------
        endcase
使用cadence工具,您可以使用“代码覆盖率”运行代码,然后imc可以加载覆盖率数据并运行FSM分析

我在下面包含了一个简单的FSM,并显示了生成的状态图

module simple_fsm(); 
  //Inputs to FSM
  logic clk;
  logic rst_n;

  logic [1:0] state    ;
  logic [1:0] nextstate;
  logic       turn_on  ;
  logic       turn_off ;

  localparam  S_OFF    = 2'b00;
  localparam  S_GO_ON  = 2'b01;
  localparam  S_ON     = 2'b10;
  localparam  S_GO_OFF = 2'b11;

  // State FlipFlop
  always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
      state <= 2'b0;
    end
    else begin
      state <= nextstate;
    end
  end

  //Nextstate Logic
  always @* begin
    case (state)
      2'd0 : if (turn_on) begin
        nextstate = S_GO_ON;
      end
      2'd1 : nextstate = S_ON;
      2'd2 : if (turn_off) begin
        nextstate = S_GO_OFF ;
      end
      2'd3 : nextstate = S_OFF;
    endcase
  end    

//TB clk
initial begin
 #1ns;
 clk = 0;
 forever begin
  #20ns;
  clk = ~clk;
 end
end

//The Test
initial begin
  rst_n    = 1'b0;
  turn_on  = 1'b0;
  turn_off = 1'b0;
  @(posedge clk);
  @(posedge clk);
  rst_n = 1'b1 ;

  @(posedge clk);
  turn_on = 1'b1;
  @(posedge clk);
  turn_on = 1'b0;

  @(posedge clk);
  @(posedge clk);
  #100ms;

  $finish();
end
endmodule
在imc中加载cov_工作(通过上述模拟创建的文件夹),选择simple_fsm并选择fsm Analysis

imc也有助于可视化您的测试覆盖率。未命中的弧和状态显示为红色

我们已经看到,有一些工具可以可视化FSM,问题的另一部分是;专用FSM的语法是否适用于这些工具

@vermaete报告说Modelsim SE无法看到FSM。从imc我得到:

它似乎没有覆盖代码的复杂性,并且显示为只有两个可到达的状态,空闲和忙碌。如果OP正在使用工具进行可视化,我建议采用更简单的(语法)FSM结构,以便工具能够更好地解析它。

case(segmentRead)
case(segmentRead)
            //-------------------
            SEGMENT0: begin
                READ_Ready_EEPROM <= 1'b0;
                READ_RDSR_Enable <= 1'b0;
                Read_Enable <= 1'b0;
                READ_RDSR_DATA_REG <= 8'b0;
//              READ_DATA_REG <= 8'b0;
            end 
            //-------------------
            SEGMENT2: begin          
                READ_RDSR_Enable <= 1'b1;
                READ_RDSR_DATA_REG <= 8'b0;
            end
//          //-------------------
            SEGMENT3: begin          
                READ_RDSR_Enable <= 1'b0;   
                READ_RDSR_DATA_REG <= RDSR_Data;
            end
            //-------------------
            SEGMENT4: begin
                Read_Enable <= 1'b1;
            end
            //-------------------
            SEGMENT5: begin
                Read_Enable <= 1'b0;
                READ_DATA_REG <= Read_Data;
            end
            //-------------------
            SEGMENT6: begin
                READ_Ready_EEPROM <= 1'b1;
            end
            //-------------------
        endcase
//------------------- 第0部分:开始 READ_Ready_EEPROM
case(分段读取)
//-------------------
第0部分:开始

READ_Ready_EEPROM我添加了一个手工制作的fsm,但无法测试是否正常。您使用哪个模拟器?做