Forum: FPGA, VHDL & Co. Sinnvollen Programmaufbau


von Markus H. (h_markus)


Lesenswert?

Hallo,
seid einiger Zeit beschäftige ich mich mit der Programmierung eines 
Xilinx-FPGAs, bisher auch erfolgreich. Jedoch macht mir die ISE-Angabe 
der maximal möglichen Clockfrequenz sorgen: wenn ich noch zwei Wochen 
weiter programmiere tendiert diese gegen null... ;-)
Ich nehme an, dass mein Programmaufbau und meine nicht wegzudenkende 
serielle Logik schuld daran sind. Gibt es gute Literatur die sich mit 
speziell dem Thema des sinnvollen Programmaufbaus beschäftigt, bzw. habt 
ihr 'nen Tipp? Bisher folge ich etwa der Logik:
1
  VARIABLE counter : unsigned(6 downto 0);
2
    
3
begin
4
5
  WAIT UNTIL clk'event AND clk='1' ;
6
    
7
  if( counter = 0 )then
8
    DA_CS <= '0';
9
  elsif( counter = 5 )then
10
    DA_CS <= '1';
11
  elsif( counter = 10 )then
12
    DA_DATA <= (others => 'Z');
13
14
  elsif( counter = 60 )then
15
    AD_CS1 <= '0';
16
  elsif( counter = 75 )then
17
    input <= DA_DATA;
18
  elsif( counter = 90 )then
19
    input(15) <= NOT input(15);
20
    AD_CS1 <= '1';
21
  elsif( counter = 110 )then
22
    DA_DATA <= voltage;
23
24
  elsif( counter = 150 )then
25
    uartData <= "0001101";
26
    uartEnable <= '1';
27
  elsif( counter = 151 )then
28
    countOut := std_logic_vector( ( unsigned(input) / 10000 ) MOD 10 );
29
    uartData <= "0110000" OR countOut(6 downto 0);
30
  elsif( counter = 152 )then
31
    countOut := std_logic_vector( ( unsigned(input) / 1000 ) MOD 10 );
32
    uartData <= "0110000" OR countOut(6 downto 0);
33
34
...
35
... usw.
36
...
37
38
  end if;
39
  counter := counter + 1;
40
end process;
Danke für Antworten und Tipps.
Markus

von Lattice User (Gast)


Lesenswert?

Das hier bremst dich aus:
1
countOut := std_logic_vector( ( unsigned(input) / 10000 ) MOD 10 );

Der Synthesiser hat hier keine andere Wahl als ein kombinatorischen 
Divider zu bauen. Und zwar je einen für jeder dieser Zeilen.

Und dividieren ist Aufwand!

von Uwe (Gast)


Lesenswert?

Lies dich doch mal ein bischen in das Thema Statemachine (FSM) ein. Und 
bau das "Ding" dann aus mehreren Staemachine sdie Miteinander 
Kommunizieren und sich Synchronisieren auf.

von berndl (Gast)


Lesenswert?

was oben schon gesagt wurde, zusaetzlich noch:
1
  VARIABLE counter : unsigned(6 downto 0);

Das ist in deinem Fall jetzt nicht wirklich ein Problem, weil du den 
Counter als letztes statement inkrementierst. Kann aber zu Problemen 
fuehren, wenn du den Code erweiterst und nicht auf die Reihenfolge der 
statements achtest. Besser ein 'signal' nehmen, dann kann das 
inkrementieren des Counters 'irgendwo' (sogar in einem anderen process) 
stehen und du umgehst einen potentiellen Fallstrick bei den 'sequential' 
Anweisungen.

Und wie Lattice User schon bemerkt hat: Die Zeile(n)
1
countOut := std_logic_vector( ( unsigned(input) / 10000 ) MOD 10 );
 sind so ziemlich das schlimmste, was du einer Hardware antun kannst...

von Falk B. (falk)


Lesenswert?

@Markus Hi (h_markus)

>speziell dem Thema des sinnvollen Programmaufbaus beschäftigt, bzw. habt
>ihr 'nen Tipp? Bisher folge ich etwa der Logik:

Wie bereits gesagt ist statemachine das Zauberwort. Eine Division 
hat man in VHDL eher selten, erst recht nicht bei solchen Abläufen. Das 
macht man um Größenordnungen einfacher mit zwei gesteuerten Zählern.

Die endlosen elseif Ketten sind auch Mist, weil sie Prioritäten und 
damit Logik erzeugen, die gar nicht nötig ist. Ein case Konstrukt ist 
hier deutlich im Vorteil, und wenn es gescheit beschrieben ist macht der 
VHDL COmpiler einen ROM draus, der in vielen FPGAs sehr 
resourcenschonend gebaut werden kann (BRAM).

von Falk B. (falk)


Lesenswert?

Ach ja, der Titel des Beitrags enthällt schon einen Irrtum. VHDL 
beschreibt KEINE Programme, sondern Logik. Auch wenn diese natürlich 
sequentiell arbeiten kann, ist das Denkmuster anders.

von Markus H. (h_markus)


Lesenswert?

Hallo,
vielen Dank für die vielen schnellen Antworten.
Da werde ich mich wohl mal näher mit den Statemachinen auseinandersetzen 
dürfen. Als Eingang für die Statemachine sehe ich aber nur wieder diesen 
Counter, daher weiß ich nicht, ob das wirklich Geschwindigkeitsvorteile 
bringt.
Gibt es zu den beanstandeten Zeilen
1
(unsigned(input)/10000) MOD 10
eine Alternative? Gewollt ist die Ausgabe der 16-Bit-Variable “input” 
als dezimal-ASCII.
@Falk: Das Ersetzen der „elsif“s durch „case“s habe ich schon getestet. 
Es scheint keinen Laufzeitunterschied zu machen. Vllt. optimiert der 
Compiler die „elsif“s automatisch zu „case“s?
Markus

