Forum: FPGA, VHDL & Co. Verständniss Signale und wann sind ihre Werte valide


von Paule (Gast)


Lesenswert?

Hallo,

ich versuche mich gerade etwas in die Welt der Hardwarebeschreibung 
einzuarbeiten und dabei habe ich anscheinend ein kleines 
Verständnissproblem. Folgendes Szenario : in einem Prozess soll zu jedem 
neuen Takt dem Signal A der Wert von Signal B zugewiesen werden UND dann 
der Wert von Signal B sagen wir mal enfach um 1 erhöht werden.
Da könnte man ja schnell auf die Idee kommen das sowas funktionieren 
würde:
1
testproc : process (clock)
2
begin
3
  if (rising_edge(clock)) then
4
    SignalA <= SignalB;
5
    SignalB <= SignalB + 1;
6
  end if;
7
end process testproc;
ABER es heißt doch, vereinfacht, ein Signal erhällt seinen engültigen 
Zustand erst am Ende eines Prozesses und da man, so wie ich das ganze 
verstehe, nicht davon ausgehen kann das die beiden Zuweisung sequentiell 
ausgeführt werden, wie verhällt sich das dann wirklich?
Hat SignalA am Ende des Prozesses wirklich den Wert von SignalB VOR dem 
+ 1? Und wenn nicht, wovon ich ausgehe (ich kann es leider gerade nicht 
simulieren bzw. auf richtiger hardware ausprobieren wobei ich schätze 
auch mal das bei der Synthese was anderes rauskommt als bei der 
Simulation), wie kann man so etwas evtl. doch realisieren?

von F. F. (foldi)


Lesenswert?

Paule schrieb:
> dabei habe ich anscheinend ein kleines Verständnissproblem.

Ein kleines ist ja niedlich ausgedrückt.

Du hast das noch gar nicht begriffen.

Dein SignalA ist eine Variable, der du den Wert deines SignalB zuweist. 
SignalB musst du erstmal einlesen und dann ist natürlich der Wert deines 
SignalA gleich dem Wert des SignalB+1.
Das "echte" Signal ist in diesem Fall dein SignalB.
Selbst das "+1", was der Realität entsprechen mag, ist ja nur eine 
mathematische Anweisung, die du bestimmst. Es könnte genauso gut "+10" 
sein.
Was aber in der Realität keinen Sinn ergeben würde, wenn du das Signal 
zählen  und auswerten willst.

: Bearbeitet durch User
von Paule (Gast)


Lesenswert?

F. F. schrieb:
> Dein SignalA ist eine Variable, der du den Wert deines SignalB zuweist.
> SignalB musst du erstmal einlesen und dann ist natürlich der Wert deines
> SignalA gleich dem Wert des SignalB+1.
...ok dann hab ich DAS ja doch richtig verstanden: SignalA hat nachher 
den Wert von SignalB +1

> Das "echte" Signal ist in diesem Fall dein SignalB.
> Selbst das "+1", was der Realität entsprechen mag, ist ja nur eine
> mathematische Anweisung, die du bestimmst. Es könnte genauso gut "+10"
> sein.
> Was aber in der Realität keinen Sinn ergeben würde, wenn du das Signal
> zählen  und auswerten willst.
... da komm ich jetzt nicht ganz dahinter was mir das sagen soll... als 
praktischeres Beispiel könnte man auch sagen SignalA ist das 
Adressregister eines Speichers, SignalB ist ein integer der die Adresse 
hällt und der Prozess sollte bei jedem neuen Takt fortlaufend etwas in 
den Speicher schreiben (der Teil fehlt natürlich in dem Beispiel code, 
und das evtl benötigte konvertieren von integer in std_logic_vector 
spare ich mir jetzt auch mal).

Wie sollte man das denn angehen ohne einen weiteren Takt zu brauchen um 
dann darin SignalB zu erhöhen?

von ollib (Gast)


Lesenswert?

Also vielleicht stehe ich ja auf dem Schlauch, aber ich glaube foldi hat 
das noch nicht richtig begriffen.
SignalA und SignalB sind eben gerade KEINE Variablen, sondern Signale. 
Das ist in VHDL ein Unterschied. Und Signale bekommen den neuen Zustand 
erst im nächsten Takt. Von daher hat SignalA natürlich den Wert von 
SignalB bevor dieses erhöht wurde.

von VHDL hotline (Gast)


Lesenswert?

Falls das hier auf Mehrheitsentscheidung hinausläuft: Ich schließe mich 
ollib an.
In Hardware hast du ein Register für SignalA und eines für SignalB und 
einen kombinatorischen Adder für das increment
1
    ____________________
2
   |                    |
3
   v                    |
