Fehler in diesem Countdown-Modul? (Verilog)

Mein Professor hat sich diesen Code gut 10 Minuten angeschaut, konnte das Problem aber nicht finden. Also hoffe ich, dass ein frisches Paar Augen etwas sehen wird, das wir beide verpasst haben. Für Hinweise bin ich wie immer dankbar.

Kontext: Ein Modul, das von FFF bis 000 zählt und sich dann wiederholt.

Problem: Nur die niedrigste 2 zeigt ein Dekrement an, überspringt mehrere Zahlen gleichzeitig und springt dann an einem scheinbar zufälligen Punkt zurück zu FF.

Annahmen: 1. Der SevenSegment-Treiber funktioniert ordnungsgemäß. Es wurde manuell überprüft und zeigt die ihm zugewiesene Hex-Zahl an. 2. Das ClockDivider-Modul funktioniert wie vorgesehen. Der Countdown verringert sich alle 1 Sekunde, wie es sollte (CLOCK_50 = 50 MHz)

Möglicher Hinweis: Verilog warnt, dass „cd“ ein abgeleiteter Latch ist und seinen Wert über einen oder mehrere Pfade im „always“-Block behält

module ClockDivider( input CLOCK_50, output reg[ 31:0] count );
parameter clockDivisor = 50_000_000;

always @( posedge CLOCK_50 )
    if ( count == 0)
        count <= clockDivisor;
        else
            count <= count - 1;
endmodule


module Test (

    //////////// CLOCK //////////
    input                       CLOCK_50,
    //////////// SEG7 //////////
    output           [6:0]      HEX0,
    output           [6:0]      HEX1,
    output           [6:0]      HEX2
);

//ClockDivider Output
wire                [31:0]          cout;

reg                 [11:0]            cd;

ClockDivider a( CLOCK_50, cout);

always @(cout)
begin