von berndl (Gast)


Lesenswert?

Dein Counter ist die state-machine! Einfach eine sequenzielle Abfolge 
von Anweisungen. Solange das 'linear' ist, ist ein Counter das 
einfachste. Wenn es Verzweigungen gibt, dann halt die Implementierung 
mit den 'states'.

Dein /10000 MOD 10 ist der Supergau fuer die Logik. Ueberleg' mal, wie 
du das mit normalen Gattern abbilden wuerdest (tu's nicht, es ist der 
GAU)...

Wenn du also diesen Term in etwas einfacheres aufloesen kannst (zur not 
mit einigen mehr 'counter' Schritten deiner FSM), dann hast du gewonnen. 
Das laesst sich dann besser in HW implementieren

von Falk B. (falk)


Lesenswert?

@ Markus Hi (h_markus)

>(unsigned(input)/10000) MOD 10

>eine Alternative? Gewollt ist die Ausgabe der 16-Bit-Variable “input”
>als dezimal-ASCII.

Ja, eine Statemachine, die diese Rechnung schrittweise macht. Aber sowas 
ist mit einigem Aufwand verbunden. Dafür erreicht man dann hohe 
Taktfrequenzen.

>Es scheint keinen Laufzeitunterschied zu machen. Vllt. optimiert der
>Compiler die „elsif“s automatisch zu „case“s?

Kann sein.

von Markus H. (h_markus)


Lesenswert?

Ok. Das mit dem Dividieren habe ich verstanden. Eine gute Anleitung zum 
schrittweisen Dividieren habe ich hier gefunden: 
http://www.lothar-miller.de/s9y/archives/29-Division-in-VHDL.html

Dass ein Counter als State-Machine durchgeht find ich gut. Aber 
spätestens wenn weitere Module dazukommen werde ich mir wohl was Neues 
überlegen müssen.

Danke für die Denkanstöße.

: Bearbeitet durch User
von Lattice User (Gast)


Lesenswert?

Markus Hi schrieb:
> Ok. Das mit dem Dividieren habe ich verstanden. Eine gute Anleitung zum
> schrittweisen Dividieren habe ich hier gefunden:
> http://www.lothar-miller.de/s9y/archives/29-Division-in-VHDL.html
>

Bei Lothar findest du auch eine direktes Beispiel für binäer nach BCD. 
Ist etwas einfacher als eine allgemeine Division.

http://www.lothar-miller.de/s9y/categories/44-BCD-Umwandlung

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


Lesenswert?

Markus Hi schrieb:
> Bisher folge ich etwa der Logik:
> VARIABLE counter : unsigned(6 downto 0);
Wozu eine Variable?

> Bisher folge ich etwa der Logik:
>     countOut := std_logic_vector( ( unsigned(input) / 10000 ) MOD 10 );
>     uartData <= "0110000" OR countOut(6 downto 0);
Warum muss dieser countOut in einem Takt(!) berechnet sein, wenn 
hinterher ein schnarchlangsamer UART sitzt?

Das ist die Logik, die du verfolgen musst:
Welche Signale müssen in einem Takt fertig berechnet sein, und welche 
dürfen locker mal 50 Takte brauchen? Eine Anzeige muss nicht 
100tausendmal pro Sekunde aktualisiert werden. so schnell kann keiner 
schauen.
Und dann musst du dich fragen: welche Ressourcen kann ich zeitversetzt 
doppelt verwenden? Und diese dann mit Multiplexern verteilen.

Markus Hi schrieb:
> Vllt. optimiert der Compiler die „elsif“s automatisch zu „case“s?
Ist jetzt Ratestunde?
Natürlich "optimiert" der SYNTHESIZER die elsif-Kette. Aber nicht zu 
einem case-Konstrukt, sondern er optimiert es einfach. Und heraus kommt 
das selbe wie bei einem case, weil sich die elsif-Bedingungen nicht 
überschneiden.

Lattice User schrieb:
> Und dividieren ist Aufwand!
Und: vergiss mal Divisionen in einer HDL-Beschreibung. Die sind entweder 
langsam und/oder ressourcenfressend.
Siehe auch Beitrag "Re: Rechnen mit unsigned vs. signed und einer division"

Un wenn du schon durch einen festen Wert dividieren musst, dann überleg 
dir, ob du diese Division nicht ausreichend genau durch eine 
Multiplikation ersetzen kannst.
Eine Division durch 10 könnstest du also z.B. durch eine Multiplikation 
mit 1/10 erreichen. Und weil 1/10 so natürlich nicht in einen Integer 
passt, multiplizierst du den Wert mit 2048 und erhältst den Wert 204,8. 
Das aufgerundet auf 205 sieht die Division durch 10 also so aus:
1
   ergebnis <= (wert*205)/2048; -- 205/2048 ist hinreichend genau 1/10
Ja, da kommst du und sagst: da ist ja immer noch die Division!
Und jetzt kommt der Trick: man muss sich nur klar machen, dass 
unsigned-Divisionen durch Zweierpotenzen (2,4,8,16,32...) weder Zeit 
noch Ressourcen kosten, weil sie nur eine Umverdrahtung der Hardware 
sind: die unteren Bits werden einfach nicht verwendet und wegoptimiert.
Also wird der Synthesizer einfach die unteren 11 Bit der Multiplikation 
nicht verwenden und mitnichten einen Divider aufbauen.


Markus Hi schrieb:
> Dass ein Counter als State-Machine durchgeht find ich gut.
Ein Zähler ist ein Zustandsautomat. Oder andersrum: jeder 
Zustandsautomat ist ein Zähler. Nur hat er machmal eine etwas schräge 
Zählreihenfolge...

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