Teil 3: Fast Linux von der SD-Karte auf RocketChip laden

Teil 3: Fast Linux von der SD-Karte auf RocketChip laden В vorherigen Abschnitt Es wurde ein mehr oder weniger funktionierender Speichercontroller implementiert, bzw. ein Wrapper über IP Core von Quartus, einem Adapter für TileLink. Heute sehen Sie im Abschnitt „Wir portieren RocketChip mit Cyclone auf ein wenig bekanntes chinesisches Board“ eine funktionierende Konsole. Der Prozess dauerte etwas länger: Ich dachte bereits, dass ich schnell Linux starten und weitermachen würde, aber das war nicht der Fall. In diesem Teil schlage ich vor, den Prozess des Startens von U-Boot, BBL und die zaghaften Initialisierungsversuche des Linux-Kernels zu betrachten. Aber es gibt eine Konsole – U-Boot, und sie ist ziemlich fortschrittlich und bietet viel von dem, was man von einer vollwertigen Konsole erwarten würde.

Zur Hardware gehören eine über die SPI-Schnittstelle angeschlossene SD-Karte sowie ein UART. Im Softwareteil wird BootROM durch ersetzt xip auf sdboot und tatsächlich wurden die folgenden Ladestufen hinzugefügt (auf der SD-Karte).

Fertigstellung der Hardware

Also, die Aufgabe: Sie müssen auf einen „großen“ Kern umsteigen und einen UART (von Raspberry) und einen SD-Adapter anschließen (wir haben eine Karte von Catalex mit sechs Pins verwendet: GND, VCC, MISO, MOSI, SCK, CS) .

Im Prinzip war alles ganz einfach. Aber bevor mir das klar wurde, wurde ich ein wenig hin und her geworfen: Nach dem letzten Mal kam ich zu dem Schluss, dass ich mich wieder einmischen musste System etwas wie HasPeripheryUART (und entsprechende Umsetzung), das Gleiche gilt für die SD-Karte – und schon ist alles fertig. Dann habe ich beschlossen, zu sehen, wie es in einem „seriösen“ Design umgesetzt wird. Also, was ist daran ernst? Arty passt offenbar nicht – das Monster bleibt unleahshed.DevKitConfigs. Und plötzlich stellte sich heraus, dass es überall Overlays gab, die über Parameter per Tasten hinzugefügt wurden. Ich vermute, dass dies wahrscheinlich sehr flexibel und konfigurierbar ist, aber ich möchte zumindest zuerst etwas ausführen ... Haben Sie nicht das Gleiche, nur einfacher und nerviger? Da bin ich rübergekommen vera.iofpga.FPGAChip für Microsemi-FPGAs und habe es sofort auseinandergenommen, um Angebote einzuholen, und versucht, meine eigene Implementierung analog zu erstellen. Glücklicherweise befindet sich mehr oder weniger das gesamte „Motherboard-Layout“ in einer Datei.

Es stellte sich heraus, dass man eigentlich nur hinzufügen muss System.scala Linien

class System(implicit p: Parameters) extends RocketSubsystem
...
  with HasPeripherySPI
  with HasPeripheryUART
...
{
  val tlclock = new FixedClockResource("tlclk", p(DevKitFPGAFrequencyKey))
  ...
}

class SystemModule[+L <: System](_outer: L)
  extends RocketSubsystemModuleImp(_outer)
...
    with HasPeripheryUARTModuleImp
    with HasPeripheryGPIOModuleImp
...

Zeile im Klassenkörper System Fügt der DTS-Datei Informationen über die Häufigkeit hinzu, mit der dieser Teil unseres SoC arbeitet. Soweit ich weiß, ist DTS/DTB ein statisches Analogon der Plug-and-Play-Technologie für eingebettete Geräte: Der DTS-Beschreibungsbaum wird in eine binäre DTB-Datei kompiliert und vom Bootloader an den Kernel übertragen, damit dieser ihn korrekt konfigurieren kann Hardware. Interessanterweise ohne die Zeile mit tlclock Alles synthetisiert perfekt, aber das Kompilieren von BootROM (ich möchte Sie daran erinnern, das wird jetzt schon sein sdboot) funktioniert nicht – während des Kompilierungsprozesses wird die DTS-Datei analysiert und ein Header mit einem Makro erstellt TL_CLK, dank dessen er in der Lage sein wird, Frequenzteiler für externe Schnittstellen korrekt zu konfigurieren.

Außerdem müssen Sie die „Verkabelung“ leicht korrigieren:

Platform.scala:

class PlatformIO(implicit val p: Parameters) extends Bundle {

...

  // UART
  io.uart_tx := sys.uart(0).txd
  sys.uart(0).rxd := RegNext(RegNext(io.uart_rx))

  // SD card
  io.sd_cs := sys.spi(0).cs(0)
  io.sd_sck := sys.spi(0).sck
  io.sd_mosi := sys.spi(0).dq(0).o
  sys.spi(0).dq(0).i := false.B
  sys.spi(0).dq(1).i := RegNext(RegNext(io.sd_miso))
  sys.spi(0).dq(2).i := false.B
  sys.spi(0).dq(3).i := false.B
}

Um ehrlich zu sein, wurden die Registerketten einfach analog zu einigen anderen Stellen im Originalcode hinzugefügt. Höchstwahrscheinlich sollten sie davor schützen Metastabilität. Vielleicht in einige Blöcke verfügen bereits über einen eigenen Schutz, aber zunächst möchte ich ihn zumindest „auf einem hohen Qualitätsniveau“ starten. Eine interessantere Frage für mich ist, warum MISO und MOSI unterschiedlich bleiben dq? Ich habe die Antwort noch nicht gefunden, aber es scheint, dass der Rest des Codes auf genau einer solchen Verbindung beruht.

Physisch habe ich einfach die Design-Pins den freien Kontakten am Block zugewiesen und den Spannungsauswahl-Jumper auf 3.3 V verschoben.

SD-Adapter

Sicht von oben:

Teil 3: Fast Linux von der SD-Karte auf RocketChip laden

Untersicht:

Teil 3: Fast Linux von der SD-Karte auf RocketChip laden

Software-Debugging: Tools

Lassen Sie uns zunächst über die verfügbaren Debugging-Tools und ihre Einschränkungen sprechen.

minicom

Zuerst müssen wir irgendwie lesen, was der Bootloader und der Kernel ausgeben. Um dies unter Linux (in diesem Fall auf dem RaspberryPi) zu tun, benötigen wir das Programm Minicom. Im Allgemeinen reicht jedes Programm aus, das mit einer seriellen Schnittstelle arbeitet.

Bitte beachten Sie, dass beim Start der Port-Gerätename als angegeben werden muss -D /dev/ttyS0 - nach der Option -D. Nun, die wichtigsten Informationen: Zum Beenden verwenden Ctrl-A, X. Ich hatte tatsächlich einen Fall, in dem diese Kombination nicht funktionierte – dann kann man einfach von einer benachbarten SSH-Sitzung aus sagen killall -KILL minicom.

