mikrocontroller.net

Forum: FPGA, VHDL & Co. Cyclone III "multiple drivers"


Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Freunde der gepflegten Samstagsarbeit,

irgendwie stehe ich gerade auf dem Schlauch. Ich versuche, für den 
Videocontroller meines Retro-Computer VHDL Projekts eine CLUT zu 
basteln. Dazu habe ich mir folgendes gebaut:
entity clut_ram is
    generic 
    (
        DATA_WIDTH : natural := 3;
        ADDR_WIDTH : natural := 4
    );

    port 
    (
        clk             : in std_logic;
        pixel_clk       : in std_logic;
        addr_a          : in std_logic_vector(ADDR_WIDTH - 1 downto 0);
        addr_b          : in std_logic_vector(ADDR_WIDTH - 1 downto 0);
        data_a          : in std_logic_vector((DATA_WIDTH - 1) downto 0);
        data_b          : in std_logic_vector((DATA_WIDTH - 1) downto 0);
        we_a            : in std_logic := '0';
        we_b            : in std_logic := '0';
        q_a             : out std_logic_vector((DATA_WIDTH - 1) downto 0);
        q_b             : out std_logic_vector((DATA_WIDTH - 1) downto 0)
    );
end clut_ram;

architecture rtl of clut_ram is
    -- Build a 2-D array type for the RAM
    subtype word_t is std_logic_vector((DATA_WIDTH - 1) downto 0);
    type memory_t is array(2 ** ADDR_WIDTH - 1 downto 0) of word_t;

    -- Declare the RAM 
    shared variable ram : memory_t;
begin
    -- Port A
    process(clk)
    begin
        if rising_edge(clk) then         -- Line 644
            if we_a = '1' then
                ram(to_integer(unsigned(addr_a))) := data_a;
            end if;
            q_a <= ram(to_integer(unsigned(addr_a)));
        end if;
    end process;
    -- Port B
    process(pixel_clk)
    begin
        if rising_edge(pixel_clk) then   -- Line 654
            if we_b = '1' then
                ram(to_integer(unsigned(addr_b))) := data_b;
            end if;
            q_b <= ram(to_integer(unsigned(addr_b)));
        end if;
    end process;
end rtl;
Das entstammt - fast ohne Änderungen - dem Quartus Templateverzeichnis 
und sollte eigentlich ein echtes Dual-Port RAM inferieren.

Das Problem ist das da:

Error (10028): Can't resolve multiple constant drivers for net 
"ram[15][2]" at video.vhd(654)
Error (10029): Constant driver at video.vhd(644)
Error (10028): Can't resolve multiple constant drivers for net 
"ram[15][1]" at video.vhd(654)
...
und so weiter für alle ram-Elemente.

Quartus scheint also irgendwie der Ansicht zu sein, daß we_a und we_b 
gleichzeitig '1' seien oder werden könnten. Genau das ist aber 
eigentlich ausgeschlossen, weil ich we_b ja fest auf '0' setze 
(schließlich will ich von der Video-Seite her ja gar nicht auf das 
Clut-RAM schreiben).
    b_cluts : block
        signal clut      : std_logic_vector(31 downto 0);
    begin
        i_clut_red : entity work.clut_ram
            generic map
            (
                DATA_WIDTH => 3,
                ADDR_WIDTH => 4
            )
            port map
            (
                clk => main_clk,
                pixel_clk => pixel_clk_i,
                addr_a => fb_adr(4 downto 1),
                addr_b => clut_adr(3 downto 0),
                data_a => fb_ad(26 downto 24),
                data_b => (others => '0'),
                we_a => clut_wr(0),
                we_b => '0',
                q_a => clut(26 downto 24),
                q_b => v_clut(23 downto 21)
            );
       -- hier kommt dasselbe nochmal für die Grün- und Blau-CLUT
    end block b_cluts;

Das ist nicht das erste Mal, daß ich ein RAM inferieren will und bisher 
hat's eigentlich immer geklappt. Hier ist mir überhaupt nicht klar, wo 
das Problem sein soll.

Hab' ich Tomaten auf den Augen, übersehe ich was? Und falls ja, was?

Dankeschön,
Markus

Autor: Burkhard K. (buks)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:
> weil ich we_b ja fest auf '0' setze

Warum beschreibst Du dann überhaupt ein RAM mit Dual write-Ports? Was 
passiert, wenn du die Anweisung
 ram(to_integer(unsigned(addr_b))) := data_b; 
durch
 Nul; 
 ersetzt oder den betreffenden If-Zweig ganz auskommentierst?

