V je bil realiziran več ali manj delujoč kontroler pomnilnika, natančneje - ovitek nad IP Core iz Quartus, ki deluje kot adapter za TileLink. Danes v rubriki 'Portiramo RocketChip na malo znano kitajsko ploščo s Cyclone' boste videli delujočo konzolo. Postopek se je nekoliko zavleklo: že sem mislil, da bom hitro zagnal Linux in bomo šli naprej, a ni bilo tako. V tem delu predlagam, da pogledamo postopek zagona U-Boota, BBL in skromne poskuse inicializacije Linux jedra. Vendar konzola je - U-Boot-ova in precej napredna, ima veliko tistega, kar pričakujete od popolne konzole.
V strojni opremi bo dodana SD-kartica, povezana preko SPI vmesnika, ter UART. V programski komponenti bo BootROM zamenjan z xip na sdboot in seveda dodane naslednje faze zagona (na SD-kartici).
Dodelava strojne opreme
Torej, naloga: potrebno je preiti na 'veliko' jedro in povezati UART (iz Raspberry) ter SD-adapter (uporabljena je neka plošča od Catalex s šestim pini: GND, VCC, MISO, MOSI, SCK, CS).
Na splošno je bilo vse dokaj preprosto. Vendar preden sem to spoznal, me je malo zmetalo sem in tja: po prejšnjem primeru sem se odločil, da ponovno preprosto dodam v System nekaj takega kot je HasPeripheryUART (in realizacijo ustrezno), to tudi za SD-kartico - in vse bo pripravljeno. Nato sem se odločil pogledati, kako je to realizirano v 'resnem' dizajnu. Kaj torej imamo tukaj iz resnega? Arty, očitno, ne ustreza - ostaja monstro unleahshed.DevKitConfigs. In nenadoma sem odkril, da so tam povsod nekakšni overlay-i, ki se dodajajo preko parametrov s ključi. Domnevam, da je to verjetno zelo fleksibilno in konfigurabilno, a potrebujem vsaj nekaj, da začnem zagnati … Ali imate takšno, le da nekoliko enostavnejšo in bolj grobo? .. Takrat sem naletel na vera.iofpga.FPGAChip za FPGA Microsemi in takoj sem ga raztrgal na citate in poskusil narediti svojo realizacijo po analogiji, saj je tukaj večinoma celotna 'razvod sistemske plošče' v enem datoteki.
Izkazalo se je, da je res potrebno preprosto dodati v System.scala vrstice
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
...Vrstica v telesu razreda System dodaja informacije o frekvenci, pri kateri ta del našega SoC deluje, v dts-datoteko. Kolikor razumem, DTS/DTB je nekakšen statični ekvivalent tehnologiji plug-and-play za vgrajene naprave: drevo dts-opisa se prevede v binarno dtb-datoteko in se prenese s prenosnikom jedru, da ga lahko pravilno nastavi na strojno opremo. Zanimivo je, da brez vrstice z tlclock se vse lepo sintetizira, a BootROM (spomnim vas, da bo to že sdboot) ne bo mogoče skompilirati — med procesom kompilacije analizira dts-datoteko in ustvari glavo z makrom TL_CLK, ki mu omogoča pravilno nastavitev delilnikov frekvence za zunanje vmesnike.
Potrebno bo tudi nekoliko popraviti "razvod":
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 kartica
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
}Verižni registri so bili dodani preprosto po analogiji z nekaterimi drugimi mesti izvirne kode. Verjetno bi morali ščititi pred . Mogoče so v nekaterih blokkih že zaščitni mehanizmi, a sprva želim zagnati vsaj "na kakovostni ravni". Bolj zanimivo vprašanje zame je, zakaj sta MISO in MOSI priključena na različne dq? Ответа я пока так и не нашёл, но, похоже, остальной код рассчитывает именно на такое подключение.
Fizično sem preprosto dodelil izhode zasnove na proste kontakte na priključku in prestavil veznik za izbiro napetosti na 3.3V.
SD-adapter
Pogled od zgoraj:

