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


von Holger K. (holgerkraehe)


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
1
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
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
5
entity main is
6
  port(
7
    --     LED : out std_logic;
8
    --    LED2   : out std_logic;
9
    --    BTN1   : in  std_logic;
10
    --    BTN2   : in  std_logic;
11
    CLK    : in  std_logic;
12
    --    BTN    : in  std_logic;
13
    PX_CLK : out std_logic;
14
    DE     : out std_logic;
15
    HSYNC  : out std_logic;
16
    VSYNC  : out std_logic;
17
    --    PWM    : out std_logic;
18
    RED    : out std_logic_vector(7 downto 0); --Bit 7..1
19
    GREEN  : out std_logic_vector(7 downto 0); --Bit 7..0
20
    BLUE   : out std_logic_vector(7 downto 0); --Bit 7..0
21
    GRST   : out std_logic := '1'
22
  );
23
end main;
24
25
architecture beh of main is
26
  signal reset         : std_logic              := '0';
27
  signal REDstate      : integer range 0 to 255 := 0;
28
  signal BLUEstate     : integer range 0 to 255 := 50;
29
  signal GREENstate    : integer range 0 to 255 := 0;
30
  signal vsyncState    : std_logic;
31
  signal delayCount    : integer range 0 to 800 := 0;
32
  signal clkpll        : std_logic;
33
  signal lcdPixelCount : integer range 0 to 800 * 480;
34
35
  signal idle    : boolean                 := false;
36
  signal xPos    : integer range 0 to 800;
37
  signal yPos    : integer range 0 to 480;
38
  signal address : STD_LOGIC_VECTOR(9 DOWNTO 0);
39
  signal addrCnt : integer range 0 to 1000 := 0;
40
  signal data    : STD_LOGIC_VECTOR(15 DOWNTO 0);
41
  signal wren    : STD_LOGIC;
42
  signal memData : STD_LOGIC_VECTOR(15 DOWNTO 0);
43
begin
44
45
  mem : entity work.ram
46
    port map(
47
      address => address,
48
      clock   => clkpll,
49
      data    => data,
50
      wren    => wren,
51
      q       => memData
52
    );
53
54
  lcd : entity work.lcd
55
    port map(
56
      xPos      => xPos,
57
      yPos      => yPos,
58
      clk       => clkpll,
59
      rst       => reset,
60
      DEN       => DE,
61
      hsync     => HSYNC,
62
      vsync     => vsyncState,
63
      NCLK      => PX_CLK,
64
      DATAcount => lcdPixelCount
65
    );
66
67
  pll : entity work.pll
68
    port map(
69
      inclk0 => CLK,
70
      c0     => clkpll
71
    );
72
73
  ramtest : process(clkpll) is
74
  begin
75
    if rising_edge(clkpll) then
76
      --addrCnt <= addrCnt + 1;
77
      
78
    end if;
79
80
    wren <= '0';
81
82
  end process ramtest;
83
84
  color : process(clkpll) is
85
  begin
86
    if rising_edge(clkpll) then
87
88
89
      if(xPos < 500) then 
90
        address <= std_logic_vector(to_unsigned(xPos+1, address'length));
91
92
93
--Ohne die untenstehende Zeile sehe ich das rote Rechteck. Mit dieser Zeile, geht nichts mehr auf dem LCD.
94
        BLUEstate <= to_integer(unsigned(memData(4 downto 0) & (2 downto 0 => '0')));
95
      end if;
96
97
--Anzeigen eines roten Rechtecks
98
      if (yPos > 200 and xPos > 50) and xPos < 200 and yPos < 300 then
99
        REDstate <= 200;
100
      else
101
        REDstate <= 0;
102
      end if;
103
104
    end if;
105
106
  end process color;
107
  
108
    RED   <= std_logic_vector(to_unsigned(REDstate, RED'length));
109
  GREEN <= std_logic_vector(to_unsigned(GREENstate, GREEN'length));
110
  BLUE  <= std_logic_vector(to_unsigned(BLUEstate, BLUE'length));
111
112
  VSYNC <= vsyncState;
113
114
end;

: Bearbeitet durch User
von SeriousSam (Gast)


Lesenswert?

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

von Holgerkraehe (Gast)


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

von Holger K. (holgerkraehe)


Lesenswert?

So, ich habe besagte Codestelle mal angepasst:
1
  if(xPos < 500) then 
2
        address <= std_logic_vector(to_unsigned(xPos+1, address'length));
3
        BLUEstate <= to_integer(unsigned(memData(4 downto 0) & (2 downto 0 => '0')));
4
      else
5
        BLUEstate <= 0;
6
      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?

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


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:
1
  signal xPos    : integer range 0 to 800;
2
  signal yPos    : integer range 0 to 480;

von Holger K. (holgerkraehe)


Angehängte Dateien:

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?
1
  mem : entity work.ram
2
    port map(
3
      address => address,
4
      clock   => not clkpll, --180Grad Phasenschieben
5
      data    => data,
6
      wren    => wren,
7
      q       => memData
8
    );

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:
1
      if(xPos < 799) then 
2
        address <= std_logic_vector(to_unsigned(xPos, address'length));
3
        BLUEstate <= to_integer(unsigned(memData(4 downto 0) & (2 downto 0 => '0')));
4
        REDstate <= to_integer(unsigned(memData(15 downto 11) & (2 downto 0 => '0')));
5
        GREENstate <= to_integer(unsigned(memData(10 downto 5) & (1 downto 0 => '0'))); 
6
      else
7
        BLUEstate <= 0;
8
        GREENstate <= 0;
9
        REDstate <= 0;
10
      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:
1
srec_cat.exe Unbenannt.bmp -binary -byte-swap -output dstFile.mif −Memory_Initialization_File 16

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


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?

von Holger K. (holgerkraehe)


Angehängte Dateien:

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.
von Holger K. (holgerkraehe)


Angehängte Dateien:

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

von Markus F. (mfro)


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.
von Holger K. (holgerkraehe)


Angehängte Dateien:

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...

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


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
von Markus F. (mfro)


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.

von Holger K. (holgerkraehe)


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.

von Markus F. (mfro)


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.

von Holger K. (holgerkraehe)


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?
1
create_clock -name {clk_50MHz} -period 20.000 -waveform { 0.000 10.000 } [get_ports {clk}]
2
3
derive_pll_clocks -create_base_clocks
4
derive_clock_uncertainty

Danke

von Mampf F. (mampf) Benutzerseite


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
von Holger K. (holgerkraehe)


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.

von Markus F. (mfro)


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.

von Mampf F. (mampf) Benutzerseite


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!

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.