mikrocontroller.net

Forum: FPGA, VHDL & Co. Quartus BlockRAM bringt FPGA zum "Absturz"


Autor: Holger K. (holgerkraehe)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen

Ich habe in meinem Projekt ein Quartus Block RAM erstellt und dieses mit 
einem mif File initialisiert. In der Simulation verhält sich das RAM 
genau so, wie ich es gerne hätte. In der Praxis, sieht es jedoch etwas 
merkwürdig aus.


Ich bekomme von meinem LCD Modul die x und y position, wo gerade 
gezeichnet wird. Dann kann ich die Farben bzw. RGB Werte mittels 
REDstate, BLUEstate und GREENstate anpassen.

Wenn ich dies wie beim Kommentar "Rechteck anzeigen" mache, dann klappt 
dies einwandfrei. Wenn ich jedoch den BLUEstate aufgrund des memData 
Inhaltes anpasse, so wie etwas weiter oben im Code, dann zeigt mir das 
Display manchmal kurz ein bisschen Blau an, und dann bleicht es aus. Ein 
rotes Rechteck ist nicht mehr sichbar. Ohne die Zeile
BLUEstate <= to_integer(unsigned(memData(4 downto 0) & (2 downto 0 => '0')));

Sehe ich jedoch das Rechteck wieder.
Für mich ist dies ziemlich unerklärlich.

Hat jemand eventuell einen Tipp oder eine Idee?

Danke!

Hier mein VHDL

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity main is
  port(
    --     LED : out std_logic;
    --    LED2   : out std_logic;
    --    BTN1   : in  std_logic;
    --    BTN2   : in  std_logic;
    CLK    : in  std_logic;
    --    BTN    : in  std_logic;
    PX_CLK : out std_logic;
    DE     : out std_logic;
    HSYNC  : out std_logic;
    VSYNC  : out std_logic;
    --    PWM    : out std_logic;
    RED    : out std_logic_vector(7 downto 0); --Bit 7..1
    GREEN  : out std_logic_vector(7 downto 0); --Bit 7..0
    BLUE   : out std_logic_vector(7 downto 0); --Bit 7..0
    GRST   : out std_logic := '1'
  );
end main;

architecture beh of main is
  signal reset         : std_logic              := '0';
  signal REDstate      : integer range 0 to 255 := 0;
  signal BLUEstate     : integer range 0 to 255 := 50;
  signal GREENstate    : integer range 0 to 255 := 0;
  signal vsyncState    : std_logic;
  signal delayCount    : integer range 0 to 800 := 0;
  signal clkpll        : std_logic;
  signal lcdPixelCount : integer range 0 to 800 * 480;

  signal idle    : boolean                 := false;
  signal xPos    : integer range 0 to 800;
  signal yPos    : integer range 0 to 480;
  signal address : STD_LOGIC_VECTOR(9 DOWNTO 0);
  signal addrCnt : integer range 0 to 1000 := 0;
  signal data    : STD_LOGIC_VECTOR(15 DOWNTO 0);
  signal wren    : STD_LOGIC;
  signal memData : STD_LOGIC_VECTOR(15 DOWNTO 0);