Es gibt noch eine weitere Funktion. Konkret verfügt der RaspberryPi über zwei UARTs, und beide Ports können bereits für etwas angepasst werden: einer für Bluetooth, der andere gibt standardmäßig die Kernel-Konsole aus. Glücklicherweise kann dieses Verhalten überschrieben werden gemäß dieser Anleitung.

Speicher neu schreiben

Beim Debuggen musste ich manchmal eine Hypothese testen Bootloader laden (sorry) direkt vom Host in den RAM. Vielleicht geht das direkt aus GDB heraus, aber am Ende bin ich einen einfachen Weg gegangen: Ich habe die nötige Datei auf Raspberry kopiert, zusätzlich Port 4444 per SSH (Telnet von OpenOCD) weitergeleitet und den Befehl verwendet load_image. Wenn Sie es tun, scheint es, als wäre alles eingefroren, aber tatsächlich „Es schläft nicht, es blinkt nur langsam“: Es lädt die Datei herunter, und zwar mit einer Geschwindigkeit von ein paar Kilobyte pro Sekunde.

Funktionen zum Installieren von Haltepunkten

Viele Leute mussten beim Debuggen normaler Programme wahrscheinlich nicht darüber nachdenken, aber Haltepunkte werden nicht immer in der Hardware festgelegt. Manchmal erfordert das Setzen eines Haltepunkts das vorübergehende Aufschreiben spezieller Anweisungen an der richtigen Stelle direkt in Maschinencode. So funktionierte zum Beispiel mein Standardbefehl b im GDB. Folgendes folgt:

  • Sie können keinen Punkt in BootROM einfügen, da ROM
  • Sie können einen Haltepunkt für den von der SD-Karte in den RAM geladenen Code festlegen, müssen jedoch warten, bis er geladen ist. Andernfalls schreiben wir keinen Code neu, aber der Loader schreibt unseren Haltepunkt neu

Ich bin mir sicher, dass Sie ausdrücklich die Verwendung von Hardware-Haltepunkten anfordern können, aber die Anzahl davon ist ohnehin begrenzt.

Schneller BootROM-Austausch

In der Anfangsphase des Debuggens besteht häufig der Wunsch, das BootROM zu reparieren und es erneut zu versuchen. Es gibt jedoch ein Problem: BootROM ist Teil des Designs, das in das FPGA geladen wird, und seine Synthese dauert nur wenige Minuten (und das nach fast sofortiger Kompilierung des BootROM-Images selbst aus C und Assembler ...). Zum Glück in Wirklichkeit alles viel schneller: Die Reihenfolge der Aktionen ist wie folgt:

  • bootrom.mif neu generieren (Ich habe auf MIF statt auf HEX umgestellt, da ich immer Probleme mit HEX hatte und MIF das native Format von Alter ist)
  • in Quartus sagen Processing -> Update Memory Initialization File
  • Geben Sie im Assembler-Element (in der linken Spalte von Aufgaben) den Befehl „Neu starten“ ein

Alles über alles – ein paar Dutzend Sekunden.

Vorbereiten der SD-Karte

Hier ist alles relativ einfach, aber Sie müssen Geduld haben und etwa 14 GB Speicherplatz haben:

git clone https://github.com/sifive/freedom-u-sdk
git submodule update --recursive --init
make

Danach müssen Sie eine saubere, bzw. eine, die nichts Notwendiges enthält, SD-Karte einlegen und ausführen

sudo make DISK=/dev/sdX format-boot-loader

… Wo sdX — Gerät, das der Karte zugewiesen ist. ACHTUNG: Die Daten auf der Karte werden gelöscht, überschrieben und generell! Es lohnt sich kaum, die gesamte Montage von unten durchzuführen sudodenn dann gehören alle Build-Artefakte dazu root, und die Montage muss von unten erfolgen sudo ständig.

Das Ergebnis ist eine in GPT markierte Karte mit vier Partitionen, von denen eine über FAT verfügt uEnv.txt und ein bootfähiges Image im FIT-Format (es enthält mehrere Unterimages, jedes mit seiner eigenen Download-Adresse), die andere Partition ist leer, sie soll in Ext4 für Linux formatiert sein. Zwei weitere Abschnitte - geheimnisvoll: U-Boot lebt auf der einen Seite (sein Offset ist, soweit ich weiß, im BootROM fest einprogrammiert), auf der anderen Seite scheinen seine Umgebungsvariablen aktiv zu sein, aber ich verwende sie noch nicht.

Ebene eins, BootROM

Die Volksweisheit sagt: „Wenn es in der Programmierung Tänze mit einem Tamburin gibt, dann gibt es in der Elektronik auch Tänze mit einem Feuerlöscher.“ Es geht nicht einmal darum, dass ich einmal fast die Platine verbrannt hätte, als ich entschied: „Nun, GND ist der gleiche niedrige Pegel.“ (anscheinend würde ein Widerstand doch nicht schaden...) Es geht vielmehr darum, dass die Elektronik immer wieder für Überraschungen sorgt, wenn die Hände da nicht wachsen: Beim Einlöten des Steckers auf die Platine konnte ich die Kontakte immer noch nicht richtig verlöten – im Video ist zu sehen, wie sich das Lot direkt verteilt Tragen Sie einfach einen Lötkolben auf die gesamte Verbindung auf. Bei mir hat er nach dem Zufallsprinzip „geschlagen“. Na ja, vielleicht war das Lot nicht für die Temperatur des Lötkolbens geeignet, vielleicht auch etwas anderes ... Als ich sah, dass ich bereits ein Dutzend Kontakte hatte, gab ich im Allgemeinen auf und begann mit dem Debuggen. Und dann fing es an rätselhaft: Ich habe RX/TX von UART angeschlossen, ich lade die Firmware – heißt es

INIT
CMD0
ERROR

Nun, alles ist logisch - ich habe das SD-Kartenmodul nicht angeschlossen. Wir korrigieren die Situation, laden die Firmware... Und Stille... Warum habe ich es mir nicht anders überlegt, aber die kleine Box öffnete sich gerade: Einer der Modulpins musste mit VCC verbunden werden. In meinem Fall unterstützte das Modul 5 V zur Stromversorgung, also habe ich ohne lange nachzudenken das vom Modul kommende Kabel an die gegenüberliegende Seite der Platine angeschlossen. Dadurch wurde der schief verlötete Stecker schief und Der UART-Kontakt ging einfach verloren. facepalm.jpg Im Allgemeinen gilt: „Ein schlechter Kopf gibt den Beinen keine Ruhe“, und krumme Hände geben dem Kopf keine Ruhe ...

Als Ergebnis sah ich das lang erwartete

INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
LOADING /

