Unerwartete Verilog-Warnung bezüglich FPGA-Taktzuweisung

Ich habe eine Frage zu etwas, das ich nicht verstehe und das in meinem FPGA-Projekt vor sich geht. Ich muss zwei Geräte (AGC und ADC) über einen SPI-Bus steuern. Da das FPGA das Master-Gerät sein wird, erzeuge ich ein Taktsignal, SCK , im Code, indem ich den Systemtakt teile. Ich leite dieses Signal dann über einen Tristate-Puffer an eine Ausgangsleitung. Unten ist mein relevanter Code. Es ist nicht gezeigt, aber das Signal, das den Tristate-Puffer steuert, en_SCK , das von einem FSM gesteuert wird, wenn es im Ruhezustand niedrig und dann für die restlichen Zustände hoch gesetzt wird.

output wire SDI

   //for SCK_clock
reg SCK_gen, SCK_hold;
integer i;
reg en_SCK;
wire neg_edge_SCK;

   //SCK_generator
    always @(posedge clk)
            begin
                i <= i+1;
                SCK_hold <= SCK_gen;
                    if(i == 10)
                        begin
                            SCK_gen <= ~SCK_gen;
                            i <= 0;
                        end
            end


assign SCK = (en_SCK) ? SCK_gen : 1'bz;

Wenn ich das Design implementiere, erhalte ich die folgende Warnung:

WARNUNG:PhysDesignRules:372 - Gated Clock. Das Taktnetz en_SCK_not0001 wird von einem kombinatorischen Pin bezogen. Dies ist keine gute Designpraxis. Verwenden Sie den CE-Pin, um das Laden von Daten in das Flip-Flop zu steuern.

Außerdem bemerke ich, dass meine Uhr sehr verzerrt erscheint. Aber wenn ich das Tristate-Gerät nicht in meinem Code verwende und das Taktsignal direkt der Ausgangsleitung zuweise (wie im folgenden Code), bekomme ich ein schönes sauberes Taktsignal.

assign SCK = SCK_gen;