4
 adder -> Register -SignalB-> Register -SignalA->

In welcher Reihenfolge du die Zuweisungen schreibst ist bei Signalen 
egal. Virtuell gedacht werden die Zuweisungen erst zu Prozessende alle 
gleichzeitig ausgeführt. Sequentiell ist innerhalb einen Prozesses nur 
wichtig, wenn du mehrere male das gleiche Signal (Register) beschreibst, 
dann wird einfach die letzte Zuweisung umgesetzt.

von F. F. (foldi)


Lesenswert?

ollib schrieb:
> Das ist in VHDL ein Unterschied.

Ups, Vhdl. Klar, da ist das anders.
Ich habe das nur als Beispiel angesehen, wie ein uC das verarbeitet.

von Paule (Gast)


Lesenswert?

ollib schrieb:
> und Signale bekommen den neuen Zustand
> erst im nächsten Takt. Von daher hat SignalA natürlich den Wert von
> SignalB bevor dieses erhöht wurde.
wenn die Signale ihren Zustand erst im nächsten Takt erhalten und das 
alles nahezu parallel ausgeführt wird, wie kann dann bitte SignalA den 
Wert von SignalB VOR dem erhöhen erhalten??? Also um jetzt endgültig 
sicher zugehen werde ichs später mal einfach auf richtiger hardware 
ausprobieren, ist dann doch praktikabler als Rätselraten.
Aber trotzdem Vielen Dank euch!

von P. K. (pek)


Lesenswert?

Paule schrieb:
> wie kann dann bitte SignalA den
> Wert von SignalB VOR dem erhöhen erhalten???

Weil in einem geclockten Prozess vor der Signalzuweisung "<=" jeweils 
der nächste Zustand des Registers, hinter der Signalzuweisung "<=" 
jeweils der aktuelle Zustand des Registers (zum Zeitpunkt der Flanke) 
relevant ist.