Pogled od spodaj:

Debugging programske opreme: orodja
Najprej se pogovorimo o razpoložljivih alatih za debugiranje in o njihovih omejitvah.
Minicom
Najprej bomo potrebovali način za branje izpisa za nalagalnik in jedro. Na Linuxu (v tem primeru na tem, ki ga ima RaspberryPi) bomo potrebovali program Minicom. Na splošno bo deloval kateri koli program za delo s serijskim vtičem.
Upoštevajte, da je ob zagonu ime naprave serijskega vtiča treba navesti kot -D /dev/ttyS0 — po možnosti -D. No in glavna informacija: za izhod uporabite Ctrl-A, X. Res je, da sem imel en primer, ko ta kombinacija ni delovala — takrat se lahko iz sosednje SSH seje preprosto povežete in rečete killall -KILL minicom.
Obstaja tudi ena posebnost. Na RaspberryPi sta na voljo dva UART, in oba porta sta lahko že za kaj prilagojena: eden za Bluetooth, preko drugega pa se privzeto prikaže jedrenska konzola. Na srečo, to vedenje je mogoče spremeniti. .
Prepisovanje pomnilnika
Pri odpravljanju napak, za preverjanje hipoteze mi je včasih bilo treba naložiti zaganjalnik (oprosti) v delovno pomnilnik neposredno s gostitelja. Morda bi to lahko storil neposredno iz GDB, a sem se na koncu odločil za enostavnejšo pot: prenesel sem na Raspberry potrebne datoteke, prestavil prek SSH tudi vrata 4444 (telnet od OpenOCD) in uporabil ukaz load_image. Ko ga izvajaš, se zdi, da se je vse ustavilo, a v resnici „ne spi, samo počasi utripa“: nalaga datoteko, preprosto to počne s hitrostjo približno nekaj kilobajtov na sekundo.
Značilnosti nastavitve breakpoint-ov
Verjetno se mnogi o tem niso nikoli zamislili pri odpravljanju napak običajnih programov, toda točke ustavljanja ne nastavljamo vedno strojno. Včasih postavitev breakpoint-a pomeni začasno zapisovanje posebne ukazne kode v pravo mesto direktno v strojni kodi. Na primer, tako se mi je obnašala standardna ukazna b v GDB. Tukaj je to, kar iz tega sledi:
- ne moreš postaviti točke znotraj BootROM, ker ROM
- lahko nastaviš točko ustavljanja na kodo, naloženo v delovno pomnilnik s SD-kartice, a moraš počakati, da se naloži. V nasprotnem primeru ne moremo prepisati dela kode, temveč naložnik prepiše naš breakpoint.
Prepričan sem, da lahko izrecno prosimo, da se uporabijo strojne točke ustavljanja, vendar je teh v vsakem primeru omejeno število.
Hitro zamenjanje BootROM
V začetni fazi odpravljanja napak pogosto pride do želje, da bi popravili BootROM in poskusili še enkrat. Toda obstaja težava: BootROM je del zasnove, naloženega v FPGA, in njegova sinteza je zadeva nekaj minut (in to po skoraj takojšnji kompilaciji same slike BootROM iz C in Asemblerja…). Na srečo, je v resnici vse veliko hitreje: zaporedje dejanj je naslednje:
- ponovno generirati bootrom.mif (prešel sem na MIF namesto HEX, ker sem imel s HEX nenehne težave, MIF pa je domači Altera format)
- v Quartus reči
Processing -> Update Memory Initialization File - na točki Assembler (v levem stolpcu Tasks) ukazati Start again
Za vse skupaj — nekaj deset sekund.
Priprava SD-kartice
Tuk je vse razmeroma preprosto, a potrebujete potrpljenje in približno 14 GB diskovnega prostora:
git clone https://github.com/sifive/freedom-u-sdk
git submodule update --recursive --init
makePo tem morate vstaviti čisto, natančneje, brez kakršne koli uporabne vsebine, SD kartico in izvesti
sudo make DISK=/dev/sdX format-boot-loader… kjer sdX — naprava dodeljena kartici. POZOR: podatki na kartici bodo izbrisani, prepisani in sploh! Nisem prepričan, da bi bilo smiselno graditi celotno zgradbo iz sudo, ker bi v tem primeru vse artefakte zgradbe imela root, in zgradbo bi bilo treba izpeljati iz sudo nenehno.
Na koncu dobite kartico, označeno v GPT s štirimi razdelki, od katerih je eden FAT z uEnv.txt in zagonsko sliko v formatu FIT (vsebuje več podslik, vsako z lastnim naslovom nalaganja), drugi razdelek pa je brez vsebine, predvideno ga je formatirati v Ext4 za Linux. Še dva razdelka sta mračna: na enem se nahaja U-Boot (njegovo pomikanje, kolikor razumem, je zakodirano v BootROM), na drugem pa se zdijo da se nahajajo njeni okoljski spremenljivki, vendar jih zaenkrat ne uporabljam.
Prva stopnja, BootROM
Pravijo, da: 'Če v programiranju obstajajo plesne koreografije, potem so v elektroniki še z gasilnim aparatom'. Ne gre zgolj za to, da sem enkrat skoraj pokvaril ploščo, ker sem se mislil: 'No, GND je vendarle samo nizek nivo' (videti je, da bi upor morda vseeno pomagal…) Gre bolj za to, da če roke ne izhajajo iz tam, elektronika kljub temu prinaša presenečenja: med zalaganjem konektorja na ploščo mi ni uspelo normalno zalotati kontaktov — na videu kažejo, kako se spajka kar sama razlije po celotni povezavi, samo pritisnite spajkalnik, pri meni pa se je 'nalepila' kamorkoli. No, morda spajkalna zlitina ni bila primerna za temperaturo spajkalnika, morda še kaj… Na splošno, ko sem videl, da imam že deset stikov, sem obupal in začel z odkrivanjem napak. In tu se je začelo mračno: priključil sem RX/TX iz UART-a, nalagam programsko opremo — piše
INIT
CMD0
ERRORNo, vse je logično — modula SD kartice nisem priključil. Popravljam situacijo, naložim programsko opremo… In tišina… Matematično sem razmišljal, a je bil problem zelo preprost: en od izhodov modula je bilo treba priključiti na VCC. V mojem primeru je modul podpiral 5V za napajanje, zato sem, ne da bi razmišljal, vtaknil žico, ki se je tiahla iz modula, na nasprotno stran plošče. Na koncu je bilo slabo zalotano konektor raztegnjeno, in preprosto je izgubil stik UART. facepalm.jpg Na splošno "slaba glava nogam ne da počitka", a pokvarjene roke ...
Na koncu sem v Minicom-u zagledal dolgo pričakovano
INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
NALAGANJE /Poleg tega se indikator nalaganja vrti in premika. Spominjam se šolskih dni in počasi nalaganja MinuetOS z disket. Le, da diskovni pogon ne škrtne.
Problem je v tem, da po sporočilu BOOT ne pride do ničesar. Torej, čas je, da se povežemo preko OpenOCD na Raspberry, GDB na hostu in pogledamo, kaj za vraga to je.
Najprej je povezava preko GDB takoj pokazala, da $pc (programski števec, naslov trenutne ukaza) beži v 0x0 – verjetno se to zgodi po večkratni napaki. Zato takoj po izdanem sporočilu BOOT dodamo neskončno zanko. To ga bo za kratek čas zadržalo ...
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;
}Takšna zvita koda se uporablja "za zanesljivost": nekje sem slišal, da naj bi neskončna zanka bila Undefined Behavior, tu pa se kompilator verjetno ne bo spomnil (spominjam se, da 0x10000 se nahaja BootROM).

