Forum: FPGA, VHDL & Co. VHDL, FPGA, RAM, Anfängerfrage


von Beckenrandschwimmer (Gast)


Lesenswert?

Hallo,

nachdem ich nun die ersten 200 Seiten vom Reichardt/Schwarz gelesen 
habe, ist es an der Zeit mal mit ein wenig eigenem Code zu beginnen.

Eine Teilaufgabe: Übertragung einiger Datensätze von einem uC auf den 
FPGA, über einen 8 Bit breiten Bus, getaktet durch ein CLK-Signal vom 
uC. Im Prinzip kein Problem, ich schicke zunächst einen Code, der angibt 
welchen Konfigurationswert ich schreiben will, danach den Wert. Das 
sieht dann wohl ganz grob so aus:

p1: process (CLK)
begin
  if rising_edge(CLK)
    case COMMAND is
      when C0 => D0 <= DATA;
      when C1 => D1 <= DATA;
      ...
    end case;
  end if;
end process p1

Soweit kein Problem, auch wenn ich vielleicht einige Syntax-Fehler 
getippt habe und sicher noch vieles beachten muss. Nur: Ich habe ca. 20 
Konfigurationswerte, die ich wahlweise setzen will, also auch 20 when 
Statements. Irgendwo anders werden die Werte dann abgefragt, etwa

if D1 == 100 then ...

Nun könnte man ja auf die Idee kommen, die Werte statt in  gewöhnlichen 
Registern im Block- oder Distributed-RAM abzulegen, der Code wäre dann 
kürzer:

p1: process (CLK)
begin
  RAM(COMMAND) <= Data;
end process p1

if RAM(C1) == 100 then ...

Bei einem uC würde man es so machen, Zugriff auf ein ARRAY-Element mit 
konstantem Index entspricht ja praktisch dem Zugriff auf eine 
gewöhnliche Variable. Aber bei VHDL? "RAM(C1) == 100" (C1 ist Konstante) 
könnte schon deutlich aufwendiger/langsamer sein als "D1 == 100"...

Und noch eine kurze Anschussfrage: Wenn ich Block- oder Distributed-RAM 
im FPGA verwende, etwa 16 bit breit. Kann ich dann einen Unterbereich 
ansprechen, etwa die unteren 8 Bit? Vielleicht mit

RAM(ADR) (7 downto 0) <= "01010101"

Gruß

Stefan

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


Lesenswert?

Beckenrandschwimmer schrieb:
> Übertragung einiger Datensätze von einem uC auf den FPGA, über
> einen 8 Bit breiten Bus, getaktet durch ein CLK-Signal vom uC.
Getaktet durch ein Taktsignal vom uC, das komplett unabhängig ist vom 
FPGA-Mastertakt? Dann solltest du dir einen Mechanismus zum sauberen 
Taktdomänenübergang ausdenken.

> könnte schon deutlich aufwendiger/langsamer sein als "D1 == 100"...
Warum? Das wird der Synthesizer schon richten ;-)
Und: was spricht gegen Ausprobieren?

> Wenn ich Block- oder Distributed-RAM im FPGA verwende, etwa 16 bit breit. > Kann 
ich dann einen Unterbereich ansprechen, etwa die unteren 8 Bit?
Nein. Du mußt immer die gesamte Breite des Ports beschreiben. Wenn du 
dort nur die unteren Bits manipulierst, dann wird für die oberen einfach 
der alte Wert oder ein Defaultwert ('0') genommen.

von Stefan Salewski (Gast)


Lesenswert?

Hallo Lothar Miller,

erstmal danke für die Antwort.

>Und: was spricht gegen Ausprobieren?
Zunächst bin ich grundsätzlich nicht so der Probiertyp -- lieber erstmal 
Lesen, Nachdenken, nochmal Lesen, dann evtl. Google Suche und Fragen. 
Ausprobieren dann ganz zum Schluss, wenn man es verstanden hat. In 
diesem Fall hätte ich das Probieren durchaus etwas vorgezogen -- nur 
habe ich dieses Xilinx Webpack derzeit nicht installiert und muss den 
Umgang damit demnächst auch noch etwas üben.

