Część 3: Prawie ładuję Linuksa z karty SD do RocketChip

Część 3: Prawie ładuję Linuksa z karty SD do RocketChip В poprzednia część zaimplementowano mniej więcej działający kontroler pamięci, a raczej nakładkę na IP Core firmy Quartus, będącą przejściówką dla TileLink. Dziś w dziale „Przenosimy RocketChip na mało znaną chińską płytę z Cyclonem” zobaczycie działającą konsolę. Proces trwał trochę dłużej: już myślałem, że szybko uruchomię Linuksa i pójdę dalej, ale tak się nie stało. W tej części proponuję przyjrzeć się procesowi uruchamiania U-Boot, BBL i nieśmiałym próbom inicjalizacji jądra Linuksa. Ale jest konsola - U-Boot i to dość zaawansowana, mająca wiele z tego, czego można oczekiwać od pełnoprawnej konsoli.

Sprzęt będzie zawierał kartę SD podłączoną poprzez interfejs SPI, a także UART. W części programowej BootROM zostanie zastąpiony xip na sdboot i faktycznie dodano następujące etapy ładowania (na karcie SD).

Wykończenie sprzętu

Czyli zadanie: trzeba przerzucić się na „duży” rdzeń i podłączyć UART (z Raspberry) oraz przejściówkę SD (użyliśmy karty Catalex z sześcioma pinami: GND, VCC, MISO, MOSI, SCK, CS) .

W zasadzie wszystko było dość proste. Ale zanim sobie to uświadomiłem, zostałem trochę rzucony z boku na bok: po poprzednim razem zdecydowałem, że znowu muszę się pomieszać System coś jak HasPeripheryUART (i odpowiednio implementacja), to samo dla karty SD - i wszystko będzie gotowe. Potem postanowiłem zobaczyć, jak to zostało zaimplementowane w „poważnym” projekcie. A co w tym poważnego? Arty najwyraźniej nie pasuje - potwór pozostaje unleahshed.DevKitConfigs. I nagle okazało się, że wszędzie były jakieś nakładki, które dodawane były poprzez parametry klawiszami. Myślę, że jest to prawdopodobnie bardzo elastyczne i konfigurowalne, ale chciałbym przynajmniej najpierw coś uruchomić ... Czy nie masz tego samego, tylko prostszego i bardziej irytującego?.. Właśnie wtedy natknąłem się vera.iofpga.FPGAChip dla Microsemi FPGA i od razu rozebrałem to do wyceny i próbowałem przez analogię zrobić własną implementację, na szczęście jest mniej więcej cały „układ płyty głównej” w jednym pliku.

Okazało się, że tak naprawdę wystarczy tylko dodać System.scala linie

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

Linia w treści klasy System dodaje do pliku dts informację o częstotliwości z jaką pracuje ta część naszego SoC. O ile rozumiem, DTS/DTB jest statycznym odpowiednikiem technologii plug-and-play dla urządzeń wbudowanych: drzewo opisu dts jest kompilowane do binarnego pliku dtb i przesyłane przez program ładujący do jądra, aby mógł poprawnie skonfigurować sprzęt komputerowy. Co ciekawe, bez linii z tlclock wszystko syntetyzuje doskonale, ale kompilacja BootROM (przypomnę, teraz to już będzie sdboot) nie będzie działać - podczas kompilacji analizuje plik dts i tworzy nagłówek z makrem TL_CLK, dzięki czemu będzie mógł poprawnie skonfigurować dzielniki częstotliwości dla interfejsów zewnętrznych.

Będziesz także musiał nieznacznie poprawić „okablowanie”:

Platforma.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
}

Łańcuchy rejestrów, szczerze mówiąc, zostały dodane po prostu przez analogię z innymi miejscami w oryginalnym kodzie. Najprawdopodobniej powinni chronić przed metastabilność. Być może w niektóre bloki mają już własną ochronę, ale najpierw chcę ją uruchomić przynajmniej „na poziomie wysokiej jakości”. Bardziej interesującym pytaniem dla mnie jest to, dlaczego MISO i MOSI trzymają się inaczej dq? Nie znalazłem jeszcze odpowiedzi, ale wygląda na to, że reszta kodu opiera się właśnie na takim połączeniu.

