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
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.
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
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
> 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...
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
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:
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.
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
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.