Außerdem bewegt es sich und die Ladeanzeige dreht sich. Ich erinnere mich sofort an meine Schulzeit und das gemütliche Laden von MinuetOS von einer Diskette. Es sei denn, das Laufwerk schleift nicht.

Das Problem ist, dass nach der BOOT-Meldung nichts passiert. Dies bedeutet, dass es an der Zeit ist, über OpenOCD eine Verbindung zu Raspberry und GDB auf dem Host herzustellen und zu sehen, was es ist.

Erstens hat die Verbindung über GDB das sofort gezeigt $pc (Programmzähler, Adresse der aktuellen Anweisung) fliegt an 0x0 - Dies geschieht wahrscheinlich nach mehreren Fehlern. Daher unmittelbar nach Ausgabe der Meldung BOOT Fügen wir eine Endlosschleife hinzu. Das wird ihn eine Weile aufhalten...

diff --git a/bootrom/sdboot/sd.c b/bootrom/sdboot/sd.c
index c6b5ede..bca1b7f 100644
--- a/bootrom/sdboot/sd.c
+++ b/bootrom/sdboot/sd.c
@@ -224,6 +224,8 @@ int main(void)

        kputs("BOOT");

+    while(*(volatile char *)0x10000){}
+
        __asm__ __volatile__ ("fence.i" : : : "memory");
        return 0;
 }

Solch kniffliger Code wird „aus Gründen der Zuverlässigkeit“ verwendet: Ich habe irgendwo gehört, dass eine Endlosschleife ein undefiniertes Verhalten ist, aber der Compiler wird es wahrscheinlich nicht erraten (ich erinnere Sie daran, dass laut 0x10000 befindet sich im BootROM).

Teil 3: Fast Linux von der SD-Karte auf RocketChip laden

Es scheint, was kann man sonst noch erwarten - hart eingebettet, welche Art von Quellcodes gibt es? Aber in dieser Artikel Der Autor hat den C-Code debuggt... Kreks-fex-pex:

(gdb) file builds/zeowaa-e115/sdboot.elf
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from builds/zeowaa-e115/sdboot.elf...done.

Teil 3: Fast Linux von der SD-Karte auf RocketChip laden

Sie müssen lediglich nicht die MIF-Datei oder den Bin herunterladen, sondern die Originalversion im ELF-Format.

Jetzt können Sie beim n-ten Versuch die Adresse erraten, an der die Ausführung fortgesetzt wird (dies ist ein weiterer Grund, warum der Compiler nicht hätte erraten dürfen, dass die Schleife unendlich war). Team

set variable $pc=0xADDR

ermöglicht Ihnen, den Registerwert im laufenden Betrieb zu ändern (in diesem Fall die Adresse des aktuellen Befehls). Mit seiner Hilfe können Sie in den Speicher (und in speicherabgebildete Register) geschriebene Werte ändern.

Letztendlich bin ich zu dem Schluss gekommen (ich bin mir nicht sicher, was richtig ist), dass wir „ein SD-Karten-Image des falschen Systems“ haben und wir nicht zum Anfang der heruntergeladenen Daten gehen müssen, sondern zu 0x89800 Bytes weiter:

diff --git a/bootrom/sdboot/head.S b/bootrom/sdboot/head.S
index 14fa740..2a6c944 100644
--- a/bootrom/sdboot/head.S
+++ b/bootrom/sdboot/head.S
@@ -13,7 +13,7 @@ _prog_start:
   smp_resume(s1, s2)
   csrr a0, mhartid
   la a1, dtb
-  li s1, PAYLOAD_DEST
+  li s1, (PAYLOAD_DEST + 0x89800)
   jr s1

   .section .rodata

Vielleicht wurde dies auch dadurch beeinflusst, dass ich, da ich keine unnötige 4-GB-Karte zur Hand hatte, eine 2-GB-Karte nahm und sie nach dem Zufallsprinzip im Makefile ersetzte DEMO_END=11718750 auf DEMO_END=3078900 (Suchen Sie nicht nach einer Bedeutung in einer bestimmten Bedeutung – es gibt keine, es ist nur so, dass das Bild jetzt auf der Karte platziert ist).

Level zwei, U-Boot

Jetzt „fallen wir noch“, aber wir sind bereits am richtigen Ort 0x0000000080089a84. Hier muss ich zugeben: Tatsächlich geht die Präsentation nicht „mit allen Registern“, sondern ist teilweise „nachher“ geschrieben, daher habe ich es hier bereits geschafft, die richtige dtb-Datei von unserem SoC einzufügen, in den Einstellungen zu korrigieren HiFive_U-Boot Variable CONFIG_SYS_TEXT_BASE=0x80089800 (Anstelle von 0x08000000), damit die Download-Adresse mit der tatsächlichen übereinstimmt. Jetzt laden wir die Karte des nächsten Levels, ein weiteres Bild:

(gdb) file ../freedom-u-sdk/work/HiFive_U-Boot/u-boot
(gdb) tui en

Und wir sehen:

   │304     /*                                               │
   │305      * trap entry                                    │
   │306      */                                              │
   │307     trap_entry:                                      │
   │308         addi sp, sp, -32*REGBYTES                    │
  >│309         SREG x1, 1*REGBYTES(sp)                      │
   │310         SREG x2, 2*REGBYTES(sp)                      │
   │311         SREG x3, 3*REGBYTES(sp)                      │

Darüber hinaus springen wir zwischen den Zeilen 308 und 309. Und das ist nicht verwunderlich, wenn man bedenkt, dass in $sp liegt der Sinn 0xfffffffe31cdc0a0. Leider „läuft“ es wegen Zeile 307 auch ständig weg. Versuchen wir daher, einen Haltepunkt bei zu setzen trap_entry, und gehen Sie dann zurück zu 0x80089800 (Einstiegspunkt von U-Boot), und hoffen wir, dass die Register vor dem Springen nicht korrekt eingestellt werden müssen ... Sieht so aus, als ob es funktioniert:

(gdb) b trap_entry
Breakpoint 1 at 0x80089a80: file /hdd/trosinenko/fpga/freedom-u-sdk/HiFive_U-Boot/arch/riscv/cpu/HiFive/start.S, line 308.
(gdb) set variable $pc=0x80089800
(gdb) c
Continuing.

Breakpoint 1, trap_entry () at /hdd/trosinenko/fpga/freedom-u-sdk/HiFive_U-Boot/arch/riscv/cpu/HiFive/start.S:308
(gdb) p/x $sp
$4 = 0x81cf950

Der Stapelzeiger ist, ehrlich gesagt, mittelmäßig: Er deutet darauf hin, den RAM vollständig zu umgehen (es sei denn natürlich, wir haben nicht bereits eine Adressübersetzung, aber hoffen wir auf eine einfache Option).

Versuchen wir, den Zeiger durch zu ersetzen 0x881cf950. Als Ergebnis kommen wir zu dem Schluss, dass handle_trap rief und rief, und gleichzeitig gehen wir hinein _exit_trap mit Argument epc=2148315240 (in Dezimalzahl):