Fizycznie po prostu przypisałem piny konstrukcyjne do wolnych styków w bloku i przesunąłem zworkę wyboru napięcia na 3.3 V.

Adapter SD

Widok z góry:

Część 3: Prawie ładuję Linuksa z karty SD do RocketChip

Widok z dołu:

Część 3: Prawie ładuję Linuksa z karty SD do RocketChip

Debugowanie oprogramowania: narzędzia

Najpierw porozmawiajmy o dostępnych narzędziach do debugowania i ich ograniczeniach.

Minicom

Najpierw będziemy musieli w jakiś sposób przeczytać, co bootloader i wyjście jądra. Aby to zrobić na Linuksie (w tym przypadku na tym na RaspberryPi), potrzebujemy programu Minicom. Ogólnie rzecz biorąc, wystarczy dowolny program współpracujący z portem szeregowym.

Należy pamiętać, że podczas uruchamiania nazwę urządzenia portu należy określić jako -D /dev/ttyS0 - po opcji -D. Cóż, główne informacje: aby wyjść, użyj Ctrl-A, X. Właściwie miałem przypadek, gdy ta kombinacja nie zadziałała - wtedy można po prostu powiedzieć z sąsiedniej sesji SSH killall -KILL minicom.

Jest jeszcze jedna funkcja. W szczególności RaspberryPi ma dwa UART i oba porty można już do czegoś przystosować: jeden dla Bluetooth, drugi domyślnie wysyła konsolę jądra. Na szczęście to zachowanie można obejść zgodnie z tą instrukcją.

Przepisanie pamięci

Podczas debugowania, aby przetestować hipotezę, czasami musiałem załaduj bootloader (przepraszam) do pamięci RAM bezpośrednio z hosta. Być może da się to zrobić bezpośrednio z GDB, ale ostatecznie poszedłem prostą ścieżką: skopiowałem potrzebny plik do Raspberry, przekierowałem także port 4444 przez SSH (telnet z OpenOCD) i użyłem komendy load_image. Kiedy to robisz, wydaje się, że wszystko jest zamrożone, ale tak naprawdę „nie śpi, tylko powoli mruga”: Pobiera plik, robi to po prostu z szybkością kilku kilobajtów na sekundę.

Funkcje instalowania punktów przerwania

Wiele osób prawdopodobnie nie musiało o tym myśleć podczas debugowania zwykłych programów, ale punkty przerwania nie zawsze są ustawiane sprzętowo. Czasami ustawienie punktu przerwania wiąże się z tymczasowym zapisaniem specjalnych instrukcji we właściwym miejscu bezpośrednio do kodu maszynowego. Na przykład tak działało moje standardowe polecenie b w GDB. Oto, co następuje:

  • nie możesz umieścić kropki w BootROM, ponieważ ROM
  • Możesz ustawić punkt przerwania na kodzie ładowanym do RAM z karty SD, ale musisz poczekać, aż zostanie załadowany. W przeciwnym razie nie przepiszemy fragmentu kodu, ale moduł ładujący przepisze nasz punkt przerwania

Jestem pewien, że możesz wyraźnie poprosić o użycie sprzętowych punktów przerwania, ale i tak jest ich ograniczona liczba.

Szybka wymiana BootROMu

Na początkowym etapie debugowania często pojawia się potrzeba naprawienia BootROMu i spróbowania ponownie. Jest jednak problem: BootROM jest częścią projektu załadowanego do FPGA, a jego synteza zajmuje kilka minut (i to po niemal natychmiastowej kompilacji samego obrazu BootROM z C i Assemblera...). Na szczęście w rzeczywistości wszystko o wiele szybciej: kolejność działań jest następująca:

  • zregeneruj bootrom.mif (przełączyłem się na MIF zamiast HEX, ponieważ zawsze miałem pewne problemy z HEX, a MIF jest natywnym formatem Altera)
  • w Quartusie Processing -> Update Memory Initialization File
  • na elemencie Asemblera (w lewej kolumnie Zadań) polecenie Uruchom ponownie

