Warum springt mein Sekundenzähler im Verilog-Werteverhalten?

Ich implementiere einen Sekundenzähler auf dem Altera DE-1 Educational Board, das vom alten Cyclone 2 FPGA angetrieben wird. Mein Plan ist es, einen "Down-Clocker" zu bauen, der den integrierten 50-MHz-Takt nimmt und ein 1-Hz-Taktsignal (als "Impuls" bezeichnet) erzeugt, das dann zum Ansteuern des normalen Zählers verwendet wird. Das Downclocker-Modul ist wie folgt:

module downClockerTest(pulse, clk, reset);
    output reg pulse;
    reg [25:0] count;
    input clk, reset;

    always @(posedge clk or negedge reset) begin
        if(~reset) begin
            count <= 26'h0;
            pulse <= 1'd0;
        end
        else if(count == 26'd49999999)
            count <= 26'd0;
        else begin
            count <= count + 26'h1;
            pulse <= (count > 26'd24999999);
        end
    end
endmodule

Bei folgendem RTL:RTL des Abwärtszählers mit einem Register am Ende

Beachten Sie, dass der Ausgang „Impuls“ registriert wird. Dieses Design funktioniert gut; Der Zähler zählt wie er soll. Wenn ich jedoch versuche, das Register am Ende zu entfernen, indem ich versuche, den „Impuls“-Ausgang durch eine einzelne Zuweisungsanweisung zu steuern, scheint der Sekundenzähler in 1 Sekunde um 1 und dann um 4 zu springen, was 5 Pos-Flanken von anzeigt Downclocker, wo es nur einen geben sollte.

Der leicht modifizierte Down-Clocker sieht wie folgt aus:

module downClockerTest(pulse, clk, reset);
    output pulse;
    reg [25:0] count;
    input clk, reset;

    assign pulse = (count > 26'd24999999);  // The counter seems to increment by 1 and then 4 in quick succession (1 second).

    always @(posedge clk or negedge reset) begin
        if(~reset) begin
            count <= 26'h0;
        end
        else if(count == 26'd49999999)
            count <= 26'd0;
        else begin
            count <= count + 26'h1;
        end
    end
endmodule

Die geänderte RTL ohne das Register ist:RTL Downclocker ohne Anmeldung

Warum funktioniert der Down-Clocker nur richtig, wenn am Ende ein Register steht? Führt das Register eine Art „Entprellung“ durch? Tritt „Prellen“ auch bei nichtmechanischen Schaltkreisen auf? Was könnte aus elektronischer Sicht der mögliche Grund sein?

Es könnte sinnvoll sein, hinzuzufügen, dass die TimeQuest-Timing-Analyse in beiden Fällen mit der kritischen Warnung fehlschlägt: „Timing-Anforderungen sind nicht erfüllt“. Aber trotzdem funktioniert das eine, wo das andere nicht funktioniert.

Ich vermute, dass dieses bizarre Verhalten mit einer Race-Condition zu tun hat, die verursacht wird, wenn eine Folge von Bits ihre Einsen und Nullen nach einem Inkrement drastisch ändert. Zum Beispiel wird 110111 nach Increment-by-1 zu 111000. Da nicht abzusehen ist, welches der Flip-Flops – das die einzelnen Bitpositionen in der Sequenz speichert – seine Werte zuerst aktualisiert, kann die Zahl (sprich: Bitsequenz) für einen Moment schwanken, bevor sie einen stabilen Wert erreicht. Es kann Zahlen (Bitfolgen) geben, die, während sie zu ihren stabilen Werten tendieren, durch den verglichenen Wert (in unserem Fall 24999999) schwanken und bewirken, dass der Komparator logisch HI ausgibt. Ein Register am Ende würde dieses Problem sicherlich lösen. All dies sind jedoch Vermutungen, die auf wenig Experimentieren basieren. Jede gelehrte Meinung wäre willkommen.

Antworten (3)

Wenn Sie die asynchrone Zuweisung für den Impuls verwenden, sehen Sie den Einfluss ungleicher Verzögerungen im Komparator als Glitches im Ausgang. Das Registrieren des Vergleichssignals verbirgt dies, sodass sich alles vor der nächsten Taktflanke beruhigen kann. (Dies wird durch das Place-Route-Tool garantiert, wenn es sagt, dass der Pfad dem Timing entspricht.)

Wenn Sie irgendwann einen Grund haben, eine Reihe von Zählerausgängen asynchron zu decodieren, und Sie möchten, dass sie störungsfrei sind , gibt es eine Antwort: Verwenden Sie Gray-Code-Zählung . Gray-Codes garantieren, dass jeweils nur ein Signal den Zustand ändert, wodurch die Race-Bedingung mit mehreren Pfaden vermieden wird, die zu Störungen führt.

Ja, es gibt „Bouncing“ – aber in diesem Zusammenhang nennen wir sie „Glitches“. Der Komparator (count > 26'd24999999)stellt eine ziemlich große Menge kombinatorischer Logik dar, und es besteht keine Chance, dass alle Pfade durch diese Logik (und die zugehörige FPGA-Verbindung) genau dieselbe Verzögerung aufweisen. Daher pulsewird die Ausgabe einen oder mehrere Störimpulse erfahren, bevor sie sich beruhigt.

Wenn Sie direkt als Takt für andere Logik verwenden pulse, erfährt diese Logik zusätzliche Taktflanken.

Komparatoren sind eine relativ komplexe Logik mit langen Übertragsketten.

Ich würde sowas empfehlen:

module downClockerTest(pulse, clk, reset);
    output reg pulse;
    reg [24:0] count;
    input clk, reset;

    always @(posedge clk or negedge reset) begin
        if(~reset) begin
            count <= 25'h0;
            pulse <= 1'd0;
        end
        else if(count == 25'd24999999) begin
            count <= 25'd0;
            pulse <= ~pulse;
        end
        else begin
            count <= count + 1;
        end
    end
endmodule

Anstatt bis 50.000.000 zu zählen und einen Komparator zu verwenden, um den pulseWert einzustellen, zähle ich einfach bis 25.000.000 und schalte um, pulsewenn ich die volle Zählung erreiche.

Dies ist sicherlich eine bessere (und meine allererste) Lösung. Die Absicht meiner Frage ist jedoch, herauszufinden, „warum“ das Problem auftritt, und nicht, was die bessere Lösung ist.
@Kraken, ich denke, Dave hat diesen Teil der Frage sehr gut beantwortet.