Unten ist eine Seite an Seite des Signals SCK ohne den Tristate-Puffer (links) und mit dem Tristate-Puffer (rechts). Ich bin ziemlich neu in FPGA und Verilog, aber ich verstehe, dass die Verwendung dieses Stils von Zuweisungscode einen Tristate-Puffer impliziert, daher bin ich verwirrt, warum er als getaktete Taktquelle interpretiert zu werden scheint (der von XST generierte Schaltplan zeigt ihn implementiert mit einem und Gate.Ich bin auch verwirrt darüber, wie es das Taktsignal verzerrt.Der FSM sollte den en_SCK erzwingenAktivieren Sie das Signal hoch für ein Vielfaches der Periode der Uhr, daher bin ich mir nicht sicher, was passiert. Laut Handbuch des Demoboards teilen sich andere Geräte dieses Signal, sodass ich es auf hohe Impedanz einstellen muss, wenn es nicht verwendet wird. Wenn mich jemand in die richtige Richtung weisen oder es mir erklären könnte, wäre ich sehr großartig. Danke

Geben Sie hier die Bildbeschreibung ein

Hier ist ein Link zu meiner ursprünglichen Frage bei Stackoverflow.com

Hier ist das gesamte Modul, wenn das die Dinge klarer macht. Ich habe bisher nur ein paar einfache Verilog-Projekte durchgeführt, daher bin ich mir sicher, dass mein Code nicht großartig ist. Aber ich habe versucht, es mit einem FSM zu entwerfen. Vielleicht ist mein Problem nicht das, was ich dachte. Danke für deine Geduld.

module AGC_controller
(
    input wire clk, reset, set_AGC, AMP_DO,
    output wire SDI,  SCK,
    output reg inhibit_ADC, AMP_CS,
    //spi disable signals  ** all disabled for =1 except AD_CONV.  When AD_CONV=0, disabled.
    output wire DAC_CS, AD_CONV, SF_CEO, FPGA_INIT_B,

    output reg [7:0] led

 );

localparam [2:0]
    idle            =   3'b000,
    set_up      =   3'b001,
    start_data  =   3'b010,
    wait_for_neg=   3'b011,
    wait_4      =   3'b100,
    next_bit    =   3'b101;
//  wait_last   =   3'b110;

//signals
//for SCK_clock
reg SCK_gen, SCK_hold;
integer i;
reg en_SCK;
wire neg_edge_SCK;

initial
begin
SCK_gen = 0;
i=0;
end

//SCK_generator
always @(posedge clk)

        begin
            i <= i+1;
            SCK_hold <= SCK_gen;
                if(i == 10)
                    begin
                        SCK_gen <= ~SCK_gen;
                        i <= 0;
                    end
        end

//detect neg edge of SCK
assign neg_edge_SCK = SCK_hold & ~SCK_gen;

//general signals
reg [2:0] state_reg, state_next;
integer bit_position;
reg [7:0] AGC_buf;
reg en_SDI;

//FSM control
always @(posedge clk, posedge reset)
    begin
        if (reset)
            state_reg <= idle;          
        else
            state_reg <= state_next;
    end

//FSM next state logic
always @*
begin
    state_next = state_reg;
    case(state_reg)
        idle:
            begin
                bit_position=7;
                AGC_buf = 8'b10011001;
                en_SCK = 1'b0;
                en_SDI = 1'b0;
                AMP_CS = 1'b1;
                inhibit_ADC = 1'b0;             
                if(set_AGC)
                begin
                        state_next = set_up;
                end
            end
        set_up:
            begin
                AMP_CS = 1'b0;
                inhibit_ADC = 1'b1;
                if ((SCK_gen) && (i < 9))
                    state_next = start_data;
            end
        start_data:
            begin
                en_SDI = 1'b1;
                if ((SCK_gen == 0) && (i==2))
                    begin
                        state_next = wait_for_neg;
                        en_SCK = 1'b1;
                    end
            end
        wait_for_neg:
            begin
                if (neg_edge_SCK)
                    begin 
                        state_next = wait_4;


                    end
            end
        wait_4:
            begin
                if (i==4)
                    begin
                    //test code to light leds
                    led[bit_position] = SDI;
                        if (bit_position == 0)
                            begin
                                state_next = idle;

                            end
                        else
                            begin
                                state_next = next_bit;

                            end
                    end
            end
        next_bit:
            begin
                bit_position = bit_position - 1;
                state_next = wait_for_neg;
            end         

    endcase
end

assign SDI = (en_SDI) ? AGC_buf[bit_position] : 1'bz;
assign SCK = (en_SCK) ? SCK_gen : 1'bz;
//spi disable signals  ** all disabled for =1 except AD_CONV.  When AD_CONV=0, disabled.
assign DAC_CS = (state_reg != idle);
assign AD_CONV = (state_reg == idle);
assign SF_CEO = (state_reg != idle);
assign FPGA_INIT_B = (state_reg != idle); 

endmodule
Geben Sie einen Verweis auf die Fotos an, und jemand wird sie hinzufügen oder mir per E-Mail zusenden, wenn Sie sie nicht auf eine Website stellen können.
@RussellMcMahon Ich habe einen Link zu den Fotos hinzugefügt. Danke.
Ein paar Fragen ... warum verwenden Sie einen Tristate-Puffer für die Uhr? Warum halten Sie es nicht einfach hoch oder niedrig (abhängig von Ihren SPI CPOL/CPHA-Einstellungen), wenn es nicht aktiviert ist? Was ist der Zweck von SCK_hold? (Tristate-Frage entfernt, zu früh am Morgen und ich habe nicht klar gelesen.)
Es ist möglich, dass sich die Warnung nicht über das beschwert, worüber sie sich Ihrer Meinung nach beschwert. Der von Ihnen gepostete Code zeigt nicht, wie clk oder en_SCK generiert werden, oder zeigt nicht einmal eine Deklaration für clk . Wenn Sie einen vollständigeren Code posten können, erhalten Sie wahrscheinlich eine bessere Antwort.
Der wahrscheinlichste Grund für die Warnung ist, wenn Sie eine externe Uhr in Ihr Design einfließen lassen, und Sie es mit etwas UND-verknüpfen, um es zu aktivieren und zu deaktivieren, und das dann verwenden, um den von Ihnen geposteten Code auszulösen.
@photon @andrew Ich habe das gesamte Modul unter meinem ursprünglichen Beitrag gepostet. Sie können sehen, dass clk ein 50-MHz-Takteingang ist. SCK_hold ist ein Register zum Halten des verzögerten Werts von SCK_gen, um einen Flankendetektor zu bilden. Ich dachte nicht, dass das alles relevant ist, als ich gepostet habe, aber es sieht so aus, als ob (wahrscheinlich) etwas anderes vor sich geht, das ich nicht verstehe.

Antworten (2)

Die Warnung schlägt im Grunde vor, dass Sie ein explizites Uhr-Primitiv verwenden, das aus irgendeinem Grund nicht automatisch von den Tools abgeleitet wird.

Es sollte eine Referenz für Ihr spezielles FPGA geben, die angibt, welche Grundelemente verfügbar sind. Für einen Spartan3E beispielsweise beschreibt Xilinx UG617 die BUFGCE-Primitive ("Global Clock Buffer with Clock Enable") und zeigt, wie man sie instanziiert. Andere Anbieter verfügen über eine ähnliche Dokumentation.

Ihre Taktverzerrung tritt wahrscheinlich auf, weil das Tristateing einer Ausgangsleitung nicht sofort möglich ist. Was auch immer angesteuert wird, hat sowohl Induktivität als auch Kapazität. Selbst wenn der Treiber in Null-Echtzeit auf High-Z gehen könnte (was er nicht kann), klingelt das angesteuerte Netzwerk noch einige Zeit. Wenn das ein Problem ist, müssten Sie einen Weg finden, um sicherzustellen, dass die Taktleitung niedrig ist, und das schon seit einiger Zeit, bevor Sie sie tristaten. Alles in allem gibt es wahrscheinlich einen besseren Weg, um Ihr Ziel zu erreichen, als eine Uhr zu tristatieren.

Ich stimme user572 bei der Antwort auf Ihre erste Frage zu, was die Warnung verursacht. Versuchen Sie explizit, einen Taktpuffer an Ihrer clk-Eingabe zu instanziieren, und teilen Sie uns mit, ob die Warnung dadurch gelöscht wird.

Ich denke jedoch, dass das seltsame Ausgabeverhalten völlig unabhängig von der Warnung ist. Ich kann die Ursache des Problems nicht erkennen, teilweise weil Ihr FSM-Codierungsstil nicht sehr klar ist. Ich bin mir nicht sicher, ob Ihr Synthesetool mit Ihrem FSM-Code das Richtige macht oder nicht, was eine Ursache für Probleme sein könnte.

Für Ihren FSM-Code empfehle ich folgende Änderungen:

  • Verwenden Sie <=statt =. Der Unterschied ändert möglicherweise nicht die logische Bedeutung des Codes, kann jedoch zu Unterschieden zwischen simuliertem Verhalten und synthetisiertem Verhalten führen, die schwer zu debuggen sind.

  • Jeder Ausgang der Zustandsmaschine sollte in jedem Zweig der case, und für jede Kombination von Eingängen zugewiesen werden. Sie verlassen sich darauf, dass Werte konstant gehalten werden, wenn Sie sie nicht zuweisen, und das sollte logischerweise passieren. In der Synthese könnte dies jedoch dazu führen, dass Latches anstelle von Flip-Flops instanziiert werden, und es handelt sich nicht um einen Best-Practice-Codierungsstil.

    Ja, der Stil, den ich empfehle, ist sehr ausführlich und mühsam zu schreiben. Ausführlicher, langwieriger Code ist eine Tatsache des Lebens in Verilog. Sei einfach froh, dass du kein VHDL schreibst.

Insbesondere dort, wo Sie state_next = state_reg;vor der Case-Anweisung stehen, bin ich mir nicht sicher, was das resultierende Verhalten sein wird. Ich denke, Sie meinen dies, um eine Standardausgabe für zu erstellen state_reg, wenn Sie nicht state_reginnerhalb der case-Anweisung zuweisen. state_next <= state_nextEs ist besser , in jeden Zweig des Falls einzufügen , wo Sie nicht möchten, dass es sich ändert.

Ich denke, aber Sie müssen experimentieren, um sicher herauszufinden, dass die Kombination aus dem blockierenden Zuweisungsstil und der @*Sensibilität des Always-Blocks, der die case-Anweisung umgibt, erklären könnte, warum Sie das fehlerhafte Ausgabeverhalten sehen. Sie haben im Grunde eine zirkuläre Logik. In einem Block weisen Sie zu state_reg <= state_nextund in einem anderen (kombinatorischen) Block weisen Sie zu state_next = state_reg.

Logischerweise bedeutet Ihr Code, dass die state_next = state_regZuweisung immer dann erfolgt, wenn sich an einem der Eingänge des Blocks mit dem Fall etwas ändert. Dies könnte bei state_regÄnderungen zu einer Race-Bedingung führen oder wenn sich ein anderer Eingang gleichzeitig mit einer clkFlanke ändert, die eine Aktualisierung von auslöst state_reg. Und es ist überhaupt nicht klar, wie dies synthetisiert wird.

OK. Ich werde es mit diesen Änderungen versuchen und sehen, was das Ergebnis ist. Außerdem versuche ich, den FSM-Codierungsstil zu verwenden, der in Pong P. Chus Verilog-Buch verwendet wird. Im Allgemeinen verwendet er einen Always-Block mit kombinatorischer Logik, dh = , um die nächste Zustandslogik zuzuweisen, und aktualisiert das Zustandsregister mit state_reg <= state_next . Ich habe versucht, diesem Programmierstil zu folgen, aber wie gesagt, ich lerne gerade. Übrigens. In der Simulation funktionierte es genau so, wie ich es erwartet hatte. Es sieht so aus, als würde ich die Feinheiten zwischen Simulation und Implementierung lernen. Danke
RE "verwendet einen Always-Block mit kombinatorischer Logik, dh =, um die nächste Zustandslogik zuzuweisen, und aktualisiert das Zustandsregister mit state_reg <= state_next."; das ist vollkommen in Ordnung. Aber das state_next = state_regim always @*Block scheint mir falsch zu sein ... Verwendet Ihr Lehrbuch wirklich diese Art von Stil?
Ja. In Pong P. Chus „FPGA Prototyping by Verilog Examples“. link Es ist jedoch kein Lehrbuch. Wenn Sie dem Link folgen, können Sie den Code herunterladen. Sehen Sie sich Listing 5.2 für eine generische FSM an. Er verwendet diesen Stil in seinem gesamten Buch. Sie bemerken, dass er seine Logik für den nächsten Zustand immer mit **state_next = state_reg // default next state ** beginnt. Bei Interesse kann man sich jedenfalls mal umschauen.
Ich empfehle, den von Ihrem Syntheseprogramm empfohlenen Codierungsstil zu verwenden. Wenn Sie Xilinx-Tools verwenden, werfen Sie einen Blick in das XST-Benutzerhandbuch ( xilinx.com/support/documentation/sw_manuals/xilinx11/xst.pdf ). Ab Seite 194 finden Sie Beispiele für drei verschiedene Codierungsstile, die sie unterstützen, wobei immer 1, 2 oder 3 Blöcke verwendet werden. Wenn Sie einen anderen FPGA-Anbieter oder eine Synthese-Engine eines Drittanbieters verwenden, stellen diese eine ähnliche Dokumentation bereit. Das FSM-Konzept bleibt dasselbe – aber es ist eine gute Idee, einen Codierungsstil zu verwenden, den das Synthesetool erwartet.