Also ich hab ein array von vectoren und einige Werte in diesem array
sollen immer gleich sein. Deswegen initialisiere ich das array gleich
bei dessen Erstellung mit diesen Werten.
Einige Speicherfelder sollen jedoch den Inhalt separater Vectoren
weiterleiten und dabei hätte ich gerne, dass das array an diesen Stellen
nichts speichert. Ich will das array also als Adressraum benutzen, wobei
hinter einigen Indizes der array-Speicher liegt und hinter anderen
Eingänge oder Vectoren. Wie bekomm ich das hin? Ich hab schon versucht
don't-cares bei der Initialisierung einzubauen, aber es werden trotzdem
immer alle Speicherplätze erzeugt.
Du denkst viel zu abstrakt. Falls du aus deinem Design Hardware (FPGA
oder ASIC) machen willst, dann musst du deine Aufgabe für die zur
Verfügung stehenden Hardware beschreiben. Wenn die deine abstrakten
Arrays (oder wie auch immer) nicht abbilden kann, sondern einen Speicher
draus macht, dann hast du die falsche Hardware oder die falsche
Beschreibung.
Man könnte natürlich alles auch mal an einem richtigen Beispiel
anschauen. Dann müsste man nicht die Prosa oben entschlüsseln. Denn:
lies deinen Post mal durch, als ob du dein Problem nicht kennen würdest.
Wirst du schlau daraus?
Ich vermute, du brauchst einen einfachen Adressdecoder, der im tiefsten
Inneren gar nichts mit Arrays oder Speichern zu tun hat...
Das array dient dazu Bytes zu speichern, die zu einem Display übertragen
werden. Alle Bytes bis auf das Letzte werden nie verändert. Nur RAM(9)
wird durch ein von außen übertragenes Byte ersetzt.
1
RAM(9)<=ram_val_cs;
Um FFs zu sparen möchte ich vermeiden, direkt das array oben zu
benutzen, da ich den Wert von RAM(9) ja sowieso in ram_val_cs
gespeichert hab.
Das ist wirklich nicht sehr hardwarenah gedacht, denn anschließend gehe
ich mit einem counter das array durch gebe nacheinander alle array-Werte
aus. Wie ließe sich diese Funktion anders erreichen? (Mit hardwarenaher
Denkweise, wobei man genau weiß was aus dem Code synthetisiert wird)
Daniel R. schrieb:> Um FFs zu sparen möchte ich vermeiden, direkt das array oben zu> benutzen, da ich den Wert von RAM(9) ja sowieso in ram_val_cs> gespeichert hab.
Du wirst einen Adressdecoder und einen Multiplexer brauchen. Ein Array
in dieser Größe wird wahrscheinlich sowieso als Distributed RAM
angelegt. Ein ganzer RAM-Block wäre dafür fast zu schade...
Ja im Moment ist es Distrubuted RAM. Meines Wissens nach braucht Block
RAM auch einen Takt zum Auslesen. Ich habe eigentlich vor den Code so
anzupassen, dass eine Block-RAM inference rauskommt. Denn die vielen
Block-RAM-Blöcke die ich zur Verfügung hab, brauche ich erstens für
nichts und außerdem umfasst mein array eine Vielzahl solcher Zeilen wie
oben dargestellt.
Im Grunde sieht es so aus, dass ich meine Schaltung mittels Multiplexer
konfigurierbar halten möchte. Gesteuert wird ein Multiplexer von FFs und
genau deren Inhalt will ich in den Ausgabespeicher mappen (in das
array).
Ich werde mir wohl einen Block-ROM erstellen lassen und hab dann
default-Werte in den Speicherstellen, deren Inhalt ich von einem
Adressdekoder beim Auslesen von woanders herholen lasse.
Was war denn damit gemeint, dass ich einen Multiplexer brauche?
Daniel R. schrieb:> Was war denn damit gemeint, dass ich einen Multiplexer brauche?
Wenn du keine Latency haben willst, dann musst du das ram_val_cs ja am
eigentlichen RAM vorbei direkt auf den "Ausgang" schleusen. Diese
Umschaltung macht ein Multiplexer.
Du könntest natürlich auch ein DPRAM instantiieren und den ram_val_cs
vom einen Port an die Adresse 9 speichern, und vom anderen Prot aus
lesen. Aber das ist wie gesagt mit Verzögerung verbunden, weil das BRAM
ja getaktet ist.
Ottmar schrieb:> Einfach zuweisen. Den Rest macht die Synthese.> architecture rtl of dummy is>> constant ram_max : integer := 9;> type ram_type is array (0 to ram_max) of std_logic_vector (7 downto> 0);> signal RAM : ram_type;>> begin>> RAM <= (X"55", X"61", X"6c", X"75", X"65", X"20", X"35", X"3a", X"20",> ram_val_cs);>> end architecture rtl;
Interessanter Vorschlag, wenn das so funktioniert: Daumen hoch!
Mich wuerden aber bei sowas schon die Details wie das in HW
implementiert wird interessieren. Prinzipiell wuerde ich eher Lothars
Vorschlag oben (dual-ported RAM) zustimmen. Man hat's dann besser unter
Kontrolle...
Und: Meist ist der 'read' aus einem RAM/ROM der kritische Teil (vor
allem wenn's dann noch ueber ein externes IF zu z.B. einem uC geht), das
schreiben in ein Array kann dann meist auch mal um ein paar CLK-Zyklen
verzoegert werden (um das Timing hinzubekommen)
berndl schrieb:> Mich wuerden aber bei sowas schon die Details wie das in HW> implementiert wird interessieren.
Ohne Optimierung sind für das obige statement acht 5er-LUT's zu
erwarten. Inputs der 5er-LUT: 4bit index für RAM und 1 bit von
ram_val_cs.
Ich glaube die Bezeichnung des Signals "RAM" hat die Diskusion etwas auf
die Speicherzellenschiene gleitet.
Ottmar schrieb:> Einfach zuweisen. Den Rest macht die Synthese.>>
1
>...X"20",ram_val_cs);
2
>
Also das finde ich ja höchst interessant.
Ich wusste gar nicht dass LUTs auch Eingangswerte direkt ausgeben können
(also Eingänge durchleiten). Dachte eine LUT wird einmal konfiguriert
und gibt dann nur die festen Werte aus...
Diese Lösung wäre wohl wohl wesentlich performanter, als ein
selbstgebauter Adressdekoder.
Daniel R. schrieb:> Ich wusste gar nicht dass LUTs auch Eingangswerte direkt ausgeben können
Wäre mir auch neu :-)
Daniel R. schrieb:> Diese Lösung wäre wohl wohl wesentlich performanter, als ein> selbstgebauter Adressdekoder.
Vermutlich nicht.
Denn wenn das Konstrukt überhaupt umsetzbar ist, dann auch nur mit den
Möglichkeiten, die die Hardware bietet. Und diese Möglichkeiten kannst
du dann auch manuell instanziieren.
Und da bin ich ganz Berndls Meinung: Ich würde solche wilden Konstrukte
(wenn sie überhaupt synthetisierbar sind) nicht blind dem Können des
Synthesizers überlassen.
berndl schrieb:> Interessanter Vorschlag, wenn das so funktioniert: Daumen hoch!
Das muss ich auch mal ausprobieren. Ich bin gespannt, was dabei mit
unterschiedlichen Synthesetools rauskommt...
Schlumpf schrieb:> Wäre mir auch neu :-)
Also stimmt die Rechnung mit 5 LUTs nicht? Oder doch?
Was könnte denn sonst anderes bei der Synthese rauskommen?
Schlumpf schrieb:> Vermutlich nicht.> Denn wenn das Konstrukt überhaupt umsetzbar ist, dann auch nur mit den> Möglichkeiten, die die Hardware bietet. Und diese Möglichkeiten kannst> du dann auch manuell instanziieren.
Also "mein" Adressdekoder würde wohl nur aus einigen Multiplexern
bestehen (bei Distributed RAM).
Bei Benutzung von Block RAM müsste man zusätzlich die Adresse aus LUTs
holen und einen Takt, bis ausgelesen wurde, warten.
Ob sich der Aufwand, der für die Einbindung eines Block RAMs nötig ist
lohnt, hängt also ganz davon ab wie groß mein Array mal werden soll und
wie weit Anzahl der benötigten FFs ansteigt.
Ich glaube ich hab das mit den 5 LUT-Eingängen verstanden. Man hat ja
eine LUT pro Ausgabe-Datenbit also 8 Stück. Zeigt die Adresse die an der
LUT anliegt auf die letzte Adresse (also 9 = "1001" als Adresse von
ram_val_cs) dann liegt an der LUT tatsächlich aber nicht "01001" für
Index=9 an, sondern "1001X" wobei 'X' ram_val_cs ist. In der LUT wären
dann folgende Zuweisungen fest gespeichert: "10010" = 0 und "10011" = 1.
Es wurde also die Anzahl der Adressen verdoppelt um eine
"Eingangsvariable" der LUT zu simulieren.
Und alle fest gespeicherten Werte sind doppelt in der LUT gespeichert,
da unabhängig von ram_val_cs. Also einmal unter der Adresse mit
letztes-Bit=0 und einmal unter letztes-Bit=1.
;)
Ottmar schrieb:> Einfach zuweisen. Den Rest macht die Synthese.
Beweisen!
Mit Xilinx klappt das nicht (wäre auch zu schön gewesen...).
Diese sehr simple Beschreibung hier:
WARNING:Xst:2094 - "RamMux.vhd" line 16: Default value is ignored for signal <RAM>.
2
WARNING:Xst:2094 - "RamMux.vhd" line 16: Default value is ignored for signal <RAM>.
3
WARNING:Xst:647 - Input <dinA> is never used. This port will be preserved and left unconnected if it belongs to a top-level block or it belongs to a sub-block and the hierarchy of this sub-block is preserved.
4
WARNING:Xst:647 - Input <dinB> is never used. This port will be preserved and left unconnected if it belongs to a top-level block or it belongs to a sub-block and the hierarchy of this sub-block is preserved.
5
WARNING:Xst:653 - Signal <RAM> is used but never assigned. This sourceless signal will be automatically connected to value 00000000.
Und es wird auch nichts synthetisiert, sondern wegen der nicht
umsetzbaren beiden variablen Initwerte gleich der komplette Initstring
ignoriert. Heraus kommen nur Nullen.
Im Gegentest ohne die beiden Ports im Init-String wird wenigstens wie
erwartet noch ein ROM instatiiert...
Ohne es ausprobiert zu haben, aber beim Vorschlag von Ottmar wird die
Zuweisung ja nicht als Initwert verwendet sondern als nebenläufige
Anweisung. Warum soll das nicht funktionieren?
VHDL hotline schrieb im Beitrag #3858187:
> Warum soll das nicht funktionieren?
Weil das der Synthesizer gar nicht kann. Du kannst nicht ein komplettes
RAM parallel zuweisen. Wie sollte denn so ein RAM-Baustein aussehen, wo
massiv parallel auf alles zugegriffen werden kann?
Mit VHDLbeschreibt man die Hardware, von der man sich vorher ein
Bild gemacht hat. Auf einem Blatt Papier oder im Kopf...
VHDL hotline schrieb im Beitrag #3858187:
> Ohne es ausprobiert zu haben
Tus doch, Ist nicht viel Aufwand...
Ok, mach ich das mal:
Daraus wird reinste pure Kombinatorik mit einem Multiplexer. Je größer
der Multiplexer wird, umso mehr (teure) LUTs "gehen drauf". Es wird aber
eben kein RAM-Block verwendet...
Mir ging es nicht um die Verwendung eines RAM-Blocks sondern um das
reine Sprachkonstrukt, das vorgeschlagen wurde. Ottmar hat ja selbst
geschrieben, dass daraus LUTs werden. Das läuft, wie du festgestellt
hast, durch und hat am Ende die (durch das VHDL-Sprachkonstrukt)
beschriebene Funktionalität.
Um auch mal was zum Thema beizutragen: So wie ich es verstehe, geht es
um x-mal 10 Bytes, die irgendwohin übertragen werden, wobei die ersten 9
Bytes für jeden 10-Byte Vektor konstant sind. Die ersten x-mal 9 Bytes
kann man quasi als ROM ablegen und das letzte Byte live bei der Ausgabe
dran hängen (ohne es vorher in einem RAM abzulegen). Falls das letzte
Byte nicht live verfügbar ist, einen (kleineren) RAM daneben, welcher
nur die x-mal 1 Byte (möglicherweise mehrere in einem RAM-Wort, je nach
RAM-Architektur) speichert und dorther holen.
Ob das so möglich ist, geht aus der Aufgabenbeschreibung des
Fragestellers für mich nicht so ganz hervor.
VHDL hotline schrieb im Beitrag #3858338:
> Ob das so möglich ist, geht aus der Aufgabenbeschreibung des> Fragestellers für mich nicht so ganz hervor.
Was ich brauche ist am Besten einen BRAM, der die bei Arraydefinition
angegebenen Werte unveränderbar speichert (wie ein ROM) und einen
Adressdekoder, der die veränderbaren Werte in den Adressbereich des BRAM
mappt. Im Grunde will ich einen Speicher, der Strings speichert und mir
Bezeichner und aktuellen Wert zurückgibt. Zum Beispiel "Wert 1: ?" wobei
das ? einem 0x00 oder 0x01 Byte entspricht je nachdem wie der aktuelle
Status eines gewissen FFs ist.
Jetzt verstehe ich was du willst. Du speicherst ASCII-Bytes und willst,
dass der erste Teil der Ausgabe größtenteils immer gleich bleibt (Uart 0
: ? bis Uart 50 : z.B.).
Dafür brauchst du keinen Speicher, da reicht ein konstanter Vektor in
dem du bei der Ausgabe ein paar Bytes anpasst (nämlich die
Adressinformation 0 bis 50 als ASCII).
VHDL hotline schrieb im Beitrag #3858374:
> Dafür brauchst du keinen Speicher, da reicht ein konstanter Vektor
Die Frage ist nun aber, wo der am ressourcenschonendsten abgelegt
wird.
In aller Regel wird das ein RAM-Block sein.
Wenn man sich nicht ganz ungeschickt anstellt, kann man dieses RAM
anfangs auch zur Initialisierung verwenden:
Beitrag "Re: EA DOG-M initialisieren"
Ja, natürlich ist ein RAM am ressourcenschondensten, wenn die ersten 9
Bytes für jeden der 10-Byte-Vektoren unterschiedlich sind. So wie ich
das verstehe, sind die aber für alle 10-Byte-Vektoren fast gleich
(nämlich "UART 0-x :" als ASCII)? Das heißt, man braucht genau einmal
(weniger als) 9 Bytes in LUTs. Wenn man einen 18Kb Block-RAM übrig hat,
kann man das sicherlich darin speichern. Wäre aber im Vergleich zu einem
std_logic_vector(71 downto 0):="..." nicht meine erste Wahl.
Es steht noch nicht fest wie ähnlich die Textzeilen sein werden. Um auch
total unterschiedliche speichern zu können gehe ich jetzt davon aus,
dass sie unterschiedliche Längen und Inhalte haben.
Ablegen würde ich sie im BRAM deshalb, da ich gedenke die übrige
Hardware des FPGAs soweit möglich auszunutzen.
In Modelsim kann ich die Initialwerte des Arrays sogar schon aus einer
Textdatei einlesen. Nur XST macht da leider nicht mit.
Du willst also ein RAM, gut, dann nimm ein RAM ;-) . Gerade die Länge
des Inhaltes ist allerdings schon relativ wichtig. Bei unterschiedlichen
Längen einfach nacheinander abgespeichert wird die Adressierung außer
herum etwas umständlicher. Wenn du so etwas wie eine Maximallänge
definierst, kannst du die Addressierung einheitlicher halten und bei
evtl. kürzeren Vektoren Speicherbereiche einfach nicht benutzen.
Eventuell ist etwas, um das du dir erstmal Gedanken machen solltest,
eine Art Speicherkonzept. Wie du also einen Eingangswert (eine Anfrage
nach einem bestimmten Wert, der auch über UART kommt vermutlich?) auf
eine RAM-Adresse mapst, was genau (Wortbreite)/wieviele (Adressbreite)
RAM-Worte dann im RAM stehen sollen, ob du z.B. die Länge gleich mit im
RAM abspeicherst usw. .
VHDL hotline schrieb im Beitrag #3858514:
> Gerade die Länge des Inhaltes ist allerdings schon relativ wichtig.
Es sollte idealerweise eine auf eine Zweierpotenz passende Länge sein:
16, 32, 64 Bytes. Wenn es "nur" 20 Bytes sind (z.B. 2x20 Display), dann
ist es trotzdem überlegenswert, für jede Zeile 32 Bytes zu verwenden.
Das, was dort "verschwendet" wird, lässt sich anderweitig durch eine
vereinfachte Adressierung schnell wieder herausholen...
Du könntest auch zwei BRAMs nehmen. Wobei in der einen immer in 32 byte
Blöcken der String gespeichert ist. Dann noch ne Statemachine die einen
davon ausließt(bis zum terminierungbyte). Die unteren 5 adressbits
werden also als index genutzt (wegen der 32byte blöcke), die oberen n
adressbits geben den Index für den anderen BRAM an oder Register in
denen das variable ASCII Zeichen steht zum korrespondierenden string.
Somit kann man relativ Große Strings im BRAM speichern und die kleinen
Zeichen landen in LUT RAM oder auch in einem anderen BRAM der
komplizierter verdrahtet ist, weil die Zeichen nicht konstant sind und
dynamisch verändert werden. Da die BRAMs Dualport fähig sind ist das
aber auch relativ einfach hinzubekommen. Bei einem 0x00 im ersten BRAM
stellt die statemachine den Output Mux vom ersten BRAM auf den zweiten
um(dessen unteren adressinputs mit den oberen n adressinputs des ersten
verdrahtet sind). Beschrieben wird das zweite BRAM halt mit ner weiteren
statemachine mit einem Busy Flag der ersten verbunden ist, damit keine
glitches bzw. race konditions der einzelnen bits auftreten falls die
eine statemachine gerade etwas ausgibt und man versucht gleichzeitig
einen neuen Wert zu schreiben.
Das schöne ist man kann dann sehr einfach erweitern und kann die BRAMs
immer größer machen und viele Strings und Parameter darin ablegen. Für
mehrere dynasch veränderbare ASCII Zeichen muß die statemachine
natürlich etwas komlizierter sein. Kannst du ja mal aufzeichnen was ich
meine...
Wenn auf Grund der Tabellengröße eine kombinatorische Implementierung
ineffizient wird, kann man natürlich auf RAM's zurückgreifen.
Hierzu mein Vorschlag. Man legt eine ROM Tabelle an mit 9-bit breite für
die Character an. Das 9te bit fungiert als Umschalter. Ist es 0 werden
die unteren 8-bit direkt ausgegeben. Ist es 1 nutzt man die unteren bits
zur Auswahl der dynamischen Signale. Somit lässt sich über einen Index
entweder ein konstanter Wert oder eines der dynamischen Signale
auswählen.
In Pseudocode:
Gegeben seien:
ROM_im_RAM := ('0'&X"AA", ...., '1'&X"01")
dynamic_signals = (dyn1, dyn2, ... dynn)
Algo:
flag_and_character = ROM_im_RAM(x)
if flag_and_character(8)='1' then
dout = dynamic_signals(flag_and_character(7:0))
else
dout = flag_and_character(7:0)
end if
Ottmar schrieb:> Hierzu mein Vorschlag.
Meiner seit langem auch:
Lothar Miller schrieb:>>>> Du wirst einen Adressdecoder und einen Multiplexer brauchen.
Und ab dieser Erkenntnis führen tausend Wege nach ROM...