Design simuliert perfekt, funktioniert aber nicht auf FPGA

Erstmal danke für die Hilfe gestern. Diesmal werde ich meinen Code korrekt dokumentieren.

Wir wurden also beauftragt, eine Parkuhr zu erstellen, die 4 Eingaben benötigt, eine addiert 50 Sekunden, eine addiert 150 Sekunden, eine addiert 250 Sekunden und eine addiert 500 Sekunden. Wenn mehr als 10 Sekunden verbleiben, sollte eine grüne LED mit einer Dauer von 2 Sekunden blinken. Wenn weniger als 10 Sekunden verbleiben, sollte eine rote LED mit einer Periode von 2 Sekunden blinken, und wenn keine Sekunden mehr übrig sind, sollte eine rote LED mit einer Periode von 1 Sekunde blinken. Jeder Taktzyklus ohne Eingabe soll eins vom Zähler subtrahieren.

Wir wurden beauftragt, einen Entpreller sowie eine Einzelimpuls-Zustandsmaschine für die Eingänge und eine 7-Segment-Anzeige für die Ausgänge zu verwenden. Also habe ich eine Zustandsmaschine für meine Addition und LED-Blinken verwendet, dann den Zähler an einen Binär-zu-BCD-Konverter und dann an eine BCD-zu-7-Segment-Anzeige gesendet. Ich frage mich, ob ich die Uhr durcheinander gebracht habe und die Simulation irgendwie meine Fehler ignoriert, wodurch das FPGA irgendwie nicht in der Lage ist, meinen Code richtig zu verwenden?

ohne weitere Umschweife, hier sind meine Codeschnipsel:

Top-Modul

module parkingmeter(clk,rst,b0,b1,b2,b3,out0,out1,out2,out3,GRNLED,REDLED);
    input b0,b1,b2,b3,clk,rst;
    output [6:0] out0,out1,out2,out3;
    output GRNLED,REDLED;
    wire outt0,outt1,outt2,outt3;
    wire [15:0] counter;
    wire [3:0] bcd0,bcd1,bcd2,bcd3;
    wire clkout;
    clockdivider onesec(clk,clkout);
    add_sub yep(b0,clkout,rst,outt0);
    add_sub yesh(b1,clkout,rst,outt1);
    add_sub yeah(b2,clkout,rst,outt2);
    add_sub ok(b3,clk,rst,outt3);
    controlparker Second(outt0,outt1,outt2,outt3,clkout,rst,counter,REDLED,GRNLED);
    EC Third(counter,bcd0,bcd1,bcd2,bcd3,out0,out1,out2,out3);
endmodule

mein Entprellmodul

module cleandebounce(clk,rst,I0,out);
    input clk,rst,I0;
    output out;
    reg f0,f1;
    always @ (posedge clk, posedge rst) begin
       if (rst==1) begin
            f0 <= I0;
            f1 <= f0;
        end else begin
            f0 <= 0;
            f1 <= 0;
       end
    end
    assign out = f1;
endmodule

Meine Einzelpuls-Zustandsmaschine

module add_sub(in,clk,rst,out);
    input in,clk,rst;
    output reg out = 1'b0;
    reg state = 1'b0;
    wire outt;
    cleandebounce one(clk,rst,in,outt);
    always @ (posedge clk,posedge rst) begin
        case(state)
            1'b0: begin
                if (rst==1) begin
                    out <= 0;
                    if (outt == 1) begin
                        out <= 1'b1;
                        state <= 1'b1;
                    end else state <= 1'b0;
                end else begin
                    out <= 1'b0;
                    state <= 1'b0;
                end
            end
            1'b1: begin
                out <= 1'b0;
                if (outt == 1) begin
                    out <= 1'b0;
                    state <= 1'b1;
                end else state <= 1'b0;
            end
        endcase
    end
endmodule

Und mein Modul zum Hinzufügen der Eingänge sowie zum Ein- und Ausschalten von LEDs

