Forum: FPGA, VHDL & Co. Frage zu TB: Kann 'procedure' rekursiv aufgerufen werden?


von berndl (Gast)


Lesenswert?

Hallo ihr VHDL-Goetter :o)

ich habe in einem Simulationspackage eine procedure, die mir den an das 
FPGA angeschlossenen Mikrocontroller spielt:
1
--
2
-- Write data to the FPGA via the EMIF interface
3
--
4
procedure uc_wr_proc (signal nCS : out std_logic_vector (3 downto 0);
5
                      signal nWR : out std_logic;
6
                      signal DQM : out std_logic_vector (1 downto 0);
7
                      signal BADD : out std_logic_vector (1 downto 0);
8
                      signal ADDR : out std_logic_vector (16 downto 0);
9
                      signal DATA : out std_logic_vector (15 downto 0);
10
                      variable addr_in : in std_logic_vector (15 downto 0);
11
                      variable data_in : in std_logic_vector (31 downto 0)
12
                     ) is
13
   begin
14
      -- und so weiter und so fort...
Funktioniert prima. Jetzt moechte ich die zu simulierende HW auf eine 
alte Plattform zurueck portieren. Da gibt es aber nur 128 Adressen fuer 
ADDR. Also wuerde ich 'bank-switching' in der 'alten' uC-SW 
implementieren um mehrere 128x32bit Werte im FPGA beschreiben zu 
koennen.
Warum ich das will: Das hehre Ziel ist, dass die ca. 100 Besitzer der 
alten HW dieselbe fuer den neuen Design nicht wegschmeissen muessen 
(Kosten ca. 1kEUR pro Kiste).

Um jetzt nicht die eigentliche TB aendern zu muessen, koennte ich:
Als erstes in der procedure uc_wr_proc auf die passende Bank umschalten. 
Um das nicht in einer extra procedure machen zu muessen, wuerde ich 
gerne als ersten Funktionsblock in o.g. procedure einen Aufruf ala:
1
procedure uc_wr_proc (signal nCS : out std_logic_vector (3 downto 0);
2
                      signal nWR : out std_logic;
3
                      signal DQM : out std_logic_vector (1 downto 0);
4
                      signal BADD : out std_logic_vector (1 downto 0);
5
                      signal ADDR : out std_logic_vector (16 downto 0);
6
                      signal DATA : out std_logic_vector (15 downto 0);
7
                      variable addr_in : in std_logic_vector (15 downto 0);
8
                      variable data_in : in std_logic_vector (31 downto 0)
9
                     ) is
10
   begin
11
      if current_bank /= new_bank then  -- Pseudocode!
12
         uc_wr_proc (........, spezifische_addr_in, neue_bank); -- Ein bestimmtes Register in der HW beschreiben um die Bank umzuschalten
13
      end if;
14
      .....
platzieren, also die procedure 'uc_wr_proc' rekursiv aufrufen. Frage: 
Geht das in VHDL? Hat da jemand Erfahrung/nen Tipp?

von berndl (Gast)


Lesenswert?

hmm, ich hab' jetzt mal eine Nacht darueber geschlafen und jetzt 
folgendes probiert:
1
procedure uc_wr_proc (signal nCS : out std_logic_vector (3 downto 0);
2
                      signal nWR : out std_logic;
3
                      signal DQM : out std_logic_vector (1 downto 0);
4
                      signal BADD : out std_logic_vector (1 downto 0);
5
                      signal ADDR : out std_logic_vector (16 downto 0);
6
                      signal DATA : out std_logic_vector (15 downto 0);
7
                      variable addr_in : in std_logic_vector (15 downto 0);
8
                      variable data_in : in std_logic_vector (31 downto 0)
9
                     ) is
10
   variable tmp_addr_in : std_logic_vector (15 downto 0) := x"007F";
11
   variable tmp_data_in : std_logic_vector (31 downto 0) := x"0000_0001";
12
   begin
13
      uc_wr_proc (nCS, nWR, DQM, BADD, ADDR, DATA, tmp_addr_in, tmp_data_in);
14
      -- und so weiter...
Funzt mit 'ghdl' aber leider nicht, Fehlermeldung ist (zur Laufzeit, 
also ghdl -r ...):
1
./tb_hugo:error: invalid memory access (dangling accesses or stack size too small)
Also scheint der rekursive Aufruf nicht so/so einfach zu 
funktionieren...

Naja, mal weiter basteln und probieren...

von Reinhard Kern (Gast)


Lesenswert?

Hallo,

meines Wissens gibt es keine rekursive Hardware, für n Aufrufe müssten 
die Gatter usw. auch n-mal vorhanden sein, und ausserdem ist n nicht 
irgendwie begrenzt, aber unendliche Hardware gibt es auch nicht. Ein 
Stacküberlauf ist also auch nicht verwunderlich.

Einen gewissen Sinn könnte das nur machen als Logikblock, dessen Ausgang 
mit einem externen Takt immer wieder auf den Eingang rückgeführt wird, 
aber das müsste ja initialisiert werden und überhaupt dürfte das die 
Möglichkeiten automatischer Synthese überfordern.