(gdb) x/10i 2148315240
   0x800cb068 <strnlen+12>:     lbu     a4,0(a5)
   0x800cb06c <strnlen+16>:     bnez    a4,0x800cb078 <strnlen+28>
   0x800cb070 <strnlen+20>:     sub     a0,a5,a0
   0x800cb074 <strnlen+24>:     ret
   0x800cb078 <strnlen+28>:     addi    a5,a5,1
   0x800cb07c <strnlen+32>:     j       0x800cb064 <strnlen+8>
   0x800cb080 <strdup>: addi    sp,sp,-32
   0x800cb084 <strdup+4>:       sd      s0,16(sp)
   0x800cb088 <strdup+8>:       sd      ra,24(sp)
   0x800cb08c <strdup+12>:      li      s0,0

Haltepunkt setzen auf strnlen, wir machen weiter und sehen:

(gdb) bt
#0  strnlen (s=s@entry=0x10060000 "", count=18446744073709551615) at lib/string.c:283
#1  0x00000000800cc14c in string (buf=buf@entry=0x881cbd4c "", end=end@entry=0x881cc15c "", s=0x10060000 "", field_width=<optimized out>, precision=<optimized out>, flags=<optimized out>) at lib/vsprintf.c:265
#2  0x00000000800cc63c in vsnprintf_internal (buf=buf@entry=0x881cbd38 "exception code: 5 , ", size=size@entry=1060, fmt=0x800d446e "s , epc %08x , ra %08lxn", fmt@entry=0x800d4458 "exception code: %d , %s , epc %08x , ra %08lxn", args=0x881cc1a0,
    args@entry=0x881cc188) at lib/vsprintf.c:619
#3  0x00000000800cca54 in vsnprintf (buf=buf@entry=0x881cbd38 "exception code: 5 , ", size=size@entry=1060, fmt=fmt@entry=0x800d4458 "exception code: %d , %s , epc %08x , ra %08lxn", args=args@entry=0x881cc188) at lib/vsprintf.c:710
#4  0x00000000800cca68 in vscnprintf (buf=buf@entry=0x881cbd38 "exception code: 5 , ", size=size@entry=1060, fmt=fmt@entry=0x800d4458 "exception code: %d , %s , epc %08x , ra %08lxn", args=args@entry=0x881cc188) at lib/vsprintf.c:717
#5  0x00000000800ccb50 in printf (fmt=fmt@entry=0x800d4458 "exception code: %d , %s , epc %08x , ra %08lxn") at lib/vsprintf.c:792
#6  0x000000008008a9f0 in _exit_trap (regs=<optimized out>, epc=2148315240, code=<optimized out>) at arch/riscv/lib/interrupts.c:92
#7  handle_trap (mcause=<optimized out>, epc=<optimized out>, regs=<optimized out>) at arch/riscv/lib/interrupts.c:55
#8  0x0000000080089b10 in trap_entry () at /hdd/trosinenko/fpga/freedom-u-sdk/HiFive_U-Boot/arch/riscv/cpu/HiFive/start.S:343
Backtrace stopped: frame did not save the PC

Es sieht so aus _exit_trap möchte Debugging-Informationen über die aufgetretene Ausnahme bereitstellen, aber er kann nicht. Irgendwie werden unsere Quellen also nicht mehr angezeigt. set directories ../freedom-u-sdk/HiFive_U-Boot/ UM! Jetzt angezeigt!

Nun, lassen Sie es uns noch einmal ausführen und anhand der Stapelverfolgung die Ursache des ursprünglichen Problems sehen, das den ersten Fehler verursacht hat (mcause == 5). Wenn ich richtig verstanden habe, was geschrieben steht hier auf Seite 37, dann bedeutet diese Ausnahme Load access fault. Der Grund scheint hier zu liegen

arch/riscv/cpu/HiFive/start.S:

call_board_init_f:
    li  t0, -16
    li  t1, CONFIG_SYS_INIT_SP_ADDR
    and sp, t1, t0  /* force 16 byte alignment */

#ifdef CONFIG_DEBUG_UART
    jal debug_uart_init
#endif

call_board_init_f_0:
    mv  a0, sp
    jal board_init_f_alloc_reserve
    mv  sp, a0
    jal board_init_f_init_reserve

    mv  a0, zero    /* a0 <-- boot_flags = 0 */
    la t5, board_init_f
    jr t5       /* jump to board_init_f() */

$sp hat die gleiche falsche Bedeutung, und zwar im Inneren board_init_f_init_reserve Es tritt ein Fehler auf. Sieht so aus, als wäre dies der Übeltäter: eine Variable mit einem eindeutigen Namen CONFIG_SYS_INIT_SP_ADDR. Es ist in der Datei definiert HiFive_U-Boot/include/configs/HiFive-U540.h. Irgendwann dachte ich sogar, vielleicht, nun ja, ich sollte einen Bootloader für den Prozessor hinzufügen – vielleicht wäre es einfacher, den Prozessor ein wenig zu reparieren? Aber dann sah ich, dass es sich eher um ein Artefakt handelte, das noch nicht vollständig fertiggestellt war#if 0-spezifische Einstellungen für eine andere Speicherkonfiguration, und Sie können Folgendes versuchen:

diff --git a/include/configs/HiFive-U540.h b/include/configs/HiFive-U540.h
index ca89383..245542c 100644
--- a/include/configs/HiFive-U540.h
+++ b/include/configs/HiFive-U540.h
@@ -65,12 +65,9 @@
 #define CONFIG_SYS_SDRAM_BASE  PHYS_SDRAM_0
 #endif
 #if 1
-/*#define CONFIG_NR_DRAM_BANKS 1*/
+#define CONFIG_NR_DRAM_BANKS   1
 #define PHYS_SDRAM_0   0x80000000              /* SDRAM Bank #1 */
-#define PHYS_SDRAM_1   
-       (PHYS_SDRAM_0 + PHYS_SDRAM_0_SIZE)      /* SDRAM Bank #2 */
-#define PHYS_SDRAM_0_SIZE      0x80000000      /* 2 GB */
-#define PHYS_SDRAM_1_SIZE      0x10000000      /* 256 MB */
+#define PHYS_SDRAM_0_SIZE      0x40000000      /* 1 GB */
 #define CONFIG_SYS_SDRAM_BASE  PHYS_SDRAM_0
 #endif
 /*
@@ -81,7 +78,7 @@
 #define CONSOLE_ARG                            "console=ttyS0,115200 "

 /* Init Stack Pointer */
-#define CONFIG_SYS_INIT_SP_ADDR                (0x08000000 + 0x001D0000 - 
+#define CONFIG_SYS_INIT_SP_ADDR                (0x80000000 + 0x001D0000 - 
                                        GENERATED_GBL_DATA_SIZE)

 #define CONFIG_SYS_LOAD_ADDR           0xa0000000      /* partway up SDRAM */

