[Verilog] Warum ist hier die Ausgabe bitte 1,3,5,7.und nicht 1,2,3,4,5

Gast #4670317
Lesenswert?

Hallo, guten Tag.
Warum ist die serielle Ausgabe bitte 1,3,5,7....und nicht 1,2,3,4,5...

Danke.
Gruss
1
module rs232tx(input clk, output UART_TX);
2
  reg [9:0] sendbuf = 9'b000000001;
3
  reg [7:0] data = 0;
4
  reg send = 0;
5
  reg sending  = 0;
6
 
7
  reg [13:0] timeout;
8
  assign UART_TX = sendbuf[0];
9
 
10
  always @(posedge clk) begin
11
    if (send && !sending) begin
12
      sendbuf <= {1'b1, data, 1'b0};
13
      sending <= 1;
14
      timeout <= 1406 - 1; 
15
    end else begin
16
      timeout <= timeout - 1;
17
    end
18
    
19
    if (sending && timeout == 0) begin
20
      timeout <= 1406 - 1; 
21
      if (sendbuf[8:0] == 9'b000000001) begin
22
        sending <= 0;
23
      end else
24
        sendbuf <= {1'b0, sendbuf[9:1]};
25
    end 
26
  end
27
  
28
  always @(posedge clk) begin
29
    if (data < 100 && sending == 0) begin
30
     data <= data+1;
31
     send <= 1;
32
    end else begin
33
      send <=0;
34
   end
35
  end
36
endmodule
Gast #4670391
Lesenswert?

Diese Programmiersprache habe ich noch nie gesehen. Ich rate mal:

Es scheint hier zwei konkurrierende Threads zu geben:

1) Der obere Thread sende Daten.
2) Der untere Thread erzeugt die Daten.

Beabsichtigt war wohl dass der untere Thread die Variable "data" immer 
dann um 1 erhöht (und das Senden startet), wenn der Sender nicht aktiv 
ist.

Dazu setzt der Sender wiederum seinen Status in der Variable "sending".

Der Fehler besteht in der fehlenden Synchronisation dieser Prozesse. 
Wenn die Threads durch einen aktiven Task-Scheduler ausgeführt werden 
(also immer ein bisschen von jedem Thread), könnte sich folgender Ablauf 
ergeben:
1
Thread 2:
2
if (data < 100 && sending == 0) begin
3
     data <= data+1; // Data wird von 0 nach 1 erhöht
4
     send <= 1;
5

6
Thread 1:
7
if (send && !sending) begin
8
      sendbuf <= {1'b1, data, 1'b0};
9

10
Thread 2:
11
// Achtung: sending ist immer noch 0!
12
if (data < 100 && sending == 0) begin
13
     data <= data+1; // Data wird von 1 nach 2 erhöht
14
     send <= 1;
15

16
Thread 1:
17
      sending <= 1;
18
      timeout <= 1406 - 1;

Ebenso kann der selbe Fehler auftreten, wenn das Programm auf mehreren 
CPU's oder CPU Kernen ausgeführt wird.

Du brauchst einen zuverlässigen Mechanismus, um die Prozesse zu 
synchronisieren. Zum Beispiel, indem du irgendwie verhinderst, dass die 
folgenden Zeilen von Thread1 für andere Threads unterbrochen werden:
1
// Andere Threads pausieren
2
if (send && !sending) begin
3
      sendbuf <= {1'b1, data, 1'b0};
4
      sending <= 1;
5
// Andere Threads fortfahren

Manche CPU's haben dazu einen speziellen Befehl, der eine Variable 
testet und zugleich setzte. In Pseudo-Code nach diesem Prinzip:
1
if (ändere sending von 0 nach 1 und wenn das geklappt hat, dann)
2
  // sende daten
3
end

Hier findet die Änderung und der Test in einer einzigen nicht 
unterbrechbaren Operation statt. Andere Threads sehen daher niemals 
einen falschen Zwischenstatus.

Auf Mikrocontrollern sieht man als einfach Lösung oft, dass kurzzeitig 
alle Interrupts deaktiviert werden. Dadurch wird der aktuelle Thread 
garantiert nicht unterbrochen.

Auf PC stellen Linux und Windows dafür spezielle Funktionen bereit, die 
in der Regel vom Framework der Anwendung gekapselt und benutzt werden.