if (cout == 32'h0)
        if (cd == 12'h0)
                cd <= 12'hFFF;
        else
                cd <= (cd - 12'h001);

end

SevenSegment    C2( cd[11:8],  1'b0, HEX2 ); 
SevenSegment    C1( cd[7:4],   1'b0, HEX1 );
SevenSegment    C0( cd[3:0],   1'b0, HEX0 );

endmodule
Wie ich es sehe, besteht das grundlegende Problem darin, dass Sie coutin der Empfindlichkeitsliste stehen, aber es erscheint nie rechts in einer der darunter liegenden Aufgaben. coutAlso was kann das Tool daraus schließen, außer dass man es als Taktsignal für Block verwenden möchte .
@ThePhoton Danke. Das Programm verwendete also das niedrigstwertige Bit von cout als Takteingang. Schön, dass du das erwischt hast.

Antworten (2)

Persönlich hatte ich Probleme mit immer @(cout) , das flockig sein kann.

Ich würde immer immer @(posedge CLOCK_50) verwenden . Meiner Meinung nach sind auf diese Weise korrekte Ergebnisse garantiert ...

BEARBEITEN: Klarerer Code für den obigen Vorschlag hinzugefügt

always @(cout) -> always @(posedge CLOCK_50)

cout sollte sich theoretisch nur mit der positiven Flanke der Uhr (oder des Codes) ändern, aber was tatsächlich passiert, ist vielleicht anders. Ihr Timing-Bericht informiert Sie über die Einrichtungszeit und den Puffer - und dies kann Ihr Problem sein. Ändern Sie Ihre Logik auf "synchron" und ich wette mit Ihnen, dass es funktioniert.

Außerdem sollten Sie den abgeleiteten Latch als bewährte Methode entfernen. alles, was dies erfordern würde, ist eine else-Anweisung:

if (cout == 32'h0)
    if (cd == 12'h0)
        cd <= 12'hFFF;
    else
        cd <= (cd - 12'h001);
else
    cd <= cd;

EDIT: vorgeschlagener Arbeitscode

module ClockDivider(input CLOCK_50, 
                    output reg[ 0:0] en 
                   );
    parameter clockDivisor = 50_000_000;
    reg [31:0] i;

    always @( posedge CLOCK_50 ) begin
            if ( i == clockDivisor ) begin
                    en <= 1;
                    i <= 0;
            end
            else begin
                    en <= 0;
                    i <= i + 32'b1;
            end
    end
endmodule

//1Hz clock output
wire                [0:0]           en;
//Holds the countdown value FFF-000
reg                 [11:0]              cd;

ClockDivider CLOCK_1Hz( CLOCK_50, en);

always @(posedge CLOCK_50)
    if (en) begin
        if (cd == 12'h0)
            cd <= 12'hFFF;
        else
            cd <= (cd - 12'h001);
    end 
    else 
        cd <= cd;
end
johnnymopo, danke für deine antwort. Der überarbeitete Code führte immer noch zu einem abgeleiteten Latch (siehe ThePhoton oben). Und CLOCK_50 (50 MHz) wäre zu schnell für den Countdown gewesen. Aber dank Ihres Vorschlags, Posege zu verwenden, habe ich es geschafft, es herauszufinden. Also, wo auch immer Sie auf dieser Welt sind, ich wünsche Ihnen viel Glück und nochmals vielen Dank, dass Sie einem zufälligen Fremden geholfen haben.
Nur um das klarzustellen, das Hinzufügen else cd <= cd;erzeugt immer noch einen Latch; Alles, was Sie getan haben, ist, es explizit und nicht implizit zu machen. Im Allgemeinen sollten auf diese Weise erstellte kombinatorische Latches vermieden werden.
@dave tweed, wie würden Sie dies schreiben, um überhaupt einen Latch zu vermeiden, wenn Sie eine synchrone Logik mit einer gemeinsamen Taktdomäne erstellen möchten? cd muss etwas sein .... Ich frage, wie ich das mache und es scheint gut zu funktionieren, zumindest bis 125 MHz, wo ich gearbeitet habe. Aber ich habe keine formelle VHDL-Ausbildung.
@johnnymopo, ich denke, Ihre Antwort wäre klarer, wenn Sie die always @(<xxx>)von Ihnen empfohlenen hinzufügen würden.
Codevorschlag hinzugefügt

Dank ThePhoton und johnymopo konnte ich die Lösung herausfinden. Die Lösung bestand darin, ClockDivisor selbst als langsamere Uhr fungieren zu lassen. Der Code wird:

module ClockDivider( input CLOCK_50, output reg[ 0:0] count );
    parameter clockDivisor = 50_000_000;
    reg [31:0] i;

always @( posedge CLOCK_50 )
    begin
            i <= i + 32'b1;
            if ( i == clockDivisor )
                    begin
                    count <= 1;
                    i <= 0;
                    end
            else
                    count <= 0;
    end

endmodule

In diesem Fall ist dies ein 1-Hz-Takt. Das Countdown-Dekrement kann dann mit der Pose dieser langsameren Uhr ausgelöst werden:

//1Hz clock output
wire                [0:0]           slowClock;

//Holds the countdown value FFF-000
reg                 [11:0]              cd;

ClockDivider CLOCK_1Hz( CLOCK_50, slowClock);

always @(posedge slowClock)
begin

if (cd == 12'h0)
    cd <= 12'hFFF;
else
    cd <= (cd - 12'h001);

end
Schön, dass es funktioniert hat. Ich persönlich würde versuchen, alles mit einer Uhr zum Laufen zu bringen. Ich glaube, dies würde auf eine neue Uhr schließen lassen, und bei einem größeren Design könnte dies das Timing erschweren. Könnten Sie die genaue abgeleitete Latch-Warnung posten?
@johnnymopo Die genaue Warnung lautete: "Ableitung von Latches für die Variable "cd", die ihren vorherigen Wert in einem oder mehreren Pfaden durch das Always-Konstrukt enthält".
Ich frage mich, ob dies geschieht, weil slowClock keine neue Taktdomäne in der Synthese ist? Wenn das der Fall wäre, cdändert es sich einmal pro Sekunde, basiert aber immer noch auf dem 50-MHz-Takt und ist daher immer noch ein Latch - aber das scheint seltsam. Ich empfehle nach wie vor die Verwendung einer gemeinsamen Uhr für die gesamte Logik, wenn möglich.