Forum: FPGA, VHDL & Co. Softcore mehrfach instanziieren


von VHDL hotline (Gast)


Lesenswert?

Es geht um die mehrfache Instanziierung eines softcore mit verschiedenen 
Programmen. Bei den softcores, mit denen ich bisher zu tun hatte 
(OpenRISC mor1kx, RISC-V neorv32) wird das Programm als VHDL-Konstante 
in einen BRAM  initialisiert. Man kompiliert also die ganze uC-source 
und den Befehlsspeicher mit der Initialisierung. Von Haus aus ist es 
zumindest bei den zweien anscheinend nicht vorgesehen, mehrere Instanzen 
des uC mit verschiedenen Befehlsspeichern zu haben (siehe z.B. [1],[2]). 
Idealerweise müsste man theoretisch nur einmal die uC-source (für Sim 
[Modelsim] / Syn [Vivado]) kompilieren und mehrere Befehlsspeicher 
generisch einbinden.

Meine Frage geht nun in die Richtung best practices, Anpassungen 
vorzunehmen, um mehrere softcore-Instanzen zu unterstützen. Bzw. 
eigentlich genauer: Wie kann ich möglichst aufwandsarm und sauber 
verschiedene Befehlsspeicher einbinden?

Meine Lösungsansätze bisher gehen in drei Richtungen.

#########

Zum einen wäre die Trennung über "namespaces", sprich man kompiliert 
jeweils die komplette uC-Source mit Befehlsspeicher in verschiedene 
libraries. Die sourcen muss man jeweils so umschreiben, dass z.B. statt 
"library neorv32" jeweils "library work" in den uC-sourcen verwendet 
wird und eine lib pro uC-Instanz zur Compilezeit festgelegt wird.

Sim: Modelsim kommt damit klar; Syn: Vivado will nicht eine Datei selben 
Namens in verschiedene libraries packen(?)

Vorteil: minimal invasiv bzgl. uC-source; Nachteil: man muss die 
uC-sourcen mehrmals in verschiedene libraries kompilieren

#########

Eine andere Möglichkeit ist, den Befehlsspeicher halbwegs generisch 
umzuschreiben. Entweder über den HDL-Weg, dass verschiedene 
Befehlsspeicher bzw. VHDL-Konstanten inkludiert werden oder über den 
"make"-Weg, wo man vor Sim/Syn mehrere passende Dateien erzeugt und 
Ersetzungen vornimmt.

HDL-Weg: zwar schöner, aber man muss die Änderungen dann immer wieder 
reinpatchen, wenn man sich Änderungen des softcore aus upstream holt; 
weiterhin müssen neben HDL tlw. auch die mitgelieferten Tools angepasst 
werden, die aus dem C-Kompilat den HDL-Befehlsspeicher erzeugen (oder 
das Resultat dieser Tools muss während des Bauprozesses nochmal 
nach-prozessiert werden)

make-Weg: fricklig, aber man muss die sourcen nicht direkt anfassen; man 
bestimmt von extern, welche uC-Instanz welches Programm bekommt, was 
schön ist, da man extern ja auch erstmal den C-Compiler anwirft und den 
output dann irgendwohin zuordnet, an genau dieser einen Stelle im 
workflow und nicht verteilt über makefile und HDL

#########

Für die Synthese habe ich den Weg, per Vivado-tcl-Befehlen den 
BRAM-Inhalt im Bitstream zu ändern. Dafür muss der Befehlsspeicher 
umgeschrieben werden, dass er nicht mehr inferiert wird, sondern per 
Primitiven instanziiert. Die Programmzuordnung für mehrere Instanzen 
würde damit funktionieren, eine Initialisierung per immer gleicher 
default-Konstante ist erstmal egal. Um dieses Prinzip für die Simulation 
zu übernehmen, bräuchte es dann aber einen dritten workflow (neben 
default HDL-Konstante und Vivado tcl), was ich unpraktisch finde.