module controlparker(B0,B1,B2,B3,clk,rst,counter,REDLED,GRNLED);
    input B0,B1,B2,B3,clk,rst;
    output reg [15:0] counter = 16'b0000000000000000;
    reg state = 1'b0;
    reg [2:0] area = 3'b000;
    output reg REDLED = 0;
    output reg GRNLED = 0;
    always @ (posedge clk, posedge rst) begin
        case(state)
            0: begin
                if (rst==1) begin
                    if (counter > 0)
                        counter <= counter - 1;
                    if (counter > 9999)begin
                        counter <= 9999;
                    end
                    state <= 1;
                end else begin
                    counter <= 0;
                    state <= 0;
                end
            end
            1: begin
                if (B0 == 1) begin
                    counter <= counter + 16'b00000000000110010;
                    state <= 0;
                end else if (B1 == 1) begin
                    counter <= counter + 16'b00000000010010110;
                    state <= 0;
                end else if (B2 == 1) begin
                    counter <= counter + 16'b00000000011111010;
                    state <= 0;
                end else if (B3 == 1) begin
                    counter <= counter + 16'b00000000111110010;
                    state <= 0;
                end else state <= 0;
            end
        endcase
    end        
    always @ (posedge clk, posedge rst) begin
        case(area)
            3'b000: begin
                if (rst==1)begin
                    if (counter >= 10)begin
                        GRNLED <= 1;
                        REDLED <= 0;
                        area <= 3'b001;
                    end
                    else if (counter < 10 && counter > 0) begin
                        REDLED <= 1;
                        GRNLED <= 0;
                        area <= 3'b010;
                    end
                    else REDLED <= ~REDLED;
                end
                else begin
                    REDLED <= 0;
                    GRNLED <= 0;
                end
            end
            3'b001: begin
                GRNLED <= 0;
                area <= 3'b000;
            end
            3'b010: begin
                REDLED <= 0;
                area <= 3'b000;
            end
        endcase
    end
endmodule

Mein Modul konvertiert BinarytoBCD sowie die Ausgabe der 7-Segment-Anzeige:

