1

当我们在verilog中编写FSM时,有两种编写FSM的方法首先是使用3个总是块(1个用于下一个状态组合逻辑+ 1个用于presene->下一个状态顺序逻辑+ 1个用于输出逻辑),第二种方法是仅使用一个总是阻塞所有 3 种操作,但两种情况下的输出波形不同..为什么会这样?

例如,我以两种方式编写了简单的 fsm,并且我的输出偏移了 20 个时间单位

第一种方式:

//using one alwys block
module moore_20_1(x_in,y_out,clk,reset);
input wire clk,reset,x_in;
output reg y_out;

reg [1:0] state;

parameter start=2'b00,s0=2'b01,s1=2'b10,s2=2'b11;

always @ (posedge clk)//next state logic + ps->ns logic + output logic
begin
  if(reset==1'b1) begin
    state<=start;
    y_out<=0;
  end
else begin
  case(state)
    start: begin if(x_in) state<=s0;
          else state<=s0;
            y_out<=0;
          end
    s0: begin if(x_in) state<=s1;
      else state<=s0;
      y_out<=0;
    end
    s1: begin if(x_in) state<=s2;
      else state<=s1 ;         
      y_out<=0;
    end
    s2: begin if(x_in) state<=s0;
      else state<=s2;
      y_out<=1;
    end
    endcase


end
end
endmodule

第二种方式

    //moore machine using 3 always block(ps->ns+output logic+next-sate logic)
module moore_5_20_2(x_in,y_out,clk,reset);
input wire clk,reset,x_in;
output reg y_out;

reg [1:0] state;
reg [1:0] next_state; 

parameter start=2'b00,s0=2'b01,s1=2'b10,s2=2'b11;

//ps->ns logic
always@ (posedge(clk))
if(reset==1'b1) 
  next_state<= #1 start;
else
  next_state<= #1 state;


//next-stae logic  
always @(next_state,x_in)
case(next_state)
  start: begin if(x_in) state<=s0;
          else state=s0;
          end
    s0: begin if(x_in) state<=s1;
      else state=s0;
    end
    s1: begin if(x_in) state<=s2;
      else state=s1 ;         
    end
    s2: begin if(x_in) state<=s0;
      else state=s2;
    end
    endcase

//OUTPUT LOGIc

always@(next_state)
if (reset==1'b1) y_out<= 0;
else begin 
    case(next_state)
      start:y_out<= 0;
      s0: y_out<=   0;
      s1: y_out<=0;
      s2: y_out<=#1 1;

    endcase
  end
endmodule

为什么输出移动了 20 个时间单位..?

4

1 回答 1

2

我将对你如何测试这两个做一些假设,即你的测试台中有这样的东西:

initial begin 
  clk = 0; 
  forever #10 clk = ~clk; 
end

而且我还假设第二种方式比第一种方式提前 20 个(或者可能是 19 个?)时间单位,基于我认为出错的原因。

基本上,第一种方法是y_out在寄存器中缓冲输出,而第二种方法是y_out组合设置。

由于这是一个摩尔机器(这两种实现都是),输出仅取决于当前状态。在第一种方式中,在时钟沿,y_out被设置为基于“当前”状态的值(即,在时钟沿之前的时刻状态是什么)。y_out虽然状态也可能在时钟边沿发生变化,但在为该时钟周期确定下一个值之后才会发生这种情况。这意味着在单个时钟周期中,y_out将根据前一个周期中的状态进行设置,并保持这种状态直到下一个时钟沿。

在第二种方式中,y_out组合设置而不是时钟沿。在这种情况下,下一个状态逻辑将确定state并且当时钟沿出现时,next_state将变为state(由于某种原因在 1 个时间单位之后......)。因此,现在确定输出的组合块y_out将唤醒并设置y_out为该状态的任何值。因此,y_out将采用当前状态的输出,该时钟周期的状态,而不是前一个时钟周期的状态,如第一种方式。

例如,假设我们处于状态s1并被x_in断言。因此,在第二种方式state中,一切准备就绪,s2为过渡做准备。时钟沿到来。

在第一种方式中,总是块被唤醒。reset没有在case语句中断言。我们进入s1并被x_in断言,因此state将变为s2y_out设置为 0。完成直到下一个时钟沿。所以看起来我们在s2并且y_out为 0,直到下一个时钟沿,此时y_out将变为 1。

第二种方式,状态寄存器总是阻塞唤醒并在1个时间单位后next_state变为。s2一旦next_state变为s2,输出逻辑总是阻塞 fory_out将唤醒,现在将next_state变为1(或在 1 个时间单位后再次变为 1?)。所以看起来我们在并且是 1(大约),大约提前 20 个时间单位。s2y_outs2y_out

注意:其中涉及的大部分混淆(部分如蒂姆提到的)并不是特别好的风格。我更喜欢第二种方式,但你实现它的方式有点不理想。这是一个建议的重写以供将来参考(尽管我也采取了一些简化的自由):

module nice_moore_machine(
  input wire clock, reset, x_in,
  output reg y_out);

reg [1:0] state, next_state;

parameter start = 2'b00, s0 = 2'b01, s1 = 2'b10, s2 = 2'b11;

/* State register (synchronous reset) */
always @(posedge clk) begin
  if (reset) begin
    state <= start;
  end
  else begin
    state <= next_state;
  end
end

/* Next state logic */
always @(*) begin
  case (state)
    start: begin
      next_state = s0;
    end
    s0: begin
      next_state = (x_in) ? s1 : s0;
    end
    s1: begin
      next_state = (x_in) ? s2 : s1;
    end
    s2: begin
      next_state = (x_in) ? s0 : s2;
    end
  endcase
end

/* Output logic */
always @(*) begin
  y_out = (state == s2); /* Note that y_out could be a net and use an assign in this case */
end

endmodule

希望这可以帮助您更好地理解!

于 2014-07-31T00:10:47.623 回答