Część 4: W końcu uruchamiamy Linux na RocketChip RISC-V

Część 4: W końcu uruchamiamy Linux na RocketChip RISC-V Na obrazku jądro Linuxa wysyła do Ciebie pozdrowienia przez GPIO.

W tej części historii dotyczącej portowania RISC-V RocketChip na chińską płytę z Cyclone IV uruchomimy Linuxa oraz nauczymy się konfigurować IP Core kontrolera pamięci i nieco poprawimy opis sprzętu w pliku dts. Ten artykuł jest kontynuacją trzeciej części, ale w przeciwieństwie do znacznie obszerniejszej poprzedniej, będzie dość krótki.

… Ponieważ setup_smp jest wywoływana tylko wtedy, gdy jądro jest skonfigurowane z obsługą SMP, można spróbować wyłączyć wielordzeniowość. Będzie to nieprzyjemne, jeśli będę musiał jeszcze wprowadzić wsparcie dla atomowości w module pamięci (wydawało mi się, że z tym cache sobie poradzi), ale w przypadku jednego rdzenia nam to nie powinno przeszkadzać.

I tak się nie udało…

[    0.000000] OF: fdt: Ignorowanie zakresu pamięci 0x80000000 - 0x80200000
[    0.000000] Wersja Linuksa 4.19.0-sifive-1+ (trosinenko@trosinenko-pc) (gcc wersja 8.3.0 (Buildroot 2019.02-07449-g4eddd28f99)) #1 Cz 4 lip 2019 18:38:10 MSK
[    0.000000] bootconsole [early0] włączone
[    0.000000] Wczesny ramdisk na: 0x(____ptrval____) (16777216 bajtów)
[    0.000000] Zakresy stref:
[    0.000000]   DMA32    [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000]   Normal   [mem 0x00000000c0000000-0x00000bffffffffff]
[    0.000000] Rozpoczęcie strefy ruchomej dla każdego węzła
[    0.000000] Wczesne zakresy węzłów pamięci
[    0.000000]   węzeł   0: [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] Ustawienia pamięci węzła 0 [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] Software IO TLB: zmapowane [mem 0xbb1fc000-0xbf1fc000] (64MB)
[    0.000000] Nie można znaleźć wpisu "cpu" w drzewie urządzeń
[    0.000000] Zbudowano 1 listy stref, grupowanie mobilności włączone.  Łączna liczba stron: 258055
[    0.000000] Linia poleceń jądra: debug console=ttySIF0,125200 root=\/dev\/mmcblk0p2 loglevel=7 rootwait
[    0.000000] Wpisy w tabeli pamięci podręcznej dentry: 131072 (zamówienie: 8, 1048576 bajtów)
[    0.000000] Wpisy w tabeli pamięci podręcznej inode: 65536 (zamówienie: 7, 524288 bajtów)
[    0.000000] Sortowanie __ex_table...
[    0.000000] BŁĄD: Zły stan strony w procesie swapper pfn:80bf7
[    0.000000] strona:ffffffe03f029e08 licznik:0 mapcount:-65280 mapowanie:0000000000000000 indeks:0x1
[    0.000000] flagi: 0x0()
[    0.000000] surowe: 0000000000000000 0000000000000100 0000000000000200 0000000000000000
[    0.000000] surowe: 0000000000000001 0000000000000000 00000000ffff00ff
[    0.000000] strona zrzutowana, ponieważ: mapcount nietypowy
[    0.000000] Moduły połączone w:
[    0.000000] CPU: 0 PID: 0 Komenda: swapper Nie zanieczyszczony 4.19.0-sifive-1+ #1
[    0.000000] Ślad wywołań:
[    0.000000] [<ffffffe00002cfde>] walk_stackframe+0x0/0xa0
[    0.000000] [<ffffffe00002d1da>] show_stack+0x2a/0x34
[    0.000000] [<ffffffe00063c898>] chachachllachachachTradeonachachach_inachachipachachachachllachachachTradeonachachachdevachachachypachachachllachachachTradeona+0x20/0x28
[    0.000000] [<ffffffe00009499c>] bad_page+0xe4/0x102
[    0.000000] [<ffffffe0000949f2>] free_pages_check_bad+0x38/0x7a
[    0.000000] [<ffffffe000094bb0>] free_pcppages_bulk+0x106/0x36c
[    0.000000] [<ffffffe0000957be>] free_unref_page_commit.isra.23+0x86/0x8e
[    0.000000] [<ffffffe0000960bc>] free_unref_page+0x44/0x58
[    0.000000] [<ffffffe0000960de>] __free_pages.part.28+0xe/0x22
[    0.000000] [<ffffffe0000056da>] __free_pages_bootmem+0x86/0x90
[    0.000000] [<ffffffe000007c46>] free_all_bootmem+0x156/0x1ba
[    0.000000] [<ffffffe0000023f8>] mem_init+0x36/0x44
[    0.000000] [<ffffffe0000007a0>] start_kernel+0x1aa/0x328
[    0.000000] [<ffffffe000000058>] _sinittext+0x58/0x5c
[    0.000000] Wyłączanie debugowania blokady z powodu zanieczyszczenia jądra
[    0.000000] BŁĄD: Zły stan strony w procesie swapper pfn:80bf9
[    0.000000] strona:ffffffe03f029e78 licznik:0 mapcount:-65280 mapowanie:0000000000000000 indeks:0x0
[    0.000000] flagi: 0x0()
[    0.000000] surowe: 0000000000000000 ffffffe03f020080 ffffffe03f020080 0000000000000000
[    0.000000] surowe: 0000000000000000 0000000000000000 00000000ffff00ff
[    0.000000] strona zrzutowana, ponieważ: mapcount nietypowy
[    0.000000] Moduły połączone w:
[    0.000000] CPU: 0 PID: 0 Komenda: swapper Zanieczyszczone: G    B             4.19.0-sifive-1+ #1
[    0.000000] Ślad wywołań:
[    0.000000] [<ffffffe00002cfde>] walk_stackframe+0x0/0xa0
[    0.000000] [<ffffffe00002d1da>] show_stack+0x2a/0x34
[    0.000000] [<ffffffe00063c898>] chachachllachachachTradeonachachach_inachachipachachachachllachachachTradeonachachachdevachachachypachachachllachachachTradeona+0x20/0x28
[    0.000000] [<ffffffe00009499c>] bad_page+0xe4/0x102
[    0.000000] [<ffffffe0000949f2>] free_pages_check_bad+0x38/0x7a
[    0.000000] [<ffffffe0000955c6>] __free_pages_ok+0x3fe/0x430
[    0.000000] [<ffffffe0000960ea>] __free_pages.part.28+0x1a/0x22
[    0.000000] [<ffffffe0000056da>] __free_pages_bootmem+0x86/0x90
[    0.000000] [<ffffffe000007c46>] free_all_bootmem+0x156/0x1ba
[    0.000000] [<ffffffe0000023f8>] mem_init+0x36/0x44
[    0.000000] [<ffffffe0000007a0>] start_kernel+0x1aa/0x328
[    0.000000] [<ffffffe000000058>] _sinittext+0x58/0x5c
[    0.000000] BŁĄD: Zły stan strony w procesie swapper pfn:80bfa
[    0.000000] strona:ffffffe03f029eb0 licznik:0 mapcount:-65280 mapowanie:0000000000000000 indeks:0x0
[    0.000000] flagi: 0x0()
[    0.000000] surowe: 0000000000000000 ffffffe03f0200b8 ffffffe03f0200b8 0000000000000000
[    0.000000] surowe: 0000000000000000 0000000000000000 00000000ffff00ff
[    0.000000] strona zrzutowana, ponieważ: mapcount nietypowy
[    0.000000] Moduły połączone w:
[    0.000000] CPU: 0 PID: 0 Komenda: swapper Zanieczyszczone: G    B             4.19.0-sifive-1+ #1
[    0.000000] Ślad wywołań:
[    0.000000] [<ffffffe00002cfde>] walk_stackframe+0x0/0xa0
[    0.000000] [<ffffffe00002d1da>] show_stack+0x2a/0x34
[    0.000000] [<ffffffe00063c898>] chachachllachachachTradeonachachach_inachachipachachachachllachachachTradeonachachachdevachachachypachachachllachachachTradeona+0x20/0x28
[    0.000000] [<ffffffe00009499c>] bad_page+0xe4/0x102
[    0.000000] [<ffffffe0000949f2>] free_pages_check_bad+0x38/0x7a
[    0.000000] [<ffffffe0000955c6>] __free_pages_ok+0x3fe/0x430
[    0.000000] [<ffffffe0000960ea>] __free_pages.part.28+0x1a/0x22
[    0.000000] [<ffffffe0000056da>] __free_pages_bootmem+0x86/0x90
[    0.000000] [<ffffffe000007c46>] free_all_bootmem+0x156/0x1ba
[    0.000000] [<ffffffe0000023f8>] mem_init+0x36/0x44
[    0.000000] [<ffffffe0000007a0>] start_kernel+0x1aa/0x328
[    0.000000] [<ffffffe000000058>] _sinittext+0x58/0x5c
[    0.000000] BŁĄD: Zły stan strony w procesie swapper pfn:80bfb
[    0.000000] strona:ffffffe03f029ee8 licznik:0 mapcount:-65280 mapowanie:0000000000000000 indeks:0x0
[    0.000000] flagi: 0x0()
[    0.000000] surowe: 0000000000000000 ffffffe03f0200f0 ffffffe03f0200f0 0000000000000000
[    0.000000] surowe: 0000000000000000 0000000000000000 00000000ffff00ff
[    0.000000] strona zrzutowana, ponieważ: mapcount nietypowy
[    0.000000] Moduły połączone w:
[    0.000000] CPU: 0 PID: 0 Komenda: swapper Zanieczyszczone: G    B             4.19.0-sifive-1+ #1
[    0.000000] Ślad wywołań:
[    0.000000] [<ffffffe00002cfde>] walk_stackframe+0x0/0xa0
[    0.000000] [<ffffffe00002d1da>] show_stack+0x2a/0x34
[    0.000000] [<ffffffe00063c898>] chachachllachachachTradeonachachach_inachachipachachachachllachachachTradeonachachachdevachachachypachachachllachachachTradeona+0x20/0x28
[    0.000000] [<ffffffe00009499c>] bad_page+0xe4/0x102
[    0.000000] [<ffffffe0000949f2>] free_pages_check_bad+0x38/0x7a
[    0.000000] [<ffffffe0000955c6>] __free_pages_ok+0x3fe/0x430
[    0.000000] [<ffffffe0000960ea>] __free_pages.part.28+0x1a/0x22
[    0.000000] [<ffffffe0000056da>] __free_pages_bootmem+0x86/0x90
[    0.000000] [<ffffffe000007c46>] free_all_bootmem+0x156/0x1ba
[    0.000000] [<ffffffe0000023f8>] mem_init+0x36/0x44
[    0.000000] [<ffffffe0000007a0>] start_kernel+0x1aa/0x328
[    0.000000] [<ffffffe000000058>] _sinittext+0x58/0x5c
[    0.000000] BŁĄD: Zły stan strony w procesie swapper pfn:80bfc
[    0.000000] strona:ffffffe03f029f20 licznik:0 mapcount:-65280 mapowanie:0000000000000000 indeks:0x0
[    0.000000] flagi: 0x0()
[    0.000000] surowe: 0000000000000000 ffffffe03f020028 ffffffe03f020028 0000000000000000
[    0.000000] surowe: 0000000000000000 0000000000000000 00000000ffff00ff
...

