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...
Ja verstehe;) So sieht mein Problem am Beispiel aus:
1 | constant ram_max : integer := 9; |
2 | type ram_type is array (0 to ram_max) of std_logic_vector (7 downto 0); |
3 | signal RAM : ram_type := (X"55", X"61", X"6c",X"75", X"65", X"20", X"35", X"3a", X"20", X"3f"); |
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.
Einfach zuweisen. Den Rest macht die Synthese.
1 | architecture rtl of dummy is |
2 | |
3 | constant ram_max : integer := 9; |
4 | type ram_type is array (0 to ram_max) of std_logic_vector (7 downto 0); |
5 | signal RAM : ram_type; |
6 | |
7 | begin
|
8 | |
9 | RAM <= (X"55", X"61", X"6c", X"75", X"65", X"20", X"35", X"3a", X"20", ram_val_cs); |
10 | |
11 | end architecture rtl; |
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:
1 | library IEEE; |
2 | use IEEE.STD_LOGIC_1164.ALL; |
3 | use IEEE.NUMERIC_STD.ALL; |
4 | |
5 | entity RamMux is |
6 | Port ( dinA : in STD_LOGIC_VECTOR (7 downto 0); |
7 | dinB : in STD_LOGIC_VECTOR (7 downto 0); |
8 | dout : out STD_LOGIC_VECTOR (7 downto 0); |
9 | addr : in STD_LOGIC_VECTOR (1 downto 0)); |
10 | end RamMux; |
11 | |
12 | architecture Behavioral of RamMux is |
13 | |
14 | type ram_type is array (0 to 3) of std_logic_vector (7 downto 0); |
15 | --signal RAM : ram_type := (X"55", X"AA", X"35", X"3a");
|
16 | signal RAM : ram_type := (X"55", X"AA", dinA, dinB); |
17 | |
18 | begin
|
19 | dout <= RAM(to_integer(unsigned(addr))); |
20 | end Behavioral; |
Bringt mit XST diese Warnings:
1 | 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...
:
Bearbeitet durch Moderator
Synplify Pro bricht mit einem Fehler ab und erzeugt gar nichts:
1 | @E: CL104 :"RamMux.vhd":17:47:17:50|Couldn't find binding for variable dina |
2 | @E: CL104 :"RamMux.vhd":17:47:17:50|Couldn't find binding for variable dinb |
Im "ROM-Fall" wird einfach Kombinatorik erzeugt (das mach XST dann aus diesem mickrigen 4-Byte-Rom letztendlich dann auch)...
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 VHDL beschreibt 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:
1 | library IEEE; |
2 | use IEEE.STD_LOGIC_1164.ALL; |
3 | use IEEE.NUMERIC_STD.ALL; |
4 | |
5 | entity RamMux is |
6 | Port ( dinA : in STD_LOGIC_VECTOR (7 downto 0); |
7 | dinB : in STD_LOGIC_VECTOR (7 downto 0); |
8 | dout : out STD_LOGIC_VECTOR (7 downto 0); |
9 | addr : in STD_LOGIC_VECTOR (1 downto 0)); |
10 | end RamMux; |
11 | |
12 | architecture Behavioral of RamMux is |
13 | |
14 | type ram_type is array (0 to 3) of std_logic_vector (7 downto 0); |
15 | signal RAM : ram_type; |
16 | begin
|
17 | |
18 | dout <= RAM(to_integer(unsigned(addr))); |
19 | RAM <= (X"55", X"AA", dinA, dinB); |
20 | end Behavioral; |
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...
:
Bearbeitet durch Moderator
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...
:
Bearbeitet durch Moderator
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.