Bei VHDL verstehe ich den Sinn des Schlüsselworts "Process" nicht ganz
und habe versucht, das ganze mit folgendem einfachen "blink" programm zu
analysieren.
> IF(clk'EVENT AND clk = '1') THEN
dient ja dazu, den Counter bei der steigenden Flanke zu inkrementieren.
Ich habe gelesen, dass mit der "sensitive list" in "PROCESS(clk)" immer
auf ein Wechsel des Signals getriggert wird.
Wenn ich also die Zeile für die steigende Flanke auskommentiere, sollte
die LED eigentlich doppelt so schnell blinken, da ja jetzt auf jede
Änderung von "clk" der Counter erhöht werden sollte.
Wo liegt der Denkfehler?
chris schrieb:> Wo liegt der Denkfehler?
Die Sensitivliste ist NUR und AUSSCHLIEßLICH für die Simulation
relevant.
> sollte die LED eigentlich doppelt so schnell blinken
Tut sie auch, wenn du das Design simulierst.
>> IF(clk'EVENT AND clk = '1') THEN> dient ja dazu, den Counter bei der steigenden Flanke zu inkrementieren.
Durch das 'event werden vom Synthesizer Flipflops ins Design eingebaut.
Meinst du in der Simulation oder auf dem Chip?
Auf dem Chip hat die Liste keine Auswirkung. Die gilt nur für die
Simulation. Wenn sich auf dem Chip nichts ändert, liegt das wohl daran,
dass Flip-Flops nur auf eine Flanke reagieren können und der Synthesizer
deshalb den ganzen Prozess auf eine der Flanken reagieren lässt.
Noch zwei Anmerkungen:
chris schrieb:>> IF(clk'EVENT AND clk = '1') THEN>> dient ja dazu, den Counter bei der steigenden Flanke zu inkrementieren.
Das gilt nicht für die Simulation. Da kann clk andere Zustände als 0 und
1 haben und dieses if reagiert auf jede Zustandsänderung, die zu einem
Wert von 1 führt.
Variablen sollte man als Anfänger aus irgendeinem Grund nicht verwenden.
Da kann man wohl irgendwas Falsches erwarten. Mir ist das noch nicht
passiert und ich verstehe auch das Problem nicht so ganz, aber so wird
es gesagt.
sounds good, doesn't work :).
In der Simulation funktioniert das, falls dein Simulator dir nicht einen
Strich dagegen zieht.
In der Realität ist das Ziel auf eine Hardware die Beschreibung zu
synthetisieren. Sensitivity-Listen sind dafür nur für die Simulation
wichtig und werden in der Synthese ignoriert. Und hier bekommst du nun
mit deiner Beschreibung Probleme.
Falls du auf beide Flanken triggern möchtest, dann wäre das so "besser".
1
IFrising_edge(clk)orfalling_edge(clk)then
In der Regel sollte dir die Synthese dir damit eine bessere Rückmeldung
geben.
Ich denke mal, so oder so, bei dir sind noch größere
Verständnisschwierigkeiten in VHDL.
Dussel schrieb:> Variablen sollte man als Anfänger aus irgendeinem Grund nicht verwenden.
Einfach mal hier rumstöbern:
Beitrag "Variable vs Signal"chris schrieb:> ch habe gelesen, dass mit der "sensitive list" in "PROCESS(clk)" immer> auf ein Wechsel des Signals getriggert wird.
Der Synthesizer wird aus dem Design ohne das 'event übrigens einfach
eine üble kombinatorische Schleife machen:
http://www.lothar-miller.de/s9y/categories/36-Kombinatorische-Schleife
Sieh dort ganz unten... ;-)
Tim schrieb:> Falls du auf beide Flanken triggern möchtest, dann wäre das so> "besser".IF rising_edge(clk) or falling_edge(clk) then
Allerdings gibt es im FPGA eben kein Bauteil, das auf beide Flanken
getriggert werden könnte. Und deshalb wird es für diese Beschreibung
eine Fehlermeldung geben.
Man muss seine VHDL Beschreibung so verfassen, dass das Design letztlich
aus LUT und Flipflops zusammengebaut werden kann. Und dafür müssen
bestimmte Regeln eingehalten werden, die im Handbuch zum Synthesizer
beschrieben sind.
Sagen wirs mal so: von 100% VHDL die für die Simulation verwendet werden
dürfen, können etwa 5% für die Synthese genutzt werden.
Vielen Dank für eure hiflreichen Antworten. Ich bin sehr begeistert :-)
Das Beispiel habe ich von hier:
https://eewiki.net/display/LOGIC/Lattice+Diamond+and+MachXO2+Breakout+Board+Tutorial
Zum Testen verwende ich dieses Board:
http://www.mouser.de/new/Lattice-Semiconductor/lattice-machxo2-breakoutboard/
Interessanterweise ergibt sich nach dem Auskommentieren des Wartens auf
die "rising edge" ein assymetrisches Ausgangssignal von ca. 4ms
Periodendauer.
Kene Ahnung, wie diese Frequenz dann entsteht.
Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
>Man muss seine VHDL Beschreibung so verfassen, dass das Design letztlich>aus LUT und Flipflops zusammengebaut werden kann. Und dafür müssen>bestimmte Regeln eingehalten werden, die im Handbuch zum Synthesizer>beschrieben sind.
Gibt es eine Möglichkeit, wie man genau analysieren kann, wie der Code
auf die Schaltung abgebildet wird? Beim MC kann man sich ja z.B. den aus
C-Code generierten Assemblercode ansehen.
>>Tim schrieb:>Ich denke mal, so oder so, bei dir sind noch größere>Verständnisschwierigkeiten in VHDL.
Na so ganz "Sattelfest" scheinst Du ja auch nicht zu sein:
>>Tim schrieb:>> Falls du auf beide Flanken triggern möchtest, dann wäre das so>> "besser".IF rising_edge(clk) or falling_edge(clk) then>Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite>Allerdings gibt es im FPGA eben kein Bauteil, das auf beide Flanken>getriggert werden könnte. Und deshalb wird es für diese Beschreibung>eine Fehlermeldung geben.
chris schrieb:> Gibt es eine Möglichkeit, wie man genau analysieren kann, wie der Code> auf die Schaltung abgebildet wird?
Der RTL-Schaltplan (RTL schematic) ist das, was du suchst. Sieh dir
einfach mal ein paar Beispiele auf meiner HP an. Wie der bei Altera
erzeugt wird musst du allerdings selber rausfinden... ;-)
chris schrieb:> Na so ganz "Sattelfest" scheinst Du ja auch nicht zu sein
Lass stecken.
Prinzipiell stimmt die Aussage und könnte mit einer passenden Library
evtl. sogar irgendwo realisiert werden. Nur eben nicht in einem FPGA.
Und ich vermute einfach mal, dass du ein FPGA als Zielsystem hast, denn
Lattice macht keine ASICs.
>Nur eben nicht in einem FPGA.>Und ich vermute einfach mal, dass du ein FPGA als Zielsystem hast,
Bei Lattice nennt es im Datenblatt "PLD".
Im Bild sieht man ein "Slice" des MACHXO7000 PLDs.
Es hat 2 FlipFlops, aber die CLK Leitung triggert für beide auf "High",
würde ich sagen.
chris schrieb:> Es hat 2 FlipFlops, aber die CLK Leitung triggert für beide auf "High",> würde ich sagen.
Ich weiß nicht, wie es da ist, aber ich glaube, das kann man heute
konfigurieren. Aber eben nur auf eine Flanke pro Flip-Flopf.
chris schrieb:> Bei Lattice nennt es im Datenblatt "PLD".
Stimmt ja auch. Ist ein "Programmable Logic Device". Auf der HP von
Lattice werden die einfach zusammengefasst:
http://www.latticesemi.com/Products.aspx#_D5A173024E414501B36997F26E842A31
Und tauchen dann als "FPGA & CPLD" auf:
http://www.latticesemi.com/en/Products/FPGAandCPLD/MachXO2.aspx> Im Bild sieht man ein "Slice" des MACHXO7000 PLDs. Es hat 2 FlipFlops,> aber die CLK Leitung triggert für beide auf "High", würde ich sagen.
Hilft alles nix, denn das sind 2 getrennte Flipflops, deren Ausgang
eben auch getrennt ist. Man kann sie also nicht irgendwie wieder
"automatisch" (z.B. in einer Prozessbeschreibung) zusammenfassen...
>Variablen sollte man als Anfänger aus irgendeinem Grund nicht verwenden.
Ok, ich habe im obigen Beispiel einfach mal den Counter durch einen
Signalvektor ersetzt:
> SIGNAL count : STD_LOGIC_VECTOR( 31 downto 0 );
und schon compiliert nichts mehr ...
> Ok, ich habe im obigen Beispiel einfach mal den Counter durch einen> Signalvektor ersetzt:>>> SIGNAL count : STD_LOGIC_VECTOR( 31 downto 0 );>> und schon compiliert nichts mehr ...
weil du noch die passende Bibliothek fürs addieren mit std_logic
einbinden musst und die Typen aller Operanden anpassen.
https://stackoverflow.com/questions/854684/why-cant-i-increment-this-std-logic-vector
chris schrieb:>>Variablen sollte man als Anfänger aus irgendeinem Grund nicht verwenden.>> Ok, ich habe im obigen Beispiel einfach mal den Counter durch einen> Signalvektor ersetzt:>>> SIGNAL count : STD_LOGIC_VECTOR( 31 downto 0 );>> und schon compiliert nichts mehr ...
Komisch. Man schreibt ahnungslos und unüberlegt irgendwas hin und schon
geht's nicht mehr...
- ein integer läßt sich nicht "einfach so" durch einen std_logic_vector
ersetzten. Sonst wäre ja beides dasselbe. Mit dem einen kann man andere
Dinge machen als mit dem anderen. Mit "Variable oder Signal" hat das
nichts zu tun.
- da, wo eine Variablendefinition stand, darf nicht unbedingt eine
Signaldefinition stehen.
In jeder Sprache gibt es Regeln, die sollte man kennen und sich daran
halten.
Markus F. schrieb:> - da, wo eine Variablendefinition stand, darf nicht unbedingt eine> Signaldefinition stehen.
Stimmt, das ist auch falsch, also versuch das ganze erst mit sIGNAL
STATT Varible ans tickern zu bringen (Tipp: schau ob es schon irgenwo
eine signaldeklaration gibt, vielleicht wäre das ja der Platz für eine
zweite?!) und dann strickst du den Typ um. Wobei der der ge-rangte
integer IMHO die bessere Wahl ist.
Und als drittens kannst Du ja noch schauen wie du die magic number für
die Zählerweite ordentlich in ein package oder generic oder beides
packst.
Bitwurschtler schrieb:> weil du noch die passende Bibliothek fürs addieren mit std_logic> einbinden musst und die Typen aller Operanden anpassen.
Aua.
Wenn man rechnen will, bindet man die ieee.numeric_std.all ein und
castet std_logic_vector zu signed oder unsigned. Alles andere ist Murks,
der später zu langer Fehlersuche führen kann. BTDT.
Duke
Autor: Bitwurschtler (Gast)
>weil du noch die passende Bibliothek fürs addieren mit std_logic>einbinden musst und die Typen aller Operanden anpassen.
Dankeschön, das war der Tipp :-)
Autor: Markus F. (mfro)
>Komisch. Man schreibt ahnungslos und unüberlegt irgendwas hin und schon>geht's nicht mehr...
Kannst Du bitte in einem anderem Thread posten? Dein Ton stört mich.
chris schrieb:> Nur zur Kontrolle:IF rising_edge(clk) or falling_edge(clk) THEN> ERROR - blink.vhd(55): multiple signals in event expression is not> synthesizable. VHDL-1136> Done: error code 2
Genau diese (negative) Rückmeldung des Tools meinte ich. Scheinbar wurde
ich nicht richtig verstanden :)
chris schrieb:> Autor: Bitwurschtler (Gast)>>weil du noch die passende Bibliothek fürs addieren mit std_logic>>einbinden musst und die Typen aller Operanden anpassen.>> Dankeschön, das war der Tipp :-)>>> Autor: Markus F. (mfro)>>Komisch. Man schreibt ahnungslos und unüberlegt irgendwas hin und schon>>geht's nicht mehr...>> Kannst Du bitte in einem anderem Thread posten? Dein Ton stört mich.
Nö. Ich schreib' nur, was ich denke.
Oder hast Du das etwa mit einer Ahnung und nach ausgiebiger
Kontemplation hingeschrieben? Das wär' eigentlich sogar noch schlimmer.
chris schrieb:> Dein Ton stört mich.
Sehr dünnhäutig...
Denn der Rest des Posts ist durchaus hilfreich: VHDL ist eben nicht
Verilog oder gar C, wo viele Typumwandlungen implizit und "hintenrum"
stattfinden. In VHDL muss jede Typumwandlung explizit ausgeschrieben
werden. Und vom std_logic_vector zum integer und zurück geht das mit der
numeric_std nur über einen Umweg:
http://www.lothar-miller.de/s9y/categories/16-Numeric_Std
> Autor: Markus F. (mfro)> Komisch. Man schreibt ahnungslos und unüberlegt irgendwas hin und schon> geht's nicht mehr...> ..> Nö. Ich schreib' nur, was ich denke.
Manchmal ist es aber besser, erst zu denken und dann zu schreiben
>In jeder Sprache gibt es Regeln, die sollte man kennen und sich daran>halten.
Da bin ich zu 100% deiner Meinung. Auch was die Umgangsformen betrifft.
Tim schrieb:
> Falls du auf beide Flanken triggern möchtest, dann wäre das so> "besser".IF rising_edge(clk) or falling_edge(clk) then> ...> Scheinbar wurde ich nicht richtig verstanden :)
Naja, für mich sah dass so aus, als wenn es kompilieren sollte.
Nichts desto trozt nehme ich deinen Vorschlag "rising_edge(clk)" zu
verwenden mal für das nächste Beispiel.
Die LED soll jetzt "faden". Dazu brauche ich also ein PWM.
Ich habe die PWM und die Zähler in eine Architektur gepackt.
Wahrscheinlich wäre es besser, ein extra PWM-vhdl File zu machen, aber
ich weiß nicht, wie man ein zweites "File" includiert.
Hier der erste funktionierende Anlauf:
1
LIBRARYieee;
2
USEieee.std_logic_1164.all;
3
USEieee.std_logic_unsigned.all;-- unbedingt notwendig für die Addition von STD_LOGIC_VECTOR
Du hörst nicht (Beitrag "Re: VHDL Grundlagen").
Musst Du ja auch nicht, ist ja deine Sache...
Trotzdem.
Man rechnet nicht mit std_logic_vector. Punkt.
Oder nur, wenn man sich unbedingt (durchaus auch erst später) selbst ins
Knie schiessen will.
Das Package ieee.std_logic_unsigned (das, obwohl's so heisst,
tatsächlich nur ein "pseudo-standard" ist) mappt std_logic_vector auf
unsigned. Bedingungslos.
Das ist zwar (erst mal) bequem, rächt sich aber spätestens dann, wenn Du
mal vorzeichenbehaftete Arithmetik brauchst.
Dann musst Du - wenn Du die Vorzeichenbits nicht selbst verwurschteln
willst - dir entweder doch die ieee.numeric_std oder die
ieee.std_logic_signed dazuholen und plötzlich hast Du in der Arithmetik
Mehrdeutigkeiten, die dich von einer Verzweiflung in die nächste
treiben.
Wie's richtig geht, steht hier:
https://www.mikrocontroller.net/articles/Rechnen_in_VHDL oder hier:
http://www.lothar-miller.de/s9y/categories/16-Numeric_Std .
Wenn dein Synthesetool VHDL 2008 kann, solltest Du das auch verwenden,
dann werden ein paar Dinge (wie z.B. die eindeutige Typisierung von
signed/unsigned Literalen) ein wenig einfacher.
Noch besser ist natürlich, unnötige Typumwandlungen gleich komplett zu
vermeiden, indem man den Signalen bei der Definition schon genau den
ihrer Verwendung entsprechenden Typ gibt. In deinem Code oben gibt's
z.B. keinen Grund, warum vorteiler, pwmCounter oder pwmValue unbedingt
ein std_logic_vector sein muss. Sie werden auch nirgends einem solchen
zugewiesen (wo dann wieder eine Typumwandlung notwendig wäre).
Hättest Du die Signale gleich als integer oder unsigned definiert,
hättest Du ganz bequem direkt damit rechnen können. Ganz ohne
ieee.std_logic_unsigned.
chris schrieb:> pwm: PROCESS(sysclk)> BEGIN> IF ( pwmValue > pwmCounter) THEN> led2 <= '0';> ELSE> led2 <= '1';> END IF;> END PROCESS pwm;
Die Sensitivliste ist falsch. Und folglich passt die Simulation nicht
zur Realität. Ich hatte die entsprechenden Links schon gepostet. Du
solltest sie lesen und versuchen ihren Inhalt zu verstehen.
Solche Kombinatorik schreibt man übrigens besser nebenläufig
(concurrent) ganz ohne Prozess:
LED <= '0' when pwmValue > pwmCounter else '1';
Fazit: aus 8 Zeilen mitsamt falscher Sensitivliste ist 1 Zeile geworden.
Und zum Thema std_logic_unsigned sieh dir den
Beitrag "IEEE.STD_LOGIC_ARITH.ALL obsolete" inklusive der darin
angeführten Links an.
Ein paar Vorschläge :
Ich würde statt buffer lieber out nehmen. Schließlich treibst du den led
Ausgang. Ob nun Strom in den pin reinfließt oder nicht, ist egal für die
Beschreibung.
Selber habe ich auch noch nie buffer verwendet bzw. sah ich noch nie die
Notwendigkeit.
Zweitens. Hast du schonmal simuliert das ganze? Led sollte nichts
zeigen, da nichts initialisiert ist. Wenn du dann auf out gestellt hast,
dann bekommst du sowieso ein Fehler. Den behebst du durch die Verwendung
eines internen signals, dass du auf led führst. Interne Signale kannst
du beim FPGA initialisieren mit Werten. Bei den meisten :)
Drittens. Ist es für deine Anwendung ok, dass zwei Zähler in den
Überlauf gehen? Kann man machen.
Viertens. Simuliere mal. Spart Zeit beim Fehler finden.
>Hast du schonmal simuliert das ganze? Led sollte nichts>zeigen, da nichts initialisiert ist.
Ich habe noch kein "Simulation-File" deshalb konnte ich es nicht
simulieren.
Deshalb habe ich es einfach runter geladen und mit dem Logik-Analysator
überprüft.
Ergebnis: es funktioniert ;-)
>Drittens. Ist es für deine Anwendung ok, dass zwei Zähler in den>Überlauf gehen? Kann man machen.
Da ich ja noch in der Experimentalphase bin, ist es eher ein Test als
eine Anwendung.
Die Zähler sind absichtlich mit 8Bit realisiert, so dass sie immer von
0-255 durchlaufen.
Damit wird erreicht, dass die LED langsam immer heller wird und dann
schlagartig wieder dunkel.
Die Entwicklungsumgebung von Altera "Diamond" hat auch den sogenannten
"Reveal In-Circuit Debugger" damit kann man direkt in das Design rein
schauen:
https://youtu.be/SmdEP_ZsBgM
( Minute 23:30 )
chris schrieb:> Altera "Diamond"
Klingt wie "Opel Golf"....
Ich empfehle dir dringendst, von der (aus der Software bekannten)
"AufderHardwareausprobierem"-Designstrategie wegzukommen und das
Hardwaredesign schon vorher funktional zu simulieren.
Wenn ein Design nämlich funktional fehlerfrei durch die Simulation geht
und grundlegende Designregeln (Einsynchronisieren...) eingehalten
wurden, dann läuft es hinterher mit.hoher Wahrscheinlichkeit ganz ohne
Herumgebastel mit internen oder externen Logikanalysern.
Die werden nur dann gebraucht, wenn etwas trotzdem nicht funktioniert...
Gemach, Gemach kommt schon noch. Ich muss die Dinge nacheinander
ausprobieren.
Mittlerweile habe ich einen Weg gefunden, den RTL-Level des PWM
Beispiels graphisch darzustellen.
Das Tool bei Lattice heißt "simplified Pro".
Ein wenig unübersichtlich sieht das Ganze schon aus ...
Tim schrieb
>Ein paar Vorschläge :>Ich würde statt buffer lieber out nehmen. Schließlich treibst du den led>Ausgang. Ob nun Strom in den pin reinfließt oder nicht, ist egal für die>Beschreibung.
Die Grundlage für den Code habe ich ja aus dem Blink-Beispiel vom Anfang
kopiert.
Buffer hat den Vorteil, dass die LED gelesen und invertiert werden kann:
1
..
2
led:BUFFERSTD_LOGIC);
3
..
4
led<=NOTled;
5
..
Die Verwendung von "OUT" wirft folgenden Fehler:
> cannot read from 'out' object led ; use 'buffer' or 'inout'
chris schrieb:> Die Verwendung von "OUT" wirft folgenden Fehler:>> cannot read from 'out' object led ; use 'buffer' or 'inout'
Das wäre jetzt ein guter Zeitpunkt für ein Google-Fenster im Browser...
Buffer oder Inout zu nehmen, nur um den Port zurücklesen zu können und
sich sich ein lokales Signal zu sparen, ist eigentlich ein
Kündigungsgrund.
Nimm meinen Blink-Code, der ist sauber mit lokalem Signal x und einem
integer als Zähler gemacht:
http://www.lothar-miller.de/s9y/archives/80-Hello-World!.html
BTW: auf dem Rest der Seiten dort findest du Antworten auf etwa 99% der
üblichen Anfängerfragen und Anfängerfehler... ;-)
>Nimm meinen Blink-Code, der ist sauber mit lokalem Signal x und einem>integer als Zähler gemacht:>http://www.lothar-miller.de/s9y/archives/80-Hello-...
Da hast Du eine sehr schöne Seite gemacht.
Das ist eigentlich genau das was ich suche :-)
Was mir gerade auffällt: In Deinen Beispielen ist ein SPI-Master.
Vielleicht wäre ein SPI-Slave auch gut. Ich plane, einen Mikrocontroller
als "CPU" zu verwenden und das PLD damit zu steuern. Die Kombination
scheint mir sehr günstig, weil sich ein MC viel einfacher programmieren
lässt und wesentlich komplexere Programme erlaubt.
chris schrieb:> Die Verwendung von "OUT" wirft folgenden Fehler:>> cannot read from 'out' object led ; use 'buffer' or 'inout'
Ich wiederhole mich: wenn dein Synthesetool VHDL 2008 kann, schalt's
ein.
Da kann man OUT-Signale lesen.
Markus F. schrieb:> Da kann man OUT-Signale lesen.
IA nein, oft aber ja.
Betrachtet man als Beispiel ein Enable-Signal
für ein Port-OUT-Signal, das registriert werden
soll. Wird das entsprechende Register in die
IO-Zelle des FPGA gelegt, dann ist das
Register-OUT-Signal nicht mehr lesbar. Wie das
das 2008er VHDL umgehen will..?
Sigi schrieb:> Wird das entsprechende Register in die> IO-Zelle des FPGA gelegt, dann ist das> Register-OUT-Signal nicht mehr lesbar. Wie das> das 2008er VHDL umgehen will..?
Macht die Synthese so was, wenn ich das OUT-Signal lese?
Markus F. schrieb:> Macht die Synthese so was, wenn ich das OUT-Signal lese?
Natürlich kannst du (bzw. der Synthesizer) das so
implementieren, dass das OUT-Signal noch gelesen
werden kann (einfach das OutputEN-Register in eine
benachbarten Logikzelle setzen). Wenn du aber
zusätzlich zu deinem VHDL-Design nich Location-
oder harte Timing- Constraints setzt, dann erzwingst
du damit idR einen Konflikt, d.h. der Synthesizer
gibt eine Warnung oder einen Fehler aus.
Konkrekt bei einem "schnellen" SDRAM-Controller: Wenn
du die entsprechenden OUT-Signale liest, dann zerstörst
du das Timing, d.h. der Controller wird langsamer.
Andere Beispiele sind z.B. schnelle PHYs/Tranceiver
oder Hardwired-Controller.
Sigi schrieb:> Konkrekt bei einem "schnellen" SDRAM-Controller: Wenn> du die entsprechenden OUT-Signale liest, dann zerstörst> du das Timing, d.h. der Controller wird langsamer.
Nun, das ist ja klar. Die Frage ist, was das Tool macht.
Quartus meckert öfters mal, es könne Signale nicht als "Fast
Out-/Input-Register" constrainen. Warum nicht, ist oft kaum rauszufinden
(nein, an VHDL 2008 liegt's in den Fällen nicht).
Markus F. schrieb:> Quartus meckert öfters mal, es könne Signale nicht als "Fast> Out-/Input-Register" constrainen. Warum nicht, ist oft kaum rauszufinden
Da wär für mich der Hauptgrund, dass das OUT-Signal
irgendwo nochmal gelesen wird, z.B. auch durch den
Quartus-LogicAnalizer (SignalTapII etc.). Aber das
lässt sich z.B. im RTL-Viewer etc. rauslesen.
Es kann aber auch sein, dass durch Timing-Constraints
das "Valid"-Fenster so gesetzt ist, dass eine
Instantiierung in der IO-Zelle nicht in Frage kommt.
Sicherlich gibt's aber noch andere Gründe.
..aber unabhängig von meinen Beispiel, in 99%
aller Fälle hast du natürlich recht, man kann
es immer so Implementieren, dass eben das
OUT-Signal auch noch gelesen werden kann
(99%: idR sind hier die Fälle gemeint, wo
die OUT-Signale in übergeordnete, nicht
TOP-Level Komponenten wandern).
Hier mein erstes Lauflicht ( aus dem modifizierten BlinkLed ).
Das Lauflicht hat 8 Zustände für die 8 LEDs.
Eigentlich wollte ich ein "Knight Rider" Lauflicht machen:
https://www.youtube.com/watch?v=4fjDWSFsv8o
wobei die originale Version sogar mit "Fading" wäre, was aber die ganze
Sache ziemlich kompliziert machen dürfte:
https://www.youtube.com/watch?v=iQwlrEdka6Q
Für ein hin und zurücklaufen der Lichterkette bräuchte man mehr als 8
Zustände. Sobald ich aber
chris schrieb:> Für ein hin und zurücklaufen der Lichterkette bräuchte man mehr als 8> Zustände. Sobald ich aber> signal x : integer range 0 to 7 := 0;> um nur einen Zustand erhöhe, geht nichts mehr
Ich würde das jetzt einfach mal simulieren. Und wie "Zustand"? Das ist
ja einfach ein Zähler für den Index...
> Sobald ich aber> signal x : integer range 0 to 7 := 0;> um nur einen Zustand erhöhe, geht nichts mehr
Sieh dir die Meldungen an. Ratzfatz optimiert dir der Synthesizer die
Schaltung weg, wenn er erkennt, dass die aus einem Zustand nicht mehr
rauskommen wird.
> if ( c < 24999999/4 ) then
Hast du dir da schon richtig überlegt, ob das mit dem /4 richtig ist?
Daraus wird ganz schnell so eine "Off by one" Geschichte...
Da ist übrigens der Knight Rider mit Fading:
http://www.lothar-miller.de/s9y/archives/61-Lauflicht.html
>Ich würde das jetzt einfach mal simulieren. Und wie "Zustand"? Das ist>ja einfach ein Zähler für den Index...
Akademisch gesprochen ist ein Zähler eine Zustandsmaschine.
Der Zustand des Zählers kann auf verschiedene Weisen auf die LEDs
abgebildet werden ( mit Vergleichsoperationen oder ROM usw .. so wie Du
es in Deinen Beispielen gemacht hast ).
Das ist die Strutur eines "Moore-Automaten":
https://de.wikipedia.org/wiki/Moore-Automat#/media/File:Moore-Automat-de.svg>Hast du dir da schon richtig überlegt, ob das mit dem /4 richtig ist?>Daraus wird ganz schnell so eine "Off by one" Geschichte...
Ich bin davon ausgegangen, dass das Ergebnis eine Integer Zahl wie bei
'c' ist. Ebenso dass durch die Priorität der Operatoren '/' vor '<'
berechnet wird. Und dass die Division ein MACRO für den Precompiler ist.
Wenn dem so wäre, sollte es für diesen Fall kein Problem sein.
>Ich würde das jetzt einfach mal simulieren.
Simulieren kann ich noch nicht. Aber im Anhang sind die beiden
RTL-Schaltplane für den Entwurf für 8 und 9 Zuständen ( Zähler von 0..7
und Zähler von 0..8 ).
Auf RTL Level kann ich nicht erkennen, dass da was wegoptimiert wird.
chris schrieb:>> Ich würde das jetzt einfach mal simulieren. Und wie "Zustand"? Das ist>> ja einfach ein Zähler für den Index...> Akademisch gesprochen ist ein Zähler eine Zustandsmaschine.
Richtig. Allerdings spricht man zur Klarstellung, dass hier ein solch
"einfacher Automat" eingesetzt wird, trotzdem vom Zählerwert und nicht
vom Zählerzustand.
> Ich bin davon ausgegangen,
Kann man machen...
> dass das Ergebnis eine Integer Zahl wie bei c' ist. Ebenso dass> durch die Priorität der Operatoren '/' vor '<' berechnet wird.> Und dass die Division ein MACRO für den Precompiler ist.
Soweit alles richtig.
> Wenn dem so wäre, sollte es für diesen Fall kein Problem sein.
Mit meiner Off-by-one Bemerkung geht es um die formale Korrektheit der
Berechnung. Denn 24999/4 sind 6249.75, und diese 0.75 werden (zum Glück)
"einfach abgeschnitten" auf den richigen Wert 6249. Formal richtig (ohne
diese implizite Abrundung) wäre die Berechnung des Zählerendwerts aber
so: (25000/4)-1, weil die 24999 ja von 25000-1 kommen.
> Auf RTL Level kann ich nicht erkennen, dass da was wegoptimiert wird.
Ja, das sieht klar so aus als ob der Zähler jetzt 4 Bit breit ist und
dass das MSB nicht verwendet wird und konstant 0 ist. Es wird also
trotzdem später noch wegoptimiert.
Wie gesagt: simuliere deine Beschreibung!
Du verlässt dich bei x (schon wieder) auf einen impliziten Überlauf oder
hoffst gar auf ein Zurücksetzen das nie stattfindet:
1
signalx:integerrange0to8:=0;
2
:
3
:
4
processbegin
5
waituntilrising_edge(clk);-- warten bis zum nächsten Takt
6
:
7
x<=x+1;
8
:
9
endprocess;
Was wird deiner Meinung nach hier das Signal x machen, wenn es den
Maximalwert 8 hat und weitergezählt wird?
Fazit: behandle auch die obere Grenze von x formal korrekt wie die von
c:
1
signalx:integerrange0to8:=0;
2
:
3
:
4
processbegin
5
waituntilrising_edge(clk);-- warten bis zum nächsten Takt
>Richtig. Allerdings spricht man zur Klarstellung, dass hier ein solch>"einfacher Automat" eingesetzt wird, trotzdem vom Zählerwert und nicht>vom Zählerzustand.
Ich denke mal, das hängt vom jeweiligen Erfahrungshintergrund ab. Für
Leute, die sich relativ viel mit der "Zustandsbetrachtung" technischer
Systeme befassen, dürfte der Begriff weniger ein Problem sein.
Wenn's dem Verständnis dient, können wir aber auch den anderen Begriff
verwenden.
>Was wird deiner Meinung nach hier das Signal x machen, wenn es den>Maximalwert 8 hat und weitergezählt wird?
Dort spielt wieder mein 'c'-Erfahrungshintergrund eine Rolle.
Ich bin davon ausgegangen, dass der Wert 8 ein Minimum für den zu
synthetisierenden Zähler darstellt und also ein 4Bit Zähler
synthetisiert wird und dieser dann beim Zählerstand ;-) 15 umklappt. Für
den Test mit den Vergleichen wäre das erst mal egal gewesen. Es hätte
halt vom Zählerstand 9 bis 15 keine LED geleuchtet.
>> Auf RTL Level kann ich nicht erkennen, dass da was wegoptimiert wird.>Ja, das sieht klar so aus als ob der Zähler jetzt 4 Bit breit ist und>dass das MSB nicht verwendet wird und konstant 0 ist. Es wird also>trotzdem später noch wegoptimiert.
Theoretisch dürfte es ja nicht konstant '0' weil 8=0b1000 ;
Dehalb hätte es mit meiner obigen Annahme funktionieren müssen und im
RTL-Schaltplan sieht man es auch nicht. Dort bin ich wirklich davon
ausgegangen, dass das die "Wahrheit" darstellt, die auf dem Chip
programmiert wird, so wie Assembler im Gegensatz zu 'c'.
>Fazit: behandle auch die obere Grenze von x formal korrekt
Tatsächlich, damit geht es, danke ;-)
Ich habe also gelernt: "in VHDL niemals auf das nicht formulierte
umklappen eines Zählerstandes vertrauen" ( obwohl es beim Zählerendwert
7 noch funktioniert )
Hier Knight Rider V1:
1
libraryieee;
2
useIEEE.STD_LOGIC_1164.ALL;
3
useIEEE.NUMERIC_STD.ALL;
4
5
LIBRARYlattice;
6
USElattice.components.all;
7
8
entityBlinkLEDis
9
Port(
10
led:outSTD_LOGIC_VECTOR(7downto0)
11
);
12
endBlinkLED;
13
14
architectureBehavioralofBlinkLEDis
15
16
signalc:integerrange0to24999999:=0;-- 0,5s bei 50MHz fosc
chris schrieb:> mein 'c'-Erfahrungshintergrund
Ist für Hardwarebeschreibung (besonders mit der Beschreibungssprache
VHDL) unnütz. Die ganzen impliziten Annahmen die dir das Leben in C so
angenehm (und auch überraschend) machen, die gibt es in VHDL nicht. Dort
zählt nur, was explizit geschrieben steht. Von allem anderen darf die
Toolchain annehmen, dass sie freie Hand hat. Und die nützt sie gnadenlos
aus: wenn du einen Zähler so beschreibst, dass er nicht zurückgesetzt
wird, dann darf die Toolchain annehmen, dass der bis "unendlich" zählt.
Und für "unendlich" optimieren. Wenn du "unendlich" nicht auswertest,
dann optimiert sie einfach alles weg, was von "unendlich" nicht
betroffen ist.
> Ich habe also gelernt: "in VHDL niemals auf das nicht formulierte> umklappen eines Zählerstandes vertrauen" ( obwohl es beim Zählerendwert> 7 noch funktioniert )
Weil zusätzlich auch zufällig alle Zählerwerte verwendet werden...
> Ich bin davon ausgegangen> Theoretisch dürfte es ja nicht konstant '0' weil 8=0b1000
Wie gesagt: nimm nichts an und erwarte nichts (von "drarauf vertrauen"
gar nicht zu reden). Es gilt in VHDL nur und ausschließlich das
geschriebene Wort.
Lothar M. schrieb:> Die ganzen impliziten Annahmen die dir das Leben in C so> angenehm (und auch überraschend) machen, die gibt es in VHDL nicht.
Gerade in C machen sich viele Entwickler leider überhaupt keine Gedanken
über Wertebereiche und die Gültigkeit von Ausdrücken. Die Compiler
drücken da insbesondere auf niedrigen Optimierungsstufen häufig auch ein
Auge zu und greifen nicht allzu tief in die Trickkiste des Optimierers.
Beispiel:
1
uint32_ta;
2
3
...
4
5
a=10;
6
a=a>>35;
7
8
...
Welchen Wert hat "a" anschließend? Die intuitive Annahme, dass auf jeden
Fall "a=0" wäre, entspricht nicht der Definition im C-Standard. Dieser
besagt nämlich, dass Schiebeoperationen um mehr als die Breite des
Datentyps undefiniert sind. Deswegen kann der Compiler die obige
Berechnung wie folgt umsetzen (ohne Anspruch auf Vollständigkeit):
1. Er ist "nett" und setzt "a" auf den zur Kompilierzeit vorberechneten
Wert "a=0".
2. Er stellt fest, dass a in ein Register passt und mit einem
Schiebebefehl des Befehlssatzes entsprechend geschoben werden kann.
Dabei ist er "nett" und begrenzt diesen Schiebebefehl auf den maximal
möglichen Wert, ohne dass es zu einem Überlauf kommt.
3. Er stellt fest, dass a in ein Register passt und mit einem
Schiebebefehl des Befehlssatzes entsprechend geschoben werden kann.
Hierbei führt er keine Kontrolle des Wertebereichs durch, sondern
verwendet einfach die unteren fünf Bit. Daher wird "a" nur um 3 statt 35
Bit geschoben. Dies ist zulässig, da nach der Operation "a" eh
undefiniert ist.
4. Nach der Berechnung ist "a" undefiniert und darf somit einen
beliebigen Wert besitzen. Daher kann die Berechnung wegoptimiert werden,
so dass weiterhin "a=10".
5. Ähnlich wie Möglichkeit 3, aber "a" befindet sich nicht im Speicher,
sondern in einem Register. Bei der nächsten Verwendung von "a" darf also
der Inhalt eines beliebigen Registers verwendet werden, z.B. aus dem
Register, welches verwendet worden wäre, wenn keine Optimierung
stattgefunden hätte. Dessen Inhalt ist entweder noch der alte Inhalt von
"a" oder irgendein anderer Wert.
Bei konstantem Offset, um den "a" geschoben wird, ist es sehr
wahrscheinlich, dass ein Compiler Möglichkeit 1 generiert. Ist jedoch
der Offset nicht bekannt, sondern wird erst zur Laufzeit bestimmt,
werden viele Compiler keine Bereichsüberprüfung durchführen, sondern ein
Programm mit dem Verhalten gemäß 3. generieren.
Aus den obigen Gründen finde ich gerade in VHDL Datentypen wie std_logic
usw. so toll, weil dort in der Simulation auch mit undefinierten Werten
weitergearbeitet werden kann. Und dann stellt man sehr schnell fest,
dass man an irgendeiner Stelle vergessen hat, ein Signal geeignet
vorzubelegen bzw. einen korrekten Wert zuzuweisen.
chris schrieb:> Akademisch gesprochen ist ein Zähler eine Zustandsmaschine.
Aaargh. Vergiss das ganz schnell wieder, wenn ich schon "akademisch"
höre, wachsen mir Nackenhaare. Lernt ihr sowas in der Vorlesung?
Ein Zähler ist immer noch ein Zähler. Wenn die Synthese daraus eine rohe
Zustandsmachine (LUT-basiert) machen würde, wäre dein FPGA gleich voll.
chris schrieb:> Dort spielt wieder mein 'c'-Erfahrungshintergrund eine Rolle.
Gut wäre, wenn man im Studium wenigstens konsequenterweise noch eine
Ada-Einführung kriegen würde. Denn VHDL nutzt dieselbe Typensicherheit
und Semantik. In C(++) Hardware zu beschreiben ist ein nicht elegant
lösbarer gordischer Knoten, obwohl es immer wieder versucht wird.
Fazit: Du musst schliesslich viel Wissen drüber ansammeln, was genau
geschieht, gerade in Bezug auf Andreas' prima Beispiel oben.
Da VHDL aber ein recht strenges Schwergewicht ist, gleichzeitig aber in
der Lernphase doch oft viel zuwenig simuliert wird (was die
Beitragszahlen zu dem Thema hier zeigen), empfehle ich immer wieder den
Einstieg per MyHDL und von Anfang an zu SIMULIEREN! Vorher macht es kaum
Sinn, sich mit semantischen Details von VHDL zu quälen und solche
Diskussionen anzufangen.
Und: Darüber lässt sich streiten, aber ich würde mir auch angewöhnen,
Zähler explizit hinzuschreiben und dafür unsigned anstatt integer zu
verwenden. MyHDL geht da noch einen Schritt weiter und lässt explizite
Wertebereichangaben für die intbv() Datentypen zu. Damit kann man prima
numerisch relevante Rechenpipelines verifizieren.
Martin S. schrieb:> Wenn die Synthese daraus eine rohe> Zustandsmachine (LUT-basiert) machen würde, wäre dein FPGA gleich voll.
Wieso das denn?
> Zähler explizit hinzuschreiben und dafür unsigned anstatt integer zu> verwenden.
+1
Markus W. schrieb:> Martin S. schrieb:>> Wenn die Synthese daraus eine rohe>> Zustandsmachine (LUT-basiert) machen würde, wäre dein FPGA gleich voll.> Wieso das denn?
Mit rowherr FSM ist wohl sowas gemeint:
case state_q is
when 0 => state_q <= 1; end if;
when 1 => state_q <= 2; end if;
...
Also für jeden Zähl-state einen Vergleicher aka eine LUT pro
Zustandsbit.
Das das Synthesetool das automatisch zu einem Zähler optimiert ist
unwahrscheinlich, bestenfalls wird draus ein BRAM-ROM gebastelt, das den
LUT-Friedhof ersparen.
Meiner Meinung nach ist ein Zähler immer eine Zustandsmaschine im
akademischen Sinn.
Ähnlich wäre es, wenn man behaupten würde, ein Apfel wäre kein Obst oder
eine Opel Corsa wäre kein Auto.
Das eine ist die Untermenge des anderen.
chris schrieb:> Meiner Meinung nach ist ein Zähler immer eine Zustandsmaschine im> akademischen Sinn.
Ja, ist er. Es geht hier eber weniger ob es ein Automat ist sondern wie
er implementiert ist. Und mit FSM meint man unter FPGA-Entwicklern eben
eine Implementierung aus einen Speicher des Zustandsvektor als FF mit
ein paar LUT's für die Transitionsübergänge und den Ausgangsvektor. Für
einen Zähler braucht es diesen "aufwand" bei dem einzigen Übergang pro
state nicht viel an Ausgangsvektor gibt es auch nicht. Also
implementiert man einen Zähler, der ja bekanntermassen genau einen
Übergang per State hat. Und je nach Zustandskodierung kann man auch eine
Alternative zum Up/Down Binärcounter wählen, wie den Gray-encoded
counter oder ein ruckgekoppeltes Schieberegister (LFSR). Diese Auswahl
an greundverschiedenen Implementierungen einer FSM hat man in C
üblicherweise nicht. Deshalb treten hier die Unterschiede zwischen eine
Implementierung als Zähler oder Roher FSM nicht gleich zu Tage.
>Ähnlich wäre es, wenn man behaupten würde, ein Apfel wäre kein Obst oder>eine Opel Corsa wäre kein Auto.>Das eine ist die Untermenge des anderen.
Beim FPGA besteht aber die Menge "Auto" nicht nur aus Opel Corsa oder
BMW Fünfer sondern umfasst auch Marsrover, Handwagen oder
Tunnelbohrmaschine. Und folglich muss man sich mehr Gedanken drüber
machen, welches Fahrzeug in dieser Situation am cleversten ist.
Tutorial ist gut, Verilog ist nur einer kleinen Teil des Ganzes.
Du kannst auch den User Guide für Active HDL irgendwo hier:
C:\lscc\diamond\3.5_x64\active-hdl
oder Äq. Ist alles da :).
Ich würde trotzdem ghdl+gtkwave auch empfehlen :). (Oder icarus
verilog+gtkwave für Verilog).
> Und mit FSM meint man unter FPGA-Entwicklern eben> eine Implementierung aus einen Speicher des Zustandsvektor als FF mit> ein paar LUT's für die Transitionsübergänge und den Ausgangsvektor.
Den Zähler wird auch mit FFs realisiert...
Danke ;-)
Ich habe einfach mal ein Beispiel von der Webseite von Lothar Miller
genommen, da dort auch eine Testbench mit dabei ist.
http://www.lothar-miller.de/s9y/categories/18-Flankenerkennung
Dabei folgendes Vorgehen:
1. edge_detect.vhd anlegen
2. testbench.vhd anlegen und rechte Maustaste "exclude von
implementation" wählen
3. tools->simulation wizard starten
Danach geht Atcive-HDL 10.3 auf. Es zeigt ein Diagram mit den 4 signalen
ysync-sig, clk, fall, rise und folgender Ausgabe:
# KERNEL: ASDB file was created in location
D:\tools\3.9.0.99.2_Diamond_x64\latticeWorkspace\testbench1\simTestBench
\src\wave.asdb
# 11:36, Sonntag, 17. September 2017
# Simulation has been initialized
add wave *
# 4 signal(s) traced.
run 1000ns
# Waveform file 'untitled.awc' connected to
'D:/tools/3.9.0.99.2_Diamond_x64/latticeWorkspace/testbench1/simTestBenc
h/src/wave.asdb'.
# KERNEL: stopped at time: 1 us
# KERNEL: Simulation has finished. There are no more test vectors to
simulate.
Aber leider sind im Diagramm keine Signale zu sehen, nur Striche. Woran
kann das liegen?
Habe das Problem gefunden, Schritt 2 ist falsch:
2. testbench.vhd anlegen und rechte Maustaste "exclude von
implementation" wählen
richtig ist
2. testbench.vhd anlegen und rechte Maustaste " -> include for ->
simulation
Einfache nicht getaktete Zuweisungen verhalten sich so, als wären sie
jeweils als eigener Prozess definiert, d.h. die Anweisung ließe sich wie
folgt lesen:
1
PROCESS(clkCounter(24))
2
BEGIN
3
led<=clkCounter(24);
4
ENDPROCESS;
Ein Unterschied tritt erst dann auf, wenn die Zuweisung in einem
getakteten Bereich erfoglt, d.h. wenn Dein Prozess wie folgt aussähe:
1
PROCESS
2
BEGIN
3
WAITUNTILrising_edge(clk);
4
clkCounter<=clkCounter+1;
5
6
led<=clkCounter(24);-- output the highest bit to the led
7
other_output<=other_input;
8
ENDPROCESS;
Die Zuweisung an led erfolgt hier erst einen Takt später als in der o.a.
Version. Und die Zuweisung von other_output erfolgt auch mit clk
getaktet, was bei einem rein kombinatorischen Signal ggf. unerwünscht
sein kann.
Markus W. schrieb im Beitrag #5161045:
> Hängt aber von der Lautzeit ab.
Falls damit die Signallaufzeit gemeint sein sollte, trifft dies nicht
zu.
In der Simulation erfolgen alle Zuweisungen erst dann, wenn alle aktiven
Prozesse abgearbeitet wurden. Hierdurch gibt es keine Abhängigkeit der
Zuweisungsreihenfolge von der Position innerhalb des Quelltextes. Es
bedeutet auch, dass man z.B. für den Tausch der Inhalte zweier Signale
nicht etwa wie in der Softwareentwicklung eine Hilfsvariable (oder einen
anderen üblen Trick) benötigt,
1
tmp=a;
2
a=b;
3
b=tmp;
sondern z.B. direkt schreiben kann:
1
A<=B;
2
B<=A;
In der Hardware werden ohnehin flankengesteuerte Latches verwendet. Bei
dem o.a. Beispiel befinden sich alle Signale in der Taktdomäne, die
durch clk definiert wird.