>Getaktet durch ein Taktsignal vom uC, das komplett unabhängig ist vom
>FPGA-Mastertakt? Dann solltest du dir einen Mechanismus zum sauberen
>Taktdomänenübergang ausdenken.

Ja, das ist wahr und mir klar. Wobei aber immer nur einer der Takte 
aktiv ist. Entweder der uC kommuniziert mit dem FPGA, dann ist der 
langsame Takt vom uC aktiv. Oder der FPGA liest Daten vom ADC und legt 
sie im RAM ab, dann setzt der FPGA ein RECORDING Flag -- der uC 
schweigt.

>> könnte schon deutlich aufwendiger/langsamer sein als "D1 == 100"...
>Warum? Das wird der Synthesizer schon richten ;-)

Das ist genau der Punkt, wo ich zweifel. Ich habe konkret dazu nicht 
viel gefunden. Eventuell handelt man sich durch RAM irgendwelche 
Nachteile ein. Bist Du denn wie ich auch der Ansicht, dass mein erster 
Code mit den ca. 20 "when" Statements recht umständlich/aufwendig ist. 
Oder sollte der Synthesizer das gut optimieren, vielleicht von selbst 
RAM verwenden? Im Prinzip ist das ja eine Art Demultiplexer, also nicht 
so total ungewöhnlich.

>> Wenn ich Block- oder Distributed-RAM im FPGA verwende, etwa 16 bit >>breit. 
Kann
>>ich dann einen Unterbereich ansprechen, etwa die unteren 8 Bit?
>Nein. Du mußt immer die gesamte Breite des Ports beschreiben. Wenn du
>dort nur die unteren Bits manipulierst, dann wird für die oberen einfach
>der alte Wert oder ein Defaultwert ('0') genommen.

Ich möchte in der Tat, dass die Bits, die ich nicht beschreibe, erhalten 
bleiben. Geht das?

Na demnächst werde ich mal versuchen einen kompletten VHDL-Code zu 
posten, dann wird alles etwas konkreter -- und dieses Webpack Monster 
runterladen und installieren.

Gruß

Stefan

von Stefan Salewski (Gast)


Lesenswert?

Hallo Lothar,

>Getaktet durch ein Taktsignal vom uC, das komplett unabhängig ist vom
>FPGA-Mastertakt? Dann solltest du dir einen Mechanismus zum sauberen
>Taktdomänenübergang ausdenken.

Nachdem ich nun ein paar Zeilen VHDL eingetipp habe, frage ich mich 
gerade, ob ich total auf dem Holzweg bin...

http://www.ssalewski.de/tmp/Start.vhdl

Mit dem inout Bus zum uC geht es so sicher nicht, dass ist mir gestern 
klar geworden, ich muss zunächst separate in und out Register definieren 
und die dann über einen Bustreiber zusammenfassen, wie bei 
Reichardt/Schwarz Code 4-4.

Aber zu den zwei Takten: Soll man ja eigentlich vermeiden, aber was 
bleibt mir übrig? Ich habe die ADC, die mit 100 MHz laufen, deren 
Taktausgang (100 MHz) geht an den FPGA, der mit dieser Frequenz die 
Daten von den ADCs liest und ins BLOCK-RAM oder externes RAM schreibt. 
Und wenn er fertig ist, dann will der uC die Daten auslesen, viel 
langsamer, und über USB zum PC schicken. Den Takt dazu erzeugt der uC 
selbst, es ist auch kein wirklich periodischer Takt, der uC legt z.B. 
ein Wort an den 8 Bit Bus und zeigt dann durch toggeln des Taktpins, 
dass der FPGA das Wort übernehmen soll.

Ich sehe momentan keine bessere Lösung. Ach ja, wo die Schaltpläne dazu 
liegen sollte ja bekannt sein: http://www.ssalewski.de/DAD.html.en

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


Lesenswert?

> Mit dem inout Bus zum uC geht es so sicher nicht, dass ist mir gestern
> klar geworden, ich muss zunächst separate in und out Register definieren
> und die dann über einen Bustreiber zusammenfassen
Du kannst durchaus einen Tristate-Bus an den FPGA-Pins realisieren...

> Aber zu den zwei Takten: Soll man ja eigentlich vermeiden, aber was
> bleibt mir übrig?
Mit einem DP-RAM lässt sich die Situation an der Taktdomänengrenze 
signifikant entspannen.

