Warum sollten wir die Eingänge einer sequentiellen Schaltung (Moore-Maschine) nicht an der Taktflanke ändern?

Der hier enthaltene Code nimmt einen Bitstrom aus Binärziffern, das niederwertigste Bit (LSB) zuerst, und gibt das Zweierkomplement des vollständigen Stroms aus, ebenfalls zuerst das LSB. Ein Moore-Zustandsdiagramm ist beigefügt.

Wenn ich jetzt versuche, den Code in einer Testbench zu testen, werden die Zustände nicht wie beabsichtigt aktualisiert.

Das FSM:

FSM

Das Design:

module seqDetector( input in,
                input clk,
                input rst,
                output reg out);
                
//Moore Machine

parameter   SX = 3'd4,
            S0 = 3'd0,
            S1 = 3'd1,
            S2 = 3'd2,
            S3 = 3'd3;
            
reg [2:0] cur_state,next_state;

//next state assignment
always @(posedge clk,negedge rst) begin
    if( rst == 1'b0)
        cur_state <= SX;
    else
        cur_state <= next_state;
end

//next state calculation
always @(cur_state,in) begin
    case(cur_state)
        SX: if(in == 1'b0) next_state = S0; else next_state = S1;           
        S0: if(in == 1'b0) next_state = S0; else next_state = S1;       
        S1: if(in == 1'b0) next_state = S3; else next_state = S2;       
        S2: if(in == 1'b0) next_state = S3; else next_state = S2;       
        S3: if(in == 1'b0) next_state = S3; else next_state = S2;
    endcase
end

//output calculation
always @(cur_state) begin
    case(cur_state)
        SX: out = 1'bx; 
        S0: out = 1'b0;
        S1: out = 1'b1;
        S2: out = 1'b0;
        S3: out = 1'b1;
    endcase
end

endmodule

Die Prüfbank:

`timescale 1ns/1ns
module tb();
initial begin
    $dumpfile("2's.vcd");
    $dumpvars(0,tb);
end

reg clk;
reg in;
reg rst;
wire out;

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

seqDetector s0(in,clk,rst,out);

initial begin
    fork
    #0 rst = 1'b1;
    #10 rst =  1'b0;
    #20 rst = 1'b1;
    #10  in = 1'b0;
    #20  in = 1'b1;
    #30  in = 1'b0;
    #40  in = 1'b1;
    #50  in = 1'b1;
    #60  in = 1'b0;
    #70  in = 1'b0;
    #80  in = 1'b1;
    #90  in = 1'b1;
    #100 in = 1'b1;
    #110 in = 1'bx;
    #120 $finish;
    join
end
endmodule

Das Problem wird in der folgenden Grafik dargestellt:

Der Graph

Aber wenn wir die Testbench so ändern, dass die Eingänge um 1 ns nach der Taktflanke verzögert werden, wird das bestehende Problem gelöst und die Funktionalität erreicht. Aber es gibt ein paar Störungen, deren Ursprung ich nicht herausfinden kann, wie hier gezeigt:

seqDetector s0(in,clk,rst,out);

initial begin
    fork
    #0 rst = 1'b1;
    #10 rst =  1'b0;
    #20 rst = 1'b1;
    #11  in = 1'b0;
    #21  in = 1'b1;
    #31  in = 1'b0;
    #41  in = 1'b1;
    #51  in = 1'b1;
    #61  in = 1'b0;
    #71  in = 1'b0;
    #81  in = 1'b1;
    #91  in = 1'b1;
    #101 in = 1'b1;
    #111 in = 1'bx;
    #120 $finish;
    join
end

Gelöst

Die erste Frage lautet also: Warum gibt es aus der Perspektive des Schreibens eines Verilog-Codes ein Problem, wenn ich die Eingabe am Rand der Uhr ändere?

Und die zweite Frage lautet: Was ist die Ursache für die Störungen in der next_state-Variablen?

Ich denke, es ist offensichtlich, dass sich der next_state ändert, weil sich die Eingaben für die next_state-Berechnung ändern, meinen Sie nicht?
Ja, es gibt einen Hinweis, aber warum folgt der aktuelle Zustand nicht dem nächsten Zustand an der Taktflanke, wie im ersten Zeitdiagramm gezeigt? Verweisen Sie auf den kommentierten Block als //nächste Zustandszuweisung.
Wenn sich 2 Eingänge gleichzeitig ändern, ändert sich einer von ihnen zuerst. Kannst du sagen, was sich zuerst ändert? Nein nicht wirklich. Es könnte also das falsche sein. Wenn Sie tatsächlich diese Schaltung bauen würden, in der sich diese beiden Eingänge gleichzeitig ändern, wäre dies eine Zeitverletzung und Sie könnten sogar Metastabilität erhalten. (Es ist in Ordnung, zwei Eingänge gleichzeitig zu ändern, aber nicht, wenn einer von ihnen ein Taktsignal ist.)

Antworten (1)

Untersuchung

Mit Mentor Questa 2020.1und Cadence Xcelium 20.09wir bekommen keine Störungen.

Mit Synopsys VCS 2020.03und Icarus Verilog 14wir bekommen Störungen.

Ich habe EDA Playground zum Simulieren verwendet, bei Interesse besuchen Sie bitte diesen Link.

Warum bekommst du einen Fehler?

Der Grund , warum Sie Störungen erhalten, sind Rennbedingungen / Renngefahren aufgrund der nicht deterministischen Reihenfolge gleichzeitiger Verilog- Blockierungsanweisungen in der Simulation.

Wie behebst du es?

Hier sind 2 Techniken, um Störungen in der sequentiellen Logiksimulation zu vermeiden:

  1. Wenden Sie den Stimulus etwas nach der aktiven Flanke der Uhr an, indem Sie eine Verzögerung verwenden, wie Sie es in Ihrer Frage getan haben.

  2. Mit nicht blockierenden Zuweisungen können Sie die Eingänge an der aktiven Flanke des Takts ansteuern, ohne dass es zu Störungen kommt.

Diese Testbench verwendet nicht blockierende Zuweisungen und simuliert ohne Störungen auf diesen Simulatoren: Mentor Questa 2020.1, Cadence Xcelium 20.09, Icarus Verilog 14 und Synopsys VCS 2020.03.

module tb();
initial begin
  $dumpfile("dump.vcd");
  $dumpvars(0,tb);
end

reg clk;
reg in;
reg rst;
wire out;

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

seqDetector s0(in,clk,rst,out);

initial begin
    fork
    #0 rst <= 1'b1;
    #10 rst <=  1'b0;
    #20 rst <= 1'b1;
    #10  in <= 1'b0;
    #20  in <= 1'b1;
    #30  in <= 1'b0;
    #40  in <= 1'b1;
    #50  in <= 1'b1;
    #60  in <= 1'b0;
    #70  in <= 1'b0;
    #80  in <= 1'b1;
    #90  in <= 1'b1;
    #100 in <= 1'b1;
    #110 in <= 1'bx;
    #120 $finish;
    join
end
endmodule

Wie vermeidet man Störungen in Zustandsmaschinen-Designs?

Registrieren Sie die Ausgänge . Um mehr zu erfahren, können Sie diese Antwort lesen.