Wszystko o wszystkim - kilkadziesiąt sekund.

Przygotowanie karty SD

Wszystko tutaj jest stosunkowo proste, trzeba jednak uzbroić się w cierpliwość i dysponować około 14Gb miejsca na dysku:

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

Następnie należy włożyć czystą, a raczej taką, która nie zawiera niczego niezbędnego, kartę SD i uruchomić

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

… Gdzie sdX — urządzenie przypisane do karty. UWAGA: dane na karcie zostaną usunięte, nadpisane i w ogóle! Nie warto robić całego montażu od dołu sudoponieważ wtedy wszystkie artefakty kompilacji będą należeć do root, a montaż będzie musiał zostać wykonany od dołu sudo postojno.

Rezultatem jest karta oznaczona w GPT z czterema partycjami, z których jedna ma FAT uEnv.txt oraz obraz startowy w formacie FIT (zawiera kilka podobrazów, każdy z własnym adresem pobierania), druga partycja jest pusta, ma być sformatowana w Ext4 dla Linuksa. Jeszcze dwie sekcje – tajemniczy: U-Boot działa na jednym (jego przesunięcie, o ile rozumiem, jest na stałe zakodowane w BootROM), na drugim, wygląda na to, że jego zmienne środowiskowe działają, ale jeszcze ich nie używam.

Poziom pierwszy, BootROM

Popularna mądrość głosi: „Jeśli w programowaniu jest taniec z tamburynem, to w elektronice jest też taniec z gaśnicą”. Nie chodzi nawet o to, że kiedyś prawie spaliłem płytkę stwierdzając, że „No cóż, GND jest na tym samym niskim poziomie”. (widocznie rezystor by jednak nie zaszkodził...) Chodzi raczej o to, że jak ręce od tego nie urosną, to elektronika nie przestaje sprawiać niespodzianek: przy wlutowaniu złącza w płytkę nadal nie udało mi się dobrze wlutować styków - na filmie widać jak lut bezpośrednio się rozprowadza na całe połączenie wystarczy przyłożyć lutownicę, u mnie „uderzył” losowo. No cóż, może lut był nieodpowiedni do temperatury lutownicy, może coś innego... Generalnie jak zobaczyłem, że mam już kilkanaście styków, to poddałem się i zacząłem debugować. I wtedy się zaczęło tajemniczy: Podłączyłem RX/TX z UART, wgrałem firmware - wyskakuje

INIT
CMD0
ERROR

No cóż, wszystko jest logiczne - nie podłączałem modułu karty SD. Naprawiamy sytuację, ładujemy firmware... I cisza... Dlaczego nie zmieniłem zdania, ale małe pudełeczko właśnie się otworzyło: jeden z pinów modułu musiał być podłączony do VCC. W moim przypadku moduł obsługiwał napięcie zasilania 5V, więc bez zastanowienia podłączyłem przewód wychodzący z modułu na przeciwną stronę płytki. W rezultacie krzywo przylutowane złącze zostało przekrzywione i Styk UART został po prostu utracony. facepalm.jpg Ogólnie rzecz biorąc, „zła głowa nie daje odpoczynku nogom”, a krzywe ręce nie dają odpoczynku głowie…

W rezultacie zobaczyłem długo oczekiwany

INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
LOADING /

Co więcej, porusza się, a wskaźnik ładowania się kręci. Od razu pamiętam czasy szkolne i spokojne ładowanie MinuetOS z dyskietki. Chyba, że ​​napęd nie będzie trzepotał.

Problem w tym, że po komunikacie BOOT nic się nie dzieje. Oznacza to, że nadszedł czas, aby połączyć się przez OpenOCD z Raspberry, z GDB na hoście i zobaczyć, co to jest.

Po pierwsze, połączenie za pomocą GDB natychmiast to pokazało $pc (licznik programu, adres aktualnej instrukcji) leci do 0x0 - prawdopodobnie dzieje się to po wielu błędach. Dlatego natychmiast po wydaniu komunikatu BOOT Dodajmy nieskończoną pętlę. To go opóźni na jakiś czas...

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;
 }