Die richtigen Suchwörter zu dem Thema sind: Synchronisierung, Threads, 
Concurreny, Mutex, Atomare Zugriffe
Gast #4670394
Lesenswert?

Nachtrag zum möglichen Ablauf:

Die Variable data wurde nun schon zweimal erhöht, bevor sie gesendet 
wurde. Die Schrittweite 2, die du beobachtest hast ist mehr oder weniger 
ein Zufallsprodukt. Die Schrittweite könnte auch viel größer sein oder 
unregelmäßig.
Gast #4670395
Lesenswert?

> Das ist gar keine Programmiersprache

Oh mein Fehler. Mit FPGA's kenne ich mich gar nicht aus. Aber ich 
glaube, die führen viele Aktionen zeitgleich aus - insofern passt meine 
Überlegung trotzdem Auch wenn es gar keine CPU's sind.
Gast #4670399
Lesenswert?

hast du einen Simulator zur Hand?

Dann nutz den mal, und schau dir an, wann send aktiv wird, wann sending 
aktiv wird und in welchen Taktzyklen data hochgezählt wird.

Stefan U. schrieb:
> ie Schrittweite könnte auch viel größer sein oder
> unregelmäßig.

Nein: die Schrittweite wird immer genau 2 sein.
Gast #4670762
Lesenswert?

Markus F. schrieb:
> ... seit Peter Otto heißt, nutzt er Verilog. Und wahrscheinlich tut's
> deswegen nicht ;)

Wenn sich sein Benehmen so bessert wie die Wahl der Beschreibungsprache 
gibts ja noch Hoffnung ;-)

Um Achim S. Hinweise etwas zu verdeutlichen:
Wenn 'sending' = 0 wird zählst du hoch.
Wenn du hochzählst setzt du im gleichen Takt 'send'.
Wenn 'send' gesetzt ist wird im nächsten Takt 'sending' gesetzt.
Wieviele Takte lang ist dann 'if (data < 100 && sending == 0)' wahr?

Du musst also in der if Bedingung noch etwas verwenden das sich schon im 
nächsten Takt geändert hat. 'send' würde sich da anbieten...

Andi
Moderator (Firma: Titel) Persönliche Seite #4670987
Lesenswert?

FRANK schrieb:
> Auch eine Hardware-Beschreibungssprache ist eine Programmiersprache.
So wie ein Traktor und ein LKW natürlich auch ein Automobil ist, weil 
sie sich selbständig bewegen...

otto schrieb:
> Warum ist die serielle Ausgabe bitte 1,3,5,7
Die Ursache ist Latency. Im Simulator siehst du, dass sending 2 Takte 
lang 0 ist...

Schreib da unten mal sowas:
1
  always @(posedge clk) begin
2
    if (data < 100 && sending == 0 && send == 0) begin
3
     data <= data+1;
4
     send <= 1;
5
    end else begin
6
      send <=0;
7
   end
8
  end

Stefan U. schrieb:
> Die Schrittweite ... ein Zufallsprodukt. Die Schrittweite könnte auch
> viel größer sein oder unregelmäßig.
Sie ist in dem Fall hier vollkommen deterministisch und immer '2'. Das 
ist das Schöne an der Hardware... ;-)
Gast #4671116
Lesenswert?

>> Die Schrittweite ... ein Zufallsprodukt. Die Schrittweite könnte auch
>> viel größer sein oder unregelmäßig.

> Sie ist in dem Fall hier vollkommen deterministisch und immer

Ich war ja auch fälschlicherweise von einem Mikroprozessor mit einem 
Multitaksing OS ausgegangen.
Moderator (Firma: Titel) Persönliche Seite #4671473
Lesenswert?

Lothar M. schrieb:
> Schreib da unten mal sowas:  ...
Besser wäre sogar sowas:
1
  always @(posedge clk) begin
2
    if (data < 100 && sending == 0 && send == 0) begin
3
     data <= data+1;
4
     send <= 1;
5
    end
6
    if (sending == 1) begin
7
      send <=0;
8
    end
9
  end
Denn dann würde das Flag send erst quittiert, wenn tatsächlich mit dem 
Senden begonnen worden wäre und dies über das Flag sending angezeigt 
wird...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren