Ich habe meine eigene Implementierung eines seriellen Empfängers codiert. Es funktioniert für eingehende Daten mit einer Baudrate von 115200.
Hier ist mein Code:
module my_serial_receiver(
input clk,
input reset_n,
input Rx,
output reg [7:0] received_byte,
output reg byte_ready
);
parameter IDLE = 4'd0, BIT_0 = 4'd1, BIT_1 = 4'd2,
BIT_2 = 4'd3, BIT_3 = 4'd4, BIT_4 = 4'd5, BIT_5 = 4'd6,
BIT_6 = 4'd7, BIT_7 = 4'd8, BYTE_READY = 4'd9;
reg [3:0] state = 0;
reg [7:0] baud_rate_clock = 0;
always @(posedge clk) begin
if (baud_rate_clock == 8'd216) begin
baud_rate_clock <= 0;
end
else begin
baud_rate_clock <= baud_rate_clock + 8'd1;
end
end
assign baud_tick = ~|baud_rate_clock;
always @(posedge baud_tick or negedge reset_n) begin
if (~reset_n) begin
state <= 0;
byte_ready <= 0;
end
case(state)
IDLE: begin
byte_ready <= 0;
if (Rx == 0) state <= BIT_0;
end
BIT_0: begin
received_byte[0] <= Rx;
state <= BIT_1;
end
BIT_1: begin
received_byte[1] <= Rx;
state <= BIT_2;
end
BIT_2: begin
received_byte[2] <= Rx;
state <= BIT_3;
end
BIT_3: begin
received_byte[3] <= Rx;
state <= BIT_4;
end
BIT_4: begin
received_byte[4] <= Rx;
state <= BIT_5;
end
BIT_5: begin
received_byte[5] <= Rx;
state <= BIT_6;
end
BIT_6: begin
received_byte[6] <= Rx;
state <= BIT_7;
end
BIT_7: begin
received_byte[7] <= Rx;
state <= BYTE_READY;
end
BYTE_READY: begin
byte_ready <= 1;
state <= IDLE;
end
default: state <= IDLE;
endcase
end
endmodule
Hier meine Simulationsergebnisse :
Also im Grunde reagiert die Simulation genau so, wie ich es will.
Ich habe 0x55, 0x74 und 0x40 gesendet, und dann wird byte_ready behauptet, also weiß ich, dass das richtig ist.
Darüber hinaus wird mein baud_tick-Signal 115200 Mal pro Sekunde richtig bestätigt.
Insgesamt scheint meine Simulation perfekt zu sein.
Wenn ich das FPGA tatsächlich programmiere und die Rx-Leitung anschließe, passiert nichts (wie in, keine LEDs blinken). Ich habe es so codiert, dass die 8 LEDs die Werte der 8 Bits des Bytes annehmen. Ich habe diese Methode zuvor mit der Implementierung einer anderen Person getestet und sie hat perfekt zum Testen des Moduls funktioniert.
Ich habe sogar den SignalTap-Logikanalysator verwendet und bestätigt, dass die eingehenden Rx-Daten korrekt sind und die Baudrate mit einem Fehler von + oder - 0,5% korrekt ist.
Ich habe keine Ahnung, was ich von hier aus tun soll. Irgendwelche Vorschläge?
FPGA-Code funktioniert in der Verhaltenssimulation, aber nicht in der Hardware
Dies passiert manchmal. Vielleicht stimmt also etwas mit der Hardware nicht, und vielleicht bildet die Simulation nicht genau ab, was in der Realität passiert. Muss beides prüfen.
Ich gehe davon aus, dass Sie ein Student sind, da es kostenlosen vorgefertigten Code gibt, der bereits serielle UARTs implementiert. Meine Antwort lautet also, wie Sie diese Art von Problem debuggen können.
1. Ist die Synthese wirklich gelungen?
Gibt es beim Erstellen des Projekts Diagnose- oder Warnmeldungen? Ist der synthetisierte Code sinnvoll?
Untersuchen Sie den Post-Übersetzungs-/Post-Synthese-Code, der von den verschiedenen Synthese-Tools generiert wird.
Wie geschrieben, ist der von Ihnen gepostete Code verhaltensorientiert ( always @posedge
), nicht strukturell (Instanzen und Drähte). Das macht die Simulation sehr einfach, aber das Synthesetool muss versuchen zu erkennen, wonach Sie fragen, indem es es mit Vorlagen abgleicht. Wenn Ihre always
Anweisung nicht mit einer der Vorlagen übereinstimmt, weiß das Synthesetool nicht, wie es sie implementieren soll. Das sollte irgendwo eine Diagnosemeldung erzeugen.
Die erste Übersetzungsphase erzeugt einen RTL-Code (Register Transfer Logic), der eine generische kombinatorische Logik plus Flip-Flops ist. In diesem Beispiel sollten Sie einige Flip-Flops für den Baudratenzähler und die Zustandsmaschine sowie einige kombinatorische Logik zur Bestimmung des nächsten Zustands erwarten. Spätere Synthesephasen finden heraus, wie das FPGA konfiguriert wird, um diesen RTL-Code zu implementieren. Diese Mapping- und Place-and-Route-Phasen wählen aus, welcher Slice (Xilinx) oder konfigurierbare Logikblock (Altera) welches Flip-Flop sein wird, welcher Pin für welche Eingangs-/Ausgangsports der obersten Ebene verwendet werden soll und wie alles miteinander verbunden werden soll.
Nach der Übersetzung/Mapping/Place-and-Route können Sie zurückgehen und die Post-Mapping- oder Post-Place-and-Route-Simulation ausführen. Dies simuliert den RTL-Code, den das Synthesetool generiert hat, nicht den Verhaltenscode, mit dem Sie begonnen haben. Diese Simulation weiß, wo sich jedes Flip-Flop befindet und wie viele Pikosekunden Verzögerung jede Route hat, sodass Sie eine realistischere Simulation der Zeitflanken sehen können. (Da Ihr Takt nur 25 MHz beträgt, sollte es genügend Zeitspielraum geben.)
Simulation und Synthese sind sehr unterschiedlich. Ein always
Block, der in der Simulation gut funktioniert, kann nach der Synthese unerwartet weggelassen werden. (Ich habe einmal die Hälfte eines ziemlich komplexen Systems aufgrund einer Warnung "unvollständige Initialisierung ignoriert" in einem Modul auf niedriger Ebene verloren, was dazu führte, dass Xilinx ISE 14.7 ein ganzes Modul durch eine konstante 0 ersetzte. Und alle Module auf höherer Ebene das hing von diesem Signal ab, wurde auch zu nichts optimiert, da ihre Ausgänge konstant wurden.Da es keine interne Logik mehr gab, um meinen SPI-Ausgang zu treiben, wurde der Ausgangspin nicht geroutet.Jetzt habe ich die Gewohnheit entwickelt, dies zu überprüfen die RTL nach der Synthese, um sicherzustellen, dass keine Teile fehlen.)
Ich habe einen C-Programmierhintergrund, daher muss ich mich beim Schreiben von Verilog ständig selbst herausfordern: Welche Hardware soll es synthetisieren, und dann sicherstellen, dass ich sie so beschreibe, dass das Synthesetool es verstehen kann.
2. Was macht die Hardware eigentlich?
Da das FPGA nichts tut, was Sie direkt beobachten können, ist es schwierig zu diagnostizieren, ob das FPGA eingeschaltet, mit Ihrem Code konfiguriert und Ihre Zustandsmaschine ausgeführt wird. Ändern Sie Ihre oberste Ebene, um einige Diagnoseausgaben hervorzuheben:
Bringen Sie state[3:0]
zu vier der LEDs
Bringen Sie baud_tick
das Signal zu einer LED
Fügen Sie Ihrem Toplevel einen "Herzschlag"-LED-Blinker hinzu (schalten Sie einfach ein D-Flip-Flop mit der halben Clk-Rate um), um zu bestätigen, dass die FPGA-Firmware konfiguriert ist. Diese Diagnose ist es wert, zumindest vorerst auf eine der LEDs zu verzichten.
Bringen Sie received_byte[0]
(das erste und niedrigstwertige Bit) zu einer LED
Bringen Sie received_byte[7]
(das letzte und höchstwertige Bit) zu einer LED
Dies bedeutet, dass Sie für diese Diagnosesignale die acht LEDs auf Ihrer Platine anstelle des received_byte[7:0]
parallelen Datenausgangs verwenden. Aber diese Signale auf niedrigerer Ebene geben Ihnen nützlichere Informationen, um zu sehen, was in Ihrem Modul vor sich geht. Sie können es wieder ändern, nachdem Sie die Fehler behoben haben.
Ich nehme an, Ihr FPGA-Board hat einige entprellte Schiebeschalter. Jetzt ist es an der Zeit, sie an die Arbeit zu bringen:
Verwenden Sie zum Fahren einen entprellten Schiebeschalterreset
Verwenden Sie zum Fahren einen entprellten SchiebeschalterRx
Verwenden Sie zum Fahren einen entprellten Schiebeschalterclk
Ändern Sie vorübergehend Ihren Baudratenparameter, sodass Sie alle 7 Taktzyklen baud_tick erhalten, sodass Sie manuell durch alle Zustände schalten können, ohne die Zeit aus den Augen zu verlieren.
Gehen Sie auf echter Hardware langsam durch die Zustandsmaschine.
Überprüfen Sie, ob die Heartbeat-LED blinkt (abwechselnd 1/2 Taktfrequenz)
Überprüfen Sie, ob Sie baud_tick
einen Impuls mit der erwarteten Rate erhalten ( TIPP : Ich denke, hier finden Sie das Problem. Was ist, wenn der Anfangswert baud_rate_clock
zufällig ist?)
Überprüfen Sie, ob die Zustandsmaschine außerhalb des Reset-Zustands in den richtigen Zustand wechselt.
Überprüfen Sie, ob Ihre Zustandsmaschine im Leerlauf bleibt, wenn sie Rx
im Leerlauf ist.
Überprüfen Sie, ob Ihre Zustandsmaschine das Startbit erkennt.
Überprüfen Sie, ob Ihre Zustandsmaschine die Zustände korrekt durchläuft und in den Ruhezustand zurückkehrt.
Überprüfen Sie, ob die Werte received_byte[0]
und Ihren Erwartungen entsprechen.received_byte[7]
Ein Oszilloskop ist eine echte Rettungsleine, wenn Sie eine zur Verfügung haben. Auf diese Weise können Sie sehen, wie die Hardware mit voller Geschwindigkeit läuft, ohne mit entprellten Schaltern durchschalten zu müssen. Für ein komplizierteres Design wäre dies ein wesentliches Werkzeug.
3. Warum stimmt die Simulation nicht mit der Realität überein?
Das vollständige Testen eines Designs ist ein wirklich schwieriges Problem. Vor allem, wenn es sich um Ihren eigenen Code handelt. In diesem Beispiel hat Ihr Code die Eingänge clk, reset und Rx. Du fährst schon clk.
Was passiert, wenn Rx beim Verlassen des Zurücksetzens im "falschen" Zustand ist? Wird sich die Zustandsmaschine erholen?
Was ist, wenn der Sender eine andere Baudrate verwendet (+5 %, -5 %, +10 %, -10 %, x2, x0,5)
Was ist, wenn die Zustandsmaschine in einem zufälligen Zustand startet, wird sie sich erholen oder bleibt sie hängen?
Was ist, wenn der Rx-Frame den falschen Stoppbitwert hat?
Was ist, wenn der Rx markiert ist, wenn Sie Platz erwarten?
Was ist, wenn der Rx invertiert ist? (Dies ist ein sehr häufiger Fehler in RS232-UART-Systemen, da der Übersetzer von Logikpegel zu RS232-Pegel normalerweise invertiert.)
Du hast die Idee. Alle Testbedingungen zu finden, ist genauso schwierig wie jede andere Art von Designarbeit. Es wird immer Lücken in der Abdeckung geben. Planen Sie also, zurückzugehen und weitere Tests hinzuzufügen, basierend auf dem, was Sie bei der Überprüfung der Hardware finden.
Eines der schwierigsten Dinge sowohl beim Programmieren als auch beim HDL-Design ist das Testen Ihrer eigenen Designs. Wenn ich eine Testbench für meinen eigenen Code schreibe, mache ich mir immer Sorgen, dass ich nicht genug mögliche Testfälle abgedeckt habe. Und jeder Testfall, der mir einfällt, habe ich wahrscheinlich in meinem Code behandelt – es sind die Tests, an die ich nicht gedacht habe, die Lücken in der Testabdeckung verursachen. Was ist beispielsweise, wenn das Eingangssignal nicht mit dem zu testenden Gerät synchronisiert ist? Das ist bei echter Hardware sehr üblich, kann aber in einer Verhaltenssimulation schwierig einzurichten sein.
Ich hoffe, das reicht, um Sie auf den Weg zu bringen. Viel Glück!
Ihr Code ist nicht synthetisierbar, da ein else
Before vorhanden sein muss case
. Sie hätten es in der Simulation erwischt, indem Sie den reset_n
Stift getestet hätten.
always @(posedge baud_tick or negedge reset_n) begin
if (~reset_n) begin
state <= IDLE; // not a bug, but state should reset to IDLE, not 0
byte_ready <= 1'b0; // not a bug, but size and radix should be defined
// it is recommend not to place a non-async reset in a block with async reset
received_byte <= 8'h0; // reset signal or move to another always block
end
else begin // <== synthesis bug here, the "else begin" was missing <==
case(state)
// ... your state code ...
endcase
end// end to match begin
end
Dieser fehlende else
Fehler sollte in Ihrem Syntheseprotokoll/Bericht enthalten sein.
Ciano
derjohnny
Apalopohapa