Taki podstępny kod jest używany „ze względu na niezawodność”: gdzieś słyszałem, że nieskończona pętla jest zachowaniem niezdefiniowanym, ale kompilator raczej tego nie odgadnie (przypominam, że według 0x10000 zlokalizowany BootROM).

Część 3: Prawie ładuję Linuksa z karty SD do RocketChip

Wydawałoby się, czego jeszcze można się spodziewać - surowe osadzone, jakie są kody źródłowe? Ale w ten artykuł autor debugował kod C... 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.

Część 3: Prawie ładuję Linuksa z karty SD do RocketChip

Wystarczy pobrać nie plik MIF lub bin, ale oryginalną wersję w formacie ELF.

Teraz przy n-tej próbie możesz odgadnąć adres, pod którym wykonanie będzie kontynuowane (jest to kolejny powód, dla którego kompilator nie powinien był zgadnąć, że pętla jest nieskończona). Zespół

set variable $pc=0xADDR

pozwala na bieżąco zmieniać wartość rejestru (w tym wypadku adres aktualnej instrukcji). Za jego pomocą można zmieniać wartości zapisane w pamięci (i rejestrach mapowanych w pamięci).

Ostatecznie doszedłem do wniosku (nie wiem który jest poprawny), że mamy „obraz karty SD złego systemu” i trzeba iść nie na sam początek pobranych danych, ale do 0x89800 bajty dalej:

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

Być może wpływ na to miał też fakt, że nie mając pod ręką niepotrzebnej karty 4Gb, wziąłem kartę 2Gb i podłożyłem ją losowo w Makefile DEMO_END=11718750 na DEMO_END=3078900 (nie szukaj znaczenia w konkretnym znaczeniu - nie ma go, po prostu teraz obraz jest umieszczony na karcie).

Poziom drugi, U-Boot

Teraz jeszcze „spadamy”, ale już jesteśmy we właściwym miejscu 0x0000000080089a84. Tutaj muszę przyznać: faktycznie prezentacja nie idzie „z przerwą”, ale częściowo jest napisana „po”, więc tutaj już udało mi się wstawić poprawny plik dtb z naszego SoC, poprawić go w ustawieniach HiFive_U-Boot zmienny CONFIG_SYS_TEXT_BASE=0x80089800 (zamiast 0x08000000), aby adres pobierania był zgodny z rzeczywistym. Teraz ładujemy mapę następnego poziomu, kolejny obraz:

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

I widzimy:

   │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)                      │

Co więcej, przeskakujemy między liniami 308 i 309. I nie jest to zaskakujące, biorąc pod uwagę, że w $sp leży sens 0xfffffffe31cdc0a0. Niestety, on też ciągle „ucieka” z powodu linii 307. Spróbujmy zatem ustawić punkt przerwania w trap_entry, a następnie wróć do 0x80089800 (punkt wejścia U-Boota) i miejmy nadzieję, że nie wymaga prawidłowego ustawienia rejestrów przed skokiem... Wygląda na to, że to działa:

