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:
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
signalclut:std_logic_vector(31downto0);
3
begin
4
i_clut_red:entitywork.clut_ram
5
genericmap
6
(
7
DATA_WIDTH=>3,
8
ADDR_WIDTH=>4
9
)
10
portmap
11
(
12
clk=>main_clk,
13
pixel_clk=>pixel_clk_i,
14
addr_a=>fb_adr(4downto1),
15
addr_b=>clut_adr(3downto0),
16
data_a=>fb_ad(26downto24),
17
data_b=>(others=>'0'),
18
we_a=>clut_wr(0),
19
we_b=>'0',
20
q_a=>clut(26downto24),
21
q_b=>v_clut(23downto21)
22
);
23
-- hier kommt dasselbe nochmal für die Grün- und Blau-CLUT
24
endblockb_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
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.
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.
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.
> 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,
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.
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'.
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.
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?
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.
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."
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.
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").
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.
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".
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...
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?