Auch wenn viele alte Hasen diese als unschön empfinden: Für Einsteiger 
ist die Zweiprozessschreibweise vermutlich plausibler, weil da dann 
explizit zwischen SignalA_cur und SignalA_next unterschieden wird (lies 
Dich mal durch 
http://www.lothar-miller.de/s9y/archives/43-Ein-oder-Zwei-Prozess-Schreibweise-fuer-FSM.html).

So oder so: Wichtig ist es in Hardware, also Registern und einer 
Übergangslogik (mit Propagationdelay > 0ns) zu denken, dann klappt es.

von Paule (Gast)


Lesenswert?

...so um die Sache abzuschließen hab ich es einfach grad ausprobiert auf 
einem de0-nano board und tja wer hätts gedacht, der folgende code gibt 
als erste Bitfolge mit den LEDs eine "1" aus und nicht etwa eine "2" !
1
library IEEE;
2
use IEEE.std_logic_1164.all;
3
use IEEE.numeric_std.all;
4
5
entity SignalTest is 
6
  port(
7
    clock : in std_logic;
8
    leds : out std_logic_vector(7 downto 0);
9
    btn : in std_logic
10
  );
11
end entity;
12
    
13
architecture behave of SignalTest is
14
15
Type Tstate is (idle, run, waiting);
16
signal state : Tstate := idle;
17
signal waitCounter : integer range 0 to 50000000 := 50000000;
18
signal signala : std_logic_vector(7 downto 0) := (others => '0');
19
signal signalb : integer range 0 to 256 := 0;
20
21
begin
22
23
  process
24
  begin
25
    wait until rising_edge(clock);
26
    
27
    case state is
28
    
29
      when idle =>
30
        signala <= (others => '0');
31
        signalb <= 1;
32
        leds <= (others => '0');
33
        if (btn = '0') then
34
          state <= run;
35
        end if;
36
        
37
      when run =>
38
        if (signalb < 8) then
39
          signala <= std_logic_vector(to_unsigned(signalb, 8));
40
          signalb <= signalb + 1;
41
          waitCounter <= 50000000;
42
          state <= waiting;
43
        else
44
          state <= idle;
45
        end if;
46
      
47
      when waiting =>
48
        leds <= signala;
49
        if (waitCounter > 0) then
50
          waitCounter <= waitCounter - 1;
51
        else
52
          state <= run;
53
        end if;
54
    
55
    end case;
56
  end process;
57
58
end behave;

von Schlumpf (Gast)


Lesenswert?

P. K. schrieb:
> Weil in einem geclockten Prozess vor der Signalzuweisung "<=" jeweils
> der nächste Zustand des Registers, hinter der Signalzuweisung "<="
> jeweils der aktuelle Zustand des Registers (zum Zeitpunkt der Flanke)
> relevant ist.

Oder anders ausgedrückt:

In einem getakteten Prozess baut die Synthese aus jeder "<=" Zuweisung 
erst mal nur ein Register und zwar der Form:

Registerausgang <= Registereingang

Diese Register haben alle den gleichen Takt (in deinem Fall clock)

Der Rest ist kombinatorik zwischen den Registern.

In deinem Fall werden also zwei 8-Bit Register gebaut "signala" und 
"signalb".
signala übernimmt mit jedem Takt den Wert von signalb.
Der Ausgang des signalb-Registers ist auf dessen Eingang mit einem 
Addierer zurückgekoppelt.

Dazu kannst dir ja mal den Schaltplan hinzeichnen und dann wird dir 
vielleicht einiges klarer.

Anmerkung:
Was ich geschrieben habe, gilt natürlich nicht 1:1 für deinen 
Beispielcode.
Denn hier wirkt z.B. auch der Zustand der FSM noch zusätzlich 
kombinatorisch auf deine Registereingänge.
Aber für den Code im Eingangspost passt das.

von Schlumpf (Gast)


Lesenswert?

ach ja.. und brandgefährlich ist die Tatsache, dass du den btn-Eingang 
nicht synchronisiert hast.
Das kann im ungünstigen Fall deine FSM, den signalb-Counter und deinen 
Waitcounter total aus dem Tritt bringen.

Also, falls du dich irgendwann wunderst, warum das Ganze nicht immer so 
läuft, wie du dir das vorstellst, dann liegt das mit großer 
Wahrscheinlichkeit daran.

Erklärung für die FSM:

Der Zustand der FSM wird in Registern gespeichert.
state <= ......

Diese Register arbeiten alle mit dem gleichen Takt.
Das Signal deines Tasters kommt jetzt zu irgendeinem beliebigen 
Zeitpunkt.
Wenn das jetzt mehr oder weniger gleichzeitig mit der Flanke des Taktes 
geschieht, dann passiert Folgendes:

Die Kombinatorik, die den Zustand des Buttons mit dem aktuellen Zustand 
von "state" verwurschtelt und daraus den Folgezustand ermittelt, wirkt 
auf die Eingänge des "state"-Registers.
Nun sind aber die logischen Pfade vom BTN-Eingang zu den 
Registereingängen nicht identisch gleich lang. Es kann dann also 
passieren, dass für einen Teil der state-Register an deren Dateneingang 
schon der Folgezustand anliegt und an einem anderen Teil noch nicht. Und 
dann kommt der Takt...
Dann kann es also passieren, dass ein "Zustandsmischmasch" in "state" 
gespeichert wird.
Entweder gibt es diesen Zustand tatsächlich, dann springt die FSM dort 
hin und kommt "vielleicht" wieder raus (je nachdem, was du in diesem 
Zustand codiert hast). Oder dieser Zustand ist gar nicht von dir 
codiert, dann hat sich die Schaltung definitiv verklemmt.

von Paule (Gast)


Lesenswert?

Schlumpf schrieb:
> Wenn das jetzt mehr oder weniger gleichzeitig mit der Flanke des Taktes
... ok, verstehe das sowas generell ein Problem geben könnte, aber ich 
dachte durch das wait until rising_edge(clock) am Anfang des Prozess 
wird das ganze synchron da ich ja sonst nirgendswo anders btn einlese 
und das immer nur zur steigenden Flanke des Taktes.

von Paule (Gast)


Lesenswert?

Paule schrieb:
> Schlumpf schrieb:
>> Wenn das jetzt mehr oder weniger gleichzeitig mit der Flanke des Taktes
> ... ok, verstehe das sowas generell ein Problem geben könnte, aber ich
> dachte durch das wait until rising_edge(clock) am Anfang des Prozess
> wird das ganze synchron da ich ja sonst nirgendswo anders btn einlese
> und das immer nur zur steigenden Flanke des Taktes.
ok hatte es wohl doch noch nicht richtig verstanden, das mit den 
unterschiedlich langen Signallaufzeiten durchs routing hatte ich 
ignoriert aber 
http://www.lothar-miller.de/s9y/categories/35-Einsynchronisieren hats 
mir dann nochmal verdeutlicht, die dort gezeigte 2 FlipFlop Lösung 
sollte für das simple design ja ausreichend sein. Da mal ein generelles 
Danke dafür das es diese geniale webseite überhaupt gibt! Natürlich auch 
Danke allen anderen für die Erklärungen!

von Weltbester FPGA-Pongo (Gast)


Lesenswert?

Bin nicht sicher, ob Du das verstanden hast. Die Zeitpunkte, bei denen 
Signale gültig werden, hat nichts mit den Durchlaufzeiten zu tun.

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.