Zu shared Variables selbst kann ich wenig sagen - auch Xilinx hat ein 
Template HDL_Coding_Techniques/rams/ram_protected_sharedvar.vhd - dort 
wird eine Funktion ram.write() aufgerufen, die aus einem Package stammt 
das den ram_type als "protected" definiert.

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Burkhard K. schrieb:
> Warum beschreibst Du dann überhaupt ein RAM mit Dual write-Ports? Was
> passiert, wenn du die Anweisung ram(to_integer(unsigned(addr_b))) :=
> data_b; durch Nul;  ersetzt oder den betreffenden If-Zweig ganz
> auskommentierst?

Danke.

Dann läuft die Synthese durch, produziert aber einen Gatterdschungel 
statt RAM. Gerade das hat mich ja dazu bewegt, die Vorlage aus dem 
Quartus Template-Katalog zu nehmen.

Autor: Burkhard K. (buks)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit Quartus kenn ich mich gar nicht aus - keine Templates für 
Dual-Ported RAM mit zwei Clocks und lediglich einem Write-Enable?

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Burkhard K. schrieb:
> Mit Quartus kenn ich mich gar nicht aus - keine Templates für
> Dual-Ported RAM mit zwei Clocks und lediglich einem Write-Enable?

Für RAM gibt's auch noch "Megafunctions" (Wizard-generierte Objekte). 
Die probier' ich mal als nächstes. Ich bin bloß etwas irritiert, daß die 
mitgelieferten Templates anscheinend nicht (mehr?) das machen, was sie 
sollen.

Autor: Fpga K. (fpgakuechle) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:

>
>
> entity clut_ram is
>     generic
>     (
>         DATA_WIDTH : natural := 3;
>         ADDR_WIDTH : natural := 4
>     );

>     );
> end clut_ram;
> 
> architecture rtl of clut_ram is
>     -- Build a 2-D array type for the RAM
>     subtype word_t is std_logic_vector((DATA_WIDTH - 1) downto 0);
>     type memory_t is array(2 ** ADDR_WIDTH - 1 downto 0) of word_t;
> 
>     -- Declare the RAM
>     shared variable ram : memory_t;
> begin

> end rtl;
> 
> Das entstammt - fast ohne Änderungen - dem Quartus Templateverzeichnis
> und sollte eigentlich ein echtes Dual-Port RAM inferieren.

Welche Änderungen hast du vorgenommen?

> Hab' ich Tomaten auf den Augen, übersehe ich was? Und falls ja, was?

Ich finde die breite des RAM's mit 3 bit etwas "ungewöhnlich", Gibt es 3 
bit breiten RAM as Macro im cyclone?! Versuch 4 bit breite

MfG,

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

Bewertung
0 lesenswert
nicht lesenswert
Fpga K. schrieb:
> Welche Änderungen hast du vorgenommen?

Lediglich die Indizes ins RAM-Array als std_logic_vector umdefiniert 
(das waren im Template naturals) und die entsprechenden Typumwandlungen 
eingefügt.

Autor: Fpga K. (fpgakuechle) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:

> Lediglich die Indizes ins RAM-Array als std_logic_vector umdefiniert
> (das waren im Template naturals) und die entsprechenden Typumwandlungen
> eingefügt.

natural und std_logic_vector ist nicht dasselbe, wenn das template für 
das interferieren eines RAM's den adressbus als nichtnegative ganzzahl 
verlangt dann sollte/muss man das so hinnehmen. Es ist halt eine 
Hardwarebeschreibungssprache und RAM's werden nun mal als Feld mit Index 
downto 0 beschrieben. Und std_logic hat mehr Werte als nur '0' und '1'.

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Fpga K. schrieb:
> natural und std_logic_vector ist nicht dasselbe, wenn das template für
> das interferieren eines RAM's den adressbus als nichtnegative ganzzahl
> verlangt dann sollte/muss man das so hinnehmen. Es ist halt eine
> Hardwarebeschreibungssprache und RAM's werden nun mal als Feld mit Index
> downto 0 beschrieben. Und std_logic hat mehr Werte als nur '0' und '1'.

Das ist mir durchaus bekannt, bin aber ziemlich sicher, daß das Quartus 
einigermaßen wurscht war, als ich das das letzte Mal gemacht habe. Auch 
nach entsprechender Änderung kommt tatsächlich immer noch kein RAM raus.

Ich werde wohl auf die altsyncram-Komponente umsteigen, danke.

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:
> Ich werde wohl auf die altsyncram-Komponente umsteigen, danke.

Damit funktioniert's jetzt so, wie ich's haben wollte.

Trotzdem würde mich interessieren, warum das Altera-Template erstens 
Fehler bringt und zweitens (nach Korrektur jener) kein RAM inferiert.

Ist das Template nicht (mehr) für Cyclones gedacht?

Autor: Fpga K. (fpgakuechle) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:

> Trotzdem würde mich interessieren, warum das Altera-Template erstens
> Fehler bringt und zweitens (nach Korrektur jener) kein RAM inferiert.
>
> Ist das Template nicht (mehr) für Cyclones gedacht?

