Hallo zusammen,
ich stehe noch ganz am Anfang mit VHDL und möchte einen Encoder-Zähler
realisieren. Ausgangspunkt soll der Drehgeber Code von
mikrocontroller.net werden. Ich werte dann einfach die Ausgaben "ce =
Encoder hat sich geändert", "up_downfür die Richtung" und "error" aus,
um einen 24-bit-Zähler t_cnt1 zu bedienen. Diesen Zähler möchte ich
sporadisch mit einem AVR abfragen. Folgendes habe ich mir überlegt:
1. Zur Abfrage nutze ich drei Eingänge
1
GET0::instd_logic;
2
GET1::instd_logic;
3
GET2::instd_logic;
2. Bei GET0='1' fordert der AVR das erste Byte der 24 Bit Zahl. Ich
kopiere nun den 24 bit Zähler t_cnt1 in den 24-bit-buffer t_cnt2, die
wie folgt beschrieben sind:
1
signalt_cnt1:signed(23downto0);
2
signalt_cnt2:signed(23downto0);
Jetzt kann ich auf die über GET1 und GET2 gestellten Anfragen reagieren
und Byte für Byte die Ausgabe mit dem in t_cnt2 eingefrorenen Wert
bedienen.
1
-- Output Process
2
process(clk)
3
begin
4
if(rising_edge(clk))then
5
ifGET0='1'then-- bei GET0 = '1' den ganzen Vektor t_cnt1 nach Vektor ...
6
t_cnt2(23downto0)<=t_cnt1(23downto0);-- ... t_cnt2 kopieren und niederwertigstes Byte von ...
7
COUNT(7downto0)<=t_cnt2(7downto0);-- ... t_cnt2 auf die Ausgabe COUNT legen
8
elsifGET1='1'then-- bei GET1 = '1' mittleres Byte von t_cnt2 auf die ...
3. Den Zähler pflege ich in einem eigenen Prozess, der auch den Zähler
Reset bewerkstelligt.
1
-- Counter Process
2
process(ce,RESET)-- reagiert auf ce (change encoder) und RESET
3
begin-- bei RESET - clear (others => '0') setzt alle Ausgaben auf '0', ...
4
if(RESET='1')then-- ... clear (others => '1') setzt alle Ausgaben auf '1', mit dem Setzen ...
5
t_cnt1<=(others=>'1');-- ... der 24 Bit Zahl auf '1' und dem Setzen des hochwertigsten Bits ...
6
t_cnt1(23)<='0';-- ... auf '0'gehe ich in die Mitte des 24 Bit Wertebereichs
7
elsif(rising_edge(clk))then-- Auswertung von ce bei jedem clock cycle
8
ifce='1'then
9
if(up_down='1')then
10
t_cnt1<=t_cnt1+1;
11
else
12
t_cnt1<=t_cnt1-1;
13
endif;
14
endif;
15
endif;
16
endprocess;
Das sind alles noch unverbundenene Prozessentwürfe und ich frage mich ob
ich nicht einen grundsätzlichen Fehler eingebaut habe.
In einem CPLD darf ich nicht sequentiell denken. Es passiert alles
parallel. Beide Prozesse nutzen t_cnt1. Sollte ich nicht besser alles in
einen Prozess legen, damit nicht zwei Prozesse an einem Vektor
rumschrauben? Oder gibt es einen Kniff in solchen Fällen.
Die Frage ist vielleicht eigenartig, ich kann sie mir aber weder durch
Recherche im Forum und Google noch durch Überlegen beantworten.
Vielleicht klappt die Synthese und es läuft mit einem ganz fiesen
sporadischen Fehler. Weiß jemand Rat?
Gruß und Danke
Reinhard
Hast Du eine Ausbilding in Hardwareentwicklung?
Man macht das so, dass man jeden Ausgang nur einmal an einem Ort
beschreibt und eine Funktion aufstellt, die den Wert in Abhängigkeit der
Eingänge beschreibt.
> Beide Prozesse nutzen t_cnt1.
Nutzen!=Nutzen.
"Lesen" ist völlig unproblematisch, solange das synchron (mit demselben
Takt+Flanke) wie die (eine) Zuweisung passiert. Nachdem du sicher
gelesen hast, dass man als Anfänger mehr als einen Takt tunlichst
vermeiden sollte, sollte das ohnehin immer der Fall sein. Dein
Code-Beispiel fällt da drunter. Da das zweimal der identische Takt ist,
kannst du aber gleich alles in einen Prozess stecken. Mehrere Prozesse
machen deine VHDL-Unerfahrenheit nicht wett ;)
Schwieriger wirds bei Zuweisungen auf dasselbe Signale (Tip: lass es
für den Anfang erst mal sein...). Da ist es dann auch egal, wenn die
Prozesse unterschiedliche Teile des Vektors nutzen. Die Zuweisung an
einen Teil ("Slice") ist implizit immer eine Zuweisung an alles. Ohne
weitere Vorkehrungen kommen dann im Simulator die X auf allen Bits raus
und man sucht sich dämlich. Da kann man mit der Defaultzuweisung auf 'Z'
für alle Bits in beiden Prozessen arbeiten, d.h. nach dem if-rising
gleich ohne Bedingung die 'Z'-Zuweisung. Das geht aber in der realen HW
auch nicht mehr, wenn das unterschiedliche Takte sind, ergo lass es ;)
BTW: in dem zweiten process muss CLK in der sensitivity-list stehen und
nicht ce.
Der eine counter t1 wird nur in dem einen Prozess beschrieben im
anderen nur gelesen. Das passt soweit. Im unteren Prozess ist die
sensitiviy list falsch (clk statt ce).
Ist dir bewusst, dass dein wert um einen Takt verzögert an COUNT
ankommt?
Interessant ist auch wie dein interface zum Kontroller realisiert ist.
Dein AVR zieht das GET0/1/2 wahrscheinlich für eine ganze Weile (aus
FPGA clk-Sicht). Bei GET0='1' wird dann kontinuerlich t_cnt2 durch
t_cnt1 überschrieben (und entsprechend einen Takt später COUNT). Da wird
also nichts eingefroren.
Dort solltest du besser auf eine Flanke von GET0 reagieren, also
GET0_q <= GET0;
if (GET0_q = '1' and GET0 = '0') then ...
dann wird das nur einmal zur steigenden Flanke von GET0 ausgeführt. Bei
GET1 und GET2 geht es pegelsensitiv.
VHDL hotline schrieb im Beitrag #3858077:
> GET0_q <= GET0;> if (GET0_q = '1' and GET0 = '0') then ...> dann wird das nur einmal zur steigenden Flanke von GET0 ausgeführt.
Aber aufpassen: hier wird ein nicht synchronisiertes Signal (GET0)
verwendet. Das kann böse ins Auge gehen! Hier trifft dann genau der Fall
dort in der Mitte zu:
http://www.lothar-miller.de/s9y/categories/35-EinsynchronisierenReinhard J. schrieb:> Diesen Zähler möchte ich sporadisch mit einem AVR abfragen.
Du solltest auch unbedingt das machen, was Atmel bei den 16Bit Timern
auch tut: beim Lesen des "untersten" Bytes die oberen in
Schattenregister abspeichern und die dann ausgeben. Denn sonst wird du
"ab und zu" seltsam "zappelnde" Werte haben. Denn wenn sich während des
Auslesens der Geber weiterdreht und der Zähler byteweise überläuft, dann
gibt es seltsame Sprünge. Mal angenommen, du liest zuerst das LSB, dann
das mittlere Byte, dann das MSB, dann kann dir sowas passieren:
Hallo Zusammen!
Erst mal herzlichen Dank für die Antworten.
Hier poste ich erst mal eine Antwort auf die von mir ursprünglich etwas
nebulös formulierte Frage. Ich arbeite mich nämlich alleine in das Thema
ein. Angefangen habe ich mit dem Buch Free Range VHDL was kostenlos als
pdf auf dem Web zur Verfügung steht. In VHDL hingekriegt habe ich
bislang lediglich einen Addierer zweier Encoder Signale und will nun den
Zähler bauen.
Dann folgen ein paar Kommentare zu Euren Hinweisen, wobei ich manches
nicht verstanden habe. Ich denke aber drüber nach, in der Hoffnung, dass
mein Verständnis besser wird.
Zum Schluss stelle ich den derzeitigen Stand vor und zur Diskussion,
falls jemand Interesse hat. Ich muss das aber nochmal durchsehen. Ich
lade den Stand nur hoch, damit Interessierte den Fortschritt verfolgen
können.
Ich werde meinen Fortschritt mitteilen, weise aber darauf hin, dass ich
hin und wieder längere Pausen einlege. Bei Interesse schaltet die email
Benachrichtigung ein.
Herzlichen Dank an alle die meinen Beitrag gelesen oder gar kommentiert
haben. Ohne dieses Forum wäre ich ziemlich aufgeschmissen. Im gesamten
FPGA Forum finde ich viele nützliche Threads.
Gruß Reinhard
1. Mein Denkfehler und ein anderer didaktischer Ansatz
------------------------------------------------------
Folgende Überlegung lag der Anfrage zugrunde:
Wenn ich zwei Entitys beschreibe und die dann als Komponenten verbinde,
so stehen diese als Hardware zur Verfügung. Die ganzen Vorgänge laufen
parallel ab. Das bedeutet es spielt keine Rolle, welche Logik zuerst
beschrieben wird, mit Ausnahme von dem was innerhalb eines Process
passiert. Nun dachte ich, wenn zu einem Zeitpunkt des Flankenanstiegs
der clock Entity_1 ein Ausgangssignal erzeugt, wie kann Entity_2 im
allerselben Moment, quasi auf die fantastillionste Sekunde genau den
Ausgang von Entity_1 verarbeiten. Wie steht es um die Kausalität eines
solchen Systems?
Hier scheint mein Denkfehler zu liegen. Besser ich betrachte nicht einen
Zeitpunkt, sondern die Zeitspanne zwischen zwei Flankenanstiegen. Dann
löst sich das Problem von selbst. Kann sein, dass mein Gedankengang für
manche verborgen bleibt, nur muss ich mir als Autodidakt meine Didaktik
selbst entwickeln. Dieses wäre nun also geklärt.
2. Meine Anmerkungen
--------------------
@Logiker: Meine Elektronikkenntnisse habe ich mir selbst angeeignet.
Startpunkt war ein Asuro. Deine Erklärung konnte ich nicht verstehen.
Für mich als Anfänger ist sie wohl zu knapp. War bestimmt auch nicht so
einfach zu erraten, wo bei mir der Schuh drückte. In Punkt 1 unten habe
ich versucht mir selbst eine Brücke zu bauen. Auf alle Fälle Danke für
die ultraschnelle Antwort.
@Georg: Ich denke, dass ich das mit den Zuweisungen nun verbessert habe.
Ich lass das Ändern eines Signals aus zwei Prozessen sein (siehe Punkt
3). Speziellen Dank für den für Anfänger verständlichen Rat.
@Matthias: Stimmt. Ich glaube auch das korrigiert zu haben (siehe Punkt
3). Falls das Projekt weiter geht, lade ich den AVR Code hoch. Ich weise
darauf hin, dass bei mir manche Projekte Monate dümpeln und dann weiter
laufen.
@Hotline: Stimmt das fiel mir beim Überarbeiten auch auf. Jetzt mache
ich das anders und zwar so, dass nur noch einmal t_cnt2 mit t_cnt1 bei
GET0 überschrieben wird. Ich nutze hierfür die Signale StateOfGet0,
StateOfGet1 und StateOfGet2 als flags. Zur Abfrage werden dann die
Signale GET0, GET1 und GET2 abwechselnd von 0 nach 1 und von 1 nach 0
geschaltet. Schau Dir es einfach unten an.
@Lothar: Deinen ersten Hinweis habe ich nicht verstanden. Ich denke
aber, dass mein jetziger Process womöglich auch hier robuster ist. Der
zweite Hinweis hat sich möglicherweise auch erledigt, da ich bei GET0
den kompletten Zähler durch Kopieren einfriere und diesen dann
Byte-weise lese. Nach dem Kopieren setze ich den Zähler zurück. der AVR
muss dann den Zählwert zum vorherigen Wert addieren. Da ich die Werte
oft genug beim CPLD abhole, dürfte der Überlauf hier kein Problem
darstellen. Wenn, dann muss ich auf der AVR Seite Obacht geben.
3. Derzeitiger Stand des Entwurfs
---------------------------------
Momentan sieht der Prozess etwas anders aus. Die Abfragen GET0, GET1 und
GET2 werden nun getoggelt und der Zähler t_cnt1 nach jedem Kopiervorgang
nach t_cnt2 zurückgesetzt. Ich muss das aber nochmal durchsehen!
> Nun dachte ich, wenn zu einem Zeitpunkt des Flankenanstiegs> der clock Entity_1 ein Ausgangssignal erzeugt, wie kann Entity_2 im> allerselben Moment, quasi auf die fantastillionste Sekunde genau den> Ausgang von Entity_1 verarbeiten. Wie steht es um die Kausalität eines> solchen Systems?
Sieh es einfach mal so, dass die Reaktion auf die steigende Flanke zwar
quasi unendlich schnell, aber doch mit einer Zeit >0 passiert (und nicht
=0). Aus dem Grund wird eben nicht schon der Ausgang einer zeitgleich
getakteten Entity verarbeitet. VHDL-Simulatoren müssen das ja auch
"nachbauen". Zuerst wird festgestellt, welche Prozesse ausgelöst wurden.
Dann werden sie sequentiell ausgeführt, aber die Signal-Zuweisungen erst
noch "aufgehoben" und nicht gleich gespeichert. Erst wenn alle Prozesse
durch sind, werden die gespeicherten Zuweisungen zurückgeschrieben.
In der Praxis ist das mit der Kausalität aber auch nicht so einfach,
wenn der Takt entweder nicht überall gleichzeitig ankommt (Clock-Skew),
oder der Ausgang eines FFs so schnell reagiert, dass er beim nächsten
FF-Eingang noch die Hold-Zeit verletzt. Nicht umsonst macht man so einen
Aufstand mit der Taktverteilung in ICs...
> UpOrDown
Das ist mal so ein richtig verwirrender Name. Wenn das Signal Up_nDown
hieße, dann wüsste ich, dass es bei '1' hoch geht, und bei '0' runter.
Wenn das Signal nur Up hieße, dann wüsste ich das auch. Aber bei
UpOrDown: kann ich mir das raussuchen, ob es bei '1' rauf oder runter
geht? Oder bedeutet dieses Signal, dass sich überhaupt was tut, also
"Rauf oder Runter". Aber wofür gäbe es dann das EncChange auch noch?
Reinhard J. schrieb:> @Lothar: Deinen ersten Hinweis habe ich nicht verstanden.
Arbeite daran. Du wirst sonst noch OFT, SEHR OFT darüber stolpern.
> Ich denke> aber, dass mein jetziger Process womöglich auch hier robuster ist.
Falsch gedacht. Auch hier sind alle GETx nicht einsynchronisiert in
einem Zustandsautomaten verwendet.
Reinhard J. schrieb:> Nun dachte ich, wenn zu einem Zeitpunkt des Flankenanstiegs der clock> Entity_1 ein Ausgangssignal erzeugt, wie kann Entity_2 im allerselben> Moment, quasi auf die fantastillionste Sekunde genau den Ausgang von> Entity_1 verarbeiten. Wie steht es um die Kausalität eines solchen> Systems?
Der FPGA-Hersteller garantiert dir, dass die Taktverteilung im ganzen
FPGA so ist, dass sich nach einer Taktflanke die Ausgänge mehrerer
hintereinandergeschalteter Flipflops so ändern, dass jedes Flipflop der
Wert, der vor der Taktflanke am Eingang anlag, zum Ausgang durchgibt.
Es kann keinen "Fall-Trough" irgendeines Signals geben.
Man hat dafür einen Namen: "Synchrones Design". Das funktioniert so: mit
jeder Taktflanke übernimmt jedes Flipflop den Wert, der zu diesem
Zeitpunkt am D-Eingang anliegt und übergibt ihn an den Ausgang. Danach
herrscht im kompletten FPGA hecktische Unruhe, weil die Kombinatorik an
den Flipflop-Ausgängen neue Werte ermittelt. Dieser Vorgang muss
rechtzeitig vor der nächsten Taktflanke fertig sein, dass das Flipflop
mit dem folgenden Takt stabile Daten übernehmen kann. Dann beginnt das
Spiel von vorn.
> Hier scheint mein Denkfehler zu liegen. Besser ich betrachte nicht einen> Zeitpunkt, sondern die Zeitspanne zwischen zwei Flankenanstiegen. Dann> löst sich das Problem von selbst.
Nein, du verdrängst es nur in die Ecke "zum Glück kann das kaum
passieren".
Lies dir mal die KOMPLETTE Seite von meinem Link durch. Und vor Allem:
du MUSST unbedingt verstehen, was ich da meine. Solange das nicht der
Fall ist, werden deine Designs nicht zuverlässig laufen...
> Kurz, es klappt nicht.
Was ist "es"? WAS klappt nicht? Was erwartest du und was passiert? Was
sagt die Simulation?
> Der zweite Hinweis hat sich möglicherweise auch erledigt, da ich bei> GET0 den kompletten Zähler durch Kopieren einfriere und diesen dann> Byte-weise lese.
"Einfrieren" ist zum Glück das falsche Wort, du machst irgendwie kurios
eine Kopie.
> Nach dem Kopieren setze ich den Zähler zurück.
Und das ist Murks. Wirst du aber noch merken.
Einen Quadraturdecoder lässt man unberührt laufen und liest ihn nur!
Dann kann man in der Software einfach per Subtraktion die Differenz zum
zuletzt verarbeiteten Wert ermitteln und damit arbeiten. Denn sonst hast
du auch noch in der Software eine Asynchronität mit eingebaut. Und Alles
wird immer komplizierter und unbeherrschbarer...
Hallo Georg, hallo Lothar,
erstmal herzlichen Dank für Eure Hilfe.
@Georg: Deine Denkweise werde ich mir zu eigen machen.
@Lothar: Habe Deinen Artikel gedruckt und arbeite mich durch.
Ich berichte den Fortschritt, sobald ich weiter fortfahre.
Gruß Reinhard
Hallo Lothar,
nachdem Studium Deines Textes ist mir eins noch klarer geworden: Mit
VHDL ist man in wirklich in der Hardware angekommen. Soweit war Logikers
Nachfrage nach meinen Hardwarekenntnissen berechtigt. Bislang dachte ich
mit Assembler ganz nah an der Maschine zu sitzen. Mit VHDL gestalte ich
die Maschine. Das verdeutlichten mir Deine Artikel ganz und ganz.
Da mir nur eingeschränkte Hardwarekenntnisse zur Verfügung stehen, kann
ich nicht behaupten, dass Deine Ausführungen hundertprozentig bei mir
angekommen sind. Aber ich stehe am Anfang und mit viel Schwung werde ich
das hoffentlich nach und nach auf die Reihe kriegen. Ich sehe es mal
positiv: Nach dem Studium Deiner Seite hat der Beginn eines Andenkens
stattgefunden. Auf Deinem Blog steht viel interessantes, was mir wie ich
hoffe nach und nach verständlich wird. Vielen Dank auch für diese Seite.
Dem Lobesthread auf Mikrcontroller.net schließe ich mich an. Seiten wie
lothar-miller.de und mikrocontroller.net tun mehr für die Bildung als
manches Hochschulförderprogramm!
Nun zum vagen, d.h. nicht unbedingt von mir erfassten. Asynchrone
Eingänge, also solche deren Auftreten kein Bezug zum Takt haben sollen
nach Deinen Ausführungen vermittels zweier Flip-Flops einsynchronisiert
werden. Für die Landbevölkerung heißt das in VHDL zweimal in anderen
std_logic ablegen. Kann man das so mal lax festhalten?
Bei meinem Tun sind ganz offensichtlich GET0, GET1, GET2 und RESET
asynchron. Diese Signale werden ja vom AVR irgendwann abgesondert und
stehen in keinem Bezug zur clock. Ich habe - bitte nicht über die
Variablennamen meckern, sie sind erst mal schnell ersonnen - den Prozess
wie folgt beschrieben:
1
architectureBehavioralofCounteris
2
signalt_cnt1:std_logic_vector(15downto0);
3
signalt_cnt2:std_logic_vector(15downto0);
4
signalStateOfGet0:std_logic;
5
signalStateOfGet1:std_logic;
6
signalStateOfGet2:std_logic;
7
signalGET0:std_logic;
8
signalGET0mitte:std_logic;
9
signalGET1:std_logic;
10
signalGET1mitte:std_logic;
11
signalGET2:std_logic;
12
signalGET2mitte:std_logic;
13
signalRESET:std_logic;
14
signalRESETmitte:std_logic;
15
16
begin
17
18
Sync:processbegin-- Einsynchronisieren
19
waituntilrising_edge(clk);
20
GET0mitte<=GET0in;
21
GET0<=GET0mitte;
22
GET1mitte<=GET1in;
23
GET1<=GET1mitte;
24
GET2mitte<=GET2in;
25
GET2<=GET2mitte;
26
RESETmitte<=RESETin;
27
RESET<=RESETmitte;
28
endprocessSync;
29
-- und so weiter
Ist das im Sinne des von Dir verfassten Artikels?
Muss ich die Encoder nicht auch einsynchronisieren?
Was mir so gar nicht einleuchtet ist folgendes. Du schreibst:
>> Nach dem Kopieren setze ich den Zähler zurück.>Und das ist Murks. Wirst du aber noch merken. ...
Ja was spricht den dagegen den Encoder nur schrittweise zu zählen? Nach
jedem Schritt wird der Zählerstand festgehalten und dann in aller Ruhe
ausgelesen. Gibt es auch hierzu einen lehrreichen link?
Abschließend nochmals Danke und Gruß
Reinhard
PS. wie schaffen es viele Forumsleser sich so rasch in fremden Code
einzulesen? Ich kann das nicht.
Reinhard J. schrieb:> Ist das im Sinne des von Dir verfassten Artikels?
Ja, aber wie Schlumpf gesagt hat: Ich verwende dafür immer ein
Schieberegister. Der Vorteil dabei ist, dass ich auf dieses Signal dann
ganz leicht eine Flankenerkennung ansetzen kann. Siehe den Code im
Beitrag "Re: Erfahrung mit SPI Slave und Spartan 6 FPGA?"Schlumpf schrieb:> GET0 <= GET0(0) & GET0in;> Das einsynchronisierte Signal ist dann GET0(1)
Bei Frequenzen unter 200MHz reicht eigentlich schon ein einziges
Flipflop aus. Aaaaaber wehe, wenn der Synthesizer Register Duplication
macht, weil zu viele Lasten (Gattereingänge oder weitere Flipflops) am
Ausgang dieses einen Flipflops angeschlossen werden müssen (Stichwort:
Fanout). Sicherer ist es daher, wenn zwei Flipflops hintereinnander
kommen und das zweite als Einsynchronisiert betrachtet wird.
Für mich würde das in deinem Fall (mit überschaubar vielen Lasten am
ersten Flipflop) also so aussehen:
1
architectureBehavioralofCounteris
2
signalt_cnt1:integerrange0to65535;-- nimm eingeschränkte Integer zum Zählen!
3
signalt_cnt2:integerrange0to65535;
4
signalsrGET0:std_logic_vector(1downto0);
5
signalsrGET1:std_logic_vector(1downto0);
6
signalsrGET2:std_logic_vector(1downto0);
7
8
begin
9
-- Concurrent statt Prozess --> kurz und kompakt ;-)
Hallo!
habe mich wieder ein wenig drangesetzt und Lothars Ratschlag:
1
signalt_cnt1:integerrange0to65535;-- nimm eingeschränkte Integer zum Zählen!
2
signalt_cnt2:integerrange0to65535;
umgesetzt. Leider krieg ich dann den integer nicht in zwei Bytes
transportiert.
Ich versuche einen slice und eine Zuweisung auf std_logic_vector mit
1
COUNT<=std_logic_vector(t_cnt2(7downto0));
und dann für das zweite Byte
1
COUNT<=std_logic_vector(t_cnt2(15downto8));
Leider kriege ich nun
ERROR:HDLParsers:3270 ... Line 92. t_cnt2 is not an array slice prefix.
ERROR:HDLParsers:822 ... Wrong slice type for t_cnt2.
Weiß jemand Rat?
Gruß Reinhard
Schau mal auf Lothar´s Seite. Der hat da eine super Übersicht zur
Konvertierung von Datentypen in VHDLhttp://www.lothar-miller.de/s9y/index.php?serendipity[action]=search&serendipity[fullentry]=1&serendipity[searchTerm]=cast&serendipity[searchButton]=%3E
Der Integer muss erst noch auf Bits "abgebildet" werden, bevor du ihn
slicen kannst. Das geschieht mit den Konvertierungen wie auf Lothar´s
Seite beschrieben unter Angabe der Anzahl der Bits, die diesen Wert
repräsentieren.
Also aus Integer wird zuerst ein "unsigned" oder "signed" mit
entsprechender Anzahl von Bits. Das könntest du jetzt bereits slicen.
Wenn du aber auf std_logic_vetor abbilden willst, dann musst du eben
noch auf std_logic_vector casten.
Reinhard J. schrieb:> Ich druck mir das aus
Gut so :-) Ich würde mal gerne wissen, wieviele Leute sich diese
kompakte Übersicht schon ausgedruckt haben..
Hallo Schlumpf,
das Ausdrucken hat sich gelohnt. Ich finde die Grafik super, obwohl ich
den Unterschied zwischen Konvertierung und Cast noch nicht erfasst habe
- ich grübel noch ein bisschen drüber nach.
Jetzt bekomme ich keine Schimpfe vom Parser mehr. Hier der Auszug meines
schrittweisen Übergangs, d.h. erst Konvertierung und dann Cast.
Hier die Definitionen:
1
entityCounteris
2
Port(clk:instd_logic;-- Systemtakt
3
GET0in:instd_logic;-- Get first byte
4
GET1in:instd_logic;-- Get second byte
5
EncChange:instd_logic;-- Encoder has changed signal
6
UpOrDown:instd_logic;-- Direction indicator
7
RESETin:instd_logic;-- Reset
8
COUNT:outstd_logic_vector(7downto0)-- Output of one byte of the snapshot
9
);
10
endCounter;
und dann in der architecture
1
signalt_cnt1:integerrange-32768to32767;-- nimm eingeschränkte Integer zum Zählen!
2
signalt_cnt2:std_logic_vector(15downto0);
3
signalt_cnt2_signed:signed(15downto0);
Nun das schrittweise Umsetzen und Lesen des LSB
1
t_cnt2_signed<=to_signed(t_cnt1,16);
2
t_cnt2<=std_logic_vector(t_cnt2_signed);
3
COUNT<=t_cnt2(7downto0);-- LSB lesen
und des MSB
1
COUNT<=t_cnt2(15downto8);-- MSB lesen
Und jetzt die wichtige Frage:
Ist das dann ein 16 Bit Integer vom Typ int16_t für den AVR?
Nochmals ganz herzlichen Dank für all die Tipps.
Gruß Reinhard
Reinhard J. schrieb:> obwohl ich den Unterschied zwischen Konvertierung und Cast noch nicht> erfasst habe
Sieh dir die entsprechenden Funktionen in der numeric_std an. Du wirst
den Unterschied leicht erkennen...
Hallo Schlumpf, hallo Lothar,
mein Urlaub geht zu Ende und nun wird es einige Zeit dauern bis ich das
Projekt fertigstellen kann. Bislang läuft das noch nicht richtig.
Mein VHDL habe ich nun mit unsigned also
1
-- signal t_cnt1 : integer range -32768 to 32767; -- nimm eingeschränkte Integer zum Zählen!
2
signalt_cnt1:integerrange0to65535;-- nimm eingeschränkte Integer zum Zählen!
3
signalt_cnt2:std_logic_vector(15downto0);
4
-- signal t_cnt2_signed : signed(15 downto 0);
5
signalt_cnt2_unsigned:unsigned(15downto0);
Ich setze dann bei Reset und beim Auslesen
1
t_cnt1<=32767;
Weter oben kam die Frage auf, wie ich die Anbindung an den AVR gestalte.
Das will ich nun auch erläutern.
Hardwaremäßig habe ich zwei Kanäle, also zwei CPLDs, auf Lochraster mit
einem Atmega32 verbunden. Die COUNT Ausgänge der CPLDs sind an PIND bzw.
an PINC des Atmega32 angeschlossen. GET0 und GET1 des ersten CPLD hängen
an PORTB.0 und PORTB.1, die des zweiten CPLD an PORTB.5 und PORTB.6. Die
Resets werden über Taster bedient, die für CPLD1 an PINB.3 und für CPLD2
an PINA.6 hängen. Der Atmega32 gibt dann die Resets über PORTB.4 an
CPLD1 und über PORTA.7 an CPLD2 weiter. Sowie ich eine elektronische
Zeichnung erstellt habe, lade ich diese hoch.
Unten steht der derzeitige AVR C-Code, welcher noch nicht richtig läuft.
Für die Interessierten poste ich ihn dennoch. Ich werde sowie ich hier
weitermache den Fortschritt berichten, sage aber schon jetzt, dass das
dauern kann.
In meiner Woche Urlaub habe ich eine Menge von den Beiträgen in diesem
Thread gelernt, was mir das Wichtigste war, und ich bedanke mich bei
allen insbesondere aber bei Lothar und Schlumpf aufs herzlichste.
Mittlerweile steht mir ein Xilinx 3AN Starter Kit zur Verfügung. Keine
Ahnung wie man damit loslegt. Das Lernen steht erst mal im Vordergrund
und es kann sein, dass ich die Versuche mit dem Board diesem Projekt
vorziehe, da mir jede Menge Kenntnisse zu VHDL fehlen. Ich denke ich
eröffne einen Thread, falls ich keinen Einstieg finde. Die AVRs hatte
ich mit myAVR kennengelernt und da gab es ein kleines Heft. In dem
Xilinx 3AN Starter Kit liegt nichts Schriftliches. Muss mal sehen, was
die auf Ihrer web-Seite zur Verfügung stellen.
Viele Grüße
Reinhard
PS
@Schlumpf: Kennst Du Peyo? Mein Favorit ist "Der Astronautenschlumpf".
1
voidResetCPLD(uint8_tCPLDNumber)
2
{
3
if(CPLDNumber==1)
4
{
5
// CPLD1
6
// Clear the GET-signal bits on Port B
7
PORTB&=~(1<<0);// Clear GET0
8
PORTB&=~(1<<1);// Clear GET1
9
10
// Reset CPLD1 - i.e. Set the CPLD1 counter to 0
11
PORTB&=~(1<<4);// Clear RESET for CPLD1
12
PORTB|=(1<<4);// set bit 4 in PORTB - Reset the CPLD1
13
_delay_ms(1);
14
PORTB&=~(1<<4);// clear bit 4 in PORTB
15
}
16
else
17
{
18
// CPLD2
19
// Clear the GET-signal bits on Port B
20
PORTB&=~(1<<5);// Clear GET0
21
PORTB&=~(1<<6);// Clear GET1
22
23
// Reset CPLD2 - i.e. Set the CPLD2 counter to 0
24
PORTA&=~(1<<7);// Clear RESET for CPLD2
25
PORTA|=(1<<7);// set bit 7 in PORTA - Reset the CPLD2
26
_delay_ms(1);
27
PORTA&=~(1<<7);// clear bit 7 in PORTA
28
}
29
}
30
31
32
voidUpdateDisplay(uint8_tiCPLDNumber,
33
uint8_t*iGet0State,
34
volatileuint8_t*iGet0Port,
35
uint8_tiGet0Pin,
36
uint8_t*iGet1State,
37
volatileuint8_t*iGet1Port,
38
uint8_tiGet1Pin,
39
volatileuint8_t*iDataInputPort,
40
int32_t*int32DisplayValue,
41
intiDisplayColumnStart,
42
intiDisplayRowStart)
43
{
44
charstrDisplayValue[10];// 8 characters plus \0
45
int16_tint16CounterValue=0;
46
int32_tint32CounterValue=0;
47
48
// Initialize string
49
strDisplayValue[0]=' ';
50
strDisplayValue[1]=' ';
51
strDisplayValue[2]=' ';
52
strDisplayValue[3]=' ';
53
strDisplayValue[4]=' ';
54
strDisplayValue[5]=' ';
55
strDisplayValue[6]=' ';
56
strDisplayValue[7]=' ';
57
strDisplayValue[8]=' ';
58
strDisplayValue[9]='\0';
59
60
uint8_tlsb;
61
uint8_tmsb;
62
63
if(*iGet0State==0)
64
{
65
*iGet0Port|=(1<<iGet0Pin);// Set GET0
66
*iGet0State=1;// Update State
67
}
68
else
69
{
70
*iGet0Port&=~(1<<iGet0Pin);// Clear GET0
71
*iGet0State=0;// Update State
72
}
73
_delay_ms(1);
74
lsb=*iDataInputPort;// Receive the least significant byte
75
76
if(*iGet1State==0)
77
{
78
*iGet1Port|=(1<<iGet1Pin);// Set GET1
79
*iGet1State=1;// Update State
80
}
81
else
82
{
83
*iGet1Port&=~(1<<iGet1Pin);// Clear GET1
84
*iGet1State=0;// Update State
85
}
86
_delay_ms(10);
87
msb=*iDataInputPort;// Receive the most significant byte
Schlumpf schrieb:> Soviel ich weiss, arbeitet der AVR little endian. Also passt es.
Ich habe total ignoriert, dass du dir die Werte über die 8-Bit GPIOs des
AVR reinnuckelst. Da spielt dann die Endianess des Controllers auch gar
keine Rolle.
Eigentlich bist du mit deiner Vorgehensweise schon auf halben Weg, ein
anständiges Businterface zu implementieren. Denn GETxy ist quasi eine
Adresse und COUNT ist der Datenbus.
Wenn du es jetzt noch geschickt anstellst, brauchst du gar nicht so
viele Pins am Controller verbraten.
Für ein "richtiges" Businterface würdest du folgendes benötigen
CE-Signal zum Auswählen des CPLDs
Adressbus (In deinem Fall reichen A0..A2)
Datenbus (mit Tri-State-Ausgang)
Und das war´s eigentlich schon. Auf OE kannst du verzichten, da du das
Deaktivieren der Datentreiber auch über CE bewerkstelligen kannst und
nachdem du nichts schreiben willst, kannst du auf das WE auch
verzichten.
Eventuell brauchst du noch ein ALE-Signal, wenn du einen gemultiplexten
Bus verwendest.
Klingt jetzt vielleicht bisschen verwirrend, wenn du dich damit noch nie
befasst hast, aber wäre in deinem Fall die elegantere Lösung, um an die
Daten in deinen CPLDs ranzukommen.
Vorallem brauchst du dann in deinem C-Code nur einen Pointer auf die
gewünschte Adresse anlegen und darin steht dann direkt der Wert. Du
musst also nicht erst "manuell" Ports einlesen, sondern die Werte stehen
dann quasi direkt in der Variable.
Aber so wie du das vor hast, geht das natürlich auch. Ist nur ein
bisschen umständlicher.
Falls es dich interessiert, deine CPLDs an das Businterface deines
Controllers zu legen, bekommst du hier auch sicher Hilfe.
Hallo Schlumpf, hallo Lothar,
erinnert ihr Euch noch an das Projekt? Ich habe wieder Zeit gefunden, um
mich damit zu beschäftigen.
Die Kommunikation habe ich wie oben beschrieben realisiert. Um es
vorwegzunehmen: Es klappt noch nicht.
Ich lasse mir die Encoderzählwerte auf einem LCD vom AVR anzeigen.
Leider liefen die Werte auch ohne dass Encodersignale am CPLD variiert
wurden. Darum habe ich versucht vermittels einer Eingrenzung den Fehler
zu lokalisieren.
In der architecture habe ich
1
architectureBehavioralofCounteris
2
signalt_cnt1:integerrange0to65535;-- nimm eingeschränkte Integer zum Zählen!
3
signalt_cnt2:std_logic_vector(15downto0);
4
5
signalStateOfGet0:std_logic;
6
signalStateOfGet1:std_logic;
7
8
signalGET0:std_logic;
9
signalGET0mitte:std_logic;
10
signalGET1:std_logic;
11
signalGET1mitte:std_logic;
12
signalRESET:std_logic;
13
signalRESETmitte:std_logic;
14
15
signalt_cnt2_unsigned:unsigned(15downto0);
alles wird einsynchronisiert
1
-- einsynchronisieren
2
GET0mitte<=GET0in;
3
GET0<=GET0mitte;
4
GET1mitte<=GET1in;
5
GET1<=GET1mitte;
Und nun der Test
1. Test
-------
1
if(rising_edge(clk))then
2
COUNT<="11101000";
3
endif;
Da das Programm zweimal liest und zweimal die gleichen Byte zu einem
int16_t zusammenbaut entsteht 0b1110100011101000 was dezimal 59624
entspricht. Das wird korrekt angezeigt. Der AVR scheint alles korrekt zu
erledigen.
2. Test
-------
1
if(rising_edge(clk))then
2
t_cnt1<=100;
3
t_cnt2_unsigned<=to_unsigned(t_cnt1,16);
4
t_cnt2<=std_logic_vector(t_cnt2_unsigned);
5
COUNT<=t_cnt2(7downto0);
6
endif;
100 das ist 0b01100100. Wieder wird zweimal gelesen und zweimal die
gleichen Byte zu einem int16_t zusammengebaut und es entsteht
0b110010001100100 wasn dezimal 25700 entspricht. Auch das wird korrekt
angezeigt.
3. Test
-------
1
if(rising_edge(clk))then
2
if(GET0/=StateOfGet0)then
3
StateOfGet0<=GET0;
4
t_cnt1<=100;
5
t_cnt2_unsigned<=to_unsigned(t_cnt1,16);
6
t_cnt2<=std_logic_vector(t_cnt2_unsigned);
7
COUNT<=t_cnt2(7downto0);
8
elsif(GET1/=StateOfGet1)then
9
StateOfGet1<=GET1;
10
t_cnt1<=50;
11
t_cnt2_unsigned<=to_unsigned(t_cnt1,16);
12
t_cnt2<=std_logic_vector(t_cnt2_unsigned);
13
COUNT<=t_cnt2(7downto0);
14
endif;
15
endif;
Der AVR toggelt zuerst GET0 und liest das LSB das sind 100 bzw.
0b01100100. Nun wird GET1 getoggelt und die 50 werden gelesen, was
0b00110010 entspricht, und ins MSB gepackt wird. Nach dem Zusammenbau
des int16_t haben wir also 0b0011001001100100 und das sind 12900. Hier
fängt die Anzeige an zu spinnen.
Sie zeigt folgende Zahlen in loser Reihenfolge
13107: 0b0011 0011 0011 0011
25907: 0b0110 0101 0011 0011
und einige Zahlen mehr und nur ab und zu 12900.
Meine Frage lautet nun können die Signale GET0 und GET1 so wie ein
Schalter prellen und mir die Kommunikation zerstören?
Hat jemand eine Idee. Ich überlege weitere Tests.
Schon jetzt Danke für alle Tipps.
Gruß Reinhard
Welcome back ;-)
Du hast da einen bösen Denkfehler drin.
Dieses Konstrukt baut dir 4 Registerstufen.
1
if(rising_edge(clk))then
2
t_cnt1<=100;
3
t_cnt2_unsigned<=to_unsigned(t_cnt1,16);
4
t_cnt2<=std_logic_vector(t_cnt2_unsigned);
5
COUNT<=t_cnt2(7downto0);
6
endif;
Es dauert also 4 Takte, bis die 100 dann endlich auf COUNT ausgegeben
werden.
Das merkst du bei Test1 und Test2 nicht, da die Werte immer die gleichen
sind.
Bei Test3 holt dich das dann allerdings ein.
1
if(GET0/=StateOfGet0)then
ist nur einen Takt lang "erfüllt". Mit diesem einen Takt wird dann alles
weitere ausgeführt.
1
StateOfGet0<=GET0;-- aktueller Zustand von GET0 wird gespeichert
2
t_cnt1<=100;-- 100 wird in t_cnt1 geschrieben
ABER in den folgenden Zeilen beschreibst du t_cnt2_unsigned nicht mit
dem in der vorangegengenen Zeile zugewiesenen t_cnt1, sondern mit dem
Wert der VOR der letzten Zuweisung von t_cnt1 gespeichert wurde.
Und der war vermutlich der Wert, der beim Zugriff über GET1 übernommen
wurde, da du in beiden Zugriffen mit t_cnt1 arbeitest.
Und mit dem nächsten Takt passiert nichts. Gar nichts. Denn dann ist
keine der beiden IF-Bedingungen erfüllt. Somit "wandert" der Wert nicht
weiter durch die Register.
Nehmen wir mal an, du hättest nur folgendes geschrieben:
1
if(rising_edge(clk))then
2
if(GET0/=StateOfGet0)then
3
StateOfGet0<=GET0;
4
t_cnt1<=100;
5
t_cnt2_unsigned<=to_unsigned(t_cnt1,16);
6
t_cnt2<=std_logic_vector(t_cnt2_unsigned);
7
COUNT<=t_cnt2(7downto0);
8
endif;
9
endif;
Dann müsstest du GET0 vier mal toggeln lassen, bis die 100 an COUNT
ausgegeben werden.
Da du aber deine Registerbänke bei deinem Zugriff mittels GET1 immer
wieder überschreibst, kommt alles durcheinander.
Hallo Schlumpf,
herzlichsten Dank für die Antwort!
Das lässt mir einfach keien Ruhe. Möchte das Projekt nur ungern aufgeben
bevor ich mich an mein Spartan 3AN board setze. Bestimmt brauche ich da
auch jede Menge Hilfe.
Deine Antwort muss ich noch mehrfach lesen, denn bis jetzt habe ich sie
nicht wirlich begriffen, wie der nachfolgende Test zeigt.
Ich habe jetzt folgendes probiert:
1
if(rising_edge(clk))then
2
if(GET0/=StateOfGet0)then
3
t_cnt1<=100;
4
t_cnt2_unsigned<=to_unsigned(t_cnt1,16);
5
t_cnt2<=std_logic_vector(t_cnt2_unsigned);
6
COUNT<=t_cnt2(7downto0);
7
StateOfGet0<=GET0;
8
endif;
9
if(GET1/=StateOfGet1)then
10
t_cnt1<=50;
11
t_cnt2_unsigned<=to_unsigned(t_cnt1,16);
12
t_cnt2<=std_logic_vector(t_cnt2_unsigned);
13
COUNT<=t_cnt2(7downto0);
14
StateOfGet1<=GET1;
15
endif;
16
else
17
null;
18
endif;
Jetzt schieße ich das Bit erst am Ende um und es geht trotzdem nicht.
Müsste es jetzt nicht gehen, da ich die Bedingung bis zum Ende aufrecht
erhalte?
Ich lese mir Deine Hinweise morgen früh noch ein paarmal durch.
Vielleicht komme ich dann weiter. Wir werden sehen.
VHDL lernen braucht wohl Ausdauer. Ist kniffelig, aber macht Schpass.
Danke und Gruß
Reinhard
Reinhard J. schrieb:> Müsste es jetzt nicht gehen, da ich die Bedingung bis zum Ende aufrecht> erhalte?
Nein, die Reihenfolge der Zeilen ist hier egal ;-)
Das ist VHDL. Du beschreibst eine Hardware und keinen sequenziellen
Ablauf an Befehlen.
Das ist das Erste, was du begreifen musst. Wenn da mal der Groschen
gefallen ist, dann geht´s besser ;-)
1
if(rising_edge(clk))then
2
if(A/=B)then
3
B<=A;
4
x<=100;
5
y<=x;
6
z<=y;
7
Q<=z
8
endif;
9
endif;
Damit beschreibst du ein Register B, x, y, z und Q, welche alle mit clk
getaktet sind, und alle ihre Eingänge nur dann übernehmen, wenn der
Ausgang von Register B und das Signal A ungleich sind.
Weiterhin beschreibst du, dass am Eingang des B-Registers das Signal A
anliegt, am Eingang des x-Registers liegt 100 an, am Eingang des
y-Registers liegt der Ausgang des x-Registers an und so weiter...
Zeichne mal den Schaltplan dazu und dann wirst du sehen, warum das nicht
das tut, was du dir vorstellst.
Hallo Schlumpf,
mit dem Schaltplan zeichen habe ich es noch nicht so. Das muss ich
lernen. Die Xilinx ISE zeichnet mir ja so eine Übersicht mit so
Blackboxen, in die Signale rein- und rausgehen. Aber Du meinst bestimmt
eine Zeichnung, welche die architecture feiner auflöst.
Deine Antwort leuchtet mir ein und ich wusste das auch. Nur hatte ich
vergessen die process Statements mit reinzukopieren. Ich habe gelesen,
dass die Statements eines process sequentiell abgearbeitet werden. Darum
verstehe ich nicht, warum es nicht klappt.
Falls das mit der sequentiellen Abarbeitung innerhalb eines process
stimmt, kannst Du mir bitte auch noch erklären wie die Abarbeitung
zeitlich verläuft. Unten habe ich nun den process komplett eingefügt.
Bisher nahm ich an, der ganze process wird in einem Takt abgespult.
Anscheinend trifft das nicht zu. Mir fehlen da noch einige
Hardware-Kenntnisse und ich hoffe das bald beheben zu können.
Braucht es für die Abarbeitung der unten gezeigten Statements fünf clock
cycles oder funktioniert ein process völlig anders?
Falls eine sequentielle Abarbeitung in fünf clock cycles geschieht,
müsste es nicht so sein, dass die if-Bedingungen erst mit Umknipsen der
StateOfGet0- und StateOfGet1-Bits nicht mehr betreten werden? Mit
anderen Worten: Wenn die statements nach und nach abgekaspert werden,
dann muss der process doch funktionieren. Nur das tut er nicht.
Hab wirklich vielen Dank für die Bemerkung zu den concurrent statements.
Mir macht aber derzeit die Abarbeitung der processes mehr Kopfweh. Bei
einem Controller ist das halbwegs klar, da steht die Adresse des
nächsten Befehls im Program Counter und damit gehts nach der
Bearbeitung, d.h. je nach Op-Code nach einer festen Anzahl von clock
cycles weiter. Wie ist das aber in Hardware? Ich bin ratlos.
Ohne derart hilfreiche Recken wie Lothar und Dich käme ich in Sachen
VHDL gar nicht weiter.
Herzliche Grüße
Reinhard
rvj schrieb:> mit dem Schaltplan zeichen habe ich es noch nicht so. Das muss ich> lernen.
Das ist aber die Grundlage, um überhaupt mit VHDL arbeiten zu können.
Mit VHDL beschreibt man eine Schaltung und programmiert nicht. Wenn man
aber von Schaltungstechnik keine Ahnung hat, dann wird man auch daran
scheitern, eine Schaltung in VHDL beschreiben zu wollen :-(
Aber zu deinem "Problem":
Ein Process wird so interpretiert, dass die letzte Zuweisung auf ein
Signal "gilt". Alles davor wird quasi überschrieben. Und jetzt kommt´s:
Die eigentliche "Übernahme" erfolgt beim Verlassen des Process.
Und nochmal: Der Process "läuft" nicht im FPGA ab, sondern er ist eine
Methode, wie durch bestimmte syntaktische Konstrukte und Regeln das
Verhalten einer Schaltung modelliert wird. Der Synthesizer interpretiert
daraus dann, wie die Schaltung aussehen muss.
Auf der echten Hardware gibt es keinen Process mehr, es wird auch kein
Process verlassen, es werden auch keine Signale zu einem bestimmten
Zeitpunkt aktualisiert etc..
Jegliche sequenzielle Funktion muss in echter Hardware über Register
beschrieben werden.
Also auch ein Process ohne clock wird in Hardware vollkommen concurrent
abgebildet.
Es spielt also keine Rolle, ob du einen Zusammenhang concurrent oder in
einem ungetakteten Process beschreibst. Solange du den gleichen
Zusammenhang beschreibst, macht die Synthese immer eine flache,
kombinatorische Verknüpfung draus. In einem Process hast du nur andere
syntaktische Elemente als in einem concurrent Statement.
Beispiel:
1
process(EN,D)
2
begin
3
ifEN='1'then
4
Q<=D;
5
else
6
Q<='Z';
7
endif;
8
endprocess;
ist exakt das gleiche wie:
1
Q<=DwhenEN='1'else'Z';
Beide Konstrukte führen dazu, dass die Synthese einen Tristate-Buffer
instanziiert, den Eingang an D, den Ausgang an Q und den high-aktiven
Enable-Eingang an EN anschließt.
Du hast ja vieles richtig geschrieben, aber der letzte Punkt hier stimmt
nicht:
Schlumpf schrieb:> Auf der echten Hardware gibt es keinen Process mehr, es wird auch kein> Process verlassen, es werden auch keine Signale zu einem bestimmten> Zeitpunkt aktualisiert etc.
Natürlich gibt es einen bestimmten Zeitpunkt, zu dem die Signale
aktualisiert werden. Die Taktflanke bestimmt den Zeitpunkt, zu dem ein
Speicherelement (FlipFlop, Register) die Werte am Eingang übernimmt und
am Ausgang zur Verfügung stellt.
Wenn es keine Flanke gibt, wird eine kombinatorische Schaltung
synthetisiert.
Duke
He..., bau dir mal diese Baugruppen mit VHDL als Componenten:
and N-input AND gate
nand N-input NAND gate
or N-input OR gate
nor N-input NOR gate
xor N-input XOR gate
xnor N-input XNOR gate
not N-output inverter
buf N-output buffer.
bufif0 Tri-state buffer, Active low en
bufif1 Tri-state buffer, Active high en
notif0 Tristate inverter, Low en
notif1 Tristate inverter, High en
Dann schaust du im RTL-Viewer wie sie aussehen und machst deinen
Schaltplan
So habe ich es in Verilog gemacht mit Quartus zb.
Danach habe ich eine Schaltplan aus den Gattern und Tristates gezeichnet
und in Verilog umgesetzt, es ist ein Minirechner geworden.
Gruss
Hier sind die Baugruppen, die ich im RTL-Viewer habe von Quartus.
Bei mir in Verilog sind es Module , bei dir in VHDL sind es Componenten.
Ich habe mir noch viele andere Module entworfen.
Diese fertigen Module lade ich ein und verdrahte die mit "wire", fertig
ist die Schaltung.
GRuss
Duke Scarring schrieb:> Natürlich gibt es einen bestimmten Zeitpunkt, zu dem die Signale> aktualisiert werden. Die Taktflanke bestimmt den Zeitpunkt, zu dem ein> Speicherelement (FlipFlop, Register) die Werte am Eingang übernimmt und> am Ausgang zur Verfügung stellt.
Richtig, aber es gibt keinen Process auf der HW. Es gibt eine
Process-Beschreibung, die ein Register erzeugt, das sich so verhält, wie
du geschrieben hast. Es gibt aber auch ungetaktete Prozesse, die dann zu
kombinatorischer Logik führen.
Aber auf was ich raus wollte ist, dass es eben auf der HW nicht einfach
ein "sequenzieller" Ablauf abgebildet wird, nur weil in einem Process
die Zuweisungen sequenziell untereinander stehen.
Vielleicht etwas verwirrend von mir formuliert.
Ich denke, du hast dich auf "den bestimmten Zeitpunkt" bezogen, den es
natürlich gibt, wenn man einen getakteten Process verwendet.
In dem Kontext als ich diese Aussage machte, ging es aber darum, dass in
einem Process die Signale erst am Ende des Process aktualisiert werden.
Darauf bezog ich mich. Ich wollte darstellen, dass es diesen Zeitpunkt
"Ende des Process" schlichtweg nicht gibt. Anders als in Software, wo es
sehr wohl den Zeitpunkt gibt, wenn ein Programm oder eine Funktion ihr
Ende erreicht hat.
Dieses Übernahme der Daten am Ende des Process gibt es nur in der
Beschreibung. In "echt" wird daraus entweder eine kombinatorische
Verknüpfung die (abgesehen von Signallaufzeiten) keinen Anfang und kein
Ende hat und somit auch keinen Übernahmezeitpunkt. Oder es wird ein
Register gebildet, das den Übernahmezeitpunkt mit der aktiven Taktflanke
hat. Aber auch dieses Register wartet nicht auf das Ende des Process.
Ich denke, das ist jedem so sonnenklar, der Erfahrung mit HDL hat, aber
ein Anfänger, der vielleicht sogar vorher Software geschrieben hat, tut
sich da sicher nicht ganz leicht.
Das Problem ist einfach das Verständnis, dass eine sequenzielle
Beschreibung (also Codezeile unter Codezeile) eben nicht zu einem
sequenziellen Ablauf im Chip führt, sondern sequenzielle Abläufe nur
durch Register gebildet werden können, die dann aber auch dediziert
beschrieben werden müssen.
Und nur der Vollständigeit halber (Reinhard, bitte das am Besten nicht
lesen). Natürlich kann man auch concurrent sequenzielles Verhalten
beschreiben, aber das würde dann nur über Signallaufzeiten im Chip
funktionieren, was absoluter Murks ist.
@Schlumpf:
Du hast das schon gut gemacht, mit Deiner Darstellung. Meine Bemerkung
war wirklich etwas spitzfindig und in diesem Kontext nicht richtig.
Duke
Duke Scarring schrieb:> Du hast das schon gut gemacht, mit Deiner Darstellung. Meine Bemerkung> war wirklich etwas spitzfindig und in diesem Kontext nicht richtig.
Ich habe sie nicht als spitzfindig empfunden.
Mir gab es nur nochmal nen Impuls nachzuschauen, ob ich mich vielleicht
missverständlich ausgedrückt habe.
Ist doch gut, wenn es solche Kommentare gibt. Wenn man etwas erklärt,
was einem selbst klar ist, kann es schon passieren, dass man sich
ungeschickt ausdrückt, so dass es missverständlich sein kann.
Also alles prima ;-)
Habe beim Schreiben erst selbst gemerkt, dass es gar nicht so einfach
ist, den Unterschied zwischen sequenzieller Beschreibung (also Process)
und tatsächlich sequenzieller Hardware für einen Anfänger plastisch
darzustellen.
Hallo Schlumpf,
nach mehrfacher Lesung Deiner Kommentare versuche ich eine naive
Vorgangsbeschreibung.
Schlumpf schrieb am 12.11. um 16:50
> In dem Kontext als ich diese Aussage machte, ging es aber darum,> dass in einem Process die Signale erst am Ende des Process> aktualisiert werden.
Nehmen wir mal an wir haben folgendes:
1
CounterHandling:process(clk)is-- reagiert auf clk
2
begin
3
if(rising_edge(clk))then
4
B<=A;-- 1
5
C<=B;-- 2
6
D<=C;-- 3
7
StateOfGet0<=GET0;-- 4
8
endif;
9
endprocessCounterHandling;
Zum Zeitpunkt t=0 seien
A "00000001"
B "00000000"
C "00000000"
D "00000000"
GET0 '1'
StateOfGet0 '0'
Jetzt gehen wir taktweise durch unter der Annahme, dass sich A und GET0
über dem gesamten betrachteten Zeitverlauf nicht ändern.
nach erstem Takt hat D das was in C stand übernommen, C was in B stand
und B was in A stand.
A "00000001"
B "00000001"
C "00000000"
D "00000000"
GET0 '1'
StateOfGet0 '1'
nach zweitem Takt gehts genauso weiter
A "00000001"
B "00000001"
C "00000001"
D "00000000"
GET0 '1'
StateOfGet0 '1'
und nach drittem Takt ist in D endlich "00000001" angekommen.
GET0 '1'
A "00000001"
B "00000001"
C "00000001"
D "00000001"
GET0 '1'
StateOfGet0 '1'
Kann es sein dass es so ist?
Wenn dem so ist, dann habe ich mich bei meinem Trick mit StateOfGet0
(siehe 12.11.2014 11:03) selbst aus dem if ausgesperrt (siehe unten).
Eigentlich will ich erreichen, dass die 100 nur einmal kopiert wird und
nicht während der gesamten Zeitdauer in der GET0 einen bestimmten Wert
annimmt.
1
if(GET0/=StateOfGet0)then
2
t_cnt1<=100;
3
t_cnt2_unsigned<=to_unsigned(t_cnt1,16);
4
t_cnt2<=std_logic_vector(t_cnt2_unsigned);
5
COUNT<=t_cnt2(7downto0);
6
StateOfGet0<=GET0;
7
endif;
Kann frühestens morgen Abend weiter machen. Versuch macht klug! Ich
werde berichten.
Danke an alle für die Diskussion! Extra Dank an Schlumpf!
Gruß Reinhard
rvj schrieb:> Kann es sein dass es so ist?
Exakt so ist es!
rvj schrieb:> Wenn dem so ist, dann habe ich mich bei meinem Trick mit StateOfGet0> (siehe 12.11.2014 11:03) selbst aus dem if ausgesperrt (siehe unten).
Exakt so ist es ;-)
Und dabei ist es egal, ob StateOfGet0 <= GET0; oben, unten oder in der
Mitte steht.
rvj schrieb:> Eigentlich will ich erreichen, dass die 100 nur einmal kopiert wird und> nicht während der gesamten Zeitdauer in der GET0 einen bestimmten Wert> annimmt.
Das dachte ich mir. Und im Prinzip machst du das ja, indem du deine
Übernahme nur mit dem Takt machst, bei dem GET0 seinen Zustand wechselt.
ABER du machst dann folgenden Fehler:
Du schiebst deinen übernommenen Wert dann nochmal durch mehrere
Register. Und diese Register übernehmen ihren Wert aber auch nur, wenn
GET0 seinen Zustand wechselt. Es Verhält sich dann mit dem Wert so, wie
du oben selbst dargestellt hast. Es wird jedesmal in das nachfolgende
Register übernommen, wenn ein Takt kommt UND GET0 bei diesem Takt seinen
Zustand gewechselt hat.
rvj schrieb:> Danke an alle für die Diskussion! Extra Dank an Schlumpf!
Sehr gerne geschehen.
Ich schätze dich mal so ein, dass du mit ein paar Schubsern in die
richtige Richtung selber draufkommen willst, oder? :-)
Hallo Schlumpf, Peter, Duke und Lothar,
Dank Eurer Hilfe komme ich der Lösung näher.
Schlumpf schrieb:
> Ich schätze dich mal so ein, dass du mit ein paar Schubsern in die> richtige Richtung selber draufkommen willst, oder? :-)
Exakt so ist es :-)
Jetzt läuft der Wert nicht mehr, bleibt aber auch nicht ruhig stehen.
Das heißt er wechselt manchmal auf was falsches und zeigt dann wieder
die richtige Zahl an. Das muss ich mir noch mal anschauen. Ich probiere
also weiter und berichte.
Unten gibts schon mal den Stand. Behandle nur noch die Zuweisung und den
state-update im process. Die Augabe wird außerhalb durchgeführt.
Habe das heute Abend ganz schnell probiert. Dauert bestimmt wieder bis
zum Wochenende bis ich weitermachen kann.
Viele Grüße
Reinhard
1
entityCounteris
2
Port(clk:instd_logic;-- Systemtakt
3
GET0in:instd_logic;-- Get first byte
4
GET1in:instd_logic;-- Get second byte
5
EncChange:instd_logic;-- Encoder has changed signal
6
UpOrDown:instd_logic;-- Direction indicator
7
RESETin:instd_logic;-- Reset
8
COUNT:outstd_logic_vector(7downto0)-- Output of one byte of the snapshot
9
);
10
endCounter;
11
12
architectureBehavioralofCounteris
13
signalt_cnt1:integerrange0to65535;-- nimm eingeschränkte Integer zum Zählen!
Metamorphose vom tcnt1 zum tcnt2? Seis drum, das geht natürlich auch
kürzer:
1
t_cnt2<=std_logic_vector(to_unsigned(t_cnt1,16));
> Behandle nur noch die Zuweisung und den state-update im process. Die> Augabe wird außerhalb durchgeführt.
Das ist egal, es ergibt die gleiche Hardware! Jede Anweisung ausserhalb
ist ganz einfach in einen Prozess überführbar. Das hier:
1
t_cnt2<=std_logic_vector(to_unsigned(t_cnt1,16));
Kann man ohne jegliche Hardwareänderung auch so schreiben:
1
process(t_cnt1)begin
2
t_cnt2<=std_logic_vector(to_unsigned(t_cnt1,16));
3
endprocess;
Und zuletzt: schmeiß diese beiden Zeilen unbedingt raus:
Reinhard, du wirst halt einfach immer Schiffbruch erleiden, solange du
dir nicht die Grundkenntnisse der digitalen Schaltungstechnik aneignest.
Ganz ohne VHDL, sondern mit echten Schaltplänen und Schaltungen.
Erst wenn du dirch da so einigermaßen auskennst, wirst du verstehen, wo
hier die Probleme sind.
Ich hoffe, du hast das nicht böse aufgefasst. So war es bestimmt nicht
gemeint.
Aber es bringt nichts, wenn wir von Register und kombinatorischer Logik
reden, und du nicht weisst, was das ist.
Denn mit VHDL beschreibt man genau solche Strukturen. Es ist keine
Programmiersprache, auch wenn es auf den ersten Blick so aussehen mag.
Man beschreibt reale Hardware. Und dazu muss man eine Vorstellung von
realer Hardware haben.
Natürlich wird dir eine Lösung gelingen, dass in deinem konkreten Fall
alles so funktioniert, wie du es dir vorgestellt hast. Du kannst dazu
auch alle syntaktischen Go´s und NoGo´s lernen und kommst so vielleicht
auch zum Ziel. Aber solange die Vorstellung dessen fehlt, was am Ende
dabei herauskommt, ist es doch total unbefriedigend und auch nicht
hilfreich bei der Fehlersuche, wenn mal was nicht klappt.
Konkrete Literatur kann ich dir ad hoc gerade nicht empfehlen. Aber
Begriffe zum Googlen wäre mal:
"Grundlagen Digitale Schaltungstechnik"
"UND-Gatter"
"OR-Gatter"
"NAND-Gatter"
"NOR-Gatter"
"Register"
"D-FF"
"Daten-FlipFlop"
"Latch"
"Tri-State-Buffer"
z.B. hier:
http://www.elektronik-kompendium.de/sites/dig/index.htm
Du solltest zumindest auf dem Stand sein, dass du die kombinatorischen
Grundelemente kennst und verstehst, was ein D-FF oder Register ist und
wie sie funktionieren.
Dann folgen Begriffe wie "Setup-Zeit", "Latency", "Synchrones Design",
"Statemachine", "Schieberegister", "Counter", "Multiplexer", "LUT".
Wenn du sagst, dass du mit mehr als zwei oder drei der genannten
Begriffe nichts anfangen kannst, dann fehlen dir einfach die Grundlagen,
um überhaupt mit VHDL irgendwas Sinnvolles erreichen zu können. Und
vorallem, zu verstehen, warum dein VHDL-Design funktioniert, oder eben
auch nicht.
Lothar Miller schrieb:> "VHDL Synthese" von Reichardt&Schwarz ist immer noch gut...
Defintiv ein gutes Buch. Aber meines Erachtens kein Buch zum Erlernen
der Grundlagen der Digitaltechnik.
Was ein Register wird, wird in dem Buch als Grundwissen bereits
vorausgesetzt, oder?
Hallo zusammen, hallo Schlumpf, hallo Lothar,
erst mal Danke für die Grundlagentipps. Ich beherzige diese. Zu Eurer
Info: Ich habe die fünfzig schon überschritten, vor langer Zeit Mechanik
studiert und mit Elektronik und Programmierung so gar nichts am Hut
gehabt. Irgendwann musste ich mal Matlab lernen und dann kam noch C/C++,
Python und dies und jenes dazu. Aus purem Zufall habe ich mir dann einen
ASURO gekauft, das ist so ein kleiner Fahrroboter auf AVR Basis. Der hat
mich dann gar nicht so interessiert, wohl aber der Atmega8 der da drauf
ist. Dann begann ich mit kleinen AVR Experimenten, die ich in Assembler
programmiert habe. Blöd war immer, dass der AVR ein kleines bisschen zu
lahm war für meine Encodersachen und da habe ich beim Lesen der
mikrocontroller.net Seiten festgestellt, dass es CPLDs gibt. Jetzt finde
ich das Thema derart faszinierend, dass ich mich viele Abende mit den
Sachen aus Interesse und Spaß beschäftige. Natürlich gibt es auch noch
andere Dinge und so kommt es, dass meine Basteleien manchmal wochenlang
ruhen - insbesondere bei schönem Wetter.
Zur Erinnerung: Ich baue einen Encoderzähler mit einem Xilinx CPLD
XC9572XL auf, welcher auf eine Leseaufforderung eines AVR den Zähler an
8 Pins ausgibt. Da das alles nicht so hingehauen hat, habe ich erst mal
zum Lernen und Testen ein Byte geschickt, was klappte und dann zwei
Byte, die von dem AVR zu einem int16_t zusammengebastelt wurden, was
nicht klappte.
Habe an diesem Wochenende den Thread nochmal gelesen und einiges
geändert:
Lattice User schrieb am 14.11.2014 11:31
> Ohne Clock ist das nur Umverdrahtung und kein Einsynchronisieren.
Hab ich geändert.
Dann habe ich noch die Zahl in ein unsigned gepackt - da schreibt man
die Bits hin. Mir als Anfänger hilft es die Bits wirklich zu sehen.
Ich schicke erst das lsb "0000000001010111", also 0x57, und dann das msb
"0000000001100101", also 0x65. Das gibt dann den uint16_t 0x6557, was
25943 macht. Und was soll ich sagen, genau das zeigt mein AVR im LCD an.
Ich bin begeistert! Den zugehörigen Code habe ich unten angefügt.
Mein weiteres Vorgehen sieht nun so aus:
1. Ich folge Eurem Rat und lese mich in das Thema ein. CPLDs und FPGAs
erfordern Hardware-Kenntnisse, die ich mir aneignen muss. Ich beginne
mit Schlumpfs Empfehlung und schaue mir später Lothars Literaturtipp an.
Falls jemand weitere Vorschläge für mich hat, würde ich mich freuen.
2. Parallel bastele ich an dem kleinen Projekt weiter und berichte.
Herzliche Grüße
Reinhard
Mein uint Transport sieht auf dem CPLD nun so aus:
1
2
architectureBehavioralofCounteris
3
-- signal t_cnt1 : integer range 0 to 65535; -- nimm eingeschränkte Integer zum Zählen!
4
signalt_cnt1:unsigned(15downto0);-- Zahlenbereich: 0 bis 2**16-1
Was ich noch sagen wollte:
Es bringt wirklich nichts für das Verständnis, wenn du jetzt so weit den
Code runterkürzt, bis irgendwas tut, und dann wieder langsam den Rest
dazu baust. Solange du nicht verstehst, welche Hardware-Strukturen du
mit deinem Code erzeugst, ist dieses Vorgehen wirklich sinnlos.
Fass mal in Textform zusammen, was du erreichen willst und wie du es
dann in Hardware realsieren würdest.
Vielleicht kommst du dann sogar drauf, dass es nicht so geschickt ist,
mit den Flanken von GET0 und GET1 die Umschaltung zu machen, sondern mit
statischen Pegeln. Dadurch sparst du Ressourcen und Latenz.
Und dann wirst du draufkommen, dass es sowieso ausreicht, wenn du nur
mit GET0 arbeitest und auf GET1 vollkommen verzichten kannst.
Ich gebe dir mal nen Tipp, wie man es deutlich einfacher machen könnte:
- Flankenerkennung des GET-Signals (steigende Flanke erkennen), indem
man GET durch ein Schieberegister schiebt und die benachbarten Register
vergleicht.
- Mit diesem Signal (flanke_erkannt) das 16-Bit Datenregister enablen,
welches den aktuellen Zählerstand kopiert.
- Multiplexer über synchronisiertes GET gesteuert, welcher bei GET = '1'
die unteren 8 Bit und bei GET0 = '0' die oberen 8 Bit des Datenregisters
auf den COUNT-Port ausgibt.
Ressourcen:
2-3 Register zur Synchronisation von GET.
16 Register, um Zählerstand zwischenzuspeichern.
Ne Handvoll LUT zur Flankenerkennung und für den Ausgangsmultiplexer.
Wenn dir diese Struktur klar ist, dann kann man das ganz einfach in HDL
umsetzen. Dann ist es nur noch notwendig zu wissen, durch welche
syntaktischen Konstrukte man den Synthsizer dazu bringt, genau das zu
bauen, was oben beschrieben ist.
Wie du siehst, ist hier ein gewisses Verständnis von Hardware einfach
erforderlich.
Dein Zugriff auf dem µC sieht dann so aus:
GET = 0
GET = 1
Lesen von Low-Byte
Get = 0
Lesen von High-Byte
Hallo Schlumpf!
Danke für Deine ausführlichen Tipps. Wie bereits geschrieben: Ich werde
sie beherzigen und dieses Projekt zurückstellen. Es ist ein Lernprojekt,
was man vielleicht auch mal nutzen kann oder auch nicht.
Ich lese mich mal ein und mache bei Bedarf den einen oder anderen Thread
auf. Erst mal folge ich weiter Deinem Link und schaue dann was ich noch
so zum Lesen finde. Irgendwann mache ich hier weiter und ich hoffe, dass
ich dann Deine Anregungen umsetzen kann.
Nochmals Tausend Dank.
Herzliche Grüße Reinhard