Zdi se, da bi še kaj drugega pričakovali – stroga embedded, kakšna naj tu bo izvorna koda. A v avtor je odpravljal C kodo ... Kreks-feks-peks:
(gdb) file builds/zeowaa-e115/sdboot.elf
Program se že zagošča.
Ali ste prepričani, da želite spremeniti datoteko? (da ali ne) da
Branje simbolov iz builds/zeowaa-e115/sdboot.elf ... končano.
Samo morate naložiti ne MIF datoteko in ne bin, ampak izvirno različico v formatu ELF.
Zdaj lahko po x poskusu uganemo naslov, kjer se bo izvajanje nadaljevalo (to je še en razlog, zakaj se kompilator ni smel spomniti, da je zanka - neskončna). Ukaz
set variable $pc=0xADDRdovoli spremeniti vrednost registra med izvajanjem (v tem primeru – naslov trenutne ukaze). Z njim lahko tudi spreminjate vrednosti, shranjene v pomnilniku (in memory-mapped registre).
Na koncu sem prišel do zaključka (nisem prepričan, da je pravilno), da imamo "sliko sd kartice napačnega sistema", in preklopiti moramo ne na sam začetek naloženih podatkov, ampak na 0x89800 byte naprej:
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 .rodataMogoče je vplivala tudi dejstvo, da sem namesto nepotrebne 4GB kartice vzel 2GB in z metodo poskusov zamenjal v Makefile. DEMO_END=11718750 na DEMO_END=3078900 (ne iščite smisla v konkretni pomensko vrednosti — nima ga, samo zdaj se slika prilega na kartico).
Druga raven, U-Boot
Zdaj še vedno «padamo», a smo že na naslovu 0x0000000080089a84. Tu se moram priznati: v resnici se razlaga ne dogaja «z vsemi postanki», temveč se deloma piše že «po tem», zato sem tu že uspel vstaviti pravilen dtb-dokument iz našega SoC, popraviti nastavitve. HiFive_U-Boot spremenljivko CONFIG_SYS_TEXT_BASE=0x80089800 (namesto 0x08000000), da se naslov nalaganja ujema z dejanskim. Zdaj nalagamo že kartico naslednje ravni druge podobe:
(gdb) file ../freedom-u-sdk/work/HiFive_U-Boot/u-boot
(gdb) tui enIn 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) │Pri čemer skačemo med vrsticama 308 in 309. Kar ni presenetljivo, glede na to, da je $sp vrednost 0xfffffffe31cdc0a0. Žal pa se tudi nenehno «umika» zaradi vrstice 307. Zato poskusimo postaviti prekinitev na trap_entry, nato pa se ponovno vrnemo na 0x80089800 (vhodna točka U-Boot), in upajmo, da ni potrebno pravilno nastaviti registrov pred prehodom… Zdi se, da deluje:
(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 = 0x81cf950Nekaj slab indikator zaslona, pravzaprav: kaže povsem mimo RAM (če, seveda, še nimamo prevajanja naslovov, a upajmo na preprosto različico).
Poskusimo zamenjati kazalec z 0x881cf950. Na koncu pridemo do tega, da se handle_trap nenehno kliče in od tam odidemo v _exit_trap s argumentom epc=2148315240 (v decimalni obliki):
(gdb) x/10i 2148315240
0x800cb068 : lbu a4,0(a5)
0x800cb06c : bnez a4,0x800cb078
0x800cb070 : sub a0,a5,a0
0x800cb074 : ret
0x800cb078 : addi a5,a5,1
0x800cb07c : j 0x800cb064
0x800cb080 : addi sp,sp,-32
0x800cb084 : sd s0,16(sp)
0x800cb088 : sd ra,24(sp)
0x800cb08c : li s0,0Устанавливаем точку останова на strnlen, продолжаем и видим:
(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=, precision=, flags=) 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 x , ra lxn", fmt@entry=0x800d4458 "exception code: %d , %s , epc x , ra lxn", 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 x , ra lxn", 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 x , ra lxn", args=args@entry=0x881cc188) at lib/vsprintf.c:717
#5 0x00000000800ccb50 in printf (fmt=fmt@entry=0x800d4458 "exception code: %d , %s , epc x , ra lxn") at lib/vsprintf.c:792
#6 0x000000008008a9f0 in _exit_trap (regs=, epc=2148315240, code=) at arch/riscv/lib/interrupts.c:92
#7 handle_trap (mcause=, epc=, regs=) 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Похоже, _exit_trap он хочет выдать отладочную информацию о произошедшем исключении, но у него не получается. Так, что-то у нас исходники опять не отображаются. установить директории .. /freedom-u-sdk/HiFive_U-Boot/ О! Теперь отображаются!
Что ж, запустим еще раз, и увидим по стек-трейсу причину исходной проблемы, вызвавшей первую ошибку (mcause == 5). Если я правильно понял, что написано на стр. 37, то это исключение означает Load access fault. Причина, по-видимому, в том, что вот здесь
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 имеет то самое некорректное значение, и внутри board_init_f_init_reserve возникает ошибка. Похоже, вот и виновник: переменная с недвусмысленным названием CONFIG_SYS_INIT_SP_ADDR. Она определена в файле HiFive_U-Boot/include/configs/HiFive-U540.h. V nekem trenutku sem pomislil, da bi lahko morda raje popravili procesor, namesto da bi do konca razvijal nalagalnik. Potem pa sem opazil, da bolj spominja na artefakt zaradi nedokončanih -#if 0-nih nastavitev za drugo konfiguracijo pomnilnika, zato lahko poskusimo narediti tako:
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 */V nekem trenutku je število krpet doseglo kritične točke. Po nekaj mukah sem prišel do potrebe po pravilnem portiranju na svojo ploščo. Zame je bilo potrebno kopirati in prilagoditi nekaj datotek za našo konfiguracijo.
No, približno, tukaj je malo
trosinenko@trosinenko-pc:/hdd/trosinenko/fpga/freedom-u-sdk/HiFive_U-Boot$ git show --name-status
commit 39cd67d59c16ac87b46b51ac1fb58f16f1eb1048 (HEAD -> zeowaa-1gb)
Author: Anatoly Trosinenko
Date: Tue Jul 2 17:13:16 2019 +0300
Prvotna podpora za ploščo Zeowaa A-E115FB
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.hPodrobnosti si lahko ogledate v .
Kot se je pokazalo, imajo registri nekaterih naprav na tej SiFive plošči drugačne naslove. In še, U-Boot se konfigurira s pomočjo mehanizma Kconfig, ki je znan iz jedra Linux — na primer, lahko damo ukaz make menuconfig, in pred vami se prikaže priročni besedilni vmesnik z opisom parametrov. ? И так далее. В общем, комбинируя описания двух плат, я составил описание третьей, убрав всякие пафосные перенастройки PLL (видимо, это связано с управлением с хост-компьютера по PCIe, но точно не знаю), я получил прошивку, которая при подходящих условиях на Марсе отправляла мне по UART сообщение о том, из какого хеша коммита она собрана, и о том, сколько у меня DRAM (но эту информацию я сам прописал в заголовке).
Жаль только, что после этого плата обычно переставала отвечать по JTAG процессора, а загрузка с SD-карты, увы, в моей конфигурации не быстрая. С другой стороны, иногда BootROM выдавал сообщение, что ERROR, загрузка не удалась, и тут же появляется U-Boot. Тут-то до меня дошло: видимо, после перезагрузки bitstream в FPGA память не перетирается и не успевает 'растренироваться' и так далее. Короче, можно просто при появлении сообщения LOADING / подключаться отладчиком и задавать set variable $pc=0x80089800, тем самым минуя эту долгую загрузку (если предположить, что в прошлый раз она сломалась достаточно рано и не успела загрузить что-то поверх оригинального кода).
Кстати, нормально ли, что процессор полностью зависает, и JTAG-отладчик не может подключиться с сообщениями
Error: unable to halt hart 0
Error: dmcontrol=0x80000001
Error: dmstatus =0x00030c82Подождите! Я это уже видел! Что-то подобное происходит при дедлоке TileLink, а автора контроллера памяти я как-то не доверяю — ведь сам же писал… Внезапно, после первой удачной пересборки процессора после редактирования контроллера я увидел:
INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
LOADING
BOOT
U-Boot 2018.09-g39cd67d-dirty (3 июл 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На эту странную строчку перед In: serial Ignoring this — I was trying to understand on a hanging processor whether it works correctly with the environment. What does "It's been hanging like this for ten minutes already" mean? At least it managed to relocate and switch to the boot menu! A small digression: although U-Boot is loaded in the first 2^24 bytes from the SD card, once it starts, it copies itself somewhere further to the address either recorded in the configuration header or just to the higher addresses of the RAM, performs ELF symbol relocation, and hands over control to that address. So, it seems this level has been passed and as a bonus, we got a processor that doesn’t hang completely after that.
So, why isn't the timer working? It seems the clock isn't running for some reason…
(gdb) x/x 0x0200bff8
0x200bff8: 0x00000000What if we manually turn the knobs?
(gdb) set variable *0x0200bff8=310000000
(gdb) cThen:
Hit any key to stop autoboot: 0
MMC_SPI: 0 at 0:1 hz 20000000 mode 0Conclusion: the clock isn't running. Most likely, this is also why keyboard input isn't working:
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 == '[') {
...The problem turned out to be that I overcomplicated a bit: I added a key to the processor configuration:
case DTSTimebase => BigInt(0)… based on the fact that the comment said "if you don’t know — leave it as 0." And indeed WithNBigCores it set it to 1MHz (as was indicated in the U-Boot configuration). But I’m being meticulous: there I don’t know, here it’s 25MHz! In the end, nothing works. I removed my "improvements" and…
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 #You can even enter commands! For example, after a bit of digging around, you might finally figure out to enter mmc_spi 1 10000000 0; mmc part, zmanjšal frekvenco SPI z 20MHz na 10MHz. Zakaj? No, v konfiguraciji je bila zapisana največja frekvenca 20MHz in tam je še vedno napisana. A kolikor sem razumel, vmesniki, vsaj tukaj, delujejo takole: koda deli frekvenco strojne opreme (pri meni – povsod 25MHz) na ciljno, in nastavi pridobljeno vrednost kot delitelj v ustrezen upravljalski register. Težava je v tem, da če bo za 115200Hz UART približno to, kar potrebujemo, bo pri deljenju 25000000 z 20000000 izšlo 1, torej bo delovalo pri 25MHz. Morda je to tudi v redu, ampak če postavijo omejitve, pomeni, da je to nekomu potrebno (ampak to ni točno)... Na kratko, lažje je nastaviti in iti naprej – daleč in, nažalost, dolgo. 25MHz ni ravno Core i9.
Izhod 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 0x82000000V redu, prešli smo na novo raven, a se še vedno zatika. Včasih pa še izpiše izjeme. Mcanje lahko vidite, če ulovite kodo na določenem naslovu $pc in potem si pristati na trap_entry. Sam obdelovalec iz U-Boot lahko izpisuje le za mcause = 0..4, zato se pripravi na zadrego pri nalaganju. Tu sem se poglobil v konfiguracijo, začel gledati, kaj sem spreminjal, in se spomnil: tam je conf/rvboot-fit.txt pisano:
fitfile=image.fit
# spodaj mora ustrezati tistemu v FIT (ugha)No, uskladili bomo vse datoteke in zamenjali ukazno vrstico jedra približno tako, ker obstajajo domneve, da SIF0 — to je izhod nekje po PCIe:
-bootargs=console=ttySIF0,921600 debug
+bootargs=console=ttyS0,125200 debugIn ob tem bomo spremenili algoritem hashanja z SHA-256 na MD5: kriptološke odpornosti ne potrebujem (še manj, pri odpravljanju napak), to se izračunava neverjetno dolgo, MD5 pa povsem zadostuje za zaznavanje napak pri nalaganju. Kakšni so rezultati? Prejšnji nivo smo začeli prehitevati opazno hitreje (zaradi enostavnejšega hashanja) in odprl se je naslednji:
...
Preverjanje integritete hasha ... md5+ OK
Nalaganje nalagalnih datotek od 0x90a5a758 do 0x82000000
libfdt fdt_check_header(): FDT_ERR_BADMAGIC
izbira {
linux,initrd-konec = ;
linux,initrd-zacetek = ;
riscv,kernel-konec = ;
riscv,kernel-zacetek = ;
bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait";
};
libfdt fdt_path_offset() je vrnil FDT_ERR_NOTFOUND
izbira {
linux,initrd-konec = ;
linux,initrd-zacetek = ;
riscv,kernel-konec = ;
riscv,kernel-zacetek = ;
bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait";
};
Nalaganje slikovnega jedra ... OK
Zagon jedra v
3Samo uro pa ne teče...
(gdb) x/x 0x0200bff8
0x200bff8: 0x00000000Ups, zdi se, da je popravilo hoje ur bilo placebo, čeprav se mi je takrat zdelo, da je pomagalo. Ne, popraviti je treba, a najprej poglejmo, če lahko obrnemo kazalce ročno in vidimo, kaj se zgodi:
0x00000000bff6dbb0 v ?? ()
(gdb) set variable *0x0200bff8=1000000
(gdb) c
Nadaljevanje.
^C
Program je prejel signal SIGINT, prekinitev.
0x00000000bff6dbb0 v ?? ()
(gdb) set variable *0x0200bff8=2000000
(gdb) c
Nadaljevanje.
^C
Program je prejel signal SIGINT, prekinitev.
0x00000000bff6dbb0 v ?? ()
(gdb) set variable *0x0200bff8=3000000
(gdb) c
Nadaljevanje.Medtem...
Nalaganje slikovnega jedra ... OK
Zagon jedra v
3
2
1
0
## Začetek aplikacije na 0x80000000 ...Ne, grem avtomatizirati hoje ur — da morda ne bo tam začel kalibrirati časovnika!
In naslov trenutne instrukcije medtem kaže nekam v
0000000080001c20 :
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
80001c2e: e406 sd ra,8(sp)
80001c30: f7fff0ef jal ra,80001bae
80001c34: 8522 mv a0,s0
80001c36: 267000ef jal ra,8000269c
80001c3a: 00010797 auipc a5,0x10
80001c3e: 41e78793 addi a5,a5,1054 # 80012058
80001c42: 639c ld a5,0(a5)
80001c44: c399 beqz a5,80001c4a
80001c46: 72c000ef jal ra,80002372
80001c4a: 45a1 li a1,8
80001c4c: 4501 li a0,0
80001c4e: dc7ff0ef jal ra,80001a14
80001c52: 10500073 wfi
80001c56: bff5 j 80001c52внутри загруженного Berkeley Boot Loader. Лично меня в этом смущает упоминание htif — интерфейс хоста, используемый для привязанного запуска ядра (то есть в сотрудничестве с хостовым ARM), я-то предполагал автономный. Впрочем, если найти эту функцию в исходниках, то видно, что не всё так плохо:
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"); }
}
}Квест: запустить часы
Поиск регистров в CLINT приводит нас к
val io = IO(new Bundle {
val rtcTick = Bool(INPUT)
})
val time = RegInit(UInt(0, width = timeWidth))
when (io.rtcTick) { time := time + UInt(1) }Который подключается к RTC, либо в загадочном MockAON, про который я изначально рассудил: «Так, что это у нас тут? Непонятно? Отключаем!» Поскольку мне до сих пор непонятно, что это за тактовая магия там творится, поэтому просто перереализую эту логику в 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
}Пройдя путь к ядру Linux
Тут повествование уже и без того затянулось и стало немного однообразным, поэтому опишу по верхам:
BBL предполагал наличие FDT по адресу 0xF0000000, а я ведь уже исправлял! Ну что ж, поищем ещё… Нашёл в HiFive_U-Boot/arch/riscv/lib/boot.c, заменил на 0x81F00000, указанное в конфигурации загрузки U-Boot.
Потом BBL жаловался, что нет памяти. Мой путь лежал в функцию mem_prop, что в riscv-pk/machine/fdt.c: оттуда я узнал, что нужно пометить узел fdt ram как device_type = "memory" — potem, mogoče, bo treba popraviti procesorski generator, a za zdaj bom vseeno ročno vpisal — ta datoteka je bila že ročno prenesena.
Zdaj sem prejel sporočilo (prikazano v formatirani obliki, z vračanjem kariet):
To je bbl-ov dummy_payload. Za zagon pravega jedra ponovno konfigurirajte bbl
z označbo --with-payload=PATH, nato ponovno sestavite bbl. Alternativno,
bbl se lahko uporablja v samo firmware načinu z dodajanjem vozlišč
za zunanje payload in uporabo QEMU-ovih -bios in -kernel možnosti.Zdi se, da so možnosti navedene tako, kot je treba riscv,kernel-start in riscv,kernel-end v DTB, a ničle se ne razčlenijo. Odpravljanje napak query_chosen je pokazalo, da BBL poskuša razčleniti 32-bitni naslov, a naleti na par <0x0 0xADDR>, in prva vrednost se zdi, da so nizki bitni. Dodal sem v razdelek chosen
chosen {
#address-cells = ;
#size-cells = ;
...
}in popravil generacijo vrednosti: ne dodajati 0x0 kot prvi element.
Ti 100500 preprostih korakov vam bodo omogočili, da enostavno preverite, kako pada pingvin:
Skriti tekst
Verifying Hash Integrity ... md5+ OK
Loading loadables from 0x90a5a758 to 0x82000000
libfdt fdt_check_header(): FDT_ERR_BADMAGIC
chosen {
linux,initrd-end = ;
linux,initrd-start = ;
riscv,kernel-end = ;
riscv,kernel-start = ;
#address-cells = ;
#size-cells = ;
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 = ;
linux,initrd-start = ;
riscv,kernel-end = ;
riscv,kernel-start = ;
#address-cells = ;
#size-cells = ;
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)(BBL displays the emblem, and what comes with the timestamps is the kernel).
Fortunately, I don't know how it is everywhere, but on RocketChip, when connecting the debugger via JTAG, you can catch traps out of the box — the debugger will stop exactly at that point.
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 PCfreedom-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); // < ТУТ ВЫ
}Kot je rekel stari vic, CPU ni najden, izvaja se programska emulacija. No ali ne izvaja. Zgubili smo se v enem jedru procesorja.
/* The lucky hart to first increment this variable will boot the other cores */
atomic_t hart_lottery;
unsigned long boot_cpu_hartid;Dober komentar v linux/arch/riscv/kernel/setup.c — nekaj takega, kot barvanje ograje po metodi Toma Sawyerja. Na splošno danes nekako ni bilo zmagovalcev, nagrada se prenese na naslednji krog...
Na tem predlagam, da končamo že tako predolgo članke.
Nadaljevanje sledi. V njem bo boj s prevarantsko napako, ki se uspe skriti, če se ji počasi približujemo s singlestep-om.
Besedilni screencast nalaganja (zunanja povezava):
Izvor: habr.com