begin

  mem : entity work.ram
    port map(
      address => address,
      clock   => clkpll,
      data    => data,
      wren    => wren,
      q       => memData
    );

  lcd : entity work.lcd
    port map(
      xPos      => xPos,
      yPos      => yPos,
      clk       => clkpll,
      rst       => reset,
      DEN       => DE,
      hsync     => HSYNC,
      vsync     => vsyncState,
      NCLK      => PX_CLK,
      DATAcount => lcdPixelCount
    );

  pll : entity work.pll
    port map(
      inclk0 => CLK,
      c0     => clkpll
    );

  ramtest : process(clkpll) is
  begin
    if rising_edge(clkpll) then
      --addrCnt <= addrCnt + 1;
      
    end if;

    wren <= '0';

  end process ramtest;

  color : process(clkpll) is
  begin
    if rising_edge(clkpll) then


      if(xPos < 500) then 
        address <= std_logic_vector(to_unsigned(xPos+1, address'length));


--Ohne die untenstehende Zeile sehe ich das rote Rechteck. Mit dieser Zeile, geht nichts mehr auf dem LCD.
        BLUEstate <= to_integer(unsigned(memData(4 downto 0) & (2 downto 0 => '0')));
      end if;

--Anzeigen eines roten Rechtecks
      if (yPos > 200 and xPos > 50) and xPos < 200 and yPos < 300 then
        REDstate <= 200;
      else
        REDstate <= 0;
      end if;

    end if;

  end process color;
  
    RED   <= std_logic_vector(to_unsigned(REDstate, RED'length));
  GREEN <= std_logic_vector(to_unsigned(GREENstate, GREEN'length));
  BLUE  <= std_logic_vector(to_unsigned(BLUEstate, BLUE'length));

  VSYNC <= vsyncState;

end;

: Bearbeitet durch User
Autor: SeriousSam (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ev. muss BLUEstate ausserhalb des sichtbaren Bereichs 0 sein, es wird 
aber nie zurückgesetzt.

Autor: Holgerkraehe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
SeriousSam schrieb:
> Ev. muss BLUEstate ausserhalb des sichtbaren Bereichs 0 sein, es
> wird aber nie zurückgesetzt.

Interessante Theorie.
Könnte tatsächlich sein. Wobei ich nicht verstehe, warum ich dann das 
rote rechteck nicht sehen kann. Wenn ich BLUEState verwende, sehe ich 
nur etwas blaues dass dann ausbleicht

Autor: Holger K. (holgerkraehe)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So, ich habe besagte Codestelle mal angepasst:
  if(xPos < 500) then 
        address <= std_logic_vector(to_unsigned(xPos+1, address'length));
        BLUEstate <= to_integer(unsigned(memData(4 downto 0) & (2 downto 0 => '0')));
      else
        BLUEstate <= 0;
      end if;


Hat tatsächlich etwas gebracht. Jedoch das selbe problem, wenn ich die 
nächste Farbe hinzufüge.

Ich glaube das Problem kommt von den Clocks.
Ich müsste glaube ich den RAM Clock um 180 Grad Phasenverschieben, damit 
ich auch gültige Daten auslesen kann.

Was meint ihr?

Autor: Lothar M. (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Holger K. schrieb:
> Ich müsste glaube ich
Was sagt denn die Simulation?

> Ich glaube das Problem kommt von den Clocks.
Worauf gründet dieser Glaube?

> den RAM Clock um 180 Grad Phasenverschieben, damit ich auch gültige
> Daten auslesen kann.
> Was meint ihr?
Das Problem liegt woanders. Sicher. Denn wenn es sowas wäre, dann 
hättest du bestenfalls irgendeine Verschiebung...

Holger K. schrieb:
> dann zeigt mir das Display manchmal kurz ein bisschen Blau an, und dann
> bleicht es aus.
Und was passiert an den Ports? Kommen die Sync-Signale noch richtig?


Mich wundertübrigens, dass dein Display 481 Pixel hoch und 801 Pixel 
breit ist:
  signal xPos    : integer range 0 to 800;
  signal yPos    : integer range 0 to 480;

Autor: Holger K. (holgerkraehe)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Lothar M. schrieb:
> Mich wundertübrigens, dass dein Display 481 Pixel hoch und 801 Pixel
> breit ist:  signal xPos    : integer range 0 to 800;
>   signal yPos    : integer range 0 to 480;

Das ist in der Tat ein Fehler.

Lothar M. schrieb:
>> Ich glaube das Problem kommt von den Clocks.
> Worauf gründet dieser Glaube?

Da es mit einem verschobenen Clok plötzlich besser wurde.
Ich habe nun folgendes gemacht:

Ist sowas in Ordnung oder eher ein Hack?
  mem : entity work.ram
    port map(
      address => address,
      clock   => not clkpll, --180Grad Phasenschieben
      data    => data,
      wren    => wren,
      q       => memData
    );


Lothar M. schrieb:
>> den RAM Clock um 180 Grad Phasenverschieben, damit ich auch gültige
>> Daten auslesen kann.
>> Was meint ihr?
> Das Problem liegt woanders. Sicher. Denn wenn es sowas wäre, dann
> hättest du bestenfalls irgendeine Verschiebung...

Ich hab mir das so vorgestellt, dass das RAM seine Daten mit der 
steigenden Flanke an seinen Ausgang legt. Auf die steigende Flanke lese 
ich jedoch auch den Ausgang des RAMs ein und gebe die Daten ans Display. 
Nun dachte ich, dass es hierbei zu zufälligen treffern kommt. manchmal 
hab ich die daten erwischt, manchmal nicht.

Nachdem ich nun den Code wie folgt angepasst habe:
      if(xPos < 799) then 
        address <= std_logic_vector(to_unsigned(xPos, address'length));
        BLUEstate <= to_integer(unsigned(memData(4 downto 0) & (2 downto 0 => '0')));
        REDstate <= to_integer(unsigned(memData(15 downto 11) & (2 downto 0 => '0')));
        GREENstate <= to_integer(unsigned(memData(10 downto 5) & (1 downto 0 => '0'))); 
      else
        BLUEstate <= 0;
        GREENstate <= 0;
        REDstate <= 0;
      end if;

Und den Memoryclock mit dem not invertiert habe, sieht das LCD wie im 
angehängten Bild aus.

Das "rote" Rechteck ist auch sichtbar.
Scheint tatsächlich ein Clock problem gewesen zu sein.
Oder sollte ich noch etwas weiter suchen?

So nebenbei, was würde man bei einem solchen Projekt für Timing 
Constraints definieren?

Falls jemand jemals über diesen Thread stolpert und gerne auch solch 
eine Grafik hätte... Ich habe lediglich eine Zeile im RAM gespeichert. 
das mif file ist im anhang. zum konvertieren von Bildern nutze ich 
srecord bzw. srec_cat.exe mit folgenden befehlen:
srec_cat.exe Unbenannt.bmp -binary -byte-swap -output dstFile.mif −Memory_Initialization_File 16

: Bearbeitet durch User
Autor: Lothar M. (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Holger K. schrieb:
> Oder sollte ich noch etwas weiter suchen?
Ja, denn du hast jetzt zwar irgendwas irgendwie hingebastelt, aber 
dieses simple Design muss auch mit 1 Takt laufen.

Warum bleibt eigentlich das Sync-Timing stehen? Wird das auch über das 
RAM gesteuert? Oder doch nur über 2 Zähler, wie üblich...

Holger K. schrieb:
> ein Quartus Block RAM erstellt
Was sagt die Doku dieses RAM zu diesem Verhalten?

Autor: Holger K. (holgerkraehe)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Lothar M. schrieb:
> Warum bleibt eigentlich das Sync-Timing stehen? Wird das auch über das
> RAM gesteuert? Oder doch nur über 2 Zähler, wie üblich...

Das Sync-Timing wird über Zähler gemacht.

Lothar M. schrieb:
> Holger K. schrieb:
>> ein Quartus Block RAM erstellt
> Was sagt die Doku dieses RAM zu diesem Verhalten?

Das ist eine gute Frage.
Siehe das Bild im Anhang.

Sehr merkwürdig ist, dass das Datenwort welches an Adresse 01 liegt, 
erst nach zwei memclk Takten kommt. alle weiteren kommen dann nach einem 
Takt.
Das macht mir das leben gerade ziemlich schwer. Denn ich habe ein xPos 
und yPos counter bzw. Integer.

Nun möchte ich ja rechtzeitig die Daten an den Datenbus legen, nämlich 
VOR der posFlanke des LCD Pixel Clocks.

Grundsätzlich hätte ich dies geschafft, nämlich durch die erzeugung des 
"framePulse" wie man im Diagramm sieht.

Dadurch dass aber aus irgendeinem Grund die Ausgabe des Worts von 
Adresse 01 um einen weiteren Clock verzögert wird, funktioniert dies so 
wieder nicht. Sehr ärgerlich.

Beitrag #5689061 wurde vom Autor gelöscht.
Autor: Holger K. (holgerkraehe)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
So, ich musste folgende Einstellung entfernen:

Im Quartus MegaFunction Wizard die Registrierung des Ausganges q mit dem 
Clock. Dann war mein erster zusätzlicher Clock weg.

Die aktuelle VHDL Datei habe ich mal angehängt.
Ich bin neu im Gebiet VHDL und würde mich sehr über Kommentare zum Code 
freuen. Was man besser machen kann oder anders machen kann und was 
vielleicht auch ok ist.


Danke

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Holger K. schrieb:
> Ich hab mir das so vorgestellt, dass das RAM seine Daten mit der
> steigenden Flanke an seinen Ausgang legt. Auf die steigende Flanke lese
> ich jedoch auch den Ausgang des RAMs ein und gebe die Daten ans Display.

Da offenbarst Du m.E. ein Verständnisdefizit ;).

Das RAM liest Du in einem getakteten Prozeß ein (eine Signalzuweisung, 
die in einem Prozeß geschieht, der ein "rising_edge(...)" enthält, 
erzeugt ein Register). Die Signalzuweisung in dem Prozeß macht im 
selben Takt erst mal gar nichts, sondern sorgt lediglich dafür, daß 
der entsprechende Wert rechtzeitig zur nächsten steigenden Taktflanke 
im Register bereitsteht.
Im selben Takt wird entsprechend der vorherige Registerwert 
verarbeitet.

Die Gedanken oben brauchst Du dir also nicht zu machen, das macht deine 
Synthese für dich.

Ein solches Verhalten erzeugt natürlich - wenn (noch) nicht verstanden - 
eine zunächst unvermutete Latenz: das Ergebnis kommt später an als 
vermutet. Ich habe mir deinen Code nicht genau angeschaut, aber die 
Symptome sprechen dafür, daß Du gerade genau dieses Problem hast...

: Bearbeitet durch User
Beitrag #5689136 wurde vom Autor gelöscht.
Autor: Holger K. (holgerkraehe)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:
> Die Gedanken oben brauchst Du dir also nicht zu machen, das macht deine
> Synthese für dich.

Bist du dir ganz sicher?
Immerhin geht es hierbei um das Einlesen von Signalen.
Soweit ich weiss, müssen diese eine gewisse Zeit vor und nach der
Clockflanke stabil bleiben.

Wenn ich mir die Simulation ohne invertierten Memoryclock ansehe, dann
kann ich nicht beurteilen, welchen Wert nun gesamplet wird, da genau
beim wechsel eingelesen wird. Ich dachte immer, sowas sollte man auf
keinen Fall machen.

Siehe das Bild im Anhang.
memData sind die Daten die AUS dem RAM rauskommen und in das Register
eingelesen werden sollen. Aber genau dann beim Einlesen ändern diese
ja....

Mit invertiertem Memoryclock sieht dies m.M.n besser aus.

Aber wie gesagt, bin ich noch kein Experte...

Autor: Lothar M. (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Holger K. schrieb:
> Mit invertiertem Memoryclock sieht dies m.M.n besser aus.
Durch das Verwenden des invertierten Taktes verdoppelst du quasi den 
Systemtakt. Deshalb bemerkst du dann eine Latency nicht, oder sie wirkt 
sich anders aus.

: Bearbeitet durch Moderator
Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Holger K. schrieb:
> Wenn ich mir die Simulation ohne invertierten Memoryclock ansehe, dann
> kann ich nicht beurteilen, welchen Wert nun gesamplet wird, da genau
> beim wechsel eingelesen wird. Ich dachte immer, sowas sollte man auf
> keinen Fall machen.

Schau' dir dasselbe Signal in TimeQuest an.

Dann wirst Du feststellen, daß - vorausgesetzt, das Design schafft die 
Taktfrequenz - die Synthese dafür sorgt, daß der im Takt vorher 
zugewiesene Wert gerade rechtzeitig zur steigenden Flanke bereitgestellt 
wird, ohne die Hold-Time des vorigen Takts oder die Setup-Time des 
aktuellen zu verletzen.

Das Invertieren des Taktes sorgt nur dafür, daß dein Design praktisch 
mit doppelter Taktfrequenz läuft und damit maximal nur einen halb so 
schnellen Takt schafft, wie es eigentlich könnte.

Autor: Holger K. (holgerkraehe)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:
> Dann wirst Du feststellen, daß - vorausgesetzt, das Design schafft die
> Taktfrequenz - die Synthese dafür sorgt, daß der im Takt vorher
> zugewiesene Wert gerade rechtzeitig zur steigenden Flanke bereitgestellt
> wird, ohne die Hold-Time des vorigen Takts oder die Setup-Time des
> aktuellen zu verletzen.

Vielen Dank.
Dies werde ich versuchen.
Ist es dazu notwendig, Constraints im SDC File anzulegen? Oder kann er 
dies ohnehin syntethisieren?

Danke.

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Holger K. schrieb:
> Ist es dazu notwendig, Constraints im SDC File anzulegen? Oder kann er
> dies ohnehin syntethisieren?

Wenn Quartus weiß, mit welchem Takt dein Design laufen soll (das 
sollte natürlich mindestens im .sdc-File stehen) und Du keine 
"Extra-Würste" (wie Taktdomänenübergänge, asynchrone Signale oder 
Multicycles) drin hast, geht der Rest innerhalb des FPGAs 
üblicherweise von alleine und braucht keine weiteren Angaben.

Entweder geht dann das Timing auf oder Du hast noch zu lange 
sequentielle Ketten drin und bekommst timing violations, die Du durch 
einfügen von Registern oder Multicycles "aufbrechen" mußt.

Autor: Holger K. (holgerkraehe)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:
> Wenn Quartus weiß, mit welchem Takt dein Design laufen soll (das
> sollte natürlich mindestens im .sdc-File stehen)

Bedeutet dies, dass sowas genügt als komplettes SDC File?
create_clock -name {clk_50MHz} -period 20.000 -waveform { 0.000 10.000 } [get_ports {clk}]

derive_pll_clocks -create_base_clocks
derive_clock_uncertainty

Danke

Autor: Mampf F. (mampf) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Holger K. schrieb:
> Sehr merkwürdig ist, dass das Datenwort welches an Adresse 01 liegt,
> erst nach zwei memclk Takten kommt. alle weiteren kommen dann nach einem
> Takt.

Das ist bei Altera BlockRAMs so - es hat ein nicht deaktivierbares 
Ausgangsregister. (Immerhin kann man Eingangs-Register deaktivieren).

Bei Xilinx kann man es weg-konfigurieren und braucht dann einen 
Taktzyklus weniger.

Das mit "alle weiteren kommen dann nach einem Takt" scheint nur so, weil 
es quasi wie eine Pipeline ist.

: Bearbeitet durch User
Autor: Holger K. (holgerkraehe)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank für die Antworten.

Das bedeutet, dass ich mich nun darum kümmern muss, dass ich keinen 
zweiten, invertierten clock benötige.

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mampf F. schrieb:
> Das ist bei Altera BlockRAMs so - es hat ein nicht deaktivierbares
> Ausgangsregister. (Immerhin kann man Eingangs-Register deaktivieren).

Andersrum (zumindest bei meinen Cyclone III M9Ks).

Man kann das Ausgangs-"Q"-Register deaktivieren. Das Eingangs 
"Address"-Register nicht (genauso das data- und wren-Register).
Quartus kann aber ein Address-Register, das man sowieso schon 
hingeschrieben hat, in das M9K "reinschieben", so daß keine weitere 
Latenz dazukommt.

Wenn ich das richtig verstehe, hat der TO an "beiden Enden" Register 
dran, ist also zumindest für einen Takt Latenz selbst verantwortlich.

Autor: Mampf F. (mampf) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:
> Mampf F. schrieb:
>> Das ist bei Altera BlockRAMs so - es hat ein nicht deaktivierbares
>> Ausgangsregister. (Immerhin kann man Eingangs-Register deaktivieren).
>
> Andersrum (zumindest bei meinen Cyclone III M9Ks).
>
> Man kann das Ausgangs-"Q"-Register deaktivieren. Das Eingangs
> "Address"-Register nicht (genauso das data- und wren-Register).
> Quartus kann aber ein Address-Register, das man sowieso schon
> hingeschrieben hat, in das M9K "reinschieben", so daß keine weitere
> Latenz dazukommt.

Jau richtig, sorry! Gerade nochmal im Quartus nachgeschaut - du hast 
recht!

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.