Ciekawe, że po całym debugowaniu postanowiłem zobaczyć, jak powinien wyglądać „referencyjny” wynik jądra, powiedział make qemu, i zobaczyłem podobny błąd.

Niestety, kolejny poziom (jądro Linuxa) jest trudniejszy: GDB, podłączony przez JTAG do RocketChip, działa, jak się zdaje, z adresami fizycznymi, niezależnie od tego, jak zdefiniowane jest MMU, a jądro w interesującym nas momencie już ustawiło mapowanie adresów, więc nie możemy po prostu powiedzieć display/10i $pc nie damy rady — zobaczymy tylko dużo unimp. Z drugiej strony, jeśli już ustaliliśmy przesunięcie (wydaje się, że 0x80200000 -> 0xffffffe000000000), można wymyślić sprytny sposób:

1: x/10i $pc
=> 0xffffffe000002436 :      unimp
   0xffffffe000002438 :    unimp
   0xffffffe00000243a :    unimp
   0xffffffe00000243c :    unimp
   0xffffffe00000243e :    unimp
   0xffffffe000002440 :   unimp
   0xffffffe000002442 :   unimp
   0xffffffe000002444 :   unimp
   0xffffffe000002446 :   unimp
   0xffffffe000002448 :   unimp
(gdb) undisplay 1
(gdb) display/10i ($pc - 0xffffffe000002436 + 0x80202436)
2: x/10i ($pc - 0xffffffe000002436 + 0x80202436)
   0x80202436:  addi    sp,sp,-96
   0x80202438:  sd      s0,80(sp)
   0x8020243a:  sd      s1,72(sp)
   0x8020243c:  sd      s2,64(sp)
   0x8020243e:  sd      s3,56(sp)
   0x80202440:  sd      s4,48(sp)
   0x80202442:  sd      s5,40(sp)
   0x80202444:  sd      s6,32(sp)
   0x80202446:  sd      s7,24(sp)
   0x80202448:  sd      s8,16(sp)

Punkty przerwania ustawia się podobnie:

