Forum: FPGA, VHDL & Co. Von 12bit auf 8bit mittels LUT


von Andreas B. (loopy83)


Lesenswert?

Hallo,

ich stehe vor einem Problem und möchte mich hier mal erkundigen, wie man 
soetwas am besten umsetzt.

Ich habe zwei parallele Datenkanäle mit je 12bit Breite.
Beide Datenkanäle sollen mit Hilfe von LUTs auf 8bit herunter gerechnet 
werden. Ich möchte also nur 8bit weiter verarbeiten, aber die Dynamik 
von 12bit annähernd erhalten.

Nun scheitere ich daran, eine solche LUT zu erstellen.
Ich habe in einem anderen Thread mit auf den Weg bekommen, dass man dzau 
wohl (Block)RAM/ROM nehmen könnte/müsste.

Mein 12bit Eingangswert ist die Adresse, von der ich dann den 8bit Wert 
lese...

Nur wie genau erstelle ich einen solchen BlockRAM/ROM?
In den Templates finde ich nur einen mit 6bit Adresse, der auch nur 1bit 
als Ausgang hat.
Wie fülle ich einen solchen ROM am besten mit Daten, die dann nicht mehr 
verändert werden können?
Muss es ein DualPort sein, wenn ich die LUT ohne Neubeschreiben des 
FPGAs ändern möchte?

Wäre denn auch folgende Lösung denkbar?
Ich unterteile meinen 12bit Wertebereich in 5 Bereiche. Je nachdem in 
welchen Bereich der Eingangswert fällt, rechne ich ihn zum Ausgangswert 
um. Dazu bräuchte ich pro Wert z.B. folgenden Ausdruck:

Ausgangswert = (Eingangswert - 3074)/128 + 192

Dauert eine solche Rechnung mit STD_LOGIC_VECTOR extrem lange, oder ist 
sie sehr aufwendig, oder könnte ich damit den BlockRAM erschlagen?
Dann habe ich einfach 5 cases nach denen der Eingangswert seinem Wert 
entsprechend umgerechnet wird.
Dank der fünf Formeln, brauche ich dann zum Ändern der LUT nur diese zu 
ändern.

Ich wäre für Hinweise zu meinem Problem sehr dankbar!

Vielen Dank!
Andi

von user (Gast)


Lesenswert?

du brauchst ein blockram mit 4096 einträgen.

das kannst du als array in vhdl hinschreiben, und die synthese die 
arbeit machen lassen das ganze auf die kleineren blockrams verteilen 
lassen

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Andreas B. schrieb:
> Nur wie genau erstelle ich einen solchen BlockRAM/ROM?
1. Du instatiierst es manuell
2. Du beschreibst es in einer Hochsprache
3. Du nimmst den Core-Generator

Zu 2. sieh dir z.B. das an:
http://www.lothar-miller.de/s9y/categories/31-DDFS
Und lies den XST User Guide...

> Wie fülle ich einen solchen ROM am besten mit Daten, die dann nicht mehr
> verändert werden können?
Das kommt darauf an, welchen Weg du oben ausgewählt hast...

> Muss es ein DualPort sein, wenn ich die LUT ohne Neubeschreiben des
> FPGAs ändern möchte?
Nein, du kannst auch ein "normales" RAM beschreiben, solange du nicht 
gleichzeitig von einer anderen Adresse lesen willst.


> Ausgangswert = (Eingangswert - 3074)/128 + 192
> Dauert eine solche Rechnung mit STD_LOGIC_VECTOR extrem lange,
Genau 1 Takt, denn zum Glück willst du nur durch eine 2er Potenz teilen.

> oder ist sie sehr aufwendig,
Nein. Ich würde dir da so ca. 100MHz Taktfrequenz prognostizieren.

> Dauert eine solche Rechnung mit STD_LOGIC_VECTOR extrem lange
Rechne besser mit geeigneten Datentypen unsigned und signed (aus der 
ieee.numeric_std.all).

von Andreas B. (loopy83)


Lesenswert?