(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

Wskaźnik stosu jest taki sobie, szczerze mówiąc: wskazuje na całkowite ominięcie pamięci RAM (chyba że nie mamy już translacji adresów, ale miejmy nadzieję na prostą opcję).

Spróbujmy zastąpić wskaźnik 0x881cf950. W rezultacie dochodzimy do wniosku, że handle_trap wołamy i wołamy, a jednocześnie wchodzimy _exit_trap z argumentem epc=2148315240 (w systemie dziesiętnym):

(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

Ustaw punkt przerwania na strnlen, kontynuujemy i widzimy:

(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

Wydaje się być, _exit_trap chce dostarczyć informacje debugujące o wyjątku, który wystąpił, ale nie może. Więc w jakiś sposób nasze źródła nie są ponownie wyświetlane. set directories ../freedom-u-sdk/HiFive_U-Boot/ O! Teraz wyświetlane!

Cóż, uruchommy to jeszcze raz i sprawdźmy na stosie przyczynę pierwotnego problemu, który spowodował pierwszy błąd (mcause == 5). Jeśli dobrze zrozumiałem co jest napisane tutaj na stronie 37, oznacza to ten wyjątek Load access fault. Wydaje się, że przyczyną jest tutaj

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 ma to samo błędne znaczenie i wewnątrz board_init_f_init_reserve pojawia się błąd. Wygląda na to, że to jest winowajca: zmienna o jednoznacznej nazwie CONFIG_SYS_INIT_SP_ADDR. Jest to zdefiniowane w pliku HiFive_U-Boot/include/configs/HiFive-U540.h. W pewnym momencie nawet pomyślałem, że może, cóż, powinienem dodać bootloader dla procesora - może łatwiej byłoby trochę naprawić procesor? Ale potem zobaczyłem, że bardziej przypominał artefakt, który nie został w pełni ukończony#if 0-specyficzne ustawienia dla innej konfiguracji pamięci i możesz spróbować to zrobić:

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 */

W pewnym momencie liczba kul elementy złączne technologiczne osiągnął punkt krytyczny. Po małych zmaganiach doszedłem do potrzeby stworzenia odpowiedniego portu dla mojej płyty. W tym celu musimy skopiować i dostosować szereg plików tak, aby odpowiadały naszej konfiguracji.

Cóż, mniej więcej, oto mały stolik

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

Szczegóły można znaleźć w repozytoria.

Jak się okazało, na tej płycie SiFive rejestry niektórych urządzeń mają różne adresy. Okazało się też, że U-Boot konfiguruje się przy pomocy znanego już z jądra Linuksa mechanizmu Kconfig – można np. make menuconfig, a przed Tobą pojawi się wygodny interfejs tekstowy pokazujący opisy parametrów wg ? itp. Ogólnie rzecz biorąc, po złożeniu opisu trzeciej z opisów dwóch płytek, wyrzucając stamtąd wszelkiego rodzaju pretensjonalne rekonfiguracje PLL (najwyraźniej jest to w jakiś sposób powiązane ze sterowaniem z komputera hosta przez PCIe, ale nie jest to pewne) , otrzymałem jakiś firmware, który przy dobrej pogodzie na Marsie dał mi wiadomość przez UART z jakiego skrótu zatwierdzenia został skompilowany i ile mam DRAM-u (ale sam zapisałem tę informację w nagłówku).

Szkoda tylko, że po tym płyta zwykle przestała odpowiadać poprzez procesor JTAG, a ładowanie z karty SD nie jest niestety szybkie w mojej konfiguracji. Z drugiej strony czasami BootROM wyświetlał taki komunikat ERROR, nie udało się uruchomić i natychmiast wyskoczył U-Boot. Wtedy właśnie dotarło do mnie: najwyraźniej po ponownym uruchomieniu strumienia bitów do FPGA pamięć nie jest kasowana, nie ma czasu na „odtrenowanie” itp. Krótko mówiąc, możesz po prostu, gdy pojawi się komunikat LOADING / połącz się z debugerem i poleceniem set variable $pc=0x80089800, omijając w ten sposób to długie ładowanie (oczywiście przy założeniu, że ostatnim razem zepsuł się na tyle wcześnie, że nie miał czasu na załadowanie czegokolwiek ponad oryginalny kod).

Swoją drogą, czy to w ogóle normalne, że procesor całkowicie się zawiesza i debugger JTAG nie może się z nim połączyć za pomocą komunikatów?

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

Więc poczekaj! Już to widziałem! Coś podobnego dzieje się, gdy TileLink się zacina, a ja jakoś nie ufam autorowi kontrolera pamięci - sam to pisałem... Nagle, po pierwszej udanej przebudowie procesora po edycji kontrolera, zobaczyłem:

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

Do tej dziwnej linijki wcześniej In: serial nie zwracaj uwagi - próbowałem zrozumieć na wiszącym procesorze, czy działa poprawnie z otoczeniem. Co masz na myśli mówiąc: „Wisiało tak przez dziesięć minut”? Przynajmniej udało się przenieść i przejść do menu startowego! Mała dygresja: chociaż U-Boot ładowany jest w pierwszych 2^24 bajtach z karty SD, to przy uruchomieniu kopiuje się na dalszy adres, albo zapisany w nagłówku konfiguracyjnym, albo po prostu na wyższe adresy RAM , i wykonuje relokację znaków ELF i przekazuje tam kontrolę. A więc: wygląda na to, że przeszliśmy ten poziom i otrzymaliśmy bonus, że procesor nie trzymał się potem mocno.

Dlaczego więc timer nie działa? Wygląda na to, że zegar z jakiegoś powodu nie działa...

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

A co jeśli obrócisz strzałki ręcznie?

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

Następnie:

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

Wniosek: zegar nie tyka. Prawdopodobnie dlatego wprowadzanie danych z klawiatury nie działa:

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 == '[') {
...

Problem okazał się taki, że byłem trochę za sprytny: dodałem klucz do konfiguracji procesora:

  case DTSTimebase => BigInt(0)

... na podstawie faktu, że w komentarzu było napisane „jeśli nie wiesz, zostaw 0”. I po wszystkim WithNBigCores Po prostu ustawiłem go na 1 MHz (tak przy okazji, jak wskazano w konfiguracji U-Boota). Ale cholera, jestem schludny i skrupulatny: tam nie wiem, tutaj jest 25 MHz! W końcu nic nie działa. Usunąłem moje „ulepszenia” i…

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 #

Możesz nawet wprowadzać polecenia! Na przykład, po krótkim poszperaniu, możesz w końcu zgadnąć, aby wejść mmc_spi 1 10000000 0; mmc part, zmniejszając częstotliwość SPI z 20 MHz do 10 MHz. Dlaczego? Cóż, maksymalna częstotliwość 20 MHz została zapisana w konfiguracji i nadal jest tam zapisana. Ale o ile rozumiem, interfejsy, przynajmniej tutaj, działają w ten sposób: kod dzieli częstotliwość jednostki sprzętowej (moja wszędzie ma 25 MHz) przez wartość docelową i ustawia wynikową wartość jako dzielnik w odpowiedniej kontrolce rejestr. Problem polega na tym, że jeśli dla UART 115200 Hz jest mniej więcej tyle, ile potrzeba, to jeśli podzielisz 25000000 przez 20000000, otrzymasz 1, tj. będzie działać na częstotliwości 25 MHz. Może to normalne, ale jeśli są postawione ograniczenia, to znaczy, że ktoś tego potrzebuje (choć to nie jest pewne)… Generalnie łatwiej jest to odłożyć i iść dalej – daleko i, niestety, na długo. 25 MHz to nie Core i9.

Wyjście konsoli

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

OK, osiągnęliśmy kolejny poziom, ale nadal jest zimno. A czasem też posypuje wyjątkami. Możesz zobaczyć przyczynę, czekając na kod pod podanym adresem $pc oraz po si być włączonym trap_entry. Sam program obsługi U-Boot może wysyłać dane tylko dla mcause = 0..4, więc przygotuj się na utknięcie w nieprawidłowym rozruchu. Potem wszedłem do konfiguracji, zacząłem patrzeć na to, co zmieniałem, i przypomniałem sobie: tam conf/rvboot-fit.txt pisemny:

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

Cóż, dostosujmy wszystkie pliki do zgodności, zamień wiersz poleceń jądra na coś takiego, ponieważ istnieją podejrzenia, że SIF0 - to jest wyjście gdzieś przez PCIe:

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

I zmieńmy algorytm mieszający z SHA-256 na MD5: nie potrzebuję siły kryptograficznej (szczególnie przy debugowaniu), zajmuje to strasznie dużo czasu, a do wyłapywania błędów integralności podczas ładowania MD5 jest zbyt łatwy. Jaki jest efekt końcowy? Zaczęliśmy zauważalnie szybciej przechodzić poprzedni poziom (dzięki prostszemu haszowaniu), a kolejny otworzył się:

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

Ale zegar nie tyka...

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

Ups, wygląda na to, że korekta zegara okazała się placebo, chociaż wtedy wydawało mi się, że pomogło. Nie, oczywiście trzeba to naprawić, ale najpierw obróćmy ręcznie strzałki i zobaczmy, co się stanie:

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.

Tymczasem…

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

Nie, pójdę zautomatyzować zegar - w przeciwnym razie może zdecyduje się tam skalibrować timer!

Tymczasem adres aktualnej instrukcji wskazuje gdzieś

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>

wewnątrz załadowanego programu ładującego Berkeley Boot Loader. Osobiście denerwuje mnie w tym przypadku wzmianka htif — interfejs hosta służący do uruchamiania jądra na uwięzi (czyli we współpracy z hostem ARM), zakładałem standalone. Jeśli jednak znajdziesz tę funkcję w kodzie źródłowym, zobaczysz, że nie wszystko jest takie złe:

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"); }
  }
}

Zadanie: uruchom zegar

Poszukiwanie rejestrów w CLINT prowadzi nas do

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

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

Co łączy się z RTC, czyli z tajemniczym MockAON-em, o którym początkowo pomyślałem: „No i co my tu mamy? Niejasny? Wyłączmy to!” Ponieważ nadal nie rozumiem, jaka magia zegara się tam dzieje, więc po prostu ponownie zaimplementuję tę logikę w 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
  }

