Forum: FPGA, VHDL & Co. UART Kommandos funktionieren nicht


von Stefan L. (aranel)


Lesenswert?

Hallo zusammen!

Anfängerproblem: Ich würde gerne über UART Kommandos und Daten an einen 
FPGA schicken (zum Testen benutze ich das BASYS 3 Board von Digilent), 
worauf dieser dann entsprechend reagieren soll. Leider funktioniert das 
überhaupt nicht.

Ich benutze dazu eine FSM, die die ankommenden Bytes vom UART Receiver 
per Handshake abholt, prüft ob diese mit definierten Bitmustern 
(Kommandos) übereinstimmen und dann eine entsprechende Ausgabe über den 
Transmitter macht. (Falls sie mit keinem Bitmuster übereinstimmen, soll 
es sich um einfache Datenbytes handeln) Um das zu testen, lasse ich alle 
"dekodierten" Eingaben so einfach auch wieder ausgeben. Die Simulation 
mit einem Testbench funktioniert.

Hier mal eine verkürzte Version dieser FSM mit dem Process (auch genauso 
getestet):
1
process(clk)
2
begin
3
    if rising_edge(clk) then
4
        if rst = '1' then
5
            -- Synchroner reset.
6
            state <= s_Idle;
7
            s_rx_ack <= '0'; -- Handshake für UART Receiver
8
            s_tx_ack <= '0'; -- Startsignal für UART Transmitter
9
            -- ...
10
        end if;
11
    else
12
        case state is
13
            when s_Idle =>
14
                if s_rx_rdy = '1' then -- neues Byte im Receiver verfügbar
15
                    s_buffer <= s_rx_data; -- Byte vom Receiver zwischenspeichern
16
                    s_rx_ack <= '1'; -- Handshake
17
                    state <= s_Decode;
18
                end if;
19
20
            when s_Decode =>
21
                s_rx_ack <= '0'; -- Handshake zurücksetzen
22
                
23
                if s_buffer(7) = '1' then -- 1XXXXXXX (Kommando)
24
                    -- Kommando
25
                    if s_buffer(6) = '1' then -- Zur Demonstration hier nur vereinfacht zwei Kommandos
26
                        s_output <= "00000001";
27
                    else
28
                        s_output <= "00000000";
29
                    end if;
30
                else -- 0XXXXXXX (Daten)
31
                    -- Datenbyte
32
                    s_output <= s_buffer; -- Datenbyte wird unverändert übernommen
33
                end if;
34
          
35
            when s_Send =>
36
                if s_tx_rdy = '1' then -- warten bis UART Tx bereit
37
                    s_tx_data <= s_output; -- zu sendendes Byte übergeben
38
                    s_tx_ack <= '1'; -- Startsignal geben
39
                    state <= s_CleanUp;
40
                end if;
41
42
            when s_CleanUp =>
43
                s_tx_ack <= '0';
44
                state <= s_Idle;
45
        end case;
46
    end if;
47
end process;

Es kommt mir so vor als würde der Schritt im s_Decode-Zustand ignoriert, 
bzw. die Ausgaben passen überhaupt nicht zu etwas erwartbarem.

Ich würde fast ausschließen, dass der UART Receiver/Transmitter 
fehlerbehaftet ist: Wenn ich den s_Decode-Zustand quasi weg lasse und 
alles eingehende unverändert wieder rausschicke, funktioniert es 
tadellos. Ich vermute daher stark, dass der Fehler hier im s_Decode 
Bereich liegt. Vermutlich ein Fehler der vom Software-denken herrührt 
und der mit VHDL gänzlich anders gelöst werden muss. Was ich bereits 
erfolglos versucht habe, ist ein Auslagern in einen kombinatorischen 
Prozess oder in eine Funktion, genauso wie ständiges Umbauen der FSM.

Ich bin über jeden Input/Tipp dankbar, weil mir mittlerweile echt die 
Ideen ausgegangen sind.

Falls weiterer Code benötigt wird, hänge ich diesen gerne an, wollte es 
nur beim wesentlichen erstmal belassen.

Vielen Dank!

von Achim S. (Gast)


Lesenswert?

Stefan L. schrieb:
> Ich bin über jeden Input/Tipp dankbar, weil mir mittlerweile echt die
> Ideen ausgegangen sind.

du wertest im Decode die Bits 7 und 6 von s_buffer aus. Kann es sein, 
dass die  Bitreihenfolge anders ist als erwartet (d.h. du wertest in 
Wirklichkeit Bit 0 und 1 aus)?

Dein Board hat doch diverse LEDs. Lass zur Kontrolle einfach mal auf den 
LEDs ausgeben, was du in s_buffer bekommst und was in s_output landet. 
Und dann schicke einzelne Bytes und schau auf den LEDs an, was decodiert 
wird.