module EC(in,bcd0,bcd1,bcd2,bcd3,out0,out1,out2,out3);
    input [15:0] in;
    output reg [3:0] bcd0 = 4'b0000;
    output reg [3:0] bcd1 = 4'b0000;
    output reg [3:0] bcd2 = 4'b0000;
    output reg [3:0] bcd3 = 4'b0000;
    output reg [6:0] out0 = 7'b0000000;
    output reg [6:0] out1 = 7'b0000000;
    output reg [6:0] out2 = 7'b0000000;
    output reg [6:0] out3 = 7'b0000000;
    reg [15:0] temp;
    integer i;
    always @ (in) begin
        bcd0 = 4'b0000;
        bcd1 = 4'b0000;
        bcd2 = 4'b0000;
        bcd3 = 4'b0000;
        temp = in;
        for(i=15; i>=0; i=i-1) begin
            if (bcd3 >= 4'b0101)
                bcd3 = bcd3 + 4'b0011;
            if (bcd2 >= 4'b0101)
                bcd2 = bcd2 + 4'b0011;
            if (bcd1 >= 4'b0101)
                bcd1 = bcd1 + 4'b0011;
            if (bcd0 >= 4'b0101)
                bcd0 = bcd0 + 4'b0011;
            bcd3 = bcd3 << 1;
            bcd3[0] = bcd2[3];
            bcd2 = bcd2 << 1;
            bcd2[0] = bcd1[3];
            bcd1 = bcd1 << 1;
            bcd1[0] = bcd0[3];
            bcd0 = bcd0 << 1;
            bcd0[0] = temp[i];
        end
    end
    always @ (bcd0) begin
        if (bcd0==4'b0000) out0 = 7'b0000001;
        else if (bcd0==4'b0001) out0 = 7'b1001111;
        else if (bcd0==4'b0010) out0 = 7'b0010010;
        else if (bcd0==4'b0011) out0 = 7'b0000110;
        else if (bcd0==4'b0100) out0 = 7'b1001100;
        else if (bcd0==4'b0101) out0 = 7'b0100100;
        else if (bcd0==4'b0110) out0 = 7'b0100000;
        else if (bcd0==4'b0111) out0 = 7'b0001111;
        else if (bcd0==4'b1000) out0 = 7'b0000000;
        else if (bcd0==4'b1001) out0 = 7'b0000100;
        else out0=7'b0000001;
    end
    always @ (bcd1) begin
        if (bcd1==4'b0000) out1 = 7'b0000001;
        else if (bcd1==4'b0001) out1 = 7'b1001111;
        else if (bcd1==4'b0010) out1 = 7'b0010010;
        else if (bcd1==4'b0011) out1 = 7'b0000110;
        else if (bcd1==4'b0100) out1 = 7'b1001100;
        else if (bcd1==4'b0101) out1 = 7'b0100100;
        else if (bcd1==4'b0110) out1 = 7'b0100000;
        else if (bcd1==4'b0111) out1 = 7'b0001111;
        else if (bcd1==4'b1000) out1 = 7'b0000000;
        else if (bcd1==4'b1001) out1 = 7'b0000100;
        else out1=7'b0000001;
    end
    always @ (bcd2) begin
        if (bcd2==4'b0000) out2 = 7'b0000001;
        else if (bcd2==4'b0001) out2 = 7'b1001111;
        else if (bcd2==4'b0010) out2 = 7'b0010010;
        else if (bcd2==4'b0011) out2 = 7'b0000110;
        else if (bcd2==4'b0100) out2 = 7'b1001100;
        else if (bcd2==4'b0101) out2 = 7'b0100100;
        else if (bcd2==4'b0110) out2 = 7'b0100000;
        else if (bcd2==4'b0111) out2 = 7'b0001111;
        else if (bcd2==4'b1000) out2 = 7'b0000000;
        else if (bcd2==4'b1001) out2 = 7'b0000100;
        else out2=7'b0000001;
    end
    always @ (bcd3) begin
        if (bcd3==4'b0000) out3 = 7'b0000001;
        else if (bcd3==4'b0001) out3 = 7'b1001111;
        else if (bcd3==4'b0010) out3 = 7'b0010010;
        else if (bcd3==4'b0011) out3 = 7'b0000110;
        else if (bcd3==4'b0100) out3 = 7'b1001100;
        else if (bcd3==4'b0101) out3 = 7'b0100100;
        else if (bcd3==4'b0110) out3 = 7'b0100000;
        else if (bcd3==4'b0111) out3 = 7'b0001111;
        else if (bcd3==4'b1000) out3 = 7'b0000000;
        else if (bcd3==4'b1001) out3 = 7'b0000100;
        else out3=7'b0000001;
    end
endmodule

Und schließlich mein Taktteiler, den ich jedem Modul zuführe, das eine Uhr benötigt, um auf dem FPGA korrekt zu laufen:

module clockdivider(clk,clkout);
    input clk;
    output clkout;
    reg [24:0] q = 0;
    always @ (posedge clk) begin
        q <= q + 1;
    end
    assign clkout = q[0];
endmodule

Da ist also alles. Wenn ich mein FPGA aktiviere, zeigt es Zufallszahlen an, obwohl ich keine Tasten gedrückt habe. Wie ist das möglich? Ich bin relativ neu bei Verilog. Wenn es also eine Möglichkeit gibt, meinen Code zu vereinfachen, wäre ich sehr dankbar. Wieder simuliert alles perfekt. Danke an alle

Hier fehlen alle wichtigen Informationen darüber, wie es im FPGA instanziiert und verbunden wird - Sie erwähnen nicht einmal das Teil oder das Synthesetool! Außerdem sollten Sie einen Takt nicht im HDL-Code teilen, einen Taktgenerator verwenden oder teilen, um eine Taktfreigabe zu erstellen .
Überprüfen Sie Ihre Hardware-Konfigurationsdatei, Ihre Anfangswerte und haben Sie Ihre externen Eingänge mit Ihrer Taktdomäne synchronisiert? In der Simulation kann viel passieren, was in der Synthensis nicht zusammenpasst.
Mein Lehrer hat noch nie etwas von euch durchgesehen. Alles, was ich getan habe, ist, FPGA-Ports den jeweiligen Eingangs- und Ausgangspins zuzuordnen. Wir haben von ihr den Taktteilercode erhalten. Unser Board ist ein Altera DE 2, falls das etwas hilft
Ja, sie bringen Ihnen nicht alles bei, was Sie wissen müssen, damit die Dinge in einem echten FPGA funktionieren
Haben Sie versucht, eine Simulation für Post-Placement- und Post-Routing-Designs auszuführen?
Der Lehrer hat niemals Post-Placement- oder Post-Routing-Design erwähnt
@helpneeded Fragen Sie also Ihren Lehrer nach "post-placed" und "post-routed" Designs und welche Vorteile haben, wenn ...
Mein Rat: Fügen Sie künstlich kleine Verzögerungen für JEDE "nicht blockierende" Zuweisung hinzu (wie Zähler <= #0.1 Zähler - 1;) siehe electronic.stackexchange.com/a/365262/117785 . Möglicherweise stellen Sie eine drastische Änderung im Verhalten Ihres Verilog fest Modell im Vergleich zu Ihrem Idealmodell.
Nun, für den Anfang teilt dieser Taktteiler nur den Eingangstakt durch zwei. Ich bin mir nicht sicher, was es tun soll, aber es wäre wahrscheinlich sinnvoll, ein anderes Ausgangsbit als Bit 0 auszuwählen.
Ja, ich entschuldige mich dafür, dass ich die Uhr zu Simulationszwecken so eingestellt habe. Ursprünglich war es auf Bit 24 eingestellt, was eine Frequenz von etwa 1,5 Hz ergibt
Wenn Ihr FPGA mit der Anzeige von Zufallszahlen beginnt, klingt es so, als hätten Sie das Zurücksetzen der Hardware beim Einschalten nicht in Ordnung gebracht, und Ihr Simulationscode erledigt dies automatisch für Sie.

Antworten (1)

Es könnte viele Probleme geben, da Sie mit echten FPGAs nicht vertraut zu sein scheinen, also werde ich sie im Folgenden auflisten:

  1. Sind alle externen Eingänge mit der Taktdomäne Ihres FPGA synchronisiert? Eingehende Signale können in der Mitte der FPGA-Taktflanken übergehen, was zu Metastabilität führt. Der Weg, damit umzugehen, besteht darin, den externen Eingang durch eine Kette von Flip-Flops zu leiten, bevor Sie ihn verwenden (dh den Eingang durch eine Kette von Signalen / Registern bei jedem Takt kopieren). Jedes Mal, wenn es passiert wird, werden die Chancen, dass ein metastabiles Signal zu Ihrem funktionellen Kern durchdringt, weniger wahrscheinlich, bis es verschwindend gering ist. Das Minimum sind zwei, hochzuverlässige Geräte können drei verwenden. Das sollte offensichtlich sein, aber ich sage es trotzdem: Dies muss der ERSTE Schritt jedes Signals sein, das in das FPGA eintritt. Wenn Sie das Signal entprellen oder filtern möchten (wie in Nr. 2), müssen Sie dies NACH diesem Schritt tun, da die Metastabilität nicht zulässt, dass etwas anderes ordnungsgemäß funktioniert.

  2. Sind Ihre externen Tasten entprellt? Wenn elektromechanische Kontakte aufeinander treffen, passen sie nicht sauber zusammen und werden wiederholt verbunden und getrennt, bis sich die Dinge beruhigt haben. Die Elektronik ist schnell genug, um dies zu erfassen. Sie können Eingänge in einem FPGA auf verschiedene Weise filtern. Einige Ansätze sind: (a) Sobald ein Übergang erkannt wird, ignorieren Sie alle nachfolgenden Übergänge für einen bestimmten Zeitraum (als Blanking bezeichnet). (b) Geben Sie den Eingangszustand nur weiter, damit er von anderen Werten verwendet werden kann, wenn er sich für eine gewisse Anzahl von Abtastwerten stabilisiert hat .

  3. Ihr FPGA muss wissen, welche Signale welchen Pins zugeordnet sind. Es muss dem FPGA auch mitteilen, welche Art von Pins das sein muss. Wie dies geschieht, hängt von Ihrer Synthesesoftware ab. Das muss stimmen.

  4. Ihr FPGA muss auch wissen, wie hoch die Taktrate sein wird, damit es weiß, wie lange es einem Signal erlauben kann, sich auszubreiten, da alle Signale (insbesondere Taktsignale) rechtzeitig an ihrem Ziel ankommen müssen, damit die Schaltung ordnungsgemäß funktioniert.

  5. Taktsignale müssen überall in einem FPGA hingehen und müssen überall ungefähr zur gleichen Zeit ankommen. Sie leiten Takte nicht durch das normale FPGA-Fabric, da es zu variabel und zu langsam ist, um Takte durch die gesamte konfigurierbare FPGA-Logik laufen zu lassen. Der Zeitversatz wird zu hoch sein. FPGAs verfügen über dedizierte Taktnetzwerke, um den Takt mit minimaler Ausbreitungsverzögerung über den gesamten Chip zu verteilen. Diese müssen Sie verwenden, wenn Sie eine Uhr verteilen möchten.

Schlüsselwörter wie „Posedge“ und „Negedge“ sind sehr speziell und teilen der Software mit, dass es sich um ein Taktsignal handelt, sodass es durch das dedizierte Taktverteilungsnetz geleitet wird. Dies geschieht automatisch bei jedem Signal, das diesen Schlüsselwörtern zugeordnet ist, aber es gibt nur sehr wenige davon auf einem FPGA. Verwenden Sie dieses Schlüsselwort also nicht für Dinge, die keine tatsächlichen Taktsignale sind. Wenn Sie eine steigende Flanke auf einem Nicht-Taktsignal betrachten möchten, schreiben Sie ein separates Modul, das den vorherigen logischen Zustand mit dem aktuellen vergleicht, um zu sehen, ob er anders ist.

  1. Teilen Sie Takte aus dem gleichen Grund nicht in der FPGA-Fabric. Leiten Sie die Uhr nicht durch die konfigurierbare Logik. Verwenden Sie entweder den Taktteiler-Hardwareblock auf dem FPGA oder verwenden Sie Taktfreigabesignale auf Ihren Modulen.

Ein Taktaktivierungssignal ist ein Signal, das nur für einen einzigen Taktimpuls hoch geht, bevor es wieder niedrig wird. Es wird ausgelöst, wenn ein Modul etwas tun wird, aber das Modul nicht davon getaktet wird. Das Modul wird immer noch vom FPGA-Haupttakt getaktet (über das Taktverteilungsnetzwerk). Sie können ein Modul erstellen, das vom FPGA-Takt getaktet wird, aber alle N Zyklen eine Taktfreigabe ausgibt, und dies zur Taktfreigabe verwenden, um Ereignisse in anderen Modulen auszulösen. Alle Module werden immer noch vom FPGA-Takt getaktet und die Tatsache, dass die Taktfreigabe nach einem Taktzyklus erlischt, stellt sicher, dass die Module nicht ständig jeden Taktzyklus auslösen, wenn sie es nicht sollten.

  1. Flip-Flops auf dem FPGA haben auch Reset-Hardware, sodass Sie asynchrone Resets haben können (da die konfigurierbare Logik auf dem FPGA-Fabric asynchrone hasst). Ein Signal, das in die Empfindlichkeitsliste aufgenommen wurde, aber KEIN Schlüsselwort wie „Posedge“ oder „Negedge“ hat, wird als Hardware-Reset interpretiert. Verwenden Sie NICHT „Posedge“ oder „Negedge“ mit einem Reset-Signal.

Vertraulichkeitslistenregel in HDL