CPLD erhöht (manchmal) den Zähler nicht

Ich habe dieses einfache Programm auf dem Altera EPM240 ausgeführt, das manchmal die Zählererhöhung nicht durchführt.

reg trigger_state;
reg [7:0] trigger_count;

always @ (posedge clk)
begin
    if (trigger != trigger_state) begin
      trigger_state <= !trigger_state;
      trigger_count <= trigger_count + 1;
    end
end

clkläuft mit 50 MHz und das Triggersignal im Bild mit 1-2 kHz.

Geben Sie hier die Bildbeschreibung ein

Auf dem Bild ( trigger=6, trigger_state=4, trigger_count[1]=1, trigger_count[0]=2)

Wie Sie sehen, trigger_statefolgt immer das Eingangssignal ( trigger), aber der Zähler ( 2) wird manchmal nicht inkrementiert.

Was könnte ein solches Verhalten erklären?

Woher kommt "Trigger"? Kommt es von einem externen Signal oder Schalter? Wenn ja, hast du es synchronisiert?

Antworten (3)

Erstens:
Ihr Trigger kommt asynchron zur Uhr. Sie müssen es zuerst synchronisieren, bevor Sie es sicher verwenden können. Der Code dafür lautet:

reg sync_trigger,safe_trigger;
always @(posedge clk)
begin
    sync_trigger <= trigger;
    safe_trigger <= sync_trigger;
end

Das Verhalten, das Sie sehen, liegt daran, dass die Hardware eher so aussieht:

if (trigger != trigger_state) 
  trigger_state <= !trigger_state;

if (trigger != trigger_state) 
  trigger_count <= trigger_count + 1;

Da der Trigger asynchron ist, können Sie diesen haben, wenn er wahr und der andere falsch ist. So wird der eine gespielt und der andere nicht. Tatsächlich ist es für jedes BIT Ihres Zählers implementiert, sodass einige Bits inkrementieren können und andere nicht.

Beitragsgedanke:
Ihr Trigger muss langsamer sein als Ihre Uhr, sonst verpassen Sie Trigger!

Was ist mit dem Speichern des externen Signals in einem SR-Latch, um zu verhindern, dass der Trigger verpasst wird?

Ist der triggerEingang vollständig asynchron zum 50-MHz-Takt? Wenn dies der Fall ist, verstößt es wahrscheinlich von Zeit zu Zeit gegen die Setup-and-Hold-Zeitanforderungen. Sie müssen es über mindestens zwei FFs mit der Uhr synchronisieren, bevor Sie es bei der Entscheidungsfindung verwenden.

Ich kann Sie sagen hören: „Aber es wird nur in einer ifAnweisung verwendet. Wenn trigger_stateaktualisiert wird, garantiert das nicht, dass trigger_countauch aktualisiert wird?“

Die Antwort ist nein."

trigger_stateund trigger_countrepräsentieren insgesamt neun separate FFs, und jeder von ihnen hat seine eigene kombinatorische Logik, um zu bestimmen, was sein nächster Zustand sein wird. Jeder nimmt triggerals Input, und jeder trifft eine unabhängige Entscheidung darüber, was der Wert von triggerist. Wenn Sie gegen die Einrichtungs-/Haltezeitanforderungen verstoßen, können Sie nicht länger garantieren, dass diese unabhängigen Entscheidungen konsistent sind, was zu dem von Ihnen beobachteten Verhalten führt.

Beachten Sie, dass dies nichts mit Metastabilität zu tun hat, obwohl das Synchronisieren asynchroner Eingaben dieses Problem ebenfalls mildert.

Das Problem ist höchstwahrscheinlich die Metastabilität, die durch die Verwendung eines asynchronen Signals (Ihre Eingabe trigger) zur Steuerung Ihres Zählers verursacht wird.

Wenn Sie ein beliebiges Signal einspeisen, müssen Sie sicherstellen, dass es mithilfe einer Mehrregister-Synchronizer-Kette mit dem Taktsignal synchronisiert ist. Etwas wie:

reg [1:0] triggerSync;
always @ (posedge clk) begin
    triggerSync <= {triggerSync[0],trigger}
end

Verwenden Sie dann triggerSync[1]in Ihrem Code. Das erste Register triggerSync[0]synchronisiert das Signal, kann aber metastabil werden, wenn sich der Eingang ändert, wenn sich die Uhr ändert (Setup/Hold-Verletzung). Das zweite Register triggerSync[1]fängt den metastabilen Zustand ab und verhindert, dass er sich durch den Rest Ihrer Logik ausbreitet und seltsame Störungen verursacht.

Ihre Logik selbst scheint auch seltsam. Ich würde vorschlagen, es in Ihre Kantendetektorlogik und Ihren Zähler aufzuteilen:

reg triggerDly;
wire triggerEdge;
always @ (posedge clk) begin
    triggerDly <= triggerSync[1];
end
assign triggerEdge = (triggerDly != triggerSync[1]);

always @ (posedge clk) begin
    if (triggerEdge) begin
        trigger_count <= trigger_count + 1;
    end
end

Wenn Sie alles aufteilen, ist der Code leichter zu verfolgen und zu debuggen. Sie können die Flankenerkennungslogik auch einfach so ändern, dass sie eine steigende Flanke, eine fallende Flanke oder beides ist. Sie können den Kantendetektor genauso gut zu einem Submodul machen, da es sich um ein nützliches Stück Code handelt, das wiederverwendet werden kann.

Es ist im Grunde unmöglich, Metastabilitätsereignisse bei 50 MHz auf moderner Technologie zu beobachten, es sei denn, Sie haben ein überlastetes Design ohne Durchhang. Wie Dave erläutert, handelt es sich wahrscheinlich um ein Kohärenzproblem. Die Lösung ist die gleiche (FFs synchronisieren), aber das Problem ist anders.