von Stefan L. (aranel)


Lesenswert?

Gute Idee! Leider liefert das auch sehr verwirrende Ergebnisse... Kann 
man das generell schon so machen wie ich das angehe? Oder sollte man den 
Code anders formulieren? Falls das okay ist, sollte ich den Fehler 
vielleicht doch wo anders suchen, eventuell mal eine andere UART 
Implementierung ausprobieren.

von Achim S. (Gast)


Lesenswert?

Stefan L. schrieb:
> Oder sollte man den
> Code anders formulieren?

Ups: jetzt sehe ich den Fehler. Du hast fast den gesamten Code im 
else-Zweig von     if rising_edge(clk) then

Du wolltest ihn eigentlich im else-Zweig von if reset='1' then...

Dein Simulator hat dir wohl Warnungen geben, dass die Sensitivity nicht 
vollständig ist, oder?

von Stefan L. (aranel)


Lesenswert?

Gut gesehen, aber das war leider ein blöder Fehler von mir, habe es 
falsch eingegeben. :/ Hier die hoffentlich korrekte Version.
1
process(clk)
2
begin
3
    if rising_edge(clk) then
4
        if rst = '1' then
5
            -- Synchroner reset.
6
            state <= s_Idle;
7
            s_rx_ack <= '0'; -- Handshake für UART Receiver
8
            s_tx_ack <= '0'; -- Startsignal für UART Transmitter
9
            -- ...
10
        else
11
            case state is
12
13
                when s_Idle =>
14
                    if s_rx_rdy = '1' then -- neues Byte im Receiver verfügbar
15
                        s_buffer <= s_rx_data; -- Byte vom Receiver zwischenspeichern
16
                        s_rx_ack <= '1'; -- Handshake
17
                        state <= s_Decode;
18
                    end if;
19
20
                when s_Decode =>
21
                    s_rx_ack <= '0'; -- Handshake zurücksetzen              
22
23
                    if s_buffer(7) = '1' then -- 1XXXXXXX (Kommando)
24
                    -- Kommando
25
                        if s_buffer(6) = '1' then -- Zur Demonstration hier nur vereinfacht zwei Kommandos
26
                            s_output <= "00000001";
27
                        else
28
                            s_output <= "00000000";
29
                        end if;
30
31
                    else -- 0XXXXXXX (Daten)
32
                        -- Datenbyte
33
                        s_output <= s_buffer; -- Datenbyte wird unverändert übernommen
34
                    end if;         
35
36
                when s_Send =>
37
                    if s_tx_rdy = '1' then -- warten bis UART Tx bereit
38
                        s_tx_data <= s_output; -- zu sendendes Byte übergeben
39
                        s_tx_ack <= '1'; -- Startsignal geben
40
                        state <= s_CleanUp;
41
                    end if;
42
43
                when s_CleanUp =>
44
                    s_tx_ack <= '0';
45
                    state <= s_Idle;
46
            end case;
47
        end if;
48
    end if;
49
end process;

Ist aber gut das Du mal drüber geschaut hast, vielen Dank!

von Achim S. (Gast)


Lesenswert?

Stefan L. schrieb:
> Gut gesehen, aber das war leider ein blöder Fehler von mir, habe es
> falsch eingegeben. :/ Hier die hoffentlich korrekte Version.

Hmmm

Kannst du nicht den tatsächlichen Code per Copy&Paste rüberziehen (ohne, 
dass sich dabei weitere Fehler einschleichen)?

Dieser Code sieht jetzt imho grundsätzlich korrekt aus (den UART-Teil 
mit seinem Handshake sehe ich im obigen Code-Schnippsel natürlich nicht. 
Selbst die Einstellung der Baudrate kann ja z.B. völlig daneben sein).

Stefan L. schrieb:
> Leider liefert das auch sehr verwirrende Ergebnisse...

Was sind denn die verwirrenden Ergebnisse? Schicke mal einzelne Bytes 
mit charakteristischen Bitmustern. Notiere, was gesendet wurde und was 
auf den LEDs angezeigt wird, und lade eine Liste von Werte hier hoch. 
Vielleicht erkennt jemand ein Muster...

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Stefan L. schrieb:
> Wenn ich ... alles eingehende unverändert wieder rausschicke,
> funktioniert es tadellos.
Was passiert denn, wenn du nicht das gerade empfangene Byte wieder 
zurückschickst, sondern einfach ein U (=0x55)? Oder wenn du mit jedem 
empfangenen Byte einen Zähler um 1 hochzählst und diesen Zählerstand 
dann zurückschickst? Was kommt dann am Terminal an?

von Duke Scarring (Gast)


Lesenswert?

Stefan L. schrieb:
> Die Simulation
> mit einem Testbench funktioniert.
Kannst Du die funktionierende Simulation (VHDL + Testbench) evtl. hier 
einstellen (als Anhang)?
Dann könnte ich mir das anschauen und testweise durch meinen Simulator 
schicken.