Es gehen halt nur die Konfigurationen die in der Doku genannt werden:
https://www.altera.com/en_US/pdfs/literature/hb/cy... 
p.9 Ein 2**nx3 ist nicht dabei und muss daher durch einen 
Gatterdschungel ersetzt werden.

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

Bewertung
0 lesenswert
nicht lesenswert
Fpga K. schrieb:
> Es gehen halt nur die Konfigurationen die in der Doku genannt werden:
> https://www.altera.com/en_US/pdfs/literature/hb/cy...
> p.9 Ein 2**nx3 ist nicht dabei und muss daher durch einen
> Gatterdschungel ersetzt werden.

Ich hab's auch mit x4 und x8 probiert. Gleiches Ergebnis, nur größerer 
Dschungel.

Autor: Fpga K. (fpgakuechle) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:
> Fpga K. schrieb:
>> Es gehen halt nur die Konfigurationen die in der Doku genannt werden:
>> https://www.altera.com/en_US/pdfs/literature/hb/cy...
>> p.9 Ein 2**nx3 ist nicht dabei und muss daher durch einen
>> Gatterdschungel ersetzt werden.
>
> Ich hab's auch mit x4 und x8 probiert. Gleiches Ergebnis, nur größerer
> Dschungel.

Pass mal die Addressbreite an, 11 bit addresse bei 4 bit word sollten 
genau einen M9K ausmachen. Und dann könnte es noch eine 
Synthese/mapperoption geben die entscheidet ob das speicherfeld aus M9K 
blöcken oder Gattern erzeugt wird.

PS:
Hab das was gefunden: 
http://www.alteraforum.com/forum/showthread.php?t=23183
"But Quartus, by default, does not use M9K blocks for such small 
memories. You can force it to, either by setting the "Allow Any RAM Size 
for Recognition" option (Analysis & Synthesis) or by specifying "M9K" as 
the ramstyle attribute."

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

Bewertung
0 lesenswert
nicht lesenswert
Fpga K. schrieb:
> Ein 2**nx3 ist nicht dabei und muss daher durch einen
> Gatterdschungel ersetzt werden.

Eigentlich müsste Quartus so flexibel sein,
bei 3 Bits z.B. 3 M9K etc. einzusetzen (oder
1 M9K und ein Bit verfallen lassen).
So werden ja auch bei 24 Bits (z.B. VGA) 3*x
M9Ks erzeugt.

Autor: Fpga K. (fpgakuechle) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sigi schrieb:
> Fpga K. schrieb:
>> Ein 2**nx3 ist nicht dabei und muss daher durch einen
>> Gatterdschungel ersetzt werden.
>
> Eigentlich müsste Quartus so flexibel sein,
> bei 3 Bits z.B. 3 M9K etc. einzusetzen (oder
> 1 M9K und ein Bit verfallen lassen).