W drodze do jądra Linuksa

Tutaj historia już się przeciągnęła i stała się trochę monotonna, więc opiszę ją od góry do dołu:

BBL zakłada obecność FDT o godz 0xF0000000, ale już to poprawiłem! Cóż, spójrzmy jeszcze raz... Znalazłem to HiFive_U-Boot/arch/riscv/lib/boot.c, zastąpione przez 0x81F00000, określone w konfiguracji rozruchowej U-Boot.

Wtedy BBL poskarżył się, że nie ma pamięci. Moja ścieżka leżała w funkcji mem_prop, co w riscv-pk/machine/fdt.c: stamtąd dowiedziałem się, że musisz oznaczyć węzeł fdt ram jako device_type = "memory" - wtedy być może trzeba będzie poprawić generator procesora, ale na razie napiszę to ręcznie - w każdym razie ręcznie przeniosłem ten plik.

Teraz otrzymałem wiadomość (dostarczoną w sformatowanej formie, ze zwrotami karetki):

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.

Wydaje się, że opcje są wskazane w razie potrzeby riscv,kernel-start и riscv,kernel-end w DTB, ale zera są analizowane. Debugowanie query_chosen pokazało, że BBL próbuje przeanalizować adres 32-bitowy, ale napotyka parę <0x0 0xADDR>, a pierwsza wartość wydaje się być najmniej znaczącymi bitami. Dodano do sekcji chosen

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