Duke

von Stefan L. (aranel)


Angehängte Dateien:

Lesenswert?

Erstmal vielen Dank für die wertvolle Hilfe! Ich habe den Code mal mit 
angehängt. Dieser folgt der Hierarchie:

1. Loopback.vhd
2. uart.vhd
3. uart_rx.vhd
4. uart_tx.vhd
(Auflistung wollte jetzt nicht so wie ich wollte, aber ich hoffe es ist 
verständlich)

Testbench: Loopback_Tb.vhd

Vivado Constraints: Constraints.xdc (FYI)



Achim S. schrieb:
> Was sind denn die verwirrenden Ergebnisse? Schicke mal einzelne Bytes
> mit charakteristischen Bitmustern. Notiere, was gesendet wurde und was
> auf den LEDs angezeigt wird, und lade eine Liste von Werte hier hoch.
> Vielleicht erkennt jemand ein Muster...

Mache ich sofort im Anschluss.


Lothar M. schrieb:
> Stefan L. schrieb:
>> Wenn ich ... alles eingehende unverändert wieder rausschicke,
>> funktioniert es tadellos.
> Was passiert denn, wenn du nicht das gerade empfangene Byte wieder
> zurückschickst, sondern einfach ein U (=0x55)? Oder wenn du mit jedem
> empfangenen Byte einen Zähler um 1 hochzählst und diesen Zählerstand
> dann zurückschickst? Was kommt dann am Terminal an?

Im Falle, dass ich ein konstantes Byte (0x55) bei jedem eintreffenden 
Byte zurückschicke, passiert gar nichts (Loopback.vhd Zeile 113-116). 
Also empfange am PC nichts (das entsprechende LED am Board leuchtet auch 
nicht auf), es wird augenscheinlich nichts geschickt. Ist mir völlig 
unverständlich vor allem weil der einfache Loopback funktioniert. Die 
Sache mit dem Zähler versuche ich gleich mal noch einzubauen.



Duke Scarring schrieb:
> Stefan L. schrieb:
>> Die Simulation
>> mit einem Testbench funktioniert.
> Kannst Du die funktionierende Simulation (VHDL + Testbench) evtl. hier
> einstellen (als Anhang)?
> Dann könnte ich mir das anschauen und testweise durch meinen Simulator
> schicken.
>
> Duke

Habe alles mal angehängt. Ich gebe zu, der Testbench ist dilettantisch. 
Es werden zwei Bytes geschickt, eines das als Datenbyte interpretiert 
werden soll und eines als Kommando.

Ich arbeite mal die Arbeitsaufträge weiter ab, falls noch weitere Daten 
gewünscht werden, gerne.

von Duke Scarring (Gast)


Angehängte Dateien:

Lesenswert?

Die Simulation läuft nach kleinen Anpassung (LED1, LED2) erstmal durch.
Ist das gewollt, das die Baudrate bei 1,15 MHz ist?
Außerdem würde ich zwischen den Übertragungen auch mal eine kleine Pause 
lassen, damit sich das Auge synchronisieren kann :-)

Duke

von Stefan L. (aranel)


Angehängte Dateien:

Lesenswert?

Duke Scarring schrieb:
> Die Simulation läuft nach kleinen Anpassung (LED1, LED2) erstmal
> durch.
> Ist das gewollt, das die Baudrate bei 1,15 MHz ist?
> Außerdem würde ich zwischen den Übertragungen auch mal eine kleine Pause
> lassen, damit sich das Auge synchronisieren kann :-)
>
> Duke

Diese Ungereimtheit mit der Baudrate hat mich etwas stutzig gemacht. Als 
Baudrate soll 115200 und als Clock Takt 10 MHz verwendet werden. Ich 
habe mal einen etwas "schöneren" Testbench geschrieben. Da scheint es 
auch korrekt zu funktionieren, bis auf das erste Byte, das nicht richtig 
erkannt wird, wird alles richtig ersetzt und interpretiert. Zumindest 
sehe ich hier keinen Fehler.

- s_tx_data[7:0] - Byte das der UART Transmitter sendet
- s_rx_data[7:0] - Byte das der UART Receiver liefert

- s_rx_port - Serieller Eingang
- s_tx_port - Serieller Ausgang

- s_buffer[7:0] - Interner Eingangsbuffer
- s_output[7:0] - Interner Ausgangsbuffer (enthält Kommandos/Daten als 
erstes)

- s_rx_rdy - Zeigt an wenn neues Byte im Receiver verfügbar ist
- s_rx_ack - Handshake wenn Byte abgeholt wird

- s_tx_ack - Sendebefehl für Transmitter
- s_tx_rdy - Zeigt an wenn Transmitter bereit ist