Irgendwann wird die Zahl der Krücken sinken technologische Verbindungselemente hat einen kritischen Punkt erreicht. Nachdem ich ein wenig gekämpft hatte, kam ich zu der Notwendigkeit, den richtigen Anschluss für mein Board zu finden. Dazu müssen wir einige Dateien kopieren und an unsere Konfiguration anpassen.

Nun, ungefähr, hier ist eine kleine Tabelle

trosinenko@trosinenko-pc:/hdd/trosinenko/fpga/freedom-u-sdk/HiFive_U-Boot$ git show --name-status
commit 39cd67d59c16ac87b46b51ac1fb58f16f1eb1048 (HEAD -> zeowaa-1gb)
Author: Anatoly Trosinenko <[email protected]>
Date:   Tue Jul 2 17:13:16 2019 +0300

    Initial support for Zeowaa A-E115FB board

M       arch/riscv/Kconfig
A       arch/riscv/cpu/zeowaa-1gb/Makefile
A       arch/riscv/cpu/zeowaa-1gb/cpu.c
A       arch/riscv/cpu/zeowaa-1gb/start.S
A       arch/riscv/cpu/zeowaa-1gb/timer.c
A       arch/riscv/cpu/zeowaa-1gb/u-boot.lds
M       arch/riscv/dts/Makefile
A       arch/riscv/dts/zeowaa-1gb.dts
A       board/Zeowaa/zeowaa-1gb/Kconfig
A       board/Zeowaa/zeowaa-1gb/MAINTAINERS
A       board/Zeowaa/zeowaa-1gb/Makefile
A       board/Zeowaa/zeowaa-1gb/Zeowaa-A-E115FB.c
A       configs/zeowaa-1gb_defconfig
A       include/configs/zeowaa-1gb.h

Einzelheiten finden Sie in Lagerstätten.

Wie sich herausstellte, haben auf diesem SiFive-Board die Register einiger Geräte unterschiedliche Adressen. Es stellte sich auch heraus, dass U-Boot über den Kconfig-Mechanismus konfiguriert wird, der bereits aus dem Linux-Kernel bekannt ist – Sie können beispielsweise befehlen make menuconfig, und vor Ihnen erscheint eine praktische Textoberfläche mit Beschreibungen der Parameter von ? usw. Im Allgemeinen habe ich aus den Beschreibungen zweier Platinen eine Beschreibung des dritten zusammengeschustert und von dort aus alle möglichen prätentiösen PLL-Rekonfigurationen weggeworfen (anscheinend hängt dies irgendwie mit der Steuerung vom Host-Computer über PCIe zusammen, aber das ist nicht sicher). Ich habe eine Firmware erhalten, die mir bei entsprechendem Wetter auf dem Mars über UART eine Nachricht darüber gab, aus welchem ​​Commit-Hash sie kompiliert wurde und wie viel DRAM ich habe (aber ich selbst habe diese Informationen in den Header geschrieben).

Schade nur, dass das Board danach normalerweise nicht mehr über Prozessor-JTAG reagiert und das Laden von einer SD-Karte in meiner Konfiguration leider nicht schnell ist. Andererseits gab BootROM manchmal eine Meldung aus ERROR, konnte nicht gestartet werden und U-Boot tauchte sofort auf. Da dämmerte es mir: Anscheinend wird der Speicher nach dem Neustart des Bitstreams im FPGA nicht gelöscht, er hat keine Zeit zum „Untrainieren“ usw. Kurz gesagt, Sie können dies einfach tun, wenn eine Nachricht angezeigt wird LOADING / Stellen Sie eine Verbindung mit einem Debugger und einem Befehl her set variable $pc=0x80089800, wodurch dieses lange Laden umgangen wird (natürlich unter der Annahme, dass es beim letzten Mal so früh kaputt ging, dass es keine Zeit hatte, etwas über den ursprünglichen Code hinaus zu laden).

Ist es übrigens generell normal, dass der Prozessor komplett einfriert und der JTAG-Debugger keine Verbindung zu ihm mit Nachrichten herstellen kann?

Error: unable to halt hart 0
Error:   dmcontrol=0x80000001
Error:   dmstatus =0x00030c82

Also warte! Das habe ich schon gesehen! Etwas Ähnliches passiert, wenn TileLink blockiert ist und ich dem Autor des Speichercontrollers irgendwie nicht vertraue – ich habe es selbst geschrieben … Plötzlich, nach dem ersten erfolgreichen Neuaufbau des Prozessors nach der Bearbeitung des Controllers, sah ich:

INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
LOADING
BOOT

U-Boot 2018.09-g39cd67d-dirty (Jul 03 2019 - 13:50:33 +0300)

DRAM:  1 GiB
MMC:
BEFORE LOAD ENVBEFORE FDTCONTROLADDRBEFORE LOADADDRIn:    serial
Out:   serial
Err:   serial
Hit any key to stop autoboot:  3

Zu dieser seltsamen Zeile vorher In: serial Passen Sie nicht auf – ich habe versucht, bei einem hängenden Prozessor herauszufinden, ob er korrekt mit der Umgebung zusammenarbeitet. Was meinst du mit „Es hängt seit zehn Minuten so“? Zumindest ist es gelungen, das Bootmenü zu verschieben und aufzurufen! Ein kleiner Exkurs: Obwohl U-Boot in den ersten 2^24 Bytes von der SD-Karte geladen wird, kopiert es sich beim Start an eine weiter entfernte Adresse, die entweder im Konfigurationsheader steht oder einfach an die höheren Adressen von RAM, führt eine ELF-Verlagerung von Zeichen durch und überträgt die Kontrolle dorthin. Also: Es sieht so aus, als hätten wir dieses Level bestanden und einen Bonus erhalten, dass der Prozessor danach nicht mehr festhängt.

Warum funktioniert der Timer nicht? Es sieht so aus, als ob die Uhr aus irgendeinem Grund nicht funktioniert ...

(gdb) x/x 0x0200bff8
0x200bff8:      0x00000000

Was passiert, wenn Sie die Pfeile manuell drehen?

(gdb) set variable *0x0200bff8=310000000
(gdb) c

Dann:

Hit any key to stop autoboot:  0
MMC_SPI: 0 at 0:1 hz 20000000 mode 0

Fazit: Die Uhr tickt nicht. Dies ist wahrscheinlich der Grund, warum die Tastatureingabe nicht funktioniert:

HiFive_U-Boot/cmd/bootmenu.c:

static void bootmenu_loop(struct bootmenu_data *menu,
        enum bootmenu_key *key, int *esc)
{
    int c;

    while (!tstc()) {
        WATCHDOG_RESET();
        mdelay(10);
    }

    c = getc();