i poprawiono generowanie wartości: nie dodawaj 0x0 pierwszy element.

Te 100500 XNUMX prostych kroków ułatwi obserwowanie upadku pingwina:

Ukryty tekst

   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)

(logo jest wyświetlane przez BBL, a to ze znacznikami czasu jest wyświetlane przez jądro).

Na szczęście nie wiem jak jest wszędzie, ale na RocketChip, gdy podłączysz debugger przez JTAG, możesz łapać pułapki od razu po wyjęciu z pudełka - debugger zatrzyma się dokładnie w tym momencie.

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); // < ВЫ НАХОДИТЕСЬ ЗДЕСЬ
}

Jak mawiał stary żart, Nie znaleziono procesora, uruchomiona jest emulacja oprogramowania. No cóż, albo nie biegać. Zagubiony w jednym rdzeniu procesora.

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

Niezły komentarz w linux/arch/riscv/kernel/setup.c - rodzaj malowania płotu metodą Tomka Sawyera. Generalnie z jakiegoś powodu dzisiaj nie było zwycięzców, nagroda przechodzi na następne losowanie...

Na tym proponuję zakończyć i tak już przydługi artykuł.

Ciąg dalszy nastąpi. Rozpocznie się walka z przebiegłym robakiem, który uda się ukryć, jeśli powoli podkradniesz się do niego pojedynczym krokiem.

Screencast do pobrania tekstu (link zewnętrzny):
Część 3: Prawie ładuję Linuksa z karty SD do RocketChip

Źródło: www.habr.com

Dodaj komentarz