Den BRAM-Update-Weg würde ich in diesem Thread sogar explizit außen vor 
lassen, da der etwas per Technologie-Workflow speziell löst, was ich 
eigentlich per HDL allgemein lösen wollen würde.

#########

Hat jemand Ideen, wie man die beschriebene mehrfache Instanziierung am 
besten macht? Vielleicht gibt es ja bei anderen softcores dafür schon 
gute Lösungen?


Beispiel für neorv32:

[1] 
https://github.com/stnolting/neorv32/blob/main/rtl/core/neorv32_application_image.vhd

[2] 
https://github.com/stnolting/neorv32/blob/main/rtl/core/mem/neorv32_imem.default.vhd

von Lexxi (Gast)


Lesenswert?

Ich glaube ich verstehe deinen Aufbau nicht so ganz... Wie sieht denn 
dein Multicore-Setup generell aus? Also wie reden die einzelnene Kerne 
miteinander?

Hängen die alle an einem Bus und haben Zugriff auf den gleichen 
Adressraum? Oder kommunizieren die über Mailboxen/FIFOs/Whatever und 
jeder Kern hat seinen eigenen Programmspeicher?

von VHDL hotline (Gast)


Lesenswert?

Lexxi schrieb:
> Wie sieht denn
> dein Multicore-Setup generell aus? Also wie reden die einzelnene Kerne
> miteinander?

Das sind mehrere unabhängige softcores in der FPGA fabric. Die können, 
müssen aber nicht miteinander reden. Bzgl. Befehls-/Datenspeicher und 
Adressraum hat jeder seinen eigenen.

Die Anwendung/Architektur ist für mein Problem nicht unbedingt wichtig. 
Mir geht es darum, wie ich mehrere dieser softcores mit verschiedener SW 
vom workflow her am besten in einem FPGA-Design instanziiert kriege.

von Lexxi (Gast)


Lesenswert?

Dann würde ich die Kern-ID per Generic bis zum Programmspeicher 
herunterreichen und dann darüber die jeweile "Memory-Init-Datei" 
auswählen. Die einzelnen init-Files vorher per Skript erzeugen lassen 
und als progmem_core_xx.vhd (xx = Kern ID) in das Projekt einfügen.

Dazu müsste man aber natürlich die Codebasis ändern... Ist vielleicht 
nicht die beste Idee, aber so würde ich das erstmal versuchen.

von uwe (Gast)


Lesenswert?

Jeder bekommt einen Bootloader und lädt sich aus einem Flash sein 
Programm.
Der Bootloader erlaubt das erneute Flashen.

von Lexxi (Gast)


Lesenswert?

Das ist sogar noch besser. Man müsste dann nur einen Kommunikation 
zwischen den Kernen ermöglichen, damit mat den Zugriff aufs Flash 
koordinieren kann (egal ob es ein globales SPI Module gibt oder jeder 
Kern sein eigenes hat).

Wenn Flash keine Option ist und du noch ordentlich viel Speicher im FPGA 
hast, könntest du auch alle Programme zu einem Paket kombinieren, in 
einem großen globalen Speicher ablegen und dann holt sich jeder Kern von 
da seinen Code - also kopiert den von da in den Kern-lokalen Speicher.

von Dieter (Gast)


Lesenswert?

Softcore ist anlocken mit leckeren Erdbeeren. Hardcore ist mit der 
Peitsche. Das funktioniert nur bei nicht hormongesteuerten µC nicht.

Lexxi schrieb:
> Wenn Flash keine Option ist und du noch ordentlich viel Speicher im FPGA
> hast, könntest du auch alle Programme zu einem Paket kombinieren, ...

Du könntest das Programm komprimiert ablegen im Flash. Jeder Kern holt 
sich dann sein Programm entpackt in den RAM. Die Häufigkeit in den Flash 
zu schreiben ist bekanntlich limitiert.