Habe das erste Byte markiert, dass vom Receiver empfangen wird und das 
zweite Byte, welches der Transmitter verschickt.

Übersetzt werden soll so:
- 1XXXXXXX -> Kommando /
- 11XXXXXX -> Cmd1 -> 0x01
- 10XXXXXX -> Cmd2 -> 0x00
- 0XXXXXXX -> Daten -> unverändert wieder rausgehen

von Achim S. (Gast)


Lesenswert?

Laut constraints und laut Kommentaren im VHDL-Code erwartest du eine 10 
MHz Clock direkt von Pin W5.

Laut Usermanual deines boards ist dort aber eine 100MHz Clock 
angeschlossen.
https://digilent.com/reference/_media/basys3:basys3_rm.pdf

Wenn du die einfach so in deine UART einfütterst ist die Einstellung für 
die Baudrate um eine Größenordnung zu hoch.

-> entweder das generic  clk_cycles_per_bit entsprechend anpassen.
oder aus dem 100 MHz an W5 per DCM einen internen 10 MHz-Takt 
generieren.

von Stefan L. (aranel)


Angehängte Dateien:

Lesenswert?

Achim S. schrieb:
> Laut constraints und laut Kommentaren im VHDL-Code erwartest du
> eine 10
> MHz Clock direkt von Pin W5.
>
> Laut Usermanual deines boards ist dort aber eine 100MHz Clock
> angeschlossen.
> https://digilent.com/reference/_media/basys3:basys3_rm.pdf
>
> Wenn du die einfach so in deine UART einfütterst ist die Einstellung für
> die Baudrate um eine Größenordnung zu hoch.
>
> -> entweder das generic  clk_cycles_per_bit entsprechend anpassen.
> oder aus dem 100 MHz an W5 per DCM einen internen 10 MHz-Takt
> generieren.

Das würde tatsächlich auch diese wirren Ausgaben erklären oder?  Linke 
Spalte ist was ich reingeschickt habe, s_buffer ist der Eingangsbuffer 
vom Uart, s_output der Ausgangsbuffer und Output zeigt, was bei mir am 
Terminal ankam. (Bis einschl. 0x7F hätte alles unverändert wieder 
rausgehen soll, ab dann entweder 0x00 oder 0x01)

Ich habe naiverweiße gedacht, man kann den Takt mittels der Constraints 
festlegen.

von Stefan L. (aranel)


Lesenswert?

Achim S. schrieb:
> Laut constraints und laut Kommentaren im VHDL-Code erwartest du
> eine 10
> MHz Clock direkt von Pin W5.
>
> Laut Usermanual deines boards ist dort aber eine 100MHz Clock
> angeschlossen.
> https://digilent.com/reference/_media/basys3:basys3_rm.pdf
>
> Wenn du die einfach so in deine UART einfütterst ist die Einstellung für
> die Baudrate um eine Größenordnung zu hoch.
>
> -> entweder das generic  clk_cycles_per_bit entsprechend anpassen.
> oder aus dem 100 MHz an W5 per DCM einen internen 10 MHz-Takt
> generieren.

Das war es tatsächlich... Kurz im Code geändert, funktioniert wie es 
soll. Sowas dummes...

Vielen Dank Achim und die anderen für die sehr große Hilfe!

von TRAU (Gast)


Lesenswert?

Es gäbe da auch noch eine andere Möglichkeit.

von Achim S. (Gast)


Lesenswert?

Stefan L. schrieb:
> Das würde tatsächlich auch diese wirren Ausgaben erklären oder?

Deine UART_RX.vhd überprüft den korrekten Wert des Stop-Bits nicht. 
Deswegen werden die empfangenen Bytes nicht wegen ungültigen Wert des 
Stop-Bits verworfen sondern als gültiges Byte gezählt. (Mit Kontrolle 
des Stopbits wäre zumindest ein Teil der übetragenen Bytes verworfen 
worden.)

Und bei den empfangenen Bytes (s_buffer LEDin) sind immer viele 
benachbarte Bits auf dem identischen Wert, weil jedes einzelne Bit der 
115kBaud Übertragung mehrfach abgetastet und an mehrere benachbarte 
Stellen von s_buffer geschrieben wird.

Stefan L. schrieb:
> Ich habe naiverweiße gedacht, man kann den Takt mittels der Constraints
> festlegen.

das war falsch gedacht. Mit den clock period Constraint teilst du deinem 
Synthesetool mit, wie schnell deine Logik laufen soll. Dann wird bei der 
Implementierung darauf geachtet, dass sie diese Geschwindigkeit auch 
schafft. (Falls nicht werden entsprechende Meldungen ausgegeben).

Stefan L. schrieb:
> kurz im Code geändert, funktioniert wie es
> soll.

gut, wieder was dazu gelernt ;-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.