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


von Dan (Gast)


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?
1
JobListSeparator : process
2
          begin
3
            wait until rising_edge(clk);
4
              CommandAdress <= CommandList(31 downto 16);
5
              CommandValue  <= CommandList(15 downto 0);
6
          end process JobListSeparator;
7
          
8
  Job: process
9
    variable vError : std_logic := '0';
10
    variable vDoneCommandIntern : std_logic := '0';
11
    begin 
12
      wait until rising_edge(clk);
13
      vDoneCommandIntern := '0';
14
      vError := '0';
15
      case CommandAdress is 
16
        when x"0001" => Value1 <= CommandValue; vDoneCommandIntern := '1';
17
        when x"0002" => Value2 <= CommandValue; vDoneCommandIntern := '1';
18
        when x"0003" => Value3 <= CommandValue; vDoneCommandIntern := '1';
19
        when others => vError := '1';
20
      end case;
21
      Error <= vError;
22
      sDoneCommandIntern <= vDoneCommandIntern;
23
    end process Job;
24
    
25
    MemAdressP: process
26
      variable vMemAdress : integer range 0 to 2**5 := 0; 
27
      begin 
28
        wait until rising_edge(clk);
29
          if (sDoneCommandIntern = '1') then
30
            vMemAdress := vMemAdress + 1;
31
          end if;
32
          MemAdress <= std_logic_vector(to_unsigned(vMemAdress, MemAdress'length));
33
      end process MemAdressP;

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


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:
1
  Job: process
2
    begin 
3
      wait until rising_edge(clk);
4
      DoneCommandIntern <= '0';
5
      Error <= '0';
6
      case CommandAdress is 
7
        when x"0001" => Value1 <= CommandValue; DoneCommandIntern := '1';
8
        when x"0002" => Value2 <= CommandValue; DoneCommandIntern := '1';
9
        when x"0003" => Value3 <= CommandValue; DoneCommandIntern := '1';
10
        when others => Error := '1';
11
      end case;
12
    end process Job;
13
14
    MemAdressP: process
15
      begin 
16
        wait until rising_edge(clk);
17
          if (sDoneCommandIntern = '1') then
18
            MemAdress := std_logic_vector(unsigned(MemAdress) + '1'));
19
          end if;
20
      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)


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:
1
architecture Behavioral of JobList is
2
signal   CommandAdress : std_logic_vector(15 downto 0) := x"0000";
3
signal   CommandValue : std_logic_vector(15 downto 0) := x"0000";
4
signal   DoneCommandIntern : std_logic := '0';
5
signal   sMemAdress : integer := 0;
6
signal   Value1 : std_logic_vector (15 downto 0) := x"0000";
7
signal   Value2 : std_logic_vector (15 downto 0) := x"0000";
8
signal   Value3 : std_logic_vector (15 downto 0) := x"0000";
9
begin
10
  JobListSeparator : process
11
          begin
12
            wait until rising_edge(clk);
13
              CommandAdress <= CommandList(31 downto 16);
14
              CommandValue  <= CommandList(15 downto 0);
15
          end process JobListSeparator;
16
          
17
   Job: process(CommandAdress)
18
     begin 
19
      DoneCommandIntern <= '0';
20
      Error <= '0';
21
      case CommandAdress is 
22
        when x"0001" => Value1 <= CommandValue; DoneCommandIntern <= '1';
23
        when x"0002" => Value2 <= CommandValue; DoneCommandIntern <= '1';
24
        when x"0003" => Value3 <= CommandValue; DoneCommandIntern <= '1';
25
        when others => Error <= '1';
26
      end case;
27
     end process Job;
28
29
    MemAdressP: process(DoneCommandIntern)
30
      begin 
31
          if (DoneCommandIntern = '1') then
32
            MemAdress <= std_logic_vector(to_unsigned(sMemAdress+1, MemAdress'length));
33
          end if;
34
      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)


Lesenswert?

Ich poste mal noch mein Simulationsprogramm, denn ich vermute, dass dort 
der Fehler liegt ;)
1
BEGIN
2
   -- Instantiate the Unit Under Test (UUT)
3
   uut: JobList PORT MAP(
4
      clk => clk,
5
    Error => Error,
6
    MemAdress => MemAdress,
7
    CommandList => CommandList,
8
    DoneCommand => DoneCommand 
9
   );
10
   
11
   clk <= not clk after 10 ns; -- 50 MHz