> DATA_RAM_END : natural := DATA_RAM_START + DATA_RAM_SIZE
Willst du wirklich 257 Bytes Data_Ram (0 to 256)?
Ich werde mir den Code in nächster Zeit mal genauer anschauen...

> RAM_POINTER == DATA_RAM_END
Geht das wirklich, dieses Doppelgleich?

>  RAM_POINTER <= RAM_POINTER + 1;
>  if RAM_POINTER == DATA_RAM_END then
>    RAM_POINTER <= DATA_RAM_START;
>  end if;
Wenn du die Sache am Anfang etwas weniger generisch angingest, würdest 
du sehen, dass hier ein einfacher Binärzähler, der automatisch 
überläuft, ideal wäre...

von Stefan Salewski (Gast)


Lesenswert?

Hallo Lothar,

>Ich werde mir den Code in nächster Zeit mal genauer anschauen...

Ja, das wäre schon ganz nett. Wobei ich zugeben muss, dass mein VHDL 
Text durchaus das Prädikat "dahingeschwiert" verdient. Mir geht es 
momentan hauptsächlich um das grobe Konzept und sinnvolle Ansätze. Nicht 
dass ich mich da in die total falsche Richtung verrenne.

>Willst du wirklich 257 Bytes Data_Ram (0 to 256)?

Nein, diese Feinheiten habe ich aber zunächst nicht so ernst genommen...

>Du kannst durchaus einen Tristate-Bus an den FPGA-Pins realisieren...

Ja, und das mache ich dann wohl am besten mit einem separaten 
Entity/Architecture Paar wie im Reichardt/Schwarz Code 4-4 BUSTREIBER.

>Geht das wirklich, dieses Doppelgleich?

Danke, muss ein einfaches = sein.

>Wenn du die Sache am Anfang etwas weniger generisch angingest, würdest
>du sehen, dass hier ein einfacher Binärzähler, der automatisch
>überläuft, ideal wäre...

Ist mir klar, nur bin ich dann auf Zweierpotenzen bei der RAM-Größe 
festgelegt -- was man wohl eh fast immer haben wird. Allerdings dachte 
ich, der einfache Test auf Gleichheit
"if RAM_POINTER = DATA_RAM_END" wäre vom Aufwand vernachlässigbar.

>Mit einem DP-RAM lässt sich die Situation an der Taktdomänengrenze
>signifikant entspannen.

Muss ich mal nachlesen. Aber wie ich schon schrieb, es ist ja immer nur 
ein Takt aktiv, das sollte daher nicht so kritisch sein. (Gleichzeitig 
Daten vom ADC im RAM abzulegen und über USB zu PC zu schicken würde auch 
kaum Sinn machen bzw. Vorteile bringen, denn die Datengenerierung von 
den ADCs ist viel schneller als das, was ich per FullSpeed USB 
wegschreiben könnte. Selbst die 40 MByte/s, die mit USB High-Speed 
möglich wären, würden da nicht viel beitragen. Und ich habe momentan nur 
den AT90USB mit FullSpeed. Anders wäre das bei langsamen 
Langzeitmessungen, aber das wäre dann eine spezielle Sonderanwendung.)

Gruß

Stefan

von Stefan Salewski (Gast)


Lesenswert?

>dass mein VHDL Text durchaus das Prädikat "dahingeschwiert" verdient.

OK, ein klein wenig aufgeräumt habe ich schon mal...

http://www.ssalewski.de/tmp/Start.vhdl

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


Lesenswert?

Hallo Stefan,
du machst deine Design eher Top-Down, nicht wahr?  ;-)

Welchen Bezug hat der CLK_UC zum Schreibsignal und zu den Daten?
Läuft der dauernd durch, oder gibt es nur 1 Takt, wenn etwas gelesen 
werden soll?
Solltest du dich nämlich mit dem BRAM anfreunden, dann hast du beim 
Lesen einen Takt Latency. Die Daten kommen also erst nach der nächsten 
Flanke. Damit muß der uC dann zurechtkommen.