Lothar Miller schrieb:
> 3. Du nimmst den Core-Generator
Das habe ich getan, an die IP-Cores habe ich mal wieder nicht gedacht, 
sondern nur die Templates durchgeschaut. Irgendwann merke ich es mir und 
schaue nach, bevor ich frage, wie ich etwas erstelle.
Das habe ich nun getan mit 8bit mal 4096 Zeilen.

Nun habe ich deinem Bsp. folgenden Code entnommen:
1
  constant Sinus_Rom : Rom64x8 := (
2
    x"00",  x"03",  x"06",  x"09",  x"0c",  x"0f",  x"12",  x"15",
3
    x"18",  x"1b",  x"1e",  x"21",  x"24",  x"27",  x"2a",  x"2d",
4
    x"30",  x"33",  x"36",  x"39",  x"3b",  x"3e",  x"41",  x"43",
5
    x"46",  x"49",  x"4b",  x"4e",  x"50",  x"52",  x"55",  x"57",
6
    x"59",  x"5b",  x"5e",  x"60",  x"62",  x"64",  x"66",  x"67",
7
    x"69",  x"6b",  x"6c",  x"6e",  x"70",  x"71",  x"72",  x"74",
8
    x"75",  x"76",  x"77",  x"78",  x"79",  x"7a",  x"7b",  x"7b",
9
    x"7c",  x"7d",  x"7d",  x"7e",  x"7e",  x"7e",  x"7f",  x"7f");

Dem entnehme ich, dass ich eine Konstante händisch mit kommagetrennten 
Werten initialisieren kann, die ich dann sicher dem RAM übergeben kann.

Ich arbeite das erste mal mit BlockRAM und habe daher noch ein paar 
Fragen dazu:
1. Wie übergebe ich den Inhalt der Constanten dem RAM? Denn ich habe nur 
folgendes INSTANTIATION Template, mit dem ich auf den RAM zugreifen 
kann.
1
your_instance_name : RAM
2
port map (
3
  clka => clka,
4
  ena => ena,
5
  wea => wea,
6
  addra => addra,
7
  dina => dina,
8
  douta => douta);
2. wenn ich den Weg über die händische Definition der Konstanten 
vermeiden will, kann ich dann mit Hilfe einer for-Schleife, die die 
Adresse hochzählt, den RAM auch beim Start beschreiben lassen?

Vielen Dank!
Andi

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Andreas B. schrieb:
> kann ich dann mit Hilfe einer for-Schleife, die die
> Adresse hochzählt, den RAM auch beim Start beschreiben lassen?
Nur, wenn die Daten zu diesem Zeitpunkt statisch sind. Dann wird daraus 
einfach wieder der Inhalt für ein BRAM erzeugt...

Andreas B. schrieb:
> Ich arbeite das erste mal mit BlockRAM und habe daher noch ein paar
> Fragen dazu:
> 1. Wie übergebe ich den Inhalt der Constanten dem RAM?
Genau so:
> Dem entnehme ich, dass ich eine Konstante händisch mit kommagetrennten
> Werten initialisieren kann, die ich dann sicher dem RAM übergeben kann.
Diese Werte werden automatisch beim Laden des FPGAs in das BRAM 
geschrieben, und sind dort dann sofort verfügbar.

> Denn ich habe nur folgendes INSTANTIATION Template, mit dem ich
> auf den RAM zugreifen kann.
Das ist der Ansatz 1 vom 
Beitrag "Re: Von 12bit auf 8bit mittels LUT"

Und dort kannst du natürlich auch beim Anlegen der Instanz die Daten 
initialiseren. Weiteres verrät dir der XST User Guide (kommt dir der 
Begriff bekannt vor?).

Du solltest unbedingt darauf achten, nicht die 3 Implementierungsarten 
miteinander zu verknoten. Das endet im Chaos...

von tom (Gast)


Lesenswert?

Also an deiner Stelle würde ich einfach von den 12-Bit, die 4 
niederwertigsten nicht beachten und gut ists. Wenn du dann noch eine 
Korrektur per LUT machen willst, würde ich diese danach durchführen. 
Eine 8-Bit-LUT hat nur 256 Einträge gegenüber deinen 4096.