12
   
13
   tb : PROCESS BEGIN
14
      wait for 10 ns;
15
      case 
16
        MemAdress is 
17
          when "000000" =>CommandList <= x"0001000A";
18
          when "000001" =>CommandList <= x"00020064";
19
          when "000010" =>CommandList <= x"000300FF";
20
          when "000011" =>CommandList <= x"000D01F4";
21
          when "000100" =>CommandList <= x"000C0003";
22
          when others =>CommandList <= x"00000000";
23
      end case;      
24
      
25
   END PROCESS;

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


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:
1
MemAdressP: process(DoneCommandIntern)
2
       begin
3
           if (DoneCommandIntern = '1') then
4
             MemAdress <= std_logic_vector(to_unsigned(sMemAdress+1, MemAdress'length));
5
           end if;
6
       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:
1
 MemAdress <= std_logic_vector(to_unsigned(sMemAdress+1, MemAdress'length))
2
              when DoneCommandIntern = '1';
Das ist wesentlich kürzer, aber blöderweise bleibt es ein Latch.

: Bearbeitet durch Moderator
von Dan (Gast)


Angehängte Dateien:

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. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Angehängte Dateien:

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:
1
        sMemAdress = 
2
            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. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Angehängte Dateien:

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)


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. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


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:
1
begin
2
3
   Job: process begin 
4
      wait until rising_edge(clk);
5
      Error <= '0';
6
      case CommandList(31 downto 16) is 
7
        when x"0001" => Value1 <= CommandList(15 downto 0); sMemAdress <= sMemAdress+1;
8
        when x"0002" => Value2 <= CommandList(15 downto 0); sMemAdress <= sMemAdress+1;
9
        when x"0003" => Value3 <= CommandList(15 downto 0); sMemAdress <= sMemAdress+1;
10
        when others => Error <= '1';
11
      end case;
12
     end process Job;
13
14
15
    MemAdress <= std_logic_vector(to_unsigned(sMemAdress, MemAdress'length));
16
        
17
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:
1
   clk <= not clk after 10 ns; -- 50 MHz
2
   
3
   tb : PROCESS BEGIN
4
      wait for rising_edge(clk);
5
      case 
6
        MemAdress is 
7
          when "000000" =>CommandList <= x"0001000A";
8
          when "000001" =>CommandList <= x"00020064";
9
          when "000010" =>CommandList <= x"000300FF";
10
          when "000011" =>CommandList <= x"000D01F4";
11
          when "000100" =>CommandList <= x"000C0003";
12
          when others =>CommandList <= x"00000000";
13
      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)


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. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


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:
1
  JobListSeparator : process
2
  begin
3
     wait on CommandList; -- auf irgendeine Änderung in der CommandList warten
4
     CommandAdress <= CommandList(31 downto 16);
5
     CommandValue  <= CommandList(15 downto 0);
6
  end process JobListSeparator;
Ist nur 1 einziges "wait" im code, kann der Prozess auch mit 
Sensitivliste geschrieben werden:
1
  JobListSeparator : process (CommandList) -- auf irgendeine Änderung in der CommandList warten
2
  begin
3
     CommandAdress <= CommandList(31 downto 16);
4
     CommandValue  <= CommandList(15 downto 0);
5
  end process JobListSeparator;

Und die finale Abkürzung ist, wenn man bei simplen Zuweisungen auf den 
Prozess verzichtet und eine nebenläufige Anweisung schreibt:
1
  CommandAdress <= CommandList(31 downto 16);
2
  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)


Lesenswert?

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

Eigentlich braucht man genau genommen ja nie einen ;)
1
process(trigger_condition)
2
    if trigger_condition
3
        irgendwas <= irgendwas_anderes;
4
    else
5
        irgendwas <= nochmal_was_anderes;
6
    end if;
7
end process;

lässt sich ja für praktisch alle Fälle ja immer auch so schreiben:
1
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)


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


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.
1
signal d : unsigned(7 downto 0) := (others => '0');
2
3
...
4
5
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)


Lesenswert?


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


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:
1
   -- Huch, ein Latch
2
   latch <= in when en='1';               
3
4
    -- Besser mit Flipflops 
5
   flip  <= in when clk='1'and clk'event;
6
   flop  <= in when rising_edge(clk);
7
8
   -- Async Reset
9
   flap  <= '0' when reset='1' else in when rising_edge(clk); 