    switch (*esc) {
    case 0:
        /* First char of ANSI escape sequence 'e' */
        if (c == 'e') {
            *esc = 1;
            *key = KEY_NONE;
        }
        break;
    case 1:
        /* Second char of ANSI '[' */
        if (c == '[') {
...

Das Problem war, dass ich etwas zu schlau war: Ich habe den Schlüssel zur Prozessorkonfiguration hinzugefügt:

  case DTSTimebase => BigInt(0)

... basierend auf der Tatsache, dass im Kommentar stand: „Wenn Sie es nicht wissen, hinterlassen Sie 0.“ Und schließlich WithNBigCores Ich habe es einfach auf 1 MHz eingestellt (wie es übrigens in der U-Boot-Konfiguration angegeben war). Aber verdammt, ich bin ordentlich und akribisch: Ich weiß es nicht, hier sind es 25 MHz! Am Ende funktioniert nichts. Ich habe meine „Verbesserungen“ entfernt und...

Hit any key to stop autoboot:  0
MMC_SPI: 0 at 0:1 hz 20000000 mode 0
## Unknown partition table type 0
libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND
** No partition table - mmc 0 **
## Info: input data size = 34 = 0x22
Running uEnv.txt boot2...
## Error: "boot2" not defined
HiFive-Unleashed #

Sie können sogar Befehle eingeben! Nachdem Sie beispielsweise ein wenig herumgestöbert haben, können Sie schließlich erraten, ob Sie eintreten möchten mmc_spi 1 10000000 0; mmc part, wodurch die SPI-Frequenz von 20 MHz auf 10 MHz reduziert wird. Warum? Nun, die maximale Frequenz von 20 MHz wurde in der Konfiguration angegeben und steht dort immer noch. Aber soweit ich weiß, funktionieren die Schnittstellen, zumindest hier, so: Der Code dividiert die Frequenz der Hardwareeinheit (meine ist überall 25 MHz) durch das Ziel und setzt den resultierenden Wert als Divisor in der entsprechenden Steuerung registrieren. Das Problem besteht darin, dass, wenn für einen 115200-Hz-UART ungefähr das Notwendige vorhanden ist, Sie 25000000 durch 20000000 dividieren und 1 erhalten, d. h. es wird mit 25 MHz arbeiten. Vielleicht ist das normal, aber wenn Einschränkungen festgelegt werden, bedeutet das, dass jemand es braucht (aber das ist nicht sicher)... Im Allgemeinen ist es einfacher, es beiseite zu legen und weiterzumachen – weit und leider für lange Zeit. 25 MHz ist kein Core i9.

Konsolenausgabe

HiFive-Unleashed # env edit mmcsetup
edit: mmc_spi 1 10000000 0; mmc part
HiFive-Unleashed # boot
MMC_SPI: 1 at 0:1 hz 10000000 mode 0

Partition Map for MMC device 0  --   Partition Type: EFI

Part    Start LBA       End LBA         Name
        Attributes
        Type GUID
        Partition GUID
  1     0x00000800      0x0000ffde      "Vfat Boot"
        attrs:  0x0000000000000000
        type:   ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
        type:   data
        guid:   76bd71fd-1694-4ff3-8197-bfa81699c2fb
  2     0x00040800      0x002efaf4      "root"
        attrs:  0x0000000000000000
        type:   0fc63daf-8483-4772-8e79-3d69d8477de4
        type:   linux
        guid:   9f3adcc5-440c-4772-b7b7-283124f38bf3
  3     0x0000044c      0x000007e4      "uboot"
        attrs:  0x0000000000000000
        type:   5b193300-fc78-40cd-8002-e86c45580b47
        guid:   bb349257-0694-4e0f-9932-c801b4d76fa3
  4     0x00000400      0x0000044b      "uboot-env"
        attrs:  0x0000000000000000
        type:   a09354ac-cd63-11e8-9aff-70b3d592f0fa
        guid:   4db442d0-2109-435f-b858-be69629e7dbf
libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND
2376 bytes read in 0 ms
Running uEnv.txt boot2...
15332118 bytes read in 0 ms
## Loading kernel from FIT Image at 90000000 ...
   Using 'config-1' configuration
   Trying 'bbl' kernel subimage
     Description:  BBL/SBI/riscv-pk
     Type:         Kernel Image
     Compression:  uncompressed
     Data Start:   0x900000d4
     Data Size:    74266 Bytes = 72.5 KiB
     Architecture: RISC-V
     OS:           Linux
     Load Address: 0x80000000
     Entry Point:  0x80000000
     Hash algo:    sha256
     Hash value:   28972571467c4ad0cf08a81d9cf92b9dffc5a7cb2e0cd12fdbb3216cf1f19cbd
   Verifying Hash Integrity ... sha256+ OK
## Loading fdt from FIT Image at 90000000 ...
   Using 'config-1' configuration
   Trying 'fdt' fdt subimage
     Description:  unavailable
     Type:         Flat Device Tree
     Compression:  uncompressed
     Data Start:   0x90e9d31c
     Data Size:    6911 Bytes = 6.7 KiB
     Architecture: RISC-V
     Load Address: 0x81f00000
     Hash algo:    sha256
     Hash value:   10b0244a5a9205357772ea1c4e135a4f882409262176d8c7191238cff65bb3a8
   Verifying Hash Integrity ... sha256+ OK
   Loading fdt from 0x90e9d31c to 0x81f00000
   Booting using the fdt blob at 0x81f00000
## Loading loadables from FIT Image at 90000000 ...
   Trying 'kernel' loadables subimage
     Description:  Linux kernel
     Type:         Kernel Image
     Compression:  uncompressed
     Data Start:   0x900123e8
     Data Size:    10781356 Bytes = 10.3 MiB
     Architecture: RISC-V
     OS:           Linux
     Load Address: 0x80200000
     Entry Point:  unavailable
     Hash algo:    sha256
     Hash value:   72a9847164f4efb2ac9bae736f86efe7e3772ab1f01ae275e427e2a5389c84f0
   Verifying Hash Integrity ... sha256+ OK
   Loading loadables from 0x900123e8 to 0x80200000
## Loading loadables from FIT Image at 90000000 ...
   Trying 'ramdisk' loadables subimage
     Description:  buildroot initramfs
     Type:         RAMDisk Image
     Compression:  gzip compressed
     Data Start:   0x90a5a780
     Data Size:    4467411 Bytes = 4.3 MiB
     Architecture: RISC-V
     OS:           Linux
     Load Address: 0x82000000
     Entry Point:  unavailable
     Hash algo:    sha256
     Hash value:   883dfd33ca047e3ac10d5667ffdef7b8005cac58b95055c2c2beda44bec49bd0
   Verifying Hash Integrity ... sha256+ OK
   Loading loadables from 0x90a5a780 to 0x82000000

Okay, wir haben das nächste Level erreicht, aber es ist immer noch eiskalt. Und manchmal streut es auch Ausnahmen. Sie können mcause erkennen, indem Sie an der angegebenen Adresse auf den Code warten $pc und danach si eingeschaltet sein trap_entry. Der U-Boot-Handler selbst kann nur für mcause = 0..4 ausgeben, also machen Sie sich darauf gefasst, bei einem falschen Start stecken zu bleiben. Dann ging ich in die Konfiguration, begann zu schauen, was ich änderte, und erinnerte mich: Da drin conf/rvboot-fit.txt geschrieben:

fitfile=image.fit
# below much match what's in FIT (ugha)

Nun, bringen wir alle Dateien in Übereinstimmung und ersetzen Sie die Kernel-Befehlszeile durch etwas in der Art, da der Verdacht besteht, dass dies der Fall ist SIF0 - das ist die Ausgabe irgendwo über PCIe:

-bootargs=console=ttySIF0,921600 debug
+bootargs=console=ttyS0,125200 debug

Und ändern wir den Hashing-Algorithmus von SHA-256 auf MD5: Ich brauche keine kryptografische Stärke (besonders beim Debuggen), es dauert furchtbar lange, und um Integritätsfehler beim Laden zu erkennen, ist MD5 zu einfach. Was ist das Endergebnis? Wir begannen, das vorherige Level merklich schneller abzuschließen (aufgrund des einfacheren Hashings), und das nächste öffnete sich:

...
   Verifying Hash Integrity ... md5+ OK
   Loading loadables from 0x90a5a758 to 0x82000000
libfdt fdt_check_header(): FDT_ERR_BADMAGIC
chosen {
        linux,initrd-end = <0x00000000 0x83000000>;
        linux,initrd-start = <0x00000000 0x82000000>;
        riscv,kernel-end = <0x00000000 0x80a00000>;
        riscv,kernel-start = <0x00000000 0x80200000>;
        bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait";
};
libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND
chosen {
        linux,initrd-end = <0x00000000 0x83000000>;
        linux,initrd-start = <0x00000000 0x82000000>;
        riscv,kernel-end = <0x00000000 0x80a00000>;
        riscv,kernel-start = <0x00000000 0x80200000>;
        bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait";
};
   Loading Kernel Image ... OK
Booting kernel in
3

Aber die Uhr tickt nicht ...

(gdb) x/x 0x0200bff8
0x200bff8:      0x00000000

Hoppla, es sieht so aus, als hätte sich die Korrektur der Uhr als Placebo herausgestellt, obwohl es mir damals so vorkam, als hätte es geholfen. Nein, natürlich muss es repariert werden, aber drehen wir zunächst die Pfeile manuell und schauen, was passiert:

0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=1000000
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=2000000
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=3000000
(gdb) c
Continuing.

Inzwischen…

   Loading Kernel Image ... OK
Booting kernel in
3
2
1
0
## Starting application at 0x80000000 ...

Nein, ich werde die Uhr automatisieren – sonst beschließt er vielleicht, den Timer dort zu kalibrieren!

Inzwischen zeigt die Adresse der aktuellen Anweisung irgendwo hinein

0000000080001c20 <poweroff>:
    80001c20:   1141                    addi    sp,sp,-16
    80001c22:   e022                    sd      s0,0(sp)
    80001c24:   842a                    mv      s0,a0
    80001c26:   00005517                auipc   a0,0x5
    80001c2a:   0ca50513                addi    a0,a0,202 # 80006cf0 <softfloat_countLeadingZeros8+0x558>
    80001c2e:   e406                    sd      ra,8(sp)
    80001c30:   f7fff0ef                jal     ra,80001bae <printm>
    80001c34:   8522                    mv      a0,s0
    80001c36:   267000ef                jal     ra,8000269c <finisher_exit>
    80001c3a:   00010797                auipc   a5,0x10
    80001c3e:   41e78793                addi    a5,a5,1054 # 80012058 <htif>
    80001c42:   639c                    ld      a5,0(a5)
    80001c44:   c399                    beqz    a5,80001c4a <poweroff+0x2a>
    80001c46:   72c000ef                jal     ra,80002372 <htif_poweroff>
    80001c4a:   45a1                    li      a1,8
    80001c4c:   4501                    li      a0,0
    80001c4e:   dc7ff0ef                jal     ra,80001a14 <send_ipi_many>
    80001c52:   10500073                wfi
    80001c56:   bff5                    j       80001c52 <poweroff+0x32>

im geladenen Berkeley Boot Loader. Was mich persönlich daran verwirrt, ist die Erwähnung htif – Host-Schnittstelle, die für den angebundenen Start des Kernels verwendet wird (d. h. in Zusammenarbeit mit dem Host-ARM), ich ging davon aus, dass sie eigenständig ist. Wenn Sie diese Funktion jedoch im Quellcode finden, können Sie sehen, dass nicht alles so schlecht ist:

void poweroff(uint16_t code)
{
  printm("Power offrn");
  finisher_exit(code);
  if (htif) {
    htif_poweroff();
  } else {
    send_ipi_many(0, IPI_HALT);
    while (1) { asm volatile ("wfin"); }
  }
}

Aufgabe: Starten Sie die Uhr

Die Suche nach Registern in CLINT führt uns zu

    val io = IO(new Bundle {
      val rtcTick = Bool(INPUT)
    })

    val time = RegInit(UInt(0, width = timeWidth))
    when (io.rtcTick) { time := time + UInt(1) }

Was mit RTC oder dem mysteriösen MockAON zusammenhängt, über das ich zunächst dachte: „Also, was haben wir hier?“ Unverständlich? Lass es uns ausschalten!“ Da ich immer noch nicht verstehe, welche Art von Uhrmagie dort vor sich geht, werde ich diese Logik einfach neu implementieren System.scala:

  val rtcDivider = RegInit(0.asUInt(16.W)) // на всякий случай поддержу до 16ГГц, я оптимист :)
  val mhzInt = p(DevKitFPGAFrequencyKey).toInt
  // Преположим, частота равна целому числу мегагерц
  rtcDivider := Mux(rtcDivider === (mhzInt - 1).U, 0.U, rtcDivider + 1.U)
  outer.clintOpt.foreach { clint =>
    clint.module.io.rtcTick := rtcDivider === 0.U
  }

Wir machen uns auf den Weg zum Linux-Kernel

Hier hat sich die Geschichte schon in die Länge gezogen und ist etwas eintönig geworden, deshalb beschreibe ich sie von oben nach unten:

BBL ging von der Anwesenheit von FDT aus 0xF0000000, aber ich habe es bereits korrigiert! Naja, schauen wir noch einmal nach... Gefunden in HiFive_U-Boot/arch/riscv/lib/boot.c, ersetzt durch 0x81F00000, angegeben in der U-Boot-Boot-Konfiguration.

Dann beklagte die BBL, dass es kein Gedächtnis gebe. Mein Weg lag in der Funktion mem_propdass riscv-pk/machine/fdt.c: Von dort habe ich erfahren, dass Sie den FDT-RAM-Knoten als markieren müssen device_type = "memory" - Dann muss vielleicht der Prozessorgenerator korrigiert werden, aber jetzt schreibe ich es einfach manuell ein - jedenfalls habe ich diese Datei manuell übertragen.

Jetzt erhielt ich die Nachricht (bereitgestellt in formatierter Form, mit Wagenrücklauf):

This is bbl's dummy_payload.  To boot a real kernel, reconfigure bbl
with the flag --with-payload=PATH, then rebuild bbl. Alternatively,
bbl can be used in firmware-only mode by adding device-tree nodes
for an external payload and use QEMU's -bios and -kernel options.

Es scheint, dass die Optionen nach Bedarf angezeigt werden riscv,kernel-start и riscv,kernel-end in DTB, aber Nullen werden analysiert. Debuggen query_chosen hat gezeigt, dass BBL versucht, eine 32-Bit-Adresse zu analysieren, aber auf ein Paar stößt <0x0 0xADDR>, und der erste Wert scheint die niedrigstwertigen Bits zu sein. Zum Abschnitt hinzugefügt chosen

chosen {
      #address-cells = <1>;
      #size-cells = <0>;
      ...
}

und die Generierung der Werte korrigiert: nicht hinzufügen 0x0 das erste Element.

Mit diesen 100500 einfachen Schritten können Sie leicht beobachten, wie ein Pinguin fällt:

Versteckter Text

   Verifying Hash Integrity ... md5+ OK
   Loading loadables from 0x90a5a758 to 0x82000000
libfdt fdt_check_header(): FDT_ERR_BADMAGIC
chosen {
        linux,initrd-end = <0x83000000>;
        linux,initrd-start = <0x82000000>;
        riscv,kernel-end = <0x80a00000>;
        riscv,kernel-start = <0x80200000>;
        #address-cells = <0x00000001>;
        #size-cells = <0x00000000>;
        bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait";
        stdout-path = "uart0:38400n8";
};
libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND
chosen {
        linux,initrd-end = <0x83000000>;
        linux,initrd-start = <0x82000000>;
        riscv,kernel-end = <0x80a00000>;
        riscv,kernel-start = <0x80200000>;
        #address-cells = <0x00000001>;
        #size-cells = <0x00000000>;
        bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait";
        stdout-path = "uart0:38400n8";
};
   Loading Kernel Image ... OK
Booting kernel in
3
2
1
0
## Starting application at 0x80000000 ...
bbl loader

                SIFIVE, INC.

         5555555555555555555555555
        5555                   5555
       5555                     5555
      5555                       5555
     5555       5555555555555555555555
    5555       555555555555555555555555
   5555                             5555
  5555                               5555
 5555                                 5555
5555555555555555555555555555          55555
 55555           555555555           55555
   55555           55555           55555
     55555           5           55555
       55555                   55555
         55555               55555
           55555           55555
             55555       55555
               55555   55555
                 555555555
                   55555
                     5

           SiFive RISC-V Core IP
[    0.000000] OF: fdt: Ignoring memory range 0x80000000 - 0x80200000
[    0.000000] Linux version 4.19.0-sifive-1+ (trosinenko@trosinenko-pc) (gcc version 8.3.0 (Buildroot 2019.02-07449-g4eddd28f99)) #1 SMP Wed Jul 3 21:29:21 MSK 2019
[    0.000000] bootconsole [early0] enabled
[    0.000000] Initial ramdisk at: 0x(____ptrval____) (16777216 bytes)
[    0.000000] Zone ranges:
[    0.000000]   DMA32    [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000]   Normal   [mem 0x00000000c0000000-0x00000bffffffffff]
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] On node 0 totalpages: 261632
[    0.000000]   DMA32 zone: 3577 pages used for memmap
[    0.000000]   DMA32 zone: 0 pages reserved
[    0.000000]   DMA32 zone: 261632 pages, LIFO batch:63
[    0.000000] software IO TLB: mapped [mem 0xbb1fc000-0xbf1fc000] (64MB)

(Das Logo wird von BBL angezeigt und das mit Zeitstempeln wird vom Kernel angezeigt).

Zum Glück weiß ich nicht, wie es überall ist, aber wenn man auf RocketChip einen Debugger über JTAG anschließt, kann man sofort Traps abfangen – der Debugger stoppt genau an dieser Stelle.

Program received signal SIGTRAP, Trace/breakpoint trap.
0xffffffe0000024ca in ?? ()
(gdb) bt
#0  0xffffffe0000024ca in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) file work/linux/vmlinux
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from work/linux/vmlinux...done.
(gdb) bt
#0  0xffffffe0000024ca in setup_smp () at /hdd/trosinenko/fpga/freedom-u-sdk/linux/arch/riscv/kernel/smpboot.c:75
#1  0x0000000000000000 in ?? ()
Backtrace stopped: frame did not save the PC

freedom-u-sdk/linux/arch/riscv/kernel/smpboot.c:

void __init setup_smp(void)
{
    struct device_node *dn = NULL;
    int hart;
    bool found_boot_cpu = false;
    int cpuid = 1;

    while ((dn = of_find_node_by_type(dn, "cpu"))) {
        hart = riscv_of_processor_hartid(dn);
        if (hart < 0)
            continue;

        if (hart == cpuid_to_hartid_map(0)) {
            BUG_ON(found_boot_cpu);
            found_boot_cpu = 1;
            continue;
        }

        cpuid_to_hartid_map(cpuid) = hart;
        set_cpu_possible(cpuid, true);
        set_cpu_present(cpuid, true);
        cpuid++;
    }

    BUG_ON(!found_boot_cpu); // < ВЫ НАХОДИТЕСЬ ЗДЕСЬ
}

Wie der alte Witz sagte: CPU nicht gefunden, Software-Emulation wird ausgeführt. Nun ja, oder nicht rennen. Verloren in einem einzelnen Prozessorkern.

/* The lucky hart to first increment this variable will boot the other cores */
atomic_t hart_lottery;
unsigned long boot_cpu_hartid;

Schöner Kommentar in linux/arch/riscv/kernel/setup.c - eine Art Bemalung eines Zauns nach der Tom Sawyer-Methode. Im Allgemeinen gab es heute aus irgendeinem Grund keine Gewinner, der Preis wird auf die nächste Ziehung übertragen...

Hier schlage ich vor, den ohnehin schon langen Artikel zu beenden.

Fortsetzung folgt. Es wird einen Kampf mit einem schlauen Käfer geben, der sich verstecken kann, wenn man sich mit einem Schritt langsam an ihn heranschleicht.

Text-Download-Screencast (externer Link):
Teil 3: Fast Linux von der SD-Karte auf RocketChip laden

Source: habr.com

Kommentar hinzufügen