von mac4ever (Gast)


Lesenswert?

Da kann ich tom nur zustimmen. Ein einfache Schift-Operation sollte hier 
genügen. Und anschließend die Anpassung per LUT oder nach deiner o.g. 
Gleichung:
1
y = (x>>3) + 168   mit x=[0..255]

von Andreas B. (loopy83)


Lesenswert?

Hallo und vielen Dank,

die Vorgehensweise mit 8 aus 12 Bits habe ich schon versucht und die 
Dynamik ist leider nicht ausreichend. Wenn ich von 12 Bits 4 verwerfe, 
fehlt mir genau dieser Dynamikumfang, der mir noch fehlt. Daher möchte 
ich mittels LUT von 12 auf 8bit kommen.

Ich habe jetzt im Core Generator ein .coe File angelegt damit testweise 
den BRAM initialisiert. Nun muss ich das Ganze nur noch passend 
einbinden und testen.

Dennoch ist mir schleierhaft, wie ich den IPCore BRAM ohne .coe File 
initialisieren kann. In Lothar seinem Bsp. wird ja ein array vom Typ 
Rom64x8 erstellt und dann mittels Konstanten gefüllt. Da fehlt mir aber 
das Verständnis oder der Blick, wie die Zuordnung der Adressen gelöst 
wird. Oder kann man bei einem array automatisch mit array (11 downto 0) 
auf die STD_LOGIC_VECTOR Adressen zugreifen, ohne das man das extra 
festlegen muss?

Könnte ich dann mein Problem (feste, unveränderbare LUT vorausgesetzt) 
auch so lösen?
1
type Rom4096x8 is array (11 downto 0) of STD_LOGIC_VECTOR(7 downto 0); 
2
constant LUT_Rom : Rom4096x8 := (
3
    x"00",
4
    x"00",
5
    x"00",
6
    --und das ganze insgesamt 4096 mal, halt nur mit sinnvollen Werten
7
    x"00",
8
    x"00");
Hätte ich so auch ohne IPCore und RAM Generator einen ROM erstellt und 
könnte auf diesen mittels 12bit Adresse zugreifen?
1
data_out <= LUT_Rom(conv_integer(Adress_vector));

Der XST User Guide sagt mir freilich etwas und hält auch viele Beispiele 
bereit, aber dennoch habe ich immer meine Problem, den dort gezeigten 
VHDL Code komplett zu durchschauen. Oft sind es Kleinigkeiten oder 
irgendwelche mir unbekannten Kniffe, über die ich stolpere.

Vielen Dank!
Andi

von Christian R. (supachris)


Lesenswert?

Die Inferrierung klappt so, allerdings muss das Auslesen getaktet 
erfolgen, damit XST da einen BRAM Block draus macht. Die BRAM Speicher 
sind synchron. Du hast dann einen Takt Latenz, aber dafür einen BRAM.

Die entscheidende Stelle auf Lothars Seite:
1
process begin
2
     wait until rising_edge(CLK);
3
     RomAddr <= Address;          -- getaktete Adresse --> BRAM
4
   end process;

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Andreas B. schrieb:
> In Lothar seinem Bsp. ...
Der Dativ ist dem Genitiv sein Tod!   :-/
Du hättest schreiben dürfen:
In Lothars Beispiel...

> Oder kann man bei einem array automatisch mit array (11 downto 0)
> auf die STD_LOGIC_VECTOR Adressen zugreifen, ohne das man das extra
> festlegen muss?
Nein.
Man muß den vorher nach integer konvertieren, denn nur mit einem 
Integer darf in VHDL ein Array indiziert werden.

Wie schon von Christian gesagt muß die Adresse des so beschriebenen RAMs 
getaktet werden, damit tatsächlich ein BlockRAM ins Spiel kommt.
Wenn du das nicht tust, dann wird stattdessen Distributed RAM (in den 
LUTs) angelegt... :-o

BTW:
>>> conv_integer()
Nimm besser die numeric_std und ihre Casts & Konvertierungen
http://www.lothar-miller.de/s9y/archives/14-Numeric_Std.html

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.