10
   
11
   -- als Schieberegister zum Eintakten von Signalen 
12
   sr    <= sr(sr'left-1 downto 0) & in when rising_edge(clk);

: Bearbeitet durch Moderator
von Markus F. (mfro)


Lesenswert?

Zumindest gemäss der 2008-Spezifikation sind else-Zweige optional:
1
conditional_waveforms ::=
2
    waveform when condition
3
    { else waveform when condition }
4
    [ else waveform ]

von Markus F. (mfro)


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. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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

> Da fehlt noch was
Habs korrigiert.

von Dan (Gast)


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 ;)
1
     wait until rising_edge(clk);
2
      Error <= '0';
3
    sTestSignal <= '0'; 
4
      case CommandList(31 downto 16) is
5
        when x"0000" => case CommandList(15 downto 0) is
6
                    when x"0000" => sMemAdress <= sMemAdress+1;
7
                    when x"0001" => If (StartSignal = '0') then sMemAdress <= sMemAdress+1; end if;  
8
                    when x"0002" => sOn<= '1'; sMemAdress <= sMemAdress+1;
9
                    when x"0003" => sOff <= '0'; sMemAdress <= sMemAdress+1;
10
                    when others => Error <= '1';
11
                  end case;
12
        when x"0001" => Value1<= CommandList(15 downto 0); sMemAdress <= sMemAdress+1;
13
        when x"0002" => Value2<= CommandList(15 downto 0); sMemAdress <= sMemAdress+1;
14
        when x"0003" => Value3<= CommandList(15 downto 0); sMemAdress <= sMemAdress+1;
15
        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;
16
        when others => Error <= '1';
17
      end case;
18
     end process Job;

von Markus F. (mfro)


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. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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

Dabei kam das heraus:
1
:
2
alias addr : std_logic_vecor(15 downto 0) is CommandList(31 downto 16);
3
alias cmd  : std_logic_vecor(15 downto 0) is CommandList(15 downto 0);
4
:
5
      sTestSignal <= '0'; 
6
      case addr is
7
        when x"0000" => case cmd is ...
8
:
9
        when x"0004" => sTestSignal <= '1';
10
                        if counter < to_integer(unsigned(cmd)))*50  then
11
                            counter    <= counter + 1;
12
                        else
13
                            sMemAdress <= sMemAdress+1;
14
                            counter    <= 0;
15
                        end if;
16
17
        when others => Error <= '1';
18
:
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-)


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)


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. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


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:
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
5
entity counter is
6
    Port ( clk : in  STD_LOGIC;
7
           a : out  STD_LOGIC;
8
           b : out  STD_LOGIC;
9
           c : out  STD_LOGIC);
10
end counter;
11
12
architecture Behavioral of counter is
13
14
signal ca, cb, cc : unsigned (7 downto 0) := (others=>'0');
15
16
begin
17
18
     -- Zähler a
19
     ca <= ca+1 when rising_edge(clk);  
20
     
21
     -- Zähler b
22
     process begin
23
        wait until rising_edge(clk);
24
        cb <= cb+1;
25
     end process;
26
     
27
     -- Zähler c
28
     process (clk) begin
29
        if rising_edge(clk) then
30
           cc <= cc+1;
31
        end if;
32
     end process;
33
     
34
     -- einzelne Bits der Zähler ausgeben
35
     a <= ca(2);
36
     b <= cb(4);
37
     c <= cc(6);
38
39
end Behavioral;

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


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:

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:
1
   clk <= not clk after 10 ns; -- 50 MHz
2
   
3
   tb : PROCESS BEGIN
4
      wait until rising_edge(clk);
5
      case 
6
        MemAdress is 
7
          when "000000" =>CommandList <= x"00000002";
8
          when "000001" =>CommandList <= x"00000003";
9
          when others =>CommandList <= x"FFFFFFFF";
10
       
11
      end case;      
12
      
13
   END PROCESS;   clk <= not clk after 10 ns; -- 50 MHz

von Duke Scarring (Gast)


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. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


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:
1
   tb : PROCESS (MemAdress) BEGIN
2
      case 
3
        MemAdress is 
4
          when "000000" =>CommandList <= x"00000002";
5
          when "000001" =>CommandList <= x"00000003";
6
          when others =>CommandList <= x"FFFFFFFF";
7
       
8
      end case;

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

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:

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?

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.