Rekursivität ist ein völlig überschätztes Konzept und eigentlich nur in 
der Theorie von Vorteil, in der praktischen Ausführung sind allgemein 
Schleifen oder ein anderer Ersatz weit effektiver, auch in der Software.

Gruss Reinhard

von berndl (Gast)


Lesenswert?

Reinhard Kern schrieb:
> meines Wissens gibt es keine rekursive Hardware

TB, also nur fuer Simulation gedacht!

Ich hab's jetzt mal etwas erweitert:
1
procedure uc_wr_proc (signal nCS : out std_logic_vector (3 downto 0);
2
                      signal nWR : out std_logic;
3
                      signal DQM : out std_logic_vector (1 downto 0);
4
                      signal BADD : out std_logic_vector (1 downto 0);
5
                      signal ADDR : out std_logic_vector (16 downto 0);
6
                      signal DATA : out std_logic_vector (15 downto 0);
7
                      variable addr_in : in std_logic_vector (15 downto 0);
8
                      variable data_in : in std_logic_vector (31 downto 0)
9
                     ) is
10
   variable tmp_addr_in : std_logic_vector (15 downto 0) := x"007F";
11
   variable tmp_data_in : std_logic_vector (31 downto 0) := x"0000_0001";
12
   begin
13
      uc_wr_temp (nCS, nWR, DQM, BADD, ADDR, DATA, tmp_addr_in, tmp_data_in);
14
      uc_wr_temp (nCS, nWR, DQM, BADD, ADDR, DATA, addr_in, data_in);
15
   end procedure;
Die procedure 'uc_wr_temp' ist die ehemalige 'uc_wr_proc'. Funzt auch 
nicht, bin aber noch am analysieren, was da schief geht. Dass der erste 
Ansatz (rekursiv) aufrufen schief ging, das kann ich mir mittlerweile 
erklaeren. Warum's jetzt noch haengt, muss ich halt mal weiter suchen...

von berndl (Gast)


Lesenswert?

argh, ich habe die Simulation zu schnell abgebrochen...

Die obige Loesung funktioniert natuerlich wunderbar (also mit den 2x 
'uc_wr_temp' Aufrufen). Also vergessen wir die urspruengliche Frage am 
besten...

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


Lesenswert?

Reinhard Kern schrieb:
> meines Wissens gibt es keine rekursive Hardware, für n Aufrufe müssten
> die Gatter usw. auch n-mal vorhanden sein, und ausserdem ist n nicht
> irgendwie begrenzt,
Doch, genau das ist der Trick an rekursiven Prozeduren: sie müssen 
irgendwann fertig werden. Am besten vor der Stack zuende ist.

> aber unendliche Hardware gibt es auch nicht.
Wenn eine Prozedur zur Synthesezeit eine begrenzte und definierte 
Stacktiefe benötigt, dann sollte sogar das Umsetzen in Hardware gehen. 
Das ist dann ja nichts anderes als eine Schleife bekannter Länge.

Muss ich bei Gelegenheit mal ausprobieren, ob die Synthesizer schon so 
weit sind... ;-)

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

Lothar Miller schrieb:
> Muss ich bei Gelegenheit mal ausprobieren, ob die Synthesizer schon so
> weit sind... ;-)

Also Synopsys DC ist schon seit mindestens 15 Jahren so weit. Siehe auch 
die beliebte rekursive ld() Implementierung, welche dank dieser 
Eigenschaft die einzige ist, die auch in Deklarationen eingesetzt werden 
kann.

Seit mindestens zehn Jahren sind Syntheseprogramme in der Lage, Logik 
automatisch von Iteration nach Rekursion zu transformieren. Man muss 
sich daher nicht mehr das Hirn verknoten, wenn man eine "rekursive 
Implementierung" haben moechte.

--
Marcus

von netseal (Gast)


Lesenswert?

Rekursionen können Probleme sehr vereinfachen.
Wenn man für jeden rekursiven Aufruf eine weitere Scheleife 
verschachteln muss, so ist das gelinde gesagt "Mist"!

von SuperWilly (Gast)


Lesenswert?

Nettes Beispiel für Rekursion in der Synthese:
http://www.alse-fr.com/archive/Factorial.pdf

von berndl (Gast)


Lesenswert?

Hi zusammen,

also so wie hier: Beitrag "Re: Frage zu TB: Kann 'procedure' rekursiv aufgerufen werden?" 
beschrieben funktioniert das (also einen Wrapper um die eigentliche 
procedure). Nur will ich ja nicht jedesmal erst die 'bank' umschalten, 
sondern nur bei Bedarf, also wenn neue 'bank' ungleich alter 'bank', 
dann soll der zusaetzliche Schreibzugriff stattfinden.

Ich habe jetzt mal im vhdl-package ein Signal
1
signal last_addr_in : std_logic_vector (15 downto 0) := (others => '0');
 definiert.