von Fitzebutze (Gast)


Lesenswert?

Als klassische State of the Art gilt u.A. die 
BF561-Doppelkern-Architektur. Da sollte man sich möglichst nah dran 
orientieren, wenn man nicht in exotische Prozessorgefilde abdriften 
will.
Die Frage ist immer, welcher Kern mit welchem Daten austauschen muss.
Jeweils zwei können über Shared Dual-Port-RAM Daten austauschen, wenn 
Peripherie im Spiel ist, empfiehlt sich Autobuffer-DMA, wo nur noch 
Deskriptorenlisten unterhalten werden müssen. Dann muss man die 
FIFO-Bufferqueues nur noch richtig in der Software 'verzahnen'.
Was die SW angeht: per Linkerscript alloziert man die gesamte Software 
inkl Libraries einfach in ein ELF-Binary, ein Loader muss dann die 
Adressbereiche in die entsprechenden CPU-Bänke mappen. Das ELF kannst du 
mit div. bestehenden Lösungen in VHDL für die einzelnen Bänke umwandeln, 
so dass ein vorinitialiertes TDP-Memory bei rauskommt (portabel). Ein 
CPU-Kern ist bei mir immer Master-Controller und lädt die anderen. Und 
um das alles auf Jahre hinweg robust unterhaltbar zu halten: 
Make-Lösung.

Ist ein bisschen die Frage, wieviele CPU-Kerne du da brauchst, ob der 
neorv32 die Performance bringt oder das eher auf einen Flaschenhals 
rausläuft und du andere Architekturen nehmen musst.

von VHDL hotline (Gast)


Lesenswert?

Danke für eure Antworten.

Lexxi schrieb:
> Dann würde ich die Kern-ID per Generic bis zum Programmspeicher
> herunterreichen und dann darüber die jeweile "Memory-Init-Datei"
> auswählen.

Ja, im Prinzip eine Anpassung der HDL wie im Eingangspost beschrieben 
mit allen damit verbundenen Vor- und Nachteilen.

uwe schrieb:
> Jeder bekommt einen Bootloader und lädt sich aus einem Flash sein
> Programm.

Lexxi schrieb:
> alle Programme zu einem Paket kombinieren, in
> einem großen globalen Speicher ablegen und dann holt sich jeder Kern von
> da seinen Code - also kopiert den von da in den Kern-lokalen Speicher.

Da ist kein Flash. Im einfachsten Fall soll es funktionieren, dass das 
Design per JTAG in den SRAM-basierten FPGA geladen wird und keine 
externe Peripherie benötigt.

Fitzebutze schrieb:
> Als klassische State of the Art gilt u.A. die
> BF561-Doppelkern-Architektur.

Ich möchte ja keine neue Architektur. Die Kerne brauchen noch nicht mal 
miteinander zu kommunizieren. Die sollen im einfachsten Fall nur ein 
paar einigermaßen flexible FSMs implementieren oder einen bestimmten 
performance-unkritischen Algorithmus bereitstellen.

Dabei finde ich es gerade vorteilhaft, wenn die Cores alle unabhängig 
voneinander sind, keine elf-files kombiniert werden müssen, ich die 
Standard-Tools des softcore verwenden kann usw. .

Auf was ich hoffe, ist etwas Richtung Kniff in VHDL, vielleicht ein 
geschickter wrapper für den Befehlsspeicher (ohne die eigentlichen 
sourcen anfassen zu müssen), oder irgendwas mit generic packages, eine 
minimale Anpassung der sourcen usw. . Ich bin ja bestimmt nicht der 
erste, der z.B. einen Softcore einfach zweimal für unabhängige Aufgaben 
in einem FPGA-Design einsetzen möchte. Die HDL-Beschreibung bzw. der 
default-workflow bei den o.g. cores verhindert das aber leider und ich 
suche einen best practice-Weg, das zu ändern.

