В
Hardver će uključivati SD karticu povezanu preko SPI interfejsa, kao i UART. U softverskom dijelu, BootROM će biti zamijenjen sa xip
na sdboot
i, zapravo, dodane su sljedeće faze učitavanja (na SD kartici).
Dovršavanje hardvera
Dakle, zadatak: morate se prebaciti na „veliku“ jezgru i spojiti UART (od Raspberryja) i SD adapter (koristili smo karticu Catalex sa šest pinova: GND, VCC, MISO, MOSI, SCK, CS) .
U principu, sve je bilo prilično jednostavno. Ali prije nego što sam to shvatio, bio sam malo bačen s jedne strane na drugu: nakon prethodnog puta, odlučio sam da opet samo trebam umiješati System
nešto kao HasPeripheryUART
(i implementacija shodno tome), isto za SD karticu - i sve će biti spremno. Onda sam odlučio da vidim kako je to implementirano u "ozbiljan" dizajn. Dakle, šta je ozbiljno u ovome? Arty, očigledno, ne odgovara - čudovište ostaje unleahshed.DevKitConfigs
. I odjednom se ispostavilo da su posvuda neki prekrivači, koji su dodavani kroz parametre pomoću tipki. Pretpostavljam da je ovo vjerovatno vrlo fleksibilno i konfigurabilno, ali htio bih barem nešto prvo pokrenuti... Zar nemate istu stvar, samo jednostavniju i dosadniju?.. Tada sam naišao vera.iofpga.FPGAChip
za Microsemi FPGA i odmah sam ga razdvojio za citate i pokušao da napravim sopstvenu implementaciju po analogiji, na sreću postoji manje-više ceo "izgled matične ploče" u jednom fajlu.
Ispostavilo se da zaista trebate samo dodati System.scala
linije
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
...
Linija u tijelu klase System
dodaje informacije o frekvenciji na kojoj ovaj dio našeg SoC-a radi u dts datoteku. Koliko sam shvatio, DTS/DTB je statički analog plug-and-play tehnologije za ugrađene uređaje: dts stablo opisa se kompajlira u binarnu dtb datoteku i prenosi bootloader u kernel tako da može ispravno konfigurirati hardver. Zanimljivo, bez linije sa tlclock
sve se savršeno sintetiše, ali kompajliranje BootROM-a (da vas podsjetim, sada će to već biti sdboot
) neće raditi - tokom procesa kompilacije analizira dts datoteku i kreira zaglavlje s makroom TL_CLK
, zahvaljujući čemu će moći ispravno konfigurirati razdjelnike frekvencije za vanjske sučelje.
Također ćete morati malo ispraviti "ožičenje":
Platform.scala:
class PlatformIO(implicit val p: Parameters) extends Bundle {
...
// UART
io.uart_tx := sys.uart(0).txd
sys.uart(0).rxd := RegNext(RegNext(io.uart_rx))
// SD card
io.sd_cs := sys.spi(0).cs(0)
io.sd_sck := sys.spi(0).sck
io.sd_mosi := sys.spi(0).dq(0).o
sys.spi(0).dq(0).i := false.B
sys.spi(0).dq(1).i := RegNext(RegNext(io.sd_miso))
sys.spi(0).dq(2).i := false.B
sys.spi(0).dq(3).i := false.B
}
Lanci registara, da budemo iskreni, dodani su jednostavno po analogiji sa nekim drugim mjestima u originalnom kodu. Najvjerovatnije bi trebali zaštititi od dq
? Još nisam našao odgovor, ali čini se da se ostatak koda oslanja upravo na takvu vezu.
Fizički, jednostavno sam dodijelio dizajnerske pinove slobodnim kontaktima na bloku i pomjerio kratkospojnik za odabir napona na 3.3V.
SD adapter
Pogled odozgo:
Pogled odozdo:
Otklanjanje grešaka u softveru: Alati
Prvo, hajde da razgovaramo o dostupnim alatima za otklanjanje grešaka i njihovim ograničenjima.
Minicom
Prvo ćemo morati nekako pročitati šta bootloader i kernel izlaze. Da bismo to učinili na Linuxu (u ovom slučaju na onom na RaspberryPi), potreban nam je program Minicom. Uopšteno govoreći, bilo koji program koji radi sa serijskim portom će odgovarati.
Imajte na umu da prilikom pokretanja, naziv uređaja porta mora biti naveden kao -D /dev/ttyS0
- nakon opcije -D
. Pa, glavna informacija: da izađete, koristite Ctrl-A, X
. Zapravo sam imao slučaj kada ova kombinacija nije radila - onda možete jednostavno reći iz susjedne SSH sesije killall -KILL minicom
.
Postoji još jedna karakteristika. Konkretno, RaspberryPi ima dva UART-a, a oba porta se već mogu prilagoditi za nešto: jedan za Bluetooth, drugi po defaultu izlazi na konzolu kernela. Na sreću, ovo ponašanje se može zaobići
Prepisivanje memorije
Prilikom otklanjanja grešaka, da bih testirao hipotezu, ponekad sam morao load bootloader (izvinite) u RAM direktno sa hosta. Možda se to može uraditi direktno iz GDB-a, ali na kraju sam slijedio jednostavan put: kopirao sam potrebnu datoteku na Raspberry, također proslijedio port 4444 preko SSH-a (telnet iz OpenOCD-a) i koristio naredbu load_image
. Kada to uradite, čini se da je sve zamrznuto, ali u stvari "ne spava, samo polako trepće": On preuzima datoteku, samo to radi brzinom od nekoliko kilobajta u sekundi.
Karakteristike instaliranja tačaka prekida
Mnogi ljudi vjerovatno nisu morali razmišljati o ovome prilikom otklanjanja grešaka u redovnim programima, ali tačke prekida nisu uvijek postavljene u hardveru. Ponekad postavljanje tačke prekida uključuje privremeno zapisivanje posebnih uputstava na pravo mjesto direktno u mašinski kod. Na primjer, ovako je funkcionirala moja standardna komanda b
u GDB. Evo šta slijedi:
- ne možete staviti tačku unutar BootROM-a jer je ROM
- Možete postaviti tačku prekida na kodu učitanom u RAM sa SD kartice, ali morate pričekati dok se ne učita. U suprotnom, nećemo prepisati dio koda, ali će učitavač ponovo napisati našu tačku prekida
Siguran sam da možete eksplicitno tražiti korištenje hardverskih tačaka prekida, ali ionako postoji ograničen broj njih.
Brza zamjena BootROM-a
U početnoj fazi otklanjanja grešaka, često postoji želja da se popravi BootROM i pokuša ponovo. Ali postoji problem: BootROM je dio dizajna učitanog u FPGA, a njegova sinteza je pitanje nekoliko minuta (a to je nakon skoro trenutne kompilacije same BootROM slike iz C i Assemblera...). Srećom, u stvarnosti sve mnogo brže: redoslijed radnji je sljedeći:
- regenerirajte bootrom.mif (prebacio sam se na MIF umjesto na HEX, jer sam uvijek imao problema sa HEX-om, a MIF je Alterov izvorni format)
- u Quartusu kažu
Processing -> Update Memory Initialization File
- na komandi Asembler (u lijevoj koloni Zadaci) Pokreni ponovo
Sve o svemu - par desetina sekundi.
Priprema SD kartice
Ovdje je sve relativno jednostavno, ali morate biti strpljivi i imati oko 14Gb prostora na disku:
git clone https://github.com/sifive/freedom-u-sdk
git submodule update --recursive --init
make
Nakon toga morate umetnuti čistu, tačnije onu koja ne sadrži ništa potrebno, SD karticu i izvršiti
sudo make DISK=/dev/sdX format-boot-loader
… Gdje sdX
— uređaj koji je dodijeljen kartici. PAŽNJA: podaci na kartici će biti izbrisani, prepisani i općenito! Teško da se isplati raditi cijelu montažu odozdo sudo
jer će tada svi građevinski artefakti pripadati root
, a montaža će se morati obaviti odozdo sudo
stalno.
Rezultat je kartica označena u GPT-u sa četiri particije, od kojih jedna ima FAT uEnv.txt
i sliku za pokretanje u FIT formatu (sadrži nekoliko pod-slika, svaka sa svojom adresom za preuzimanje), druga particija je prazna, trebalo bi da bude formatirana u Ext4 za Linux. Još dve sekcije - misteriozan: U-Boot živi na jednom (njegov ofset je, koliko sam shvatio, tvrdo kodiran u BootROM), na drugom, čini se, žive njegove varijable okruženja, ali ih još ne koristim.
Prvi nivo, BootROM
Narodna mudrost kaže: „Ako u programiranju postoji ples s tamburom, onda u elektronici postoji i ples s aparatom za gašenje požara.” Ne radi se čak ni o tome da sam jednom zamalo spalio ploču, odlučivši da je "Pa, GND je isto tako nizak nivo." (očigledno, otpornik ipak ne bi škodio...) Više se radi o tome da ako ruke ne rastu odatle, onda elektronika ne prestaje donositi iznenađenja: prilikom lemljenja konektora na ploču, još uvijek nisam mogao zalemiti kontakte kako treba - video pokazuje kako se lem direktno širi preko cijele veze, samo nanesite lemilicu, za mene je nasumce "šamarao". Pa, mozda lem nije odgovarao temperaturi lemilice, mozda nesto drugo... Uopste, kada sam video da imam vec desetak kontakata, odustao sam i poceo da otklanjam greske. A onda je počelo misteriozan: Povezao sam RX/TX sa UART-a, učitavam firmver - piše
INIT
CMD0
ERROR
Pa, sve je logično - nisam spojio modul SD kartice. Ispravljamo situaciju, učitavamo firmver... I tišina... Zašto se nisam predomislio, već se mala kutija upravo otvorila: jedan od pinova modula je morao biti spojen na VCC. U mom slučaju, modul je podržavao 5V za napajanje, tako da sam bez razmišljanja spojio žicu koja je dolazila od modula na suprotnu stranu ploče. Kao rezultat toga, krivo zalemljeni konektor postao je iskrivljen i UART kontakt je jednostavno izgubljen. facepalm.jpg Generalno, "loša glava ne daje odmor nogama", a krive ruke ne daju odmora glavi...
Kao rezultat toga, vidio sam dugo očekivano
INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
LOADING /
Štaviše, kreće se i indikator punjenja se okreće. Odmah se sjetim svojih školskih dana i laganog učitavanja MinuetOS-a sa diskete. Osim ako pogon ne brusi.
Problem je što se nakon BOOT poruke ništa ne dešava. To znači da je vrijeme da se povežete preko OpenOCD-a na Raspberry, na GDB na hostu i vidite šta je to.
Prvo, povezivanje pomoću GDB-a je to odmah pokazalo $pc
(programski brojač, adresa trenutne instrukcije) leti na 0x0
- ovo se vjerovatno dešava nakon više grešaka. Dakle, odmah nakon slanja poruke BOOT
Dodajmo beskonačnu petlju. Ovo će ga odložiti na neko vrijeme...
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;
}
Takav lukav kod se koristi "za pouzdanost": negdje sam čuo da je beskonačna petlja nedefinirano ponašanje, ali je malo vjerovatno da će kompajler pogoditi (podsjećam da prema 0x10000
nalazi BootROM).
Čini se, šta drugo očekivati - grubo ugrađen, kakvi izvorni kodovi postoje? Ali unutra
(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.
Samo trebate preuzeti ne MIF datoteku ili bin, već originalnu verziju u ELF formatu.
Sada možete pogoditi sa n-tim pokušajem adresu na kojoj će se izvršavanje nastaviti (ovo je još jedan razlog zašto kompajler nije trebao pogoditi da je petlja beskonačna). Tim
set variable $pc=0xADDR
omogućava vam da promenite vrednost registra u hodu (u ovom slučaju, adresu trenutne instrukcije). Uz njegovu pomoć možete promijeniti vrijednosti upisane u memoriju (i memorijsko mapirane registre).
Na kraju sam došao do zaključka (nisam siguran šta je tačno) da imamo “sliku SD kartice pogrešnog sistema” i da ne treba da idemo na sam početak preuzetih podataka, već na 0x89800
bajtova dalje:
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
Možda je na to uticala i činjenica da sam, bez nepotrebne 4Gb kartice pri ruci, uzeo 2Gb i nasumično je zamijenio u Makefile-u DEMO_END=11718750
na DEMO_END=3078900
(ne tražite značenje u određenom značenju - nema ga, samo je sada slika postavljena na karticu).
Drugi nivo, U-Boot
Sada još uvijek „padamo“, ali smo već na pravom mjestu 0x0000000080089a84
. Ovdje moram priznati: u stvari, prezentacija ne ide "sa svim zaustavljanjima", već je djelomično napisana "poslije", tako da sam ovdje već uspio ubaciti ispravan dtb fajl sa našeg SoC-a, ispraviti ga u postavkama HiFive_U-Boot
varijabla CONFIG_SYS_TEXT_BASE=0x80089800
(umesto 0x08000000
) tako da adresa preuzimanja odgovara stvarnoj. Sada učitavamo mapu sljedećeg nivoa, drugu sliku:
(gdb) file ../freedom-u-sdk/work/HiFive_U-Boot/u-boot
(gdb) tui en
I vidimo:
│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) │
Štaviše, skačemo između redova 308 i 309. I nije iznenađujuće, s obzirom da je u $sp
leži značenje 0xfffffffe31cdc0a0
. Jao, i on stalno "bježi" zbog linije 307. Stoga, pokušajmo postaviti tačku prekida na trap_entry
, a zatim se vratite na 0x80089800
(U-Boot-ova ulazna tačka), i nadajmo se da ne zahtijeva da se registri ispravno podese prije skoka... Izgleda da radi:
(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
Pokazivač steka je tako-tako, iskreno govoreći: pokazuje zaobilazeći RAM u potpunosti (osim ako, naravno, već nemamo prevod adrese, ali nadajmo se jednostavnoj opciji).
Pokušajmo zamijeniti pokazivač sa 0x881cf950
. Kao rezultat, dolazimo do zaključka da handle_trap
zvali i zvali, a u isto vrijeme ulazimo u _exit_trap
sa argumentima epc=2148315240
(decimalno):
(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
Postavite tačku prekida na strnlen
, nastavljamo i vidimo:
(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
Izgleda kao, _exit_trap
želi pružiti informacije za otklanjanje grešaka o izuzetku koji se dogodio, ali ne može. Dakle, nekako se naši izvori više ne prikazuju. set directories ../freedom-u-sdk/HiFive_U-Boot/
O! Sada prikazano!
Pa, hajde da ga ponovo pokrenemo i da vidimo iz steka da pratimo uzrok originalnog problema koji je izazvao prvu grešku (mcause == 5
). Ako sam dobro razumeo šta piše Load access fault
. Čini se da je razlog to ovdje
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
ima isto netačno značenje, i unutra board_init_f_init_reserve
dolazi do greške. Izgleda da je ovo krivac: varijabla sa nedvosmislenim imenom CONFIG_SYS_INIT_SP_ADDR
. Definisano je u datoteci HiFive_U-Boot/include/configs/HiFive-U540.h
. U nekom trenutku sam čak pomislio, možda, pa, trebao bih dodati pokretač za procesor - možda bi bilo lakše malo popraviti procesor? Ali onda sam vidio da je to više kao artefakt koji nije u potpunosti završen#if 0
-specifične postavke za drugačiju konfiguraciju memorije, a možete pokušati učiniti ovo:
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 */
U nekom trenutku broj štaka
Pa, otprilike, evo malog stola
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
Detalje možete pronaći u
Kako se ispostavilo, na ovoj SiFive ploči registri nekih uređaja imaju različite adrese. Također se pokazalo da je U-Boot konfiguriran pomoću mehanizma Kconfig, već poznatog iz Linux kernela - na primjer, možete komandovati make menuconfig
, a pred vama će se pojaviti zgodan tekstualni interfejs koji prikazuje opise parametara po ?
itd. Općenito, spojivši opis treće iz opisa dvije ploče, izbacivši odatle sve vrste pretencioznih PLL rekonfiguracija (očito je to nekako povezano s kontrolom s glavnog računala preko PCIe, ali to nije sigurno) , dobio sam neki firmware, koji mi je, po dobrom vremenu na Marsu, dao poruku preko UART-a o tome iz kojeg commit hash-a je kompajliran i koliko DRAM-a imam (ali sam sam ovu informaciju napisao u zaglavlju).
Jedina šteta je što je nakon ovoga ploča obično prestala da reaguje preko JTAG procesora, a učitavanje sa SD kartice, nažalost, nije brzo u mojoj konfiguraciji. S druge strane, ponekad je BootROM dao poruku da ERROR
, nije uspio da se pokrene i U-Boot se odmah pojavio. Tada mi je sinulo: očito, nakon ponovnog pokretanja bitstreama u FPGA, memorija se ne briše, nema vremena da se "oduči" itd. Ukratko, možete jednostavno kada se pojavi poruka LOADING /
povežite se sa debugerom i komandom set variable $pc=0x80089800
, čime se zaobilazi ovo dugo učitavanje (naravno, pod pretpostavkom da se zadnji put pokvario dovoljno rano da nije imao vremena da učita bilo šta preko originalnog koda).
Usput, da li je općenito normalno da se procesor potpuno zamrzne i da se JTAG debugger ne može povezati s njim s porukama?
Error: unable to halt hart 0
Error: dmcontrol=0x80000001
Error: dmstatus =0x00030c82
Dakle, čekajte! Ovo sam već video! Nešto slično se dešava kada je TileLink u ćorsokaku, a ja nekako ne vjerujem autoru memorijskog kontrolera - sam sam to napisao... Odjednom, nakon prve uspješne rekonstrukcije procesora nakon uređivanja kontrolera, vidio sam:
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
Na ovu čudnu liniju prije In: serial
ne obraćajte pažnju - pokušavao sam da shvatim na visećem procesoru da li radi ispravno sa okruženjem. Kako to mislite: „Ovako visi deset minuta“? Barem se uspio premjestiti i otići u boot meni! Mala digresija: iako se U-Boot učitava u prvih 2^24 bajta sa SD kartice, kada se pokrene, kopira se na udaljeniju adresu, bilo napisanu u zaglavlju konfiguracije, ili jednostavno na više adrese RAM, i vrši ELF preseljenje -karaktera, i tamo prenosi kontrolu. Dakle: izgleda da smo prošli ovaj nivo i dobili bonus da procesor nakon toga nije čvrsto visio.
Pa zašto tajmer ne radi? Izgleda da sat ne radi iz nekog razloga...
(gdb) x/x 0x0200bff8
0x200bff8: 0x00000000
Šta ako ručno okrenete strelice?
(gdb) set variable *0x0200bff8=310000000
(gdb) c
onda:
Hit any key to stop autoboot: 0
MMC_SPI: 0 at 0:1 hz 20000000 mode 0
Zaključak: sat ne otkucava. Ovo je vjerovatno razlog zašto unos sa tastature ne radi:
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 == '[') {
...
Ispostavilo se da je problem u tome što sam bio malo previše pametan: dodao sam ključ u konfiguraciju procesora:
case DTSTimebase => BigInt(0)
... na osnovu činjenice da je u komentaru pisalo "ako ne znaš, ostavi 0." I posle svega WithNBigCores
Samo sam ga podesio na 1MHz (kao što je, usput rečeno, naznačeno u U-Boot konfiguraciji). Ali dovraga, ja sam uredan i pedantan: ne znam tamo, ovdje je 25MHz! Na kraju ništa ne radi. Uklonio sam svoja "poboljšanja" 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žete čak i unositi komande! Na primjer, nakon što malo provirujete, konačno možete pogoditi da uđete mmc_spi 1 10000000 0; mmc part
, smanjujući SPI frekvenciju sa 20MHz na 10MHz. Zašto? Pa, maksimalna frekvencija od 20MHz je napisana u konfiguraciji i još uvijek piše tamo. Ali, koliko ja razumijem, sučelja, barem ovdje, funkcioniraju ovako: kod dijeli frekvenciju hardverske jedinice (moja je 25MHz svuda) sa ciljem i postavlja rezultujuću vrijednost kao djelitelj u odgovarajućoj kontroli registar. Problem je u tome što ako za 115200Hz UART postoji otprilike ono što je potrebno, onda ako podijelite 25000000 sa 20000000 dobit ćete 1, tj. radit će na 25MHz. Možda je to normalno, ali ako se postavljaju ograničenja, to znači da nekome treba (ali to nije sigurno)... Uglavnom, lakše je to spustiti i krenuti dalje - daleko i, nažalost, dugo. 25MHz nije Core i9.
Izlaz konzole
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
U redu, stigli smo do sljedećeg nivoa, ali još uvijek se smrzava. A ponekad i izuzecima. Možete vidjeti mcause tako što ćete čekati kod na navedenoj adresi $pc
i poslije si
budi na trap_entry
. Sam U-Boot rukovalac može da izbaci samo mcause = 0..4, tako da se pripremite da zaglavite u pogrešnom pokretanju. Onda sam ušao u konfiguraciju, počeo da gledam šta menjam i setio se: tamo unutra conf/rvboot-fit.txt
napisano:
fitfile=image.fit
# below much match what's in FIT (ugha)
Pa hajde da uskladimo sve fajlove, zamenimo komandnu liniju kernela nečim ovakvim, pošto postoje sumnje da SIF0
- ovo je izlaz negdje preko PCIe:
-bootargs=console=ttySIF0,921600 debug
+bootargs=console=ttyS0,125200 debug
I hajde da promijenimo algoritam heširanja sa SHA-256 na MD5: ne treba mi kriptografska snaga (posebno kod otklanjanja grešaka), potrebno je užasno dugo vremena, a za hvatanje grešaka u integritetu tokom učitavanja, MD5 je previše lak. Šta je krajnji rezultat? Prethodni nivo smo počeli da završavamo primetno brže (zbog jednostavnijeg heširanja), a otvorio se sledeći:
...
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
Ali sat ne otkucava...
(gdb) x/x 0x0200bff8
0x200bff8: 0x00000000
Ups, izgleda da je ispravljanje sata bio placebo, iako mi se tada činilo da je pomoglo. Ne, naravno da to treba popraviti, ali hajdemo prvo ručno okrenuti strelice i vidjeti šta će se dogoditi:
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.
U međuvremenu…
Loading Kernel Image ... OK
Booting kernel in
3
2
1
0
## Starting application at 0x80000000 ...
Ne, ići ću automatizirati sat - inače će možda odlučiti kalibrirati tajmer tamo!
U međuvremenu, adresa trenutne instrukcije ukazuje negdje u
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>
unutar napunjenog Berkeley Boot Loadera. Lično, ono što me zbunjuje u vezi ovoga je pominjanje htif
— host interfejs koji se koristi za vezano pokretanje kernela (tj. u saradnji sa host ARM-om), pretpostavio sam samostalno. Međutim, ako ovu funkciju pronađete u izvornom kodu, možete vidjeti da nije sve tako loš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"); }
}
}
Potraga: pokrenite sat
Potraga za registrima u CLINT vodi nas do toga
val io = IO(new Bundle {
val rtcTick = Bool(INPUT)
})
val time = RegInit(UInt(0, width = timeWidth))
when (io.rtcTick) { time := time + UInt(1) }
Koja se povezuje sa RTC-om, ili sa misterioznim MockAON-om, o čemu sam u početku pomislio: „Pa, šta to imamo ovde? Nejasno? Ugasimo ga!" Pošto još uvijek ne razumijem kakva se magija sata dešava tamo, pa ću samo ponovo implementirati ovu logiku u 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
}
Krećemo do Linux kernela
Ovdje se priča već odužila i postala malo monotona, pa ću je opisati od vrha do dna:
BBL je pretpostavio prisustvo FDT u 0xF0000000
, ali već sam ispravio! Pa, pogledajmo ponovo... Našao sam HiFive_U-Boot/arch/riscv/lib/boot.c, zamijenjen sa 0x81F00000
, navedeno u U-Boot konfiguraciji pokretanja.
Tada se BBL požalio da nema sjećanja. Moj put je ležao u funkciji mem_prop
, šta u riscv-pk/machine/fdt.c: odatle sam saznao da morate označiti fdt ram čvor kao device_type = "memory"
- onda će, možda, generator procesora trebati ispraviti, ali za sada ću ga samo ručno upisati - u svakom slučaju, ručno sam prenio ovu datoteku.
Sada sam dobio poruku (dostavljenu u formatiranom obliku, sa povratnim informacijama):
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.
Čini se da su opcije naznačene prema potrebi riscv,kernel-start
и riscv,kernel-end
u DTB-u, ali nule se analiziraju. Otklanjanje grešaka query_chosen
pokazao je da BBL pokušava raščlaniti 32-bitnu adresu, ali nailazi na par <0x0 0xADDR>
, a čini se da je prva vrijednost najmanji bitni bitovi. Dodato u odjeljak chosen
chosen {
#address-cells = <1>;
#size-cells = <0>;
...
}
i ispravljeno generisanje vrijednosti: ne dodaj 0x0
prvi element.
Ovih 100500 jednostavnih koraka će vam olakšati gledanje pada pingvina:
Skriveni 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 prikazuje BBL, a onaj sa vremenskim oznakama prikazuje kernel).
Na sreću, ne znam kako je svuda, ali na RocketChip-u, kada povežete debugger preko JTAG-a, možete uhvatiti zamke iz kutije - debugger će se zaustaviti upravo u ovom trenutku.
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
sloboda-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); // < ВЫ НАХОДИТЕСЬ ЗДЕСЬ
}
Kao što je stari vic rekao, CPU nije pronađen, radi softverska emulacija. Pa, ili ne trči. Izgubljeni u jednom jezgru procesora.
/* The lucky hart to first increment this variable will boot the other cores */
atomic_t hart_lottery;
unsigned long boot_cpu_hartid;
Lep komentar u linux/arch/riscv/kernel/setup.c - vrsta bojenja ograde metodom Tom Sawyer. Uglavnom, iz nekog razloga danas nije bilo pobjednika, nagrada se prenosi na sljedeće izvlačenje...
Ovdje predlažem da završim ionako poduži članak.
Nastavlja se. Doći će do borbe sa lukavom bubom koja se uspije sakriti ako joj se polako prikradete pojedinačnim korakom.
Preuzimanje teksta screencast-a (vanjska veza):
izvor: www.habr.com