mikrocontroller.net

Forum: FPGA, VHDL & Co. vhdl Fragen zum Code


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Dan (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Ich bin ein Neueinsteiger und habe ein paar Fragen zu meinem vhdl Code. 
Ich möchte gerne über eine CommandList Werte aus einem Speicher lesen. 
Im JobListSeparator wird das Commando getrennt zwischen Adresse und 
Wert. Das Funktioniert auch soweit. Im nächsten Process sollte 
eigentlich nach der Adresse der Wert in Value geschrieben werden. Dieser 
Werte werden in weiteren Processe verwendet. Durch das DoneCommandIntern 
sollte dann die Adresse im einen Wert erhöht werden. Bei der Simulation 
bekomme ich immer nur den ersten Wert. Woran kann dies liegen?
JobListSeparator : process
          begin
            wait until rising_edge(clk);
              CommandAdress <= CommandList(31 downto 16);
              CommandValue  <= CommandList(15 downto 0);
          end process JobListSeparator;
          
  Job: process
    variable vError : std_logic := '0';
    variable vDoneCommandIntern : std_logic := '0';
    begin 
      wait until rising_edge(clk);
      vDoneCommandIntern := '0';
      vError := '0';
      case CommandAdress is 
        when x"0001" => Value1 <= CommandValue; vDoneCommandIntern := '1';
        when x"0002" => Value2 <= CommandValue; vDoneCommandIntern := '1';
        when x"0003" => Value3 <= CommandValue; vDoneCommandIntern := '1';
        when others => vError := '1';
      end case;
      Error <= vError;
      sDoneCommandIntern <= vDoneCommandIntern;
    end process Job;
    
    MemAdressP: process
      variable vMemAdress : integer range 0 to 2**5 := 0; 
      begin 
        wait until rising_edge(clk);
          if (sDoneCommandIntern = '1') then
            vMemAdress := vMemAdress + 1;
          end if;
          MemAdress <= std_logic_vector(to_unsigned(vMemAdress, MemAdress'length));
      end process MemAdressP;
            

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Warum die vielen Variablen?
Die unteren beiden Prozesse kannst du bei Benutzung der sowieso 
definierten Signale ohne jegliche Variable und ohne jegliche 
funktionelle Änderung so schreiben:
  Job: process
    begin 
      wait until rising_edge(clk);
      DoneCommandIntern <= '0';
      Error <= '0';
      case CommandAdress is 
        when x"0001" => Value1 <= CommandValue; DoneCommandIntern := '1';
        when x"0002" => Value2 <= CommandValue; DoneCommandIntern := '1';
        when x"0003" => Value3 <= CommandValue; DoneCommandIntern := '1';
        when others => Error := '1';
      end case;
    end process Job;

    MemAdressP: process
      begin 
        wait until rising_edge(clk);
          if (sDoneCommandIntern = '1') then
            MemAdress := std_logic_vector(unsigned(MemAdress) + '1'));
          end if;
      end process MemAdressP;

Und warum eigentlich die vielen Prozesse?
Weil die zudem allesamt getaktet sind, hast du jede Menge Latency an der 
Backe. Ich bin mir nicht so ganz sicher, dass das so gewollt ist.

Was du mit den vielen Takten in den vielen Prozessen geschafft hast, 
ist, dass von einem gültigen Eintrag in die CommandList bis zum 
Hochzählen der Adresse 3 Takte vergehen müssen.

Denn eigentlich braucht dieser "Job" Prozess keinen Takt, weil 
CommandAdress und CommandValue ja bereits im Prozess JobListSeparator 
gespeichert wurden. Und vermutlich braucht auch dieser Prozess keinen 
Takt, weil die CommandList auch schon in Registern stabil gespeichert 
ist.

Dan schrieb:
> Bei der Simulation bekomme ich immer nur den ersten Wert.
Zeig doch mal.

: Bearbeitet durch Moderator
von Dan (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Wenn ich den Code wie Sie es vorgegeben haben und Ihre Vorschläge 
eingepflegt habe, simuliere, dann passiert nichts. Ich habe folgenden 
Code simuliert:
architecture Behavioral of JobList is
signal   CommandAdress : std_logic_vector(15 downto 0) := x"0000";
signal   CommandValue : std_logic_vector(15 downto 0) := x"0000";
signal   DoneCommandIntern : std_logic := '0';
signal   sMemAdress : integer := 0;
signal   Value1 : std_logic_vector (15 downto 0) := x"0000";
signal   Value2 : std_logic_vector (15 downto 0) := x"0000";
signal   Value3 : std_logic_vector (15 downto 0) := x"0000";
begin
  JobListSeparator : process
          begin
            wait until rising_edge(clk);
              CommandAdress <= CommandList(31 downto 16);
              CommandValue  <= CommandList(15 downto 0);
          end process JobListSeparator;
          
   Job: process(CommandAdress)
     begin 
      DoneCommandIntern <= '0';
      Error <= '0';
      case CommandAdress is 
        when x"0001" => Value1 <= CommandValue; DoneCommandIntern <= '1';
        when x"0002" => Value2 <= CommandValue; DoneCommandIntern <= '1';
        when x"0003" => Value3 <= CommandValue; DoneCommandIntern <= '1';
        when others => Error <= '1';
      end case;
     end process Job;

    MemAdressP: process(DoneCommandIntern)
      begin 
          if (DoneCommandIntern = '1') then
            MemAdress <= std_logic_vector(to_unsigned(sMemAdress+1, MemAdress'length));
          end if;
      end process MemAdressP;

Ich habe ein Bild der Simulation angehängt. Was mache ich falsch :(
Den Process MemAdressP habe ich in einen extra Process gemacht, da ich 
später über einen Reset die MemAdress zurücksetzten will. Wahrscheinlich 
geht es auch einfacher in einem Process :)

von Dan (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Ich poste mal noch mein Simulationsprogramm, denn ich vermute, dass dort 
der Fehler liegt ;)
BEGIN
   -- Instantiate the Unit Under Test (UUT)
   uut: JobList PORT MAP(
      clk => clk,
    Error => Error,
    MemAdress => MemAdress,
    CommandList => CommandList,
    DoneCommand => DoneCommand 
   );
   
   clk <= not clk after 10 ns; -- 50 MHz
   
   tb : PROCESS BEGIN
      wait for 10 ns;
      case 
        MemAdress is 
          when "000000" =>CommandList <= x"0001000A";
          when "000001" =>CommandList <= x"00020064";
          when "000010" =>CommandList <= x"000300FF";
          when "000011" =>CommandList <= x"000D01F4";
          when "000100" =>CommandList <= x"000C0003";
          when others =>CommandList <= x"00000000";
      end case;      
      
   END PROCESS;

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Dan schrieb:
> dann passiert nichts
Was macht MemAdress?
Nach diesem Code wird das bestenfalls von 0 auf 1 wechseln und dann dort 
bleiben, denn das hier sieht zwar aus wie ein Zähler:
MemAdress <= std_logic_vector(to_unsigned(sMemAdress+1, 
MemAdress'length));
Es ist aber keiner, weil sMemAdress immer 0 ist und nie hochgezählt 
wird...

Dan schrieb:
> Ich poste mal noch mein Simulationsprogramm, denn ich vermute, dass dort
> der Fehler liegt ;)
Das kann man aber doch im Simulator leicht feststellen. Genau der ist 
doch zum Debuggen da.

> Ich poste mal noch mein Simulationsprogramm
Häng einfach beide komplette VHDL Dateien an. Es ist echt unnötig viel 
Arbeit, die zum Ausprobieren zu vervollständigen...

EDIT: nochmal drüber geschaut, das sieht wild aus:
MemAdressP: process(DoneCommandIntern)
       begin
           if (DoneCommandIntern = '1') then
             MemAdress <= std_logic_vector(to_unsigned(sMemAdress+1, MemAdress'length));
           end if;
       end process MemAdressP;
Das ist ein Latch (mit einer Tendenz zur kombinatorischen Schleife...). 
Das wolltest du ganz sicher auch nicht. Denn das funktioniert im echten 
Leben nicht zuverlässig.

Du könntest hier übrigens das Prozess-Gemapel auch weglassen und sowas 
schreiben:
 MemAdress <= std_logic_vector(to_unsigned(sMemAdress+1, MemAdress'length))
              when DoneCommandIntern = '1';
Das ist wesentlich kürzer, aber blöderweise bleibt es ein Latch.

: Bearbeitet durch Moderator
von Dan (Gast)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ok ich habe die 2 vhdl Dateien angehängt. Woran erkennt man, dass es 
sich dabei um einen Latch handelt? Das ist vielleicht eine Anfänger 
Frage, aber nur so lernt man dazu :)

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Dan schrieb:
> Woran erkennt man, dass es sich dabei um einen Latch handelt?
Es speichert und hat keinen Takt.

> Ok ich habe die 2 vhdl Dateien angehängt.
Aber die haben nichts mit dem Bild zu tun, das du nicht angehängt hast.
Denn das ist ein Fehler:
        sMemAdress = 
            MemAdress <= std_logic_vector(to_unsigned(sMemAdress+1, MemAdress'length));

Seis drum: dein Code tut doch was. Und wie zu erwarten bleibt das Ding 
natürlich bei bei MemAdress = 1 hängen. Den Grund habe ich ja schon 
erwähnt...

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich hab da mal das weggekürzt, was mir unerklärlich ist und komme auf 
sowas. Gegen deine Testbench tut sich da schon mehr als vorher... ;-)

von Dan (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Danke für die Verbesserung jetzt funktioniert es auch bei meiner 
Simulation :)… Ich verstehe nur eins nicht, wieso jetzt CommandAdress 
und CommandValue in keinem Process stehen und nicht getaktet sind. Ich 
dachte, dass dann ein Latch auftreten kann oder bin ich falsch. Bzw. 
wann werden dann diese dann immer aktualisiert? Denn wenn ich 
rising_edge verwende wird es immer dann aktualisiert wenn eine steigende 
Flanke kommt. Aber das haben wir hier ja nicht.

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Dan schrieb:
> Ich verstehe nur eins nicht, wieso jetzt CommandAdress und CommandValue
> in keinem Process stehen und nicht getaktet sind. Ich dachte, dass dann
> ein Latch auftreten kann oder bin ich falsch.
Da ist kein Latch, weil das nur eine Zuweisung ist. Es wird nirgends was 
gespeichert, sondern einfach nur zugewiesen.
Ich könnte das auch so schreiben, dann gäbe es die CommandAdress und 
CommandValue gar nicht merh:
begin

   Job: process begin 
      wait until rising_edge(clk);
      Error <= '0';
      case CommandList(31 downto 16) is 
        when x"0001" => Value1 <= CommandList(15 downto 0); sMemAdress <= sMemAdress+1;
        when x"0002" => Value2 <= CommandList(15 downto 0); sMemAdress <= sMemAdress+1;
        when x"0003" => Value3 <= CommandList(15 downto 0); sMemAdress <= sMemAdress+1;
        when others => Error <= '1';
      end case;
     end process Job;


    MemAdress <= std_logic_vector(to_unsigned(sMemAdress, MemAdress'length));
        
end Behavioral;

Dan schrieb:
> Bzw. wann werden dann diese dann immer aktualisiert?
Immer genau dann, wenn sich die CommandList ändert.

Der Witz an der Sache ist aber, dass die Testbench für die CommandList 
per "wait for 10 ns;" eine Art "Pseudotakt" erzeugt: obwohl kein Takt 
bei der CommandList beteiligt ist, sieht es aus, als ob das irgendwie 
taktsynchron sei...

Korrekterweise müsste man das etwa so machen:
   clk <= not clk after 10 ns; -- 50 MHz
   
   tb : PROCESS BEGIN
      wait for rising_edge(clk);
      case 
        MemAdress is 
          when "000000" =>CommandList <= x"0001000A";
          when "000001" =>CommandList <= x"00020064";
          when "000010" =>CommandList <= x"000300FF";
          when "000011" =>CommandList <= x"000D01F4";
          when "000100" =>CommandList <= x"000C0003";
          when others =>CommandList <= x"00000000";
      end case;      
Denn dann gilt der Takt für die CommandList und für das gesamte Modul 
JobList. Das ist dann schönes synchrones Design.

: Bearbeitet durch Moderator
von Dan (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank für die große Unterstützung, das hat mir sehr 
weitergeholfen. Ich habe nun eine letzte Frage. Wann wird ein neuer 
Process gemacht?

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Dan schrieb:
> Ich habe nun eine letzte Frage. Wann wird ein neuer Process gemacht?
Die Frage muss eigentlich lauten: was macht ein Prozess?

Hier eine vereinfachte Kurzversion:
Ein Prozess ist erstmal eine sequentielle Hintereinanderreihung von 
Befehlen. Der Prozess wird vom Simulator neu berechnet, wenn sich eines 
der Signale in der Sensitivliste ändert (der Synthesizer schert sich 
dagegen nicht um die Sensitivliste). Ist die Sensitivliste nicht 
vollständig, passt die Simulation nicht zur Realität. Das passiert gern 
mal bei der Verwendung von Variablen, wie im 
Beitrag "Variable vs Signal" verdeutlicht.

Das Neckische und Schöne am Prozess im Zusammenspiel mit Signalen ist, 
dass die "letzte" Wertzuweisung an ein Signal "gewinnt". Man kann also 
einem Signal im Verlauf eines Prozesses beliebige Werte zuweisen. Der 
jeweils zuletzt zugewiesene Wert wird aber nur "zwischengespeichert", 
das Signal änert seinen ursprünglichen Wert nicht.

Prozesse, die einen Takt abfragen, erzeugen Flipflops. Prozesse, die 
Zustände abfragen im Idealfall nur Logik, wenn man nicht aufpasst aber 
auch Latches oder gar kombinatorische Schleifen.

Die Allgemeinform eines Prozesses ist der Prozess mit einem oder 
mehreren "wait" im Code:
  JobListSeparator : process
  begin
     wait on CommandList; -- auf irgendeine Änderung in der CommandList warten
     CommandAdress <= CommandList(31 downto 16);
     CommandValue  <= CommandList(15 downto 0);
  end process JobListSeparator;
Ist nur 1 einziges "wait" im code, kann der Prozess auch mit 
Sensitivliste geschrieben werden:
  JobListSeparator : process (CommandList) -- auf irgendeine Änderung in der CommandList warten
  begin
     CommandAdress <= CommandList(31 downto 16);
     CommandValue  <= CommandList(15 downto 0);
  end process JobListSeparator;

Und die finale Abkürzung ist, wenn man bei simplen Zuweisungen auf den 
Prozess verzichtet und eine nebenläufige Anweisung schreibt:
  CommandAdress <= CommandList(31 downto 16);
  CommandValue  <= CommandList(15 downto 0);
Hier wird das Ergebnis vom Simulator naheliegenderweise neu berechnet, 
wenn sich die CommandList ändert.

> Wann wird ein neuer Process gemacht?
Eigentlich recht einfach: dann, wenn man einen braucht...  ;-)

Man muss aber nicht jede mickrige Berechnung oder Zuweisung in einen 
Prozess packen.
Viel machen z.B. für irgendwelche "Takterzeugungen" eigene Prozesse. Ich 
packe das auch mal einfach in den selben Prozess:
http://www.lothar-miller.de/s9y/categories/45-SPI-Master
http://www.lothar-miller.de/s9y/categories/42-RS232

: Bearbeitet durch Moderator
von Markus F. (mfro)


Bewertung
0 lesenswert
nicht lesenswert
Lothar M. schrieb:
> Eigentlich recht einfach: dann, wenn man einen braucht...  ;-)

Eigentlich braucht man genau genommen ja nie einen ;)
process(trigger_condition)
    if trigger_condition
        irgendwas <= irgendwas_anderes;
    else
        irgendwas <= nochmal_was_anderes;
    end if;
end process;

lässt sich ja für praktisch alle Fälle ja immer auch so schreiben:
irgendwas <= irgendwas_anderes when trigger_condition else nochmal_was_anderes;

Ob man nun das eine, das andere oder eine Kombination davon nimmt, hängt 
m.E. wesentlich davon ab, ob man das Ergebnis dann noch versteht oder 
nicht.

von Gustl B. (gustl_b)


Bewertung
0 lesenswert
nicht lesenswert
Das ist auch nur Kombinatorik oder Latches, aber kann man auch getaktete 
Sachen ohne Process machen? Ich meine damit taktflankengesteuerte Dinge.

von Tobias B. (Firma: www.elpra.de) (ttobsen) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Gustl B. schrieb:
> Das ist auch nur Kombinatorik oder Latches, aber kann man auch
> getaktete
> Sachen ohne Process machen? Ich meine damit taktflankengesteuerte Dinge.

signal d : unsigned(7 downto 0) := (others => '0');

...

d <= d + 1 when rising_edge(clk);

ist valides VHDL. Ob das dein Synthesizer akzeptiert und Sinn macht 
steht auf einem anderen Blatt und ist ausserhalb des VHDL Scopes.

: Bearbeitet durch User
von Gustl B. (gustl_b)


Bewertung
0 lesenswert
nicht lesenswert

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Bewertung
1 lesenswert
nicht lesenswert
Gustl B. schrieb:
> Fehlt da nicht ein else?
Bei der nebenläufigen getaketen Anweisung? Nein, das passt.

Denn das fehlende else zeigt ja deutlich, dass da etwas gespeichert 
wird:
   -- Huch, ein Latch
   latch <= in when en='1';               

    -- Besser mit Flipflops 
   flip  <= in when clk='1'and clk'event;
   flop  <= in when rising_edge(clk);

   -- Async Reset
   flap  <= '0' when reset='1' else in when rising_edge(clk); 
   
   -- als Schieberegister zum Eintakten von Signalen 
   sr    <= sr(sr'left-1 downto 0) & in when rising_edge(clk);

: Bearbeitet durch Moderator
von Markus F. (mfro)


Bewertung
0 lesenswert
nicht lesenswert
Zumindest gemäss der 2008-Spezifikation sind else-Zweige optional:
conditional_waveforms ::=
    waveform when condition
    { else waveform when condition }
    [ else waveform ]

von Markus F. (mfro)


Bewertung
0 lesenswert
nicht lesenswert
Lothar M. schrieb:
> -- als Schieberegister zum Eintakten von Signalen
>    sr    <= sr(sr'left-1 downto 0) & in;

Da fehlt noch was, Lothar. So ist das erst mal - zugegeben, eine 
ziemlich effektive - kombinatorische Schleife ;).

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:
> eine ziemlich effektive - kombinatorische Schleife
Huah, ausgerechnet mir passiert das... ;-)

> Da fehlt noch was
Habs korrigiert.

von Dan (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Ich habe nochmal ein bisschen an meinem Code herumgespielt und habe 
nochmals eine Frage. Ich möchte jetzt gerne noch über ein StartSignal 
ein Job starten z.B. Wenn das Startsignal kommt, soll das sMemAdress um 
eins erhöhen und dann z.B. über die CommandList sOn einschalten und das 
Signal eine Zeit anlassen (when x"0004" … ). Danach soll über 
Commandlist SOff wieder ausgeschaltet werden. Die Simulation 
funktioniert, leider sieht es aber bei der Hardware nicht so aus :(. 
Woran kann dies liegen ;)
     wait until rising_edge(clk);
      Error <= '0';
    sTestSignal <= '0'; 
      case CommandList(31 downto 16) is
        when x"0000" => case CommandList(15 downto 0) is
                    when x"0000" => sMemAdress <= sMemAdress+1;
                    when x"0001" => If (StartSignal = '0') then sMemAdress <= sMemAdress+1; end if;  
                    when x"0002" => sOn<= '1'; sMemAdress <= sMemAdress+1;
                    when x"0003" => sOff <= '0'; sMemAdress <= sMemAdress+1;
                    when others => Error <= '1';
                  end case;
        when x"0001" => Value1<= CommandList(15 downto 0); sMemAdress <= sMemAdress+1;
        when x"0002" => Value2<= CommandList(15 downto 0); sMemAdress <= sMemAdress+1;
        when x"0003" => Value3<= CommandList(15 downto 0); sMemAdress <= sMemAdress+1;
        when x"0004" => sTestSignal <= '1'; if (counter < (to_integer(unsigned(CommandList(15 downto 0))))*50) then counter <= counter + 1; else sMemAdress <= sMemAdress+1; counter  <= 0; end if;
        when others => Error <= '1';
      end case;
     end process Job;

von Markus F. (mfro)


Bewertung
0 lesenswert
nicht lesenswert
Dan schrieb:
> when x"0004" => sTestSignal <= '1'; if (counter <
> (to_integer(unsigned(CommandList(15 downto 0))))*50)

Was soll die Synthese daraus machen?

Eine Warteschleife, die unabhängig von ihrer Dauer immer in einem Takt 
fertig wird?

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Ich musste das auch erst mal umformen, damit ich es lesen kann... ;-)

Dabei kam das heraus:
:
alias addr : std_logic_vecor(15 downto 0) is CommandList(31 downto 16);
alias cmd  : std_logic_vecor(15 downto 0) is CommandList(15 downto 0);
:
      sTestSignal <= '0'; 
      case addr is
        when x"0000" => case cmd is ...
:
        when x"0004" => sTestSignal <= '1';
                        if counter < to_integer(unsigned(cmd)))*50  then
                            counter    <= counter + 1;
                        else
                            sMemAdress <= sMemAdress+1;
                            counter    <= 0;
                        end if;

        when others => Error <= '1';
:
Dann sieht man, dass der counter mit jedem Takt hochzählt, solange sein 
Wert klein genug ist.

Dan schrieb:
> Die Simulation funktioniert, leider sieht es aber bei der Hardware nicht
> so aus :(. Woran kann dies liegen ;)
Eigentlich nur an der Hardware? Wie sieht die aus? Woher kommt da der 
Takt? Woher die Kommandos?

: Bearbeitet durch Moderator
von Gustl B. (-gb-)


Bewertung
0 lesenswert
nicht lesenswert
Lothar M. schrieb:
> Bei der nebenläufigen getaketen Anweisung? Nein, das passt.

Danke! Dann braucht man tatsächlich keinen Process, aber es ist schon 
bequem weil es Schreibarbeit spart.

Und eine Nebenfrage habe ich noch (ist aber etwas OT):

Wenn ich einen Takt habe, macht es einen unterschied für die Synthese 
wenn ich alles in einen getakteten Process packe oder das auf mehrere 
verteile?

Ich könnte mir vorstellen, dass die Tool versuchen das was in einem 
Process steht irgendwie näher zusammen im FPGA zu platzieren.

Es gibt dann auch manchmal Warnungen wegen einem hohen Fanout eines 
Taktes, da könnte ja die Synthese das auch aufteilen und dann 
selbständig BUFG und BUFR dazwischensetzen.

von Dan (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Ich habe das Intel® Cyclone 10 LP Evaluation Kit Board. Der Takt kommt 
direkt von einem Quarz. Und die Adresse wird an den Nios OnChipMemory 
gesendet und dann den Wert CommandList gelesen.

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Dan schrieb:
> Und die Adresse wird an den Nios OnChipMemory gesendet und dann den
> Wert CommandList gelesen.
Da scheint wohl in der realen Hardware noch was anders abzulaufen als in 
der Testbench. Wenn die Testbench nicht die Realität abbildet, passiert 
sowas.

Gustl B. schrieb:
> Ich könnte mir vorstellen, dass die Tool versuchen das was in einem
> Process steht irgendwie näher zusammen im FPGA zu platzieren.
Dort wird soweit möglich alles zusammengefasst und Unnötiges 
weggelassen.
Dieses Design hier braucht deshalb trotz der 3 Counter mit je 8 Bits im 
FPGA nur noch 7 Flipflops:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity counter is
    Port ( clk : in  STD_LOGIC;
           a : out  STD_LOGIC;
           b : out  STD_LOGIC;
           c : out  STD_LOGIC);
end counter;

architecture Behavioral of counter is

signal ca, cb, cc : unsigned (7 downto 0) := (others=>'0');

begin

     -- Zähler a
     ca <= ca+1 when rising_edge(clk);  
     
     -- Zähler b
     process begin
        wait until rising_edge(clk);
        cb <= cb+1;
     end process;
     
     -- Zähler c
     process (clk) begin
        if rising_edge(clk) then
           cc <= cc+1;
        end if;
     end process;
     
     -- einzelne Bits der Zähler ausgeben
     a <= ca(2);
     b <= cb(4);
     c <= cc(6);

end Behavioral;

: Bearbeitet durch Moderator
von Gustl B. (-gb-)


Bewertung
0 lesenswert
nicht lesenswert
Danke! Also kann ich das wie bisher so auf Prozesse verteilen wie es für 
mich die Lesbarkeit erhöht.

von Dan (Gast)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich bin immer noch am überlegen, warum die Hardware anders aussieht als 
meine Simulation. Ich habe jetzt ein Bild der Simulation angehängt. Was 
mir nicht ganz einleuchtet ist, wieso die MemAdress und die CommandList 
um einen Takt verschoben ist? Die Umsetzung sieht genau gleich aus wie 
am 24.01 um 11.43 Uhr. Es ist nur so dass das sOff nicht mehr gibt und 
alles beides auf sOn ein bzw. ausgeschaltet wird. Was ich erreichen 
möchte ist, dass ich durch Änderung der Memory Adresse das sOn ein bzw. 
ausschalten kann. Im folgenden noch meine Umsetzung der Testbench:
   clk <= not clk after 10 ns; -- 50 MHz
   
   tb : PROCESS BEGIN
      wait until rising_edge(clk);
      case 
        MemAdress is 
          when "000000" =>CommandList <= x"00000002";
          when "000001" =>CommandList <= x"00000003";
          when others =>CommandList <= x"FFFFFFFF";
       
      end case;      
      
   END PROCESS;   clk <= not clk after 10 ns; -- 50 MHz

von Duke Scarring (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Zweimal "clk <= not clk after 10 ns;" ? Da müßte der Simulator 
eigentlich meckern. Zumindest wenn clk vom Typ std_ulogic ist.

Davon abgesehen: Vor dem Takt ist nach dem Takt. Du änderst die 
Eingangssignale Deines DUT genau mit der Flanke. Das DUT sieht aber noch 
die 'alten' Eingangssignale. Für das bessere Verständnis sollte man die 
Eingangssignale kurz vor der steigenden Flanke ändern.

Duke

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Dan schrieb:
> Was mir nicht ganz einleuchtet ist, wieso die MemAdress und die
> CommandList um einen Takt verschoben ist?
Weil wegen des "wait until rising_edge(clk)" nach einer Änderung der 
MemAdress erst beim nächsten Takt die CommandList geändert wird.

Oder: herzlichen Glückwunsch, du hast das entdeckt, was sich Latency 
nennt. Ich hatte übrigens den Begriff hier im Thread schon ganz am 
Anfang mal einfgeführt...

Sieh mal, was passiert, wenn du das machst:
   tb : PROCESS (MemAdress) BEGIN
      case 
        MemAdress is 
          when "000000" =>CommandList <= x"00000002";
          when "000001" =>CommandList <= x"00000003";
          when others =>CommandList <= x"FFFFFFFF";
       
      end case;      

Oder wenn du den Prozess gleich weglässt und ein Array nimmst (was ja 
einem ROM an ehesten entspricht):
:
type Speicher is array(0 to 63) of STD_LOGIC_VECTOR(31 downto 0);
signal ROM : Speicher := ( 0 => x"00000002", 1 => x"00000003", others => x"FFFFFFFF" );
:
:
CommandList <= ROM(to_integer(unsigned(MemAdress)));
:

Duke Scarring schrieb:
> Für das bessere Verständnis sollte man die Eingangssignale kurz vor der
> steigenden Flanke ändern.
Man muss eigentlich nur kapieren, dass sich diese Werte allesamt wegen 
eines Taktes ändern. Also ändern sie sich im Grunde deshalb immmer 
nach dem Takt. Auch wenn das in der Verhaltenssimulation (die ja kein 
Pseudotiming mit Pseudodelays simulieren sollte) so aussieht, als ob das 
gleichzeitig wäre...

: Bearbeitet durch Moderator
von Dan (Gast)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Meine Simulation sieht jetzt gut aus :). Ich habe es mit dem Array 
umgesetzt und funktioniert. Ich habe jetzt nur noch das Problem, dass 
wenn ich die auf meine Hardware übertrage, und ich den Speicher mit den 
gleichen Werten wie in der Simulation beschreibe, dann ist das SOn 
Signal immer high. Ich verwende den Speicher On Memory chip und dieser 
wird über eine Serielle Schnittstelle beschrieben. Dies funktioniert 
auch, da ich es mit dem Tool "In Memory content" überprüft habe. Da der 
Speicher ein Read latency von 1 hat, wird dies mit einer Frequenz von 
100 Mhz betrieben. Der Rest der Anwendung wird mit 50 MHz betrieben. 
Liegt hierbei der Fehler, dass ich einmal mit 100 MHz und den Rest mit 
50 MHz betreibe?

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [vhdl]VHDL-Code[/vhdl]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.