von Lexxi (Gast)


Lesenswert?

Wenn du den neorv32 verwendest kannst du da ja mal auf GitHub im "Forum" 
fragen. Vielleicht hat einer eine Idee oder die können dein Feature 
direkt auf die TODO-Liste setzen.

von Fitzebutze (Gast)


Lesenswert?

VHDL hotline schrieb im Beitrag #7169146:
> Auf was ich hoffe, ist etwas Richtung Kniff in VHDL, vielleicht ein
> geschickter wrapper für den Befehlsspeicher (ohne die eigentlichen
> sourcen anfassen zu müssen), oder irgendwas mit generic packages, eine
> minimale Anpassung der sourcen usw. . Ich bin ja bestimmt nicht der
> erste, der z.B. einen Softcore einfach zweimal für unabhängige Aufgaben
> in einem FPGA-Design einsetzen möchte. Die HDL-Beschreibung bzw. der
> default-workflow bei den o.g. cores verhindert das aber leider und ich
> suche einen best practice-Weg, das zu ändern.

Ja, gemacht haben das schon einige. Wenn es aber nur um ROM-Generierung 
geht, hat dein Problem kaum noch mit Multicore-Architektur zu tun, 
sondern nur, wie du elegant das ROM als HDL generierst. Das wird hier 
mit Python-Skripten aus dem Opensource-Fundus 
(https://github.com/hackfin/MaSoCist) generiert. Aus dem ELF werden 
damit ROM-Files in VHDL die als generics übergeben werden. Damit wird 
auch das komplette Multicore-System (4 Kerne) gebaut und simuliert. Die 
Makefiles (an Linux kconfig angelehnt) muss man sich für seine Zwecke 
anpassen und seinen Wunsch-Core ev. selber anbinden. Wenn die Cores 
komplett unabhängig voneinander laufen sollen, spielt es auch nicht die 
grosse Rolle, welche CPU-Architektur man auswählt.

von VHDL hotline (Gast)


Lesenswert?

Fitzebutze schrieb:
> Ja, gemacht haben das schon einige. Wenn es aber nur um ROM-Generierung
> geht, hat dein Problem kaum noch mit Multicore-Architektur zu tun,
> sondern nur, wie du elegant das ROM als HDL generierst.

Genau, die Suche nach diesem eleganten Weg ist die Intention hier.

Fitzebutze schrieb:
> Das wird hier
> mit Python-Skripten aus dem Opensource-Fundus
> (https://github.com/hackfin/MaSoCist) generiert.

Danke, schau ich mir mal an.

von Christoph Z. (christophz)


Lesenswert?

VHDL hotline schrieb im Beitrag #7168597:
> Zum einen wäre die Trennung über "namespaces", sprich man kompiliert
> jeweils die komplette uC-Source mit Befehlsspeicher in verschiedene
> libraries. Die sourcen muss man jeweils so umschreiben, dass z.B. statt
> "library neorv32" jeweils "library work" in den uC-sourcen verwendet
> wird und eine lib pro uC-Instanz zur Compilezeit festgelegt wird.

Meinst du damit nicht so etwas wie die VHDL configurations?

Also dort, wo man angeben kann, welche genaue Instanz jetzt inferiert 
werden soll für einen Component. Damit sollte auch der Synthesizer klar 
kommen.

von VHDL hotline (Gast)


Lesenswert?

Christoph Z. schrieb:
> Meinst du damit nicht so etwas wie die VHDL configurations?
> Also dort, wo man angeben kann, welche genaue Instanz jetzt inferiert
> werden soll für einen Component. Damit sollte auch der Synthesizer klar
> kommen.

Du hast recht, mit configurations könnte das klappen. Die hatte ich gar 
nicht auf dem Schirm, zwar irgendwann im Studium mal gehört aber seitdem 
nie produktiv benutzt.

Danke für den Tipp!

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.