Zum Thema bidirektionaler Buffer:
wenn das hier das einzige Modul ist (bzw. sein wird), in dem auf den 
UC-Datenbus zugegriffen wird, und zudem das Signal WE das 
Schreib/Lesesignal ist, dann solltest du es in etwa so machen:
1
   port ( UC_CLK, ADC_CLK, UC_WE, RESET: in std_logic;
2
          RECORDING: out std_logic;
3
          UC_DATA: inout std_logic_vector(BUS_UC_WIDTH - 1 downto 0); 
4
   :
5
   signal UC_DIN:  std_logic_vector(BUS_UC_WIDTH - 1 downto 0); -- Daten uC -> FPGA
6
   signal UC_DOUT: std_logic_vector(BUS_UC_WIDTH - 1 downto 0); -- Daten FPGA -> uC
7
   :
8
   -- Bustreiberumschaltung
9
   UC_DATA <= UC_DOUT when UC_WE='0' else (others=>'Z');
10
   UC_DIN  <= UC_DATA;
Das asynchrone Umschalten des IO-Treibers ist nötig, weil du sonst immer 
wieder einen Buskonflikt oder einen offenen Bus hast.

Ich sehe noch ein paar graue Haare für den ByteCounter. Wie wirst du den 
synchronisieren?

BTW: nimm doch statt der bit nur std_logic (und deren Vektoren). Bei 
bestimmten Funktionen könnte es sonst sein, dass du unnötig zwischen den 
beiden Datentypen hin- und herwandeln mußt.

von Stefan Salewski (Gast)


Lesenswert?

Hallo  Lothar,

>du machst deine Design eher Top-Down, nicht wahr?  ;-)

Grundsätzlich ja. Auch wenn es die Meisten hier wohl anders machen -- 
z.B. mit AVR-Studio mal eine Zeile etwas ändern, Compilieren und sehen 
was raus kommt. Warum sollte man auch erst Nachdenken...

Bottom-Up hat sicher auch seine Berechtigung, würde ich sogar machen, 
wenn ich meine bestückte Platine schon vorliegen hätte. Da mich ein 
Prototyp ca. 1000 Euro kostet, will ich zunächst etwas VHDL-Code 
entwerfen -- noch kann ich an der Hardware notfalls Änderungen machen. 
(Ich habe zwar ein Spartan3E Board von Digilent im Schrank liegen -- 
aber meine konkrete Anwendung kann ich damit nicht wirklich gut testen.)

Die Platine ist übrigens auch eher Top-Down entstanden. Wenn ich da jede 
Teil-Komponente zunächst auf Lochraster getestet hätte, dann wäre ich 
wohl nie fertig geworden. Aber mit der Platine habe ich eigentlich ein 
ganz gutes Gefühl. Störungen von den Schaltreglern könnten mir etwas zu 
viel "Rauschen" erzeugen, ja. Aber Hauptsache sie funktioniert 
überhaupt. Das andere Problem wäre der Frequenzgang der Reed-Relais -- 
hätte man eigentlich zu Anfang mal messen sollen, aber bis 50 MHz wird 
der Frequenzgang wohl glatt sein, mit etwas Glück bis 100 MHz. Pech 
wäre, wenn ich bei der Beschaltung des FPGA etwas falsch gemacht habe, 
dann muss ich gleich die nächste Platine ordern.

>Welchen Bezug hat der CLK_UC zum Schreibsignal und zu den Daten?
>Läuft der dauernd durch, oder gibt es nur 1 Takt, wenn etwas gelesen
>werden soll?

Letzteres hatte ich vor. Ich muss dazu sagen, dass ich mit der Anzahl 
der zur Datenübertragung zwischen FPGA und uC verwendbaren Pins nicht 
sehr großzügig ausgestattet bin. Ich möchte ein komplettes Port-Byte des 
uC für die eigentlichen Daten nehmen, und maximal 4 weitere Signale: 
CLK_UC, WE (Write_Enable), BUSY/RECORDING (output from FPGA when 
recording data) und vermutlich nötig RESET. USB Fullspeed kann ja 
maximal ca. 1 MByte/s übertragen, daher möchte ich zwichen FPGA und uC 
nicht wesentlich weniger Datendurchsatz haben. Wobei der uC mit 8MHz 
rennt.