Wenn ich jetzt in meiner procedure 'last_addr_in' mit 'addr_in' 
vergleichen wuerde, dann koennte ich den Schreibzugriff selektiv, also 
nur wenn noetig, machen.
Sieht jetzt so aus:
1
  procedure uc_wr_proc (signal nCS : out std_logic_vector (3 downto 0);
2
                    signal nWR : out std_logic;
3
                         signal DQM : out std_logic_vector (1 downto 0);
4
                         signal BADD : out std_logic_vector (1 downto 0);
5
                 signal ADDR : out std_logic_vector (16 downto 0);
6
                 signal DATA : out std_logic_vector (15 downto 0);
7
                 variable addr_in : in std_logic_vector (15 downto 0);
8
                 variable data_in : in std_logic_vector (31 downto 0)
9
                ) is
10
      variable tmp_addr_in : std_logic_vector (15 downto 0) := x"007F";
11
      variable tmp_data_in : std_logic_vector (31 downto 0) := x"0400_100B";
12
  begin
13
      if addr_in (15 downto 7) = b"0000_0000_0" then
14
         tmp_data_in := (others => '0');
15
         uc_wr_temp (nCS, nWR, DQM, BADD, ADDR, DATA, tmp_addr_in, tmp_data_in);
16
      else
17
         tmp_data_in := (0 => '1', others => '0');
18
         uc_wr_temp (nCS, nWR, DQM, BADD, ADDR, DATA, tmp_addr_in, tmp_data_in);
19
      end if;
20
      last_addr_in <= addr_in;   -- HIER DER KNACKPUNKT!
21
      uc_wr_temp (nCS, nWR, DQM, BADD, ADDR, DATA, addr_in, data_in);
22
  end procedure;
Also am Ende der procedure merke ich mir die letzte Adresse, das koennte 
ich dann (wenn es funktionieren wuerde) im 'if' mit einbeziehen (also ob 
ich den 'uc_wr_temp()' machen muss oder nicht.

Jetzt kriege ich leider in GHDL folgende Fehlermeldung:
1
ghdl -m --workdir=work -P/......./unisim --ieee=synopsys -fexplicit tb_top_hugo
2
../tb_top_hugo_sim_pkg.vhd:255:7: signal "last_addr_in" is not a variable to be assigned
Das ist die Zeile
1
      last_addr_in <= addr_in;
Nun zu der eigentlichen Frage: Kann ich sowas wie ein 'globales' Signal 
im Package definieren und in einer procedure verwenden? (Anstelle 
'global' waere auch ein 'static' (fuer C-Programmierer) moeglich)...

Hat da jemand 'nen Tipp fuer mich?

von berndl (Gast)


Lesenswert?

so, nach gefuehlt ein paar hundert Aenderungen in der TB funktioniert 
das ganze jetzt so:
1
procedure uc_wr_proc (signal nCS : out std_logic_vector (3 downto 0);
2
   signal nWR : out std_logic;
3
   signal DQM : out std_logic_vector (1 downto 0);
4
   signal BADD : out std_logic_vector (1 downto 0);
5
   signal ADDR : out std_logic_vector (16 downto 0);
6
   signal DATA : out std_logic_vector (15 downto 0);
7
   variable last_addr_in : inout std_logic_vector (15 downto 0); -- NEU!
8
   variable addr_in : in std_logic_vector (15 downto 0);
9
   variable data_in : in std_logic_vector (31 downto 0)
10
  ) is
11
   variable tmp_addr_in : std_logic_vector (15 downto 0) := x"007F";
12
   variable tmp_data_in : std_logic_vector (31 downto 0);
13
begin
14
   if addr_in (15 downto 7) /= last_addr_in (15 downto 7) then
15
      if addr_in (15 downto 7) = b"0000_0000_0" then
16
         tmp_data_in := (others => '0');
17
         uc_wr_temp (nCS, nWR, DQM, BADD, ADDR, DATA, tmp_addr_in, tmp_data_in);
18
      else
19
         tmp_data_in := (0 => '1', others => '0');
20
         uc_wr_temp (nCS, nWR, DQM, BADD, ADDR, DATA, tmp_addr_in, tmp_data_in);
21
      end if;
22
   end if;
23
   uc_wr_temp (nCS, nWR, DQM, BADD, ADDR, DATA, addr_in, data_in);
24
   last_addr_in := addr_in;
25
end procedure;
Also ich habe in meinem Stimuli-Prozess der TB eine variable 'last_addr' 
eingefuegt, dann in jedem (auch indirekten) Aufruf der procedure 
'uc_wr_proc' diesen Parameter als inout hinzugefuegt.
'last_addr' wird in der procedure gesetzt, ich muss also nicht noch in 
der TB da irgendwelche weiteren statements einbauen. Naja, so funzt es 
auf jeden Fall...

Funzt zwar, aber mit einer 'globalen' Variablen waere es weniger 
Tipperei gewesen... Also wenn da jemand (fuer zukuenftige Sachen) einen 
Tipp haette...

von berndl (Gast)


Lesenswert?

PS: Danke an alle die bisher hier gepostet haben. Hat mir beim konkreten 
Problem zwar nicht 'ueber den Jordan' geholfen, aber da waren einige 
interessante Anmerkungen dabei. Also merci!

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.