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


von Markus F. (mfro)


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:
1
entity clut_ram is
2
    generic 
3
    (
4
        DATA_WIDTH : natural := 3;
5
        ADDR_WIDTH : natural := 4
6
    );
7
8
    port 
9
    (
10
        clk             : in std_logic;
11
        pixel_clk       : in std_logic;
12
        addr_a          : in std_logic_vector(ADDR_WIDTH - 1 downto 0);
13
        addr_b          : in std_logic_vector(ADDR_WIDTH - 1 downto 0);
14
        data_a          : in std_logic_vector((DATA_WIDTH - 1) downto 0);
15
        data_b          : in std_logic_vector((DATA_WIDTH - 1) downto 0);
16
        we_a            : in std_logic := '0';
17
        we_b            : in std_logic := '0';
18
        q_a             : out std_logic_vector((DATA_WIDTH - 1) downto 0);
19
        q_b             : out std_logic_vector((DATA_WIDTH - 1) downto 0)
20
    );
21
end clut_ram;
22
23
architecture rtl of clut_ram is
24
    -- Build a 2-D array type for the RAM
25
    subtype word_t is std_logic_vector((DATA_WIDTH - 1) downto 0);
26
    type memory_t is array(2 ** ADDR_WIDTH - 1 downto 0) of word_t;
27
28
    -- Declare the RAM 
29
    shared variable ram : memory_t;
30
begin
31
    -- Port A
32
    process(clk)
33
    begin
34
        if rising_edge(clk) then         -- Line 644
35
            if we_a = '1' then
36
                ram(to_integer(unsigned(addr_a))) := data_a;
37
            end if;
38
            q_a <= ram(to_integer(unsigned(addr_a)));
39
        end if;
40
    end process;
41
    -- Port B
42
    process(pixel_clk)
43
    begin
44
        if rising_edge(pixel_clk) then   -- Line 654
45
            if we_b = '1' then
46
                ram(to_integer(unsigned(addr_b))) := data_b;
47
            end if;
48
            q_b <= ram(to_integer(unsigned(addr_b)));
49
        end if;
50
    end process;
51
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).
1
    b_cluts : block
2
        signal clut      : std_logic_vector(31 downto 0);
3
    begin
4
        i_clut_red : entity work.clut_ram
5
            generic map
6
            (
7
                DATA_WIDTH => 3,
8
                ADDR_WIDTH => 4
9
            )
10
            port map
11
            (
12
                clk => main_clk,
13
                pixel_clk => pixel_clk_i,
14
                addr_a => fb_adr(4 downto 1),
15
                addr_b => clut_adr(3 downto 0),
16
                data_a => fb_ad(26 downto 24),
17
                data_b => (others => '0'),
18
                we_a => clut_wr(0),
19
                we_b => '0',
20
                q_a => clut(26 downto 24),
21
                q_b => v_clut(23 downto 21)
22
            );
23
       -- hier kommt dasselbe nochmal für die Grün- und Blau-CLUT
24
    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

von Burkhard K. (buks)


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
1
 ram(to_integer(unsigned(addr_b))) := data_b;
durch
1
 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.

von Markus F. (mfro)


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.

von Burkhard K. (buks)


Lesenswert?

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

von Markus F. (mfro)


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.

von Fpgakuechle K. (Gast)


Lesenswert?

Markus F. schrieb:

>
>
1
> entity clut_ram is
2
>     generic
3
>     (
4
>         DATA_WIDTH : natural := 3;
5
>         ADDR_WIDTH : natural := 4
6
>     );
7
8
>     );
9
> end clut_ram;
10
> 
11
> architecture rtl of clut_ram is
12
>     -- Build a 2-D array type for the RAM
13
>     subtype word_t is std_logic_vector((DATA_WIDTH - 1) downto 0);
14
>     type memory_t is array(2 ** ADDR_WIDTH - 1 downto 0) of word_t;
15
> 
16
>     -- Declare the RAM
17
>     shared variable ram : memory_t;
18
> begin
19
20
> end rtl;
21
>
> 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,

von Markus F. (mfro)


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.

von Fpgakuechle K. (Gast)


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

von Markus F. (mfro)


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.

von Markus F. (mfro)


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?

von Fpgakuechle K. (Gast)


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/cyc3/cyc3_ciii51004.pdf 
p.9 Ein 2**nx3 ist nicht dabei und muss daher durch einen 
Gatterdschungel ersetzt werden.

von Markus F. (mfro)


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/cyc3/cyc3_ciii51004.pdf
> 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.

von Fpgakuechle K. (Gast)


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/cyc3/cyc3_ciii51004.pdf
>> 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."

von Sigi (Gast)


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.

von Fpgakuechle K. (Gast)


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

von Sigi (Gast)


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.

von Fpgakuechle K. (Gast)


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

von Markus F. (mfro)


Angehängte Dateien:

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...
1
architecture rtl of vhdl_ram_test is
2
    signal ad              : std_logic_vector(31 downto 0);
3
    signal data_a          : std_logic_vector(31 downto 0);
4
    signal data_b          : std_logic_vector(31 downto 0);
5
    signal we_a            : std_logic;
6
    signal we_b            : std_logic;
7
    signal q_a             : std_logic_vector(31 downto 0);
8
    signal q_b             : std_logic_vector(31 downto 0);
9
    
10
    constant DW            : natural := 3;
11
    constant AW            : natural := 3;
12
begin
13
    i_dp_dc : entity work.true_dual_port_ram_dual_clock
14
        generic map
15
        (
16
            DATA_WIDTH => DW,
17
            ADDR_WIDTH => AW
18
        )
19
        port map
20
        (
21
            clk_a => main_clk,
22
            clk_b => main_clk,
23
            addr_a => to_integer(unsigned(fb_ad)),
24
            addr_b => to_integer(unsigned(ad)),
25
            data_a => data_a(DW - 1 downto 0),
26
            data_b => data_b(DW - 1 downto 0),
27
            we_a => we_a,
28
            we_b => we_b,
29
            q_a => q_a(DW - 1 downto 0),
30
            q_b => q_b(DW - 1 downto 0)
31
        );
32
end architecture rtl;


Hätte mal jemand einen Storch parat?

von J. S. (engineer) Benutzerseite


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?

von Markus F. (mfro)


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.

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.