>Ich sehe noch ein paar graue Haare für den ByteCounter. Wie wirst du den
>synchronisieren?

Die grauen Haare habe bzw. hatte ich auch -- bei einem Profi wie dir 
hätte ich sie eher nicht erwartet. Meine Lösung wäre: Ich schicke drei 
mal in Folge das Byte 0 über den 8 Bit breiten Bus -- da immer Adresse 
gefolgt von zwei Daten-Byte vom FPGA erwartet wird, ist so 
gewährleistet, dass ich an die Adresse 0 schreibe, und das wird vom FPGA 
als Reset erkannt, er setzt Byte-Counter auf 0. Könnte das so in etwa 
funktionieren? Eine andere Lösung, an die ich zunächst gedacht hatte: 
Pegeländerung von WE setzt Byte-Counter jeweils auf Null. Würde Sinn 
machen, mir ist aber nicht eingefallen, ob man das in VHDL realisieren 
kann.

>BTW: nimm doch statt der bit nur std_logic (und deren Vektoren). Bei
>bestimmten Funktionen könnte es sonst sein, dass du unnötig zwischen den
>beiden Datentypen hin- und herwandeln mußt.

Ja, danke für den Hinweis, aber das ist eher ein untergeordnetes 
Problem, später bei der Synthese sehe ich dann schon, welcher Typ am 
besten ist und welche Conversionen erforderlich sind.

>dann solltest du es in etwa so machen:

Darüber muss ich etwas nachdenken...

Danke für Deine Unterstützung.

Gruß

Stefan

von Stefan Salewski (Gast)


Lesenswert?

Hallo Lothar,

>Ich sehe noch ein paar graue Haare für den ByteCounter.

Ich könnte natürlich das CONF_RAM lediglich 8 Bit breit machen, und über 
ein Port-Signal jeweils anzeigen, ob ich die CONF_RAM-Adresse oder deren 
Inhalt schreiben bzw. lesen will. Dann brauche ich neben den 8 Bit für 
den Datenbus mindestens die externen Signale CLK_UC, WE, und ADR 
(Adresse lesen/schreiben). Eventuell noch RECORDING und RESET, aber ich 
habe die Vermutung, dass man die durch den Inhalt von Speicherzellen von 
CONF_RAM "simulieren" kann.

Klingt gut und einfacher.

Nur arbeite ich im FPGA eher mit 32 oder 16 Bit Werten wie PRETRIGGER 
usw. -- die muss ich dann wohl zunächst aus den einzelnen Bytes von 
CONF_RAM zusammensetzen. Aber gut. Das werde ich morgen mal probieren.

>Solltest du dich nämlich mit dem BRAM anfreunden, dann hast du beim
>Lesen einen Takt Latency.

BRAM==Block-Ram? Weiter oben hattest Du ja DP-RAM erwähnt, das ist wohl 
wieder etwas anderes, Dual-Ported vielleicht, muss ich mal nachlesen.

Gruß

Stefan

Halt, da fällt mir noch ein...

Wenn ich drei Signale habe, std_logic_vector oder bit_vector...

signal a: bit_vector (7 downto 0);
signal b: bit_vector (7 downto 0);
signal c: bit_vector (11 downto 0);

hat man Ausdrücke wie

c <= b(3 downto 0) & a(7 downto 0);

Soweit klar, wenn ich mich nicht in der Syntax vertan habe. Aber wenn 
die Anzahl der Bits von c nun durch eine Generics Anweisung bestimmt 
ist, und ich alle Bits von a und b einsammeln möchte, soweit sie eben in 
c hineinpassen? Wie schreibt man das dann hin. Kann man irgendwie eine 
Fallunterscheidung machen, ähnlich wie in C mit #ifdef. Oder führt man 
eine Variable ein, an die man a und b zunächst zuweist, und dann von der 
Variablen wieder die durch generics bestimmten bits an c? Schlecht 
formuliert von mir, aber sollte verständlich sein, und hier list ja 
ausser uns beiden eh keiner mehr mit.

von Steffen H. (avrsteffen)


Lesenswert?

Doch! Ich lese noch mit.. Aber wie gehts weiter?

@Stefan
Wie weit bist du mit deinem Projekt?

Grüße
Steffen H.

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.