"Verfallen lassen" funktioniert nicht bei Hardwarebeschreibungssprachen, 
da muss man wohl schon angeben welche bitline man mit constant '0' (oder 
'1' )setzen will und welches bit vom Ausgangsbus man ignoriert. Und 
"nicht schreiben" geht nicht da werden immer 4 bit geschrieben (und 
verändern u.U. den Power-UP RAM-Inhalt.

Vielleicht liegt ja das Problem bei der "dead logic elimination"?!

Das Zusammensetzen aus mehrenen funktioniert dagegen oft gut, da ist ja 
auch das gesamte Verhalten eindeutig beschrieben (im Unterschied zu "Ich 
will nur 3 von bits aber ich sag dir nicht welche").

Autor: Sigi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Fpga K. schrieb:
> "Verfallen lassen" funktioniert nicht bei Hardwarebeschreibungssprachen,
> da muss man wohl schon angeben welche bitline man mit constant '0' (oder
> '1' )setzen will und welches bit vom Ausgangsbus man ignoriert. Und
> "nicht schreiben" geht nicht da werden immer 4 bit geschrieben (und
> verändern u.U. den Power-UP RAM-Inhalt.

Nenn's verfallen oder ungenutzt lassen, ist
ja vergleichbar mit der Situation, in der
z.B. nur 1000 Arrayelemente verwendet werden,
der Rest bleibt dann ungenutzt. Quartus jedenfalls
macht(e) das so.

Autor: Fpga K. (fpgakuechle) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sigi schrieb:
> Fpga K. schrieb:
>> "Verfallen lassen" funktioniert nicht bei Hardwarebeschreibungssprachen,
>> da muss man wohl schon angeben welche bitline man mit constant '0' (oder
>> '1' )setzen will und welches bit vom Ausgangsbus man ignoriert. Und
>> "nicht schreiben" geht nicht da werden immer 4 bit geschrieben (und
>> verändern u.U. den Power-UP RAM-Inhalt.
>
> Nenn's verfallen oder ungenutzt lassen, ist
> ja vergleichbar mit der Situation, in der
> z.B. nur 1000 Arrayelemente verwendet werden,
> der Rest bleibt dann ungenutzt. Quartus jedenfalls
> macht(e) das so.

Ich macht das aus Gewohnheit/Erfahrung anders. Ich beschreib den Ram so 
das genau das es genau eine Konfiguration gibt die daraus passt. Hier 
würde ich (wenn es denn ein M9K sein muss) ein 2*11x4 Feld definieren, 
und bei der Umwandlung in natural 7 adress bit und ein datenbit auf '0' 
festlegen. Halt so wie wenn ich es in Hardware aufbauen muesste und 
keine Eingänge "floaten lassen kann".

Autor: Markus F. (mfro)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Fpga K. schrieb:
> Hab das was gefunden:
> http://www.alteraforum.com/forum/showthread.php?t=23183
> "But Quartus, by default, does not use M9K blocks for such small
> memories. You can force it to, either by setting the "Allow Any RAM Size
> for Recognition" option (Analysis & Synthesis) or by specifying "M9K" as
> the ramstyle attribute."

Jetzt bin ich einerseits völlig irritiert (weil sich die Geschichte 
plötzlich nochmal anders verhält, andererseits aber auch beruhigt (weil 
es doch so funktioniert, wie ich dachte):

ich wollte gerade einen Riesenbeitrag mit allen möglichen Vermutungen 
über Fehler und Seltsamkeiten beim RAM-inferrieren schreiben und habe 
(um das vorher nochmal auszuprobieren) ein neues Projekt aufgesetzt (mit 
"import project settings from" von meinem eigentlichen, bei dem das 
Problem auftrat) und dort (nur) das Template noch mal reingenommen.

Und - (fast) egal was ich mache (für DW und AW alle möglichen und 
unmöglichen Kombinationen probiert) - es kommt 1. immer ein RAM raus und 
2. keine Fehler...
architecture rtl of vhdl_ram_test is
    signal ad              : std_logic_vector(31 downto 0);
    signal data_a          : std_logic_vector(31 downto 0);
    signal data_b          : std_logic_vector(31 downto 0);
    signal we_a            : std_logic;
    signal we_b            : std_logic;
    signal q_a             : std_logic_vector(31 downto 0);
    signal q_b             : std_logic_vector(31 downto 0);
    
    constant DW            : natural := 3;
    constant AW            : natural := 3;
begin
    i_dp_dc : entity work.true_dual_port_ram_dual_clock
        generic map
        (
            DATA_WIDTH => DW,
            ADDR_WIDTH => AW
        )
        port map
        (
            clk_a => main_clk,
            clk_b => main_clk,
            addr_a => to_integer(unsigned(fb_ad)),
            addr_b => to_integer(unsigned(ad)),
            data_a => data_a(DW - 1 downto 0),
            data_b => data_b(DW - 1 downto 0),
            we_a => we_a,
            we_b => we_b,
            q_a => q_a(DW - 1 downto 0),
            q_b => q_b(DW - 1 downto 0)
        );
end architecture rtl;


Hätte mal jemand einen Storch parat?

Autor: Jürgen S. (engineer) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:
> Ich werde wohl auf die altsyncram-Komponente umsteigen, danke.

Es ist eigentlich ziemlich egal, wie Du das RAM einbaust. Die tools sind 
inzwischen schlau genug, um block RAM gegen Zellen und umgekehrt 
auszutauschen, je nach Platz und Tempoanforderung. Früher war das 
anders: Wenn Du da ein RAM optimiert aufgebaut hast kam mitunter schon 
was Kleineres bei raus - besonders bei Xilinx habe ich da einige Effekte 
gehabt. Inzwischen sind die RAMs aber so groß und multifunktionell 
aufgezogen, dass es da kaum noch Optionen gibt: Das, was der Hersteller 
eingebaut hat, ist drin - ob es genutzt wird oder nicht. Von daher liegt 
da bei geringer Nutzung automatisch schon mal was brach. Lediglich in 
Einzelfällen kann man es ungeschickt programmieren, um nennenswerte 
Einbussen zu haben.

Wenn Du herstellerunabhängig sein möchtest, nimmst du einfach einen 
VHDL-wrapper für das RAM, das auch die jeweilige Primitive linkt.

Markus F. schrieb:
> Ist das Template nicht (mehr) für Cyclones gedacht?
Woher stammt denn das?

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jürgen S. schrieb:
> Woher stammt denn das?

Du hast bloß ein paar Beiträge im Fred gelesen, gell?

Das Problem ist mittlerweile gelöst. Danke trotzdem.

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.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

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