(gdb) p setup_smp
$1 = {} 0xffffffe000002436 
(gdb) b *0x80202436
Breakpoint 1 at 0x80202436
(gdb) c
Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.
setup_smp () at /home/trosinenko/freedom-u-sdk/linux/arch/riscv/kernel/smpboot.c:58
58              while ((dn = of_find_node_by_type(dn, "cpu"))) {
(gdb) bt
#0  setup_smp () at /home/trosinenko/freedom-u-sdk/linux/arch/riscv/kernel/smpboot.c:58
#1  0xffffffe0000022a2 in setup_arch () at /home/trosinenko/freedom-u-sdk/linux/arch/riscv/kernel/setup.c:253
#2  0x0000000000000000 in ?? ()
Backtrace stopped: frame did not save the PC

Tylko że muszę je ręcznie usunąć, aby pójść dalej — dla GDB to jakiś zupełnie niezrozumiały pułapka. A tak, jeśli przypadkiem powiesz s lub n zamiast si, gdb spróbuje wstawić niejawny punkt przerwania, który z oczywistych powodów nie zostanie ustawiony, ale nie ma sposobu, aby przekonać GDB do zmiany. Ale nie spiesz się z restartowaniem płytki — z moich obserwacji wystarczy tylko uruchomić gdb ponownie (pamiętaj, że stare punkty przerwania mogą nie zniknąć).

Właściwie trzeba będzie zająć się pierwotnym problemem: ustawieniem punktów przerwania printk do funkcji of_find_node_by_type, używaną przez kod wyszukiwania rdzenia procesora startowego, pokazuje, że widzi jakieś śmieci zamiast nazw typów urządzeń. Na przykład skądś bierze linię „Port szeregowy”, chociaż takiego wpisu rzeczywiście brakuje w dtb w polu typ_urządzenia. Po poszukiwania tych bajtów w obrazie jądra, znalazłem je, spojrzałem na przesunięcie i zrozumiałem oczywistą rzecz: rozmiar jądra w parametrach rozruchowych jest wyraźnie niedoszacowany: przydzielono mu 8Mb, a zajmuje ponad dziesięć. Przydzieliłem 16Mb, ale dziwności nie ustały. Po tańcach z czytaniem FDT z różnych adresów (standardowo adres jest przekazywany do jądra w rejestrze a1), w końcu zdecydowałem się na śledzenie, co właściwie teraz jest analizowane. Na szczęście, zamiast przechodzić przez debugger (a pamiętamy, że są niuanse z translacją adresów, które trzeba jakoś rozwiązać), można po prostu i w sposób nieco prowizoryczny w pliku drivers/of/fdt.c wskazać #define pr_debug printk. W rezultacie wyszło:

dodanie wypisania diagnostycznego

diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c
index 3db0d71d128b..fac47df9d1db 100644
--- a/arch/riscv/kernel/setup.c
+++ b/arch/riscv/kernel/setup.c
@@ -173,6 +173,7 @@ asmlinkage void __init setup_vm(void)

 void __init parse_dtb(unsigned int hartid, void *dtb)
 {
+       printk("HART = %u DTB = %lxn", hartid, dtb);
        early_init_dt_scan(__va(dtb));
 }

diff --git a/drivers/of/base.c b/drivers/of/base.c
index 74eaedd5b860..c019e4158e1d 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -940,10 +940,12 @@ struct device_node *of_find_node_by_type(struct device_node *from,
        unsigned long flags;

        raw_spin_lock_irqsave(&devtree_lock, flags);
-       for_each_of_allnodes_from(from, np)
+       for_each_of_allnodes_from(from, np) {
+               printk("%lx %lx [%s] | [%s]n", from, np, np->type, type);
                if (np->type && (of_node_cmp(np->type, type) == 0)
                    && of_node_get(np))
                        break;
+       }
        of_node_put(from);
        raw_spin_unlock_irqrestore(&devtree_lock, flags);
        return np;
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 800ad252cf9c..2b45870ebada 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -31,6 +31,8 @@

 #include "of_private.h"

+#define pr_debug printk
+
 /*
  * of_fdt_limit_memory - limit the number of regions in the /memory node
  * @limit: maximum entries
@@ -1253,6 +1255,7 @@ bool __init early_init_dt_scan(void *params)
 */
 void __init unflatten_device_tree(void)
 {
+       printk("INITIAL: %lxn", initial_boot_params);
        __unflatten_device_tree(initial_boot_params, NULL, &of_root,
                                early_init_dt_alloc_memory_arch, false);

Wyjście na sprzęcie

                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
 {
  #address-cells = ;
  #size-cells = ;
  compatible = "freechips,rocketchip-unknown-dev";
  model = "freechips,rocketchip-unknown";
  chosen {
    linux,initrd-start = ;
    riscv,kernel-end = ;
    riscv,kernel-start = ;
    #address-cells = ;
    #size-cells = ;
    bootargs = ;
  }
  firmware {
    sifive,uboot = "YYYY-MM-DD";
  }
  aliases {
    serial0 = ;
  }
  cpus {
    #address-cells = ;
    #size-cells = ;
    cpu@0 {
      device_type = "cpu";
      clock-frequency = ;
      compatible = "sifive,rocket0", "riscv";
      d-cache-block-size = ;
      d-cache-sets = ;
      d-cache-size = ;
      d-tlb-sets = ;
      d-tlb-size = ;
      i-cache-block-size = ;
      i-cache-sets = ;
      i-cache-size = ;
      i-tlb-sets = ;
      i-tlb-size = ;
      mmu-type = "riscv,sv39";
      next-level-cache = ;
      reg = ;
      riscv,isa = "rv64imafdc";
      status = "okay";
      timebase-frequency = ;
      tlb-split;
      interrupt-controller {
        #interrupt-cells = ;
        compatible = "riscv,cpu-intc";
        interrupt-controller;
        phandle = ;
      }
    }
  }
  ram@80000000 {
    device_type = "memory";
    reg = ;
    reg-names = "mem";
    phandle = ;
  }
  soc {
    #address-cells = ;
    #size-cells = ;
    compatible = "freechips,rocketchip-unknown-soc", "simple-bus";
    ranges;
    clint@2000000 {
      compatible = "riscv,clint0";
      interrupts-extended = ;
      reg = ;
      reg-names = "control";
    }
    debug-controller@0 {
      compatible = "sifive,debug-013", "riscv,debug-013";
      interrupts-extended = ;
      reg = ;
      reg-names = "control";
    }
    gpio@64002000 {
      #gpio-cells = ;
      #interrupt-cells = ;
      compatible = "sifive,gpio0";
      gpio-controller;
      interrupt-controller;
      interrupt-parent = ;
      interrupts = ;
      reg = ;
      reg-names = "control";
    }
    interrupt-controller@c000000 {
      #interrupt-cells = ;
      compatible = "riscv,plic0";
      interrupt-controller;
      interrupts-extended = ;
      reg = ;
      reg-names = "control";
      riscv,max-priority = ;
      riscv,ndev = ;
      phandle = ;
    }
    rom@10000 {
      compatible = "sifive,maskrom0";
      reg = ;
      reg-names = "mem";
    }
    serial@64000000 {
      compatible = "sifive,uart0";
      interrupt-parent = ;
      interrupts = ;
      reg = ;
      reg-names = "control";
    }
    spi@64001000 {
      #address-cells = ;
      #size-cells = ;
      compatible = "sifive,spi0";
      interrupt-parent = ;
      interrupts = ;
      reg = ;
      reg-names = "control";
      mmc@0 {
        compatible = "mmc-spi-slot";
        disable-wp;
        reg = ;
        spi-max-frequency = ;
        voltage-ranges = ;
      }
    }
    tlclk {
      #clock-cells = ;
      clock-frequency = ;
      clock-output-names = "tlclk";
      compatible = "fixed-clock";
    }
  }
}
[    0.000000] HART = 0 DTB = 81200000
[    0.000000] search "chosen", depth: 0, uname:
[    0.000000] search "chosen", depth: 1, uname: chosen
[    0.000000] Looking for initrd properties...
[    0.000000] Command line is: debug console=ttySIF0,125200 root=\/dev\/mmcblk0p2 loglevel=7 rootwait
[    0.000000] dt_root_size_cells = 1
[    0.000000] dt_root_addr_cells = 1
[    0.000000] memory scan node ram@80000000, reg size 8,
[    0.000000]  - 80000000 ,  40000000
[    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 Sat Jul 6 12:14:41 MSK 2019
[    0.000000] bootconsole [early0] enabled
[    0.000000] initrd not found or empty - disabling initrd
[    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] INITIAL: ffffffe001000000
[    0.000000]  -> unflatten_device_tree()
[    0.000000] Unflattening device tree:
[    0.000000] magic: d00dfeed
[    0.000000] size: 00001000
[    0.000000] version: 00000011
[    0.000000]   size is 13512, allocating...
[    0.000000]   unflattening (____ptrval____)...
[    0.000000] fixed up name for  ->
[    0.000000] fixed up name for chosen -> chosen
[    0.000000] fixed up name for firmware -> firmware
[    0.000000] fixed up name for aliases -> aliases
[    0.000000] fixed up name for cpus -> cpus
[    0.000000] fixed up name for cpu@0 -> cpu
[    0.000000] fixed up name for interrupt-controller -> interrupt-controller
[    0.000000] fixed up name for ram@80000000 -> ram
[    0.000000] fixed up name for soc -> soc
[    0.000000] fixed up name for clint@2000000 -> clint
[    0.000000] fixed up name for debug-controller@0 -> debug-controller
[    0.000000] fixed up name for gpio@64002000 -> gpio
[    0.000000] fixed up name for interrupt-controller@c000000 -> interrupt-controller
[    0.000000] fixed up name for rom@10000 -> rom
[    0.000000] fixed up name for serial@64000000 -> serial
[    0.000000] fixed up name for spi@64001000 -> spi
[    0.000000] fixed up name for mmc@0 -> mmc
[    0.000000] fixed up name for tlclk -> tlclk
[    0.000000]  <- unflatten_device_tree()
[    0.000000] software IO TLB: mapped [mem 0xbb1fc000-0xbf1fc000] (64MB)
[    0.000000] 0 ffffffe03effcab0 [attach() failed for %s with error %d
[    0.000000] ] | [cpu]
[    0.000000] 0 ffffffe03eff0020 [(null)] | [cpu]

Wyjście w QEMU

                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
 {
  #address-cells = ;
  #size-cells = ;
  compatible = "riscv-virtio";
  model = "riscv-virtio,qemu";
  chosen {
    linux,initrd-start = ;
    linux,initrd-end = ;
    riscv,kernel-start = ;
    riscv,kernel-end = ;
    bootargs = "";
    stdout-path = ;
  }
  uart@10000000 {
    interrupts = ;
    interrupt-parent = ;
    clock-frequency = ;
    reg = ;
    compatible = "ns16550a";
  }
  test@100000 {
    reg = ;
    compatible = "sifive,test0";
  }
  virtio_mmio@10008000 {
    interrupts = ;
    interrupt-parent = ;
    reg = ;
    compatible = "virtio,mmio";
  }
  virtio_mmio@10007000 {
    interrupts = ;
    interrupt-parent = ;
    reg = ;
    compatible = "virtio,mmio";
  }
  virtio_mmio@10006000 {
    interrupts = ;
    interrupt-parent = ;
    reg = ;
    compatible = "virtio,mmio";
  }
  virtio_mmio@10005000 {
    interrupts = ;
    interrupt-parent = ;
    reg = ;
    compatible = "virtio,mmio";
  }
  virtio_mmio@10004000 {
    interrupts = ;
    interrupt-parent = ;
    reg = ;
    compatible = "virtio,mmio";
  }
  virtio_mmio@10003000 {
    interrupts = ;
    interrupt-parent = ;
    reg = ;
    compatible = "virtio,mmio";
  }
  virtio_mmio@10002000 {
    interrupts = ;
    interrupt-parent = ;
    reg = ;
    compatible = "virtio,mmio";
  }
  virtio_mmio@10001000 {
    interrupts = ;
    interrupt-parent = ;
    reg = ;
    compatible = "virtio,mmio";
  }
  cpus {
    #address-cells = ;
    #size-cells = ;
    timebase-frequency = ;
    cpu@0 {
      device_type = "cpu";
      reg = ;
      status = "okay";
      compatible = "riscv";
      riscv,isa = "rv64imafdcsu";
      mmu-type = "riscv,sv48";
      clock-frequency = ;
      interrupt-controller {
        #interrupt-cells = ;
        interrupt-controller;
        compatible = "riscv,cpu-intc";
        linux,phandle = ;
        phandle = ;
      }
    }
  }
  memory@80000000 {
    device_type = "memory";
    reg = ;
  }
  soc {
    #address-cells = ;
    #size-cells = ;
    compatible = "simple-bus";
    ranges;
    pci@30000000 {
      interrupt-map-mask = ;
      interrupt-map = ;
      ranges = ;
      reg = ;
      dma-coherent;
      bus-range = ;
      linux,pci-domain = ;
      device_type = "pci";
      compatible = "pci-host-ecam-generic";
      #size-cells = ;
      #interrupt-cells = ;
      #address-cells = ;
    }
    interrupt-controller@c000000 {
      linux,phandle = ;
      phandle = ;
      riscv,ndev = ;
      riscv,max-priority = ;
      reg-names = "control";
      reg = ;
      interrupts-extended = ;
      interrupt-controller;
      compatible = "riscv,plic0";
      #interrupt-cells = ;
      #address-cells = ;
    }
    clint@2000000 {
      interrupts-extended = ;
      reg = ;
      compatible = "riscv,clint0";
    }
  }
}
[    0.000000] HART = 0 DTB = 80e00000
[    0.000000] search "chosen", depth: 0, uname:
[    0.000000] search "chosen", depth: 1, uname: chosen
[    0.000000] Looking for initrd properties...
[    0.000000] initrd_start=0x88000000  initrd_end=0x88442b86
[    0.000000] Command line is: earlyprintk
[    0.000000] dt_root_size_cells = 2
[    0.000000] dt_root_addr_cells = 2
[    0.000000] memory scan node memory@80000000, reg size 16,
[    0.000000]  - 80000000 ,  40000000
[    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 Sat Jul 6 12:14:41 MSK 2019
[    0.000000] bootconsole [early0] enabled
[    0.000000] Initial ramdisk at: 0x(____ptrval____) (4467590 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] INITIAL: ffffffe000c00000
[    0.000000]  -> unflatten_device_tree()
[    0.000000] Unflattening device tree:
[    0.000000] magic: d00dfeed
[    0.000000] size: 00000d56
[    0.000000] version: 00000011
[    0.000000]   size is 14192, allocating...
[    0.000000]   unflattening (____ptrval____)...
[    0.000000] fixed up name for  ->
[    0.000000] fixed up name for chosen -> chosen
[    0.000000] fixed up name for uart@10000000 -> uart
[    0.000000] fixed up name for test@100000 -> test
[    0.000000] fixed up name for virtio_mmio@10008000 -> virtio_mmio
[    0.000000] fixed up name for virtio_mmio@10007000 -> virtio_mmio
[    0.000000] fixed up name for virtio_mmio@10006000 -> virtio_mmio
[    0.000000] fixed up name for virtio_mmio@10005000 -> virtio_mmio
[    0.000000] fixed up name for virtio_mmio@10004000 -> virtio_mmio
[    0.000000] fixed up name for virtio_mmio@10003000 -> virtio_mmio
[    0.000000] fixed up name for virtio_mmio@10002000 -> virtio_mmio
[    0.000000] fixed up name for virtio_mmio@10001000 -> virtio_mmio
[    0.000000] fixed up name for cpus -> cpus
[    0.000000] fixed up name for cpu@0 -> cpu
[    0.000000] fixed up name for interrupt-controller -> interrupt-controller
[    0.000000] fixed up name for memory@80000000 -> memory
[    0.000000] fixed up name for soc -> soc
[    0.000000] fixed up name for pci@30000000 -> pci
[    0.000000] fixed up name for interrupt-controller@c000000 -> interrupt-controller
[    0.000000] fixed up name for clint@2000000 -> clint
[    0.000000]  <- unflatten_device_tree()
[    0.000000] software IO TLB: mapped [mem 0xbb1fc000-0xbf1fc000] (64MB)
[    0.000000] 0 ffffffe03effc808 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effca78 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effcd98 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effd068 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effd230 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effd4b8 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effd740 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effd9c8 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effdc50 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effded8 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effe160 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effe3e8 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effe670 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effe888 [cpu] | [cpu]
[    0.000000] ffffffe03effe888 ffffffe03effec00 [<NULL>] | [cpu]
[    0.000000] ffffffe03effe888 ffffffe03effeee8 [memory] | [cpu]
[    0.000000] ffffffe03effe888 ffffffe03efff0b0 [<NULL>] | [cpu]
[    0.000000] ffffffe03effe888 ffffffe03efff320 [pci] | [cpu]
[    0.000000] ffffffe03effe888 ffffffe03efff858 [<NULL>] | [cpu]
[    0.000000] ffffffe03effe888 ffffffe03efffd58 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effc808 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effca78 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effcd98 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effd068 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effd230 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effd4b8 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effd740 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effd9c8 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effdc50 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effded8 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effe160 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effe3e8 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effe670 [<NULL>] | [cpu]
[    0.000000] 0 ffffffe03effe888 [cpu] | [cpu]
[    0.000000] elf_hwcap is 0x112d
[    0.000000] percpu: Embedded 17 pages/cpu @(____ptrval____) s29400 r8192 d32040 u69632
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 258055
[    0.000000] Kernel command line: earlyprintk
[    0.000000] Dentry cache hash table entries: 131072 (order: 8, 1048576 bytes)
[    0.000000] Inode-cache hash table entries: 65536 (order: 7, 524288 bytes)
[    0.000000] Sorting __ex_table...
[    0.000000] Memory: 949716K/1046528K available (6565K kernel code, 342K rwdata, 2564K rodata, 208K init, 832K bss, 96812K reserved, 0K cma-reserved)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] rcu: Hierarchical RCU implementation.
[    0.000000] rcu:     RCU event tracing is enabled.
[    0.000000] rcu:     RCU restricting CPUs from NR_CPUS=8 to nr_cpu_ids=1.
[    0.000000] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=1
[    0.000000] NR_IRQS: 0, nr_irqs: 0, preallocated irqs: 0
[    0.000000] plic: mapped 53 interrupts to 2 (out of 2) handlers.
[    0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x24e6a1710, max_idle_ns: 440795202120 ns
[    0.000000] Console: colour dummy device 80x25
[    0.000000] console [tty0] enabled
[    0.000000] bootconsole [early0] disabled
[    0.000000] Calibrating delay loop (skipped), value calculated using timer frequency.. 20.00 BogoMIPS (lpj=100000)
[    0.000000] pid_max: default: 32768 minimum: 301
[    0.000000] Mount-cache hash table entries: 2048 (order: 2, 16384 bytes)
[    0.000000] Mountpoint-cache hash table entries: 2048 (order: 2, 16384 bytes)
[    0.020000] rcu: Hierarchical SRCU implementation.
[    0.020000] smp: Bringing up secondary CPUs ...
[    0.020000] smp: Brought up 1 node, 1 CPU
[    0.050000] devtmpfs: initialized
[    0.050000] 0 ffffffe03effc808 [<NULL>] | [cpu]
[    0.050000] 0 ffffffe03effca78 [<NULL>] | [cpu]
[    0.050000] 0 ffffffe03effcd98 [<NULL>] | [cpu]
[    0.050000] 0 ffffffe03effd068 [<NULL>] | [cpu]
[    0.050000] 0 ffffffe03effd230 [<NULL>] | [cpu]
[    0.050000] 0 ffffffe03effd4b8 [<NULL>] | [cpu]
[    0.050000] 0 ffffffe03effd740 [<NULL>] | [cpu]
[    0.050000] 0 ffffffe03effd9c8 [<NULL>] | [cpu]
[    0.050000] 0 ffffffe03effdc50 [<NULL>] | [cpu]
[    0.050000] 0 ffffffe03effded8 [<NULL>] | [cpu]
[    0.050000] 0 ffffffe03effe160 [<NULL>] | [cpu]
[    0.050000] 0 ffffffe03effe3e8 [<NULL>] | [cpu]
[    0.050000] 0 ffffffe03effe670 [<NULL>] | [cpu]
[    0.050000] 0 ffffffe03effe888 [cpu] | [cpu]
[    0.050000] random: get_random_u32 called from bucket_table_alloc+0x76/0x17a with crng_init=0
[    0.050000] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
[    0.050000] futex hash table entries: 256 (order: 2, 16384 bytes)
[    0.060000] NET: Registered protocol family 16
[    0.090000] vgaarb: loaded
[    0.090000] SCSI subsystem initialized
[    0.090000] usbcore: registered new interface driver usbfs
[    0.090000] usbcore: registered new interface driver hub
[    0.090000] usbcore: registered new device driver usb
[    0.090000] pps_core: LinuxPPS API ver. 1 registered
[    0.090000] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti@linux.it>
[    0.090000] PTP clock support registered
[    0.100000] Advanced Linux Sound Architecture Driver Initialized.
[    0.100000] clocksource: Switched to clocksource riscv_clocksource
[    0.110000] NET: Registered protocol family 2
[    0.110000] tcp_listen_portaddr_hash hash table entries: 512 (order: 1, 8192 bytes)
[    0.110000] TCP established hash table entries: 8192 (order: 4, 65536 bytes)
[    0.110000] TCP bind hash table entries: 8192 (order: 5, 131072 bytes)
[    0.110000] TCP: Hash tables configured (established 8192 bind 8192)
[    0.110000] UDP hash table entries: 512 (order: 2, 16384 bytes)
[    0.110000] UDP-Lite hash table entries: 512 (order: 2, 16384 bytes)
[    0.110000] NET: Registered protocol family 1
[    0.120000] Unpacking initramfs...
[    0.590000] workingset: timestamp_bits=62 max_order=18 bucket_order=0
[    0.620000] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 251)
[    0.620000] io scheduler noop registered
[    0.620000] io scheduler deadline registered
[    0.620000] io scheduler cfq registered (default)
[    0.620000] io scheduler mq-deadline registered
[    0.620000] io scheduler kyber registered
[    0.620000] shpchp: Standard Hot Plug PCI Controller Driver version: 0.4
[    0.690000] Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
[    0.690000] 10000000.uart: ttyS0 at MMIO 0x10000000 (irq = 10, base_baud = 230400) is a 16550A
[    0.720000] console [ttyS0] enabled
[    0.720000] [drm] radeon kernel modesetting enabled.
[    0.720000] [drm] amdgpu kernel modesetting enabled.
[    0.730000] loop: module loaded
[    0.730000] libphy: Fixed MDIO Bus: probed
[    0.740000] e1000e: Intel(R) PRO/1000 Network Driver - 3.2.6-k
[    0.740000] e1000e: Copyright(c) 1999 - 2015 Intel Corporation.
[    0.740000] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
[    0.740000] ehci-pci: EHCI PCI platform driver
[    0.740000] ehci-platform: EHCI generic platform driver
[    0.740000] mousedev: PS/2 mouse device common for all mice
[    0.740000] usbcore: registered new interface driver usbhid
[    0.740000] usbhid: USB HID core driver
[    0.740000] usbcore: registered new interface driver snd-usb-audio
[    0.740000] NET: Registered protocol family 10
[    0.750000] Segment Routing with IPv6
[    0.750000] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
[    0.750000] NET: Registered protocol family 17
[    0.750000] Key type dns_resolver registered
[    0.760000] ALSA device list:
[    0.760000]   No soundcards found.
[    0.770000] Freeing unused kernel memory: 208K
[    0.770000] This architecture does not have kernel memory protection.
[    0.770000] Run /init as init process

(„Timestamped“ linie wydaje rdzeń, przed nimi drzewo wyświetla BBL) Z jednej strony, FDT jest skutecznie czytany, z drugiej strony - otrzymujemy pewną bzdurę w sparsowanej „obiektowej” drzewie.

W trakcie debugowania zrozumiałem, że BBL nie mógł odczytać „podwójnego” adresu nie dlatego, że w konfiguracji był błąd, ale po prostu dlatego, że dla węzła głównego ustawiony był #address-cells = <1>; #size-cells = <1>;.

Rozszerzając wyjście debugowania, zobaczyłem, że wskaźniki type i name potrzebowały, co było dość dziwne: pierwszy (zaczynając od zera) bajt się wyzerował, coś w rodzaju 0xabcdef -> 0xab00ef. Musiałem skorzystać ze znanej taktyki: znając adresy, na których znajduje się uszkodzona pamięć, dopisałem do funkcji printk drukowanie tych danych. No i samych wywołań printk rozprowadzić, tam gdzie ich mało. Niestety, nie stało się to znacznie jaśniejsze. Potem zacząłem, porównując z zdisassemblowanym objdump wersja, ręcznie ustawiać breakpoints i przechodzić przez problematyczny fragment kodu krok po kroku. I tutaj odkryłem zagadkowe i nieprzyjemne: jeśli od razu ustawię punkt zatrzymania gdzieś relatywnie daleko od stanu known-good, to zatrzymując się na nim, zobaczę już uszkodzoną pamięć. Ale jeśli spróbuję podejść cicho do niej krokami, to błąd zdąży się schować! Wygląda na to, że to uszkodzony kontroler pamięci i cache, ukrywający błąd, jeśli ciągle żądać potrzebnej wartości. A co jeśli…

Wymieniamy pamięć

Przyczepiając się do możliwości rozwiązania problemu nie po swojej stronie, zasugerowałem, że może po prostu przyszła mi uszkodzona kość pamięci. Przypomniałem sobie, że netbook sprzed dziesięciu lat - to nie tylko przenośny komputer, ale także cenne źródło DDR2 SO-DIMM. Bonus: można do niego wsadzić pamięć wyjętą z płyty i uzyskać niemal natychmiastowe zawieszenie Memtest (Linux, swoją drogą, ładował się prawie do graficznego interfejsu. Ale lepiej nie powtarzać takiego eksperymentu, jeśli cenisz sobie informacje na zamontowanych dyskach…). Z jednej strony, można założyć, że po prostu pamięć nie pasowała do tego netbooka. Z drugiej strony, on mógł się uruchomić! Pojawiła się nadzieja, że po prostu pamięć jest „uszkodzona”, a problem nie tkwi w moim opakowaniu.

Więc musimy spróbować tymczasowo uruchomić się z pamięcią netbooka. Niestety, Quartus 18 nie potrafi zaktualizować tego IP Core w taki sposób, aby można go było edytować. Oznacza to, że nadszedł czas, aby nauczyć się tworzyć nowe instancje. Okazało się to dosyć proste: szukamy w katalogu IP pozycji DDR2 SDRAM Controller with ALTMEMPHY Intel FPGA IP i otrzymujemy blok z takim samym interfejsem jak wcześniej. Jedynym problemem jest ustawienie prawidłowych parametrów pamięci.

Otrzymujemy parametry modułu pamięci

Dostępny jest dość oczywisty sposób skonfigurowania modułu: odczytać parametry z SPD. Rzeczywiście, jeśli zwykły komputer odczytuje dane SPD i konfiguruje kontroler na ich podstawie, to spróbujmy odczytać te dane z posiadanego netbooka i wpisać je do tabeli.

Na Ubuntu robi się to dość łatwo: trzeba zainstalować pakiet i2c-tools, załadować moduły eeprom i i2c-i801 (nie jestem pewien, czy drugi sterownik będzie odpowiedni wszędzie), po czym uruchomić decode-dimms:

Wynik decode-dimms

# decode-dimms version 6231 (2014-02-20 10:54:34 +0100)

Memory Serial Presence Detect Decoder
By Philip Edelbrock, Christian Zuckschwerdt, Burkart Lingner,
Jean Delvare, Trent Piepho and others

Decoding EEPROM: /sys/bus/i2c/drivers/eeprom/0-0050
Guessing DIMM is in                             bank 1

---=== SPD EEPROM Information ===---
EEPROM Checksum of bytes 0-62                   OK (0x00)
SPD Revision                                    Invalid
Fundamental Memory type                         Unknown (0x00)

---=== Manufacturing Information ===---
Manufacturer                                    Undefined
Part Number                                     Undefined

Decoding EEPROM: /sys/bus/i2c/drivers/eeprom/4-0050
Guessing DIMM is in                             bank 1

---=== SPD EEPROM Information ===---
EEPROM Checksum of bytes 0-62                   OK (0x00)
SPD Revision                                    Invalid
Fundamental Memory type                         Unknown (0x00)

---=== Manufacturing Information ===---
Manufacturer                                    Undefined
Part Number                                     Undefined

Decoding EEPROM: /sys/bus/i2c/drivers/eeprom/6-0050
Guessing DIMM is in                             bank 1

---=== SPD EEPROM Information ===---
EEPROM Checksum of bytes 0-62                   OK (0x76)
# of bytes written to SDRAM EEPROM              128
Total number of bytes in EEPROM                 256
Fundamental Memory type                         DDR2 SDRAM
SPD Revision                                    1.2

---=== Memory Characteristics ===---
Maximum module speed                            800 MHz (PC2-6400)
Size                                            1024 MB
Banks x Rows x Columns x Bits                   8 x 13 x 10 x 64
Ranks                                           2
SDRAM Device Width                              16 bits
Module Height                                   30.0 mm
Module Type                                     SO-DIMM (67.6 mm)
DRAM Package                                    Planar
Voltage Interface Level                         SSTL 1.8V
Module Configuration Type                       No Parity
Refresh Rate                                    Reduced (7.8 us) - Self Refresh
Supported Burst Lengths                         4, 8
tCL-tRCD-tRP-tRAS                               6-6-6-18
Supported CAS Latencies (tCL)                   6T, 5T, 4T
Minimum Cycle Time                              2.50 ns at CAS 6 (tCK min)
                                                3.00 ns at CAS 5
                                                3.75 ns at CAS 4
Maximum Access Time                             0.40 ns at CAS 6 (tAC)
                                                0.45 ns at CAS 5
                                                0.50 ns at CAS 4
Maximum Cycle Time (tCK max)                    8.00 ns

---=== Timing Parameters ===---
Address/Command Setup Time Before Clock (tIS)   0.17 ns
Address/Command Hold Time After Clock (tIH)     0.25 ns
Data Input Setup Time Before Strobe (tDS)       0.05 ns
Data Input Hold Time After Strobe (tDH)         0.12 ns
Minimum Row Precharge Delay (tRP)               15.00 ns
Minimum Row Active to Row Active Delay (tRRD)   10.00 ns
Minimum RAS# to CAS# Delay (tRCD)               15.00 ns
Minimum RAS# Pulse Width (tRAS)                 45.00 ns
Write Recovery Time (tWR)                       15.00 ns
Minimum Write to Read CMD Delay (tWTR)          7.50 ns
Minimum Read to Pre-charge CMD Delay (tRTP)     7.50 ns
Minimum Active to Auto-refresh Delay (tRC)      60.00 ns
Minimum Recovery Delay (tRFC)                   127.50 ns
Maximum DQS to DQ Skew (tDQSQ)                  0.20 ns
Maximum Read Data Hold Skew (tQHS)              0.30 ns

---=== Manufacturing Information ===---
Manufacturer                                    Samsung
Manufacturing Location Code                     0x02
Part Number                                     M4 70T2864QZ3-CF7
Revision Code                                   0x3351
Manufacturing Date                              2009-W34
Assembly Serial Number                          0x79C64E9A

Number of SDRAM DIMMs detected and decoded: 3

Problem polega na tym, że niektóre czasy nie są tutaj podane, więc pójdźmy drugą drogą.

Co mamy zapisane na modułach:

Część 4: W końcu uruchamiamy Linux na RocketChip RISC-VCzęść 4: W końcu uruchamiamy Linux na RocketChip RISC-V

W tym przypadku ciekawszy jest napis na samych układach pamięci: K4T1G164QQ-HCF7. Wpisując ten tekst w Google, otrzymujemy kartę katalogową na kilkadziesiąt stron ze wszystkimi niezbędnymi parametrami, które można dokładnie przepisać w właściwościach IP Core. Zwróć tylko uwagę na jednostki miary: gdzieś ns, gdzie indziej — ps, a czasami w ogóle ułamki tCK. Nie zamierzam uczyć, na co zwracać uwagę, sam wiele konfigurowałem metodą prób i błędów (nie mogę przy tym zagwarantować, że tak nie można "spalić" pamięci lub FPGA). Zwrócę tylko uwagę na jedno: długo nie mogłem zmusić kontrolera do pracy, a ostatnim krokiem było uświadomienie sobie, że muszę sprawdzić szerokość wszystkich interfejsów do pamięci w odniesieniu do wygenerowanego kodu w Verilog: bo gdy w MemIfBundle jest val mem_ba = IO(Output(UInt(2.W))), a banków 8 (czyli adres szerokości 3 bity), to wszystko, co mamy — wiszący w powietrzu output z kontrolera pamięci — to problem, ale nie krytyczny, więc wszystko się syntetyzuje, ale nie działa.

Możliwe, że wszystko by zadziałało i ze starym modułem, gdyby zaktualizować kontroler, ale na razie nie chce mi się bawić w potencjalne ukryte błędy: mam wystarczająco dużo swoich.

I tak, po przeniesieniu timebase-frequency do DTS z poziomu konkretnego CPU na poziom węzła cpus (na dość jasno wyrażoną prośbę jądra), otrzymujemy:

Ukryty tekst

[    0.000000] OF: fdt: Ignorowanie zakresu pamięci 0x80000000 - 0x80200000
[    0.000000] Wersja Linuksa 4.19.0-sifive-1+ (trosinenko@trosinenko-pc) (gcc version 8.3.0 (Buildroot 2019.02-07449-g4eddd28f99)) #1 SMP Wt 9 lip 22:42:36 MSK 2019
[    0.000000] bootconsole [early0] włączony
[    0.000000] Inicjalna pamięć RAM w: 0x(____ptrval____) (16777216 bajtów)
[    0.000000] Zakresy stref:
[    0.000000]   DMA32    [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000]   Normal   [mem 0x00000000c0000000-0x00000bffffffffff]
[    0.000000] Rozpoczęcie strefy ruchomej dla każdego węzła
[    0.000000] Wczesne zakresy pamięci węzła
[    0.000000]   węzeł   0: [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] Ustawienie pamięci inicjalizacyjnej węzła 0 [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] oprogramowanie IO TLB: zmapowane [mem 0xbb1fc000-0xbf1fc000] (64MB)
[    0.000000] elf_hwcap to 0x112d
[    0.000000] percpu: Osadzono 17 stron/cpu @(____ptrval____) s29400 r8192 d32040 u69632
[    0.000000] Zbudowano 1 listy stref, grupowanie mobilności w toku.  Łączna liczba stron: 258055
[    0.000000] Linia komend jądra: debug console=ttySIF0,125200 root=\dev\mmcblk0p2 loglevel=7 rootwait
[    0.000000] Tablica haszy pamięci podręcznej Dentry: 131072 elementów (porządek: 8, 1048576 bajtów)
[    0.000000] Tablica haszy pamięci podręcznej Inode: 65536 elementów (porządek: 7, 524288 bajtów)
[    0.000000] Sortowanie __ex_table...
[    0.000000] Pamięć: 937696K/1046528K dostępne (6565K kodu jądra, 343K rwdata, 2564K rodata, 208K init, 832K bss, 108832K zarezerwowane, 0K zarezerwowane dla cma)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPU=1, Węzły=1
[    0.000000] rcu: Hierarchiczna implementacja RCU.
[    0.000000] rcu:     Śledzenie zdarzeń RCU jest włączone.
[    0.000000] rcu:     RCU ogranicza CPU z NR_CPUS=8 do nr_cpu_ids=1.
[    0.000000] rcu: Dostosowanie geometrii dla rcu_fanout_leaf=16, nr_cpu_ids=1
[    0.000000] NR_IRQS: 0, nr_irqs: 0, prealokowane przerwania: 0
[    0.000000] plic: zmapowano 8 przerwań do 1 (z 2) handlerów.
[    0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x1d854df40, max_idle_ns: 3526361616960 ns
[    0.000000] Konsola: kolor urządzenia dummy 80x25
[    0.000000] Kalibracja pętli opóźnienia (pominięto), wartość obliczona przy użyciu częstotliwości timera.. 2.00 BogoMIPS (lpj=10000)
[    0.020000] pid_max: domyślnie: 32768 minimum: 301
[    0.030000] Tablica haszy pamięci podręcznej montowania: 2048 elementów (porządek: 2, 16384 bajtów)
[    0.040000] Tablica haszy punktów montowania: 2048 elementów (porządek: 2, 16384 bajtów)
[    0.140000] rcu: Hierarchiczna implementacja SRCU.
[    0.190000] smp: Włączanie drugorzędnych CPU ...
[    0.190000] smp: Włączono 1 węzeł, 1 CPU
[    0.220000] devtmpfs: zainicjowane
[    0.300000] losowe: wywołano get_random_u32 z bucket_table_alloc+0x76/0x17a z crng_init=0
[    0.330000] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
[    0.340000] Tablica haszy futex: 256 (porządek: 2, 16384 bajtów)
[    0.380000] NET: Zarejestrowana rodzina protokołów 16
[    1.850000] vgaarb: załadowano
[    1.870000] Podsystem SCSI zainicjowany
[    1.900000] usbcore: zarejestrowany nowy sterownik interfejsu usbfs
[    1.910000] usbcore: zarejestrowany nowy sterownik interfejsu hub
[    1.920000] usbcore: zarejestrowany nowy sterownik urządzenia usb
[    1.930000] pps_core: Zarejestrowano API LinuxPPS w wersji 1
[    1.940000] pps_core: Wersja oprogramowania 5.3.6 - Prawa autorskie 2005-2007 Rodolfo Giometti 
[    1.950000] wsparcie zegara PTP zarejestrowane
[    1.970000] Zainicjowany sterownik Architektura Dźwięku Linuxa.
[    2.010000] clocksource: Przełączono na clocksource riscv_clocksource
[    2.600000] NET: Zarejestrowana rodzina protokołów 2
[    2.640000] tcp_listen_portaddr_hash tablica haszy: 512 (porządek: 1, 8192 bajtów)
[    2.650000] TCP zarejestrowana tablica haszy: 8192 (porządek: 4, 65536 bajtów)
[    2.670000] TCP związek tablica haszy: 8192 (porządek: 5, 131072 bajtów)
[    2.700000] TCP: Tablice haszy skonfigurowane (ustawione 8192 przylągnięte 8192)
[    2.720000] UDP tablica haszy: 512 (porządek: 2, 16384 bajtów)
[    2.730000] UDP-Lite tablica haszy: 512 (porządek: 2, 16384 bajtów)
[    2.750000] NET: Zarejestrowana rodzina protokołów 1
[    2.770000] Rozpakowywanie initramfs...
[   23.980000] Rozpakowywanie initramfs nie powiodło się: śmieci w skompresowanym archiwum
[   24.030000] workingset: timestamp_bits=62 max_order=18 bucket_order=0
[   25.020000] Warstwa blokowa SCSI generic (bsg) zainstalowana w wersji 0.4 (maj 251)
[   25.030000] planista we/wy noop zarejestrowany
[   25.040000] planista we/wy deadline zarejestrowany
[   25.050000] planista we/wy cfq zarejestrowany (domyślny)
[   25.060000] planista we/wy mq-deadline zarejestrowany
[   25.060000] planista we/wy kyber zarejestrowany
[   25.120000] sifive_gpio 64002000.gpio: Zarejestrowany chip GPIO SiFive 6 GPIOs
[   25.150000] shpchp: Standardowy sterownik Kontrolera PCI z Hot Plug wersja: 0.4
[   28.870000] Serial: 8250/16550 driver, 4 porty, dzielenie IRQ wyłączone
[   28.940000] sifive-serial 64000000.serial: nie można znaleźć zegara kontrolera
[   28.950000] sifive-serial: próba 64000000.serial zakończona niepowodzeniem z błędem -2
[   28.980000] [drm] radeon kernel modesetting włączone.
[   28.990000] [drm] amdgpu kernel modesetting włączone.
[   29.500000] loop: moduł załadowany
[   29.610000] sifive_spi 64001000.spi: Nie można znaleźć zegara magistrali
[   29.620000] sifive_spi: próba 64001000.spi zakończona niepowodzeniem z błędem -2
[   29.640000] libphy: Zbadana Sztywna Magistrala MDIO
[   29.680000] e1000e: Intel(R) PRO/1000 Sterownik Sieciowy - 3.2.6-k
[   29.690000] e1000e: Prawa autorskie(c) 1999 - 2015 Intel Corporation.
[   29.700000] ehci_hcd: Kontroler Host USB 2.0 'Ulepszony' (EHCI) Sterownik
[   29.710000] ehci-pci: Sterownik platformy EHCI PCI
[   29.720000] ehci-platform: ogólny sterownik platformy EHCI
[   29.750000] mousedev: urządzenie myszy PS/2 wspólne dla wszystkich myszy
[   29.790000] usbcore: zarejestrowany nowy sterownik interfejsu usbhid
[   29.800000] usbhid: Sterownik rdzenia USB HID
[   29.820000] usbcore: zarejestrowany nowy sterownik interfejsu snd-usb-audio
[   29.900000] NET: Zarejestrowana rodzina protokołów 10
[   29.960000] Routing segmentowy z użyciem IPv6
[   29.960000] sit: tunelowanie IPv6, IPv4 i MPLS przez IPv4
[   30.020000] NET: Zarejestrowana rodzina protokołów 17
[   30.020000] Typ klucza dns_resolver zarejestrowany
[   30.070000] bootconsole [early0] używa pamięci inicjalizacyjnej i musi być wyłączona nawet przed tym, jak prawdziwa będzie gotowa
[   30.080000] bootconsole [early0] wyłączony

Po czym nic widocznego się nie dzieje.

Teraz można spróbować zmierzyć się z trzema problemami:

[   23.980000] Nie udało się rozpakować initramfs: śmieci w skompresowanym archiwum
...
[   28.940000] sifive-serial 64000000.serial: nie można znaleźć zegara kontrolera
...
[   29.610000] sifive_spi 64001000.spi: nie można znaleźć zegara magistrali

… a także z zatrzymaniem wyjścia na konsolę.

Walka z dwoma problemami z częstotliwościami jest całkiem prosta: wystarczy dodać punkty clocks do poddrzew wymaganych urządzeń, dla wygody nadając zrozumiałą nazwę etykiety dla węzła tlclk:

diff --git a/arch/riscv/dts/zeowaa-1gb.dts b/arch/riscv/dts/zeowaa-1gb.dts
index 4b2cebc..1347e76 100644
--- a/arch/riscv/dts/zeowaa-1gb.dts
+++ b/arch/riscv/dts/zeowaa-1gb.dts
@@ -96,6 +97,7 @@
                 L8: serial@64000000 {
                         compatible = "sifive,uart0";
                         interrupt-parent = <&L0>;
+                        clocks = <&tlclk>;
                         interrupts = <>;
                         reg = <0x64000000 0x1000>;
                         reg-names = "control";
@@ -107,6 +109,7 @@
                         interrupt-parent = <&L0>;
                         interrupts = <>;
                         reg = <0x64001000 0x1000>;
+                        clocks = <&tlclk>;
                         reg-names = "control";
                         L12: mmc@0 {
                                 compatible = "mmc-spi-slot";
@@ -116,7 +119,7 @@
                                 voltage-ranges = <3300 3300>;
                         };
                 };
-                L11: tlclk {
+                tlclk: tlclk {
                         #clock-cells = <>;
                         clock-frequency = <>;
                         clock-output-names = "tlclk";

Dodając linię #define pr_debug printk na początku pliku init/initramfs.c, wydobywając komunikat o śmieciach zamiast initramfs, otrzymujemy nieco bardziej szczegółowy komunikat:

[    2.760000] Rozpakowywanie initramfs...
[    2.770000] Wykryto skompresowane dane gzip
[   23.940000] Wykryto dane (null) skompresowane
[   23.970000] Nie udało się rozpakować initramfs: śmieci w skompresowanym archiwum

To znaczy, że być może śmieci nie są zamiast initramfs, a razem.

Ok, coś tam znalazł, może wystarczy. A gdzie się podziała konsola? Szukam i znajduję odniesienie do problemu na Githubie: linux failed to bringup on vc707 board — okazuje się, że jeden UART to zdaniem sterownika nawet nie jest temat do rozmowy. Ale poprawkę datowane na 11 lipca 2018 roku — rok temu. Okazało się, że ten commit nie trafił do gałęzi, z której kompiluję jądro. Po ręcznym zastosowaniu tej łatki, uzyskałem działający Linux! Okazało się, że problem z initramfs był właściwie niekrytyczny.

Logując się jako user=root, password=sifive, mogłem bez większych problemów (poza zrozumieniem, że numer GPIO ma bazę, od której wszystko się liczy, i którą trzeba po prostu odczytać z odpowiedniego pliku pseudo) przez sysfs włączyć diodę LED na płycie.

Podobnie jak ostatnio, dołączam tekstowy film wideo uruchomienia Linuxa. W momencie przełączania konsoli z earlyprintk na standardową pojawia się jakiś szum, przypominający wyjście przy źle ustawionej prędkości UART. Co ciekawe, odtwarzacz skutecznie poradził sobie z tą „papką”.

Część 4: W końcu uruchamiamy Linux na RocketChip RISC-V

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster