Uues olid töötav mälu kontroller, täpsemalt — IP Core'i ümbertöötamine Quartusest, mis toimib üleminekuna TileLinkile. Täna rubriigis "Kandke RocketChip üle vähemtuntud Hiina plaadile Cycloniga" näete töötavat konsooli. Protsess venis veidi: ma juba arvasin, et saan kiiresti Linuxi üles ja edasi liikuda, kuid mitte seekord. Selles osas pakun vaadata U-Booti, BBL-i ja algama üritusi Linuxi tuuma initsialiseerimiseks. Kuid konsool on — U-Booti konsool ja üsna arenenud, sisaldades palju sellest, mida ootate korralikult töötavalt konsoolilt.
Riistvaras lisandub SD-kaart, mis on ühendatud SPI liidese kaudu, samuti UART. Tarkvaras BootROM asendatakse xip . Tundub, et sdboot ja tegelikult lisatakse järgmised laadimisetapid (SD-kaardil).
Riistvara meetodite täiustamine
Nii, ülesanne: tuleb minna "suurema" tuuma ja ühendada UART (Raspberry'lt) ning SD-adapter (kasutati mingit Catalexi plaati kuue piniga: GND, VCC, MISO, MOSI, SCK, CS).
Tegelikult oli kõik üsna lihtne. Kuid enne, kui seda mõistsin, viskas mind veidi pooleldi: pärast eelmist korda otsustasin, et pean taas lihtsalt segama sisse Süsteem midagi sarnast HasPeripheryUART (ja vastavas seadistuses), sama SD-kaardiga — ja kõik on valmis. Siis otsustasin vaadata, kuidas see on realiseeritud "tõsises" disainis. Nii, mis meil siin tõsise kohta on? Arty, ilmselt, ei sobi — jääb hirv kõigele unleahshed.DevKitConfigs. Ja ootamatult avastasin, et seal on igal pool mingid overlayd, mis lisatakse võtmeargumentide kaudu. Ma aiman, et see on tõenäoliselt väga paindlik ja konfigureeritav, kuid mul oleks vaja vähemalt midagi, et alustada... Kuid teil ei ole midagi sarnast, vaid pisut lihtsamat ja konarlikumat? Siin ma komistasin vera.iofpga.FPGAChip Microsemi FPGA ja kohe kopeerisin tsitaate, proovisin teha oma rakendust sarnaselt, õnneks oli siin enam-vähem kogu "emaplaatide tasakaalustus" ühes failis.
Selgus, et tegelikult peab lihtsalt lisama System.scala read
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
...Rea klassi kehake Süsteem lisab teavet selle sageduse kohta, millega see meie SoC osa töötab, dts-faili. Mida ma aru saan, on DTS/DTB staatiline analoog plug-and-play tehnoloogiale siseseadmetes: dts- kirjelduste puu kompileeritakse binaarseks dtb-failiks ja edastatakse laadijale, et tuum saaks korralikult riistvara seadistada. Mis on huvitav, ilma reaväli tlclock kõik sünteesitakse suurepäraselt, kuid BootROM-i kompileerida ei saa (tuletan meelde, et nüüd on see juba sdboot) — kompileerimise käigus analüüsib ta dts-faili ja loob päise makro TL_CLK, mille abil saab ta õigesti seadistada sageduse jagureid välistes liidetes.
Samuti tuleb veidi muuta "juhendit":
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 kaardile
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
}Registriketid on ausalt öeldes lisatud lihtsalt sarnastest kohtadest algses koodis. Tõenäoliselt peavad nad kaitsma . Võib-olla on mõnedes plokkides juba oma kaitse, kuid soovin esmalt käivitada vähemalt "kvaliteetsel tasemel". Minu jaoks huvitavam küsimus on, miks MISO ja MOSI on erinevates dq? Ответа я пока так и не нашёл, но, похоже, остальной код рассчитывает именно на такое подключение.
Füüsiliselt määrasin ma lihtsalt disaini väljundid vabade kontaktide peale ja viisin pingeteguri valimise jumperi 3.3V peale.
SD-adapter
Ülevaade ülalt:

Ülevaade alt:

Tarkvara osa tõrgete tõrkeotsing: tööriistad
Alustame olemasolevate tõrkeotsingute tööriistade ja nende piirangute arutamist.
Minicom
Esiteks peame me kuidagi lugema, mida laadija ja tuum väljastavad. Selleks vajame Linuxis (antud juhul RaspberryPi peal) programmi Minicom. Üldiselt sobib iga programm, mis töötab sarjaliidese portidega.
Pange tähele, et seadme porti nime tuleb käivitamisel määrata kui -D /dev/ttyS0 — pärast valikut -D. Peamine teave: väljumiseks kasutage Ctrl-A, X. Mul oli tõesti juhtum, kui see kombinatsioon ei töötanud — sel juhul saab naaber SSH seansist lihtsalt öelda killall -KILL minicom.
On Raspberry Pi, there is another feature. Specifically, there are two UARTs, and both ports can be used for something: one for Bluetooth, while the other outputs the kernel console by default. Fortunately, this behavior can be reconfigured. .
Memory rewriting.
During debugging, to test the hypothesis, I sometimes had to load the bootloader (sorry) into RAM directly from the host. Maybe it can be done directly from GDB, but I ultimately took the simpler route: I copied the required file to the Raspberry and forwarded port 4444 (telnet from OpenOCD) via SSH, using the command load_image.When you execute it, it seems like everything is frozen, but actually it's not asleep, it's just blinking slowly.: it loads the file, just doing it at a speed of a few kilobytes per second.
Features of setting breakpoints.
Probably, many have not thought about this when debugging ordinary programs, but breakpoints are not always set in hardware. Sometimes setting a breakpoint involves temporarily writing a special instruction at the desired location directly in machine code.For example, that's how the standard command b in GDB worked for me. Here’s what follows from this:
- you cannot set a breakpoint inside BootROM because ROM
- you can set a breakpoint on code loaded into RAM from the SD card, but you need to wait until it is loaded. Otherwise, we won't overwrite a piece of code, but the bootloader will overwrite our breakpoint.
I am sure you can explicitly request to use hardware breakpoints, but they are limited in number in any case.
Quickly replacing BootROM.
At the beginning of debugging, there is often a desire to modify BootROM and try again. But there’s a problem: BootROM is part of the design loaded into the FPGA, and its synthesis takes a few minutes (and this is after almost instantaneous compilation of the BootROM image itself from C and Assembler...). Fortunately, everything is actually much faster:the sequence of actions is as follows:
- regenerate bootrom.mif (I switched to MIF instead of HEX because I always had some issues with HEX, and MIF is the native Altera format).
- in Quartus, say
Processing -> Update Memory Initialization File - under Assembler (in the left column of Tasks), command Start again.
The whole process takes just a couple of dozen seconds.
Preparing the SD card.
Siin on kõik suhteliselt lihtne, kuid tuleb varuda kannatust ja umbes 14 Gb kettaruumi:
git clone https://github.com/sifive/freedom-u-sdk
git submodule update --recursive --init
makePärast seda tuleb sisestada puhas, täpsemalt öeldes, mitte midagi vajalikku sisaldav SD-kaart ja teha
sudo make DISK=/dev/sdX format-boot-loader… kus sdX — seadme nimi kaardile. KUIDAS: kaardilt andmed kustutatakse, kirjutatakse üle ja muul moel! Tõenäoliselt ei tasuks kogu ülesehitust teha sudo, sest siis kuuluvad kõik ülesehituse artefaktid root, ja ülesehitust tuleb teha sudo korralikult.
Lõpuks on meil kaart, mis on jaotatud GPT-sse neljaks osaks, millest ühes on FAT-ga uEnv.txt ja laadimisvõime FIT formaadis (see sisaldab mitmeid allpildi, igaühel oma laadimisadressiga), teine osa on puhas, mis tuleb vormindada Ext4 Linuxile. Veel kaks osa on müstilised: ühes asub U-Boot (tema nihke, kui ma õigesti aru saan, on BootROM-is sisse kirjutatud), teises tundub, et on tema keskkonnamuutujid, kuid ma neid praegu ei kasuta.
Tase esimene, BootROM
Rahva tarkus ütleb: "Kui programmeerimises on trall, siis elektroonikas on see veel koos tulekustutiga." Asi pole isegi selles, et korra olin ma peaaegu plaati põletanud, mõeldes, et "Noh, GND on ju sama madal tase" (näib, et takisti ei oleks kahjuks ära teinud...) Asi on pigem selles, et kui käed ei kasva sealt, siis elektroonika ei lõpeta üllatusi: kui ma panin pistikut plaadile, ei suutnud ma korralikult kontaktide kohale joota — videos näidatakse, kuidas jootetükk laiali valgub kogu ühenduses, ainult et pintsett viibuta, minule aga see „kleepus“ kuidas juhtub. Noh, võib-olla ei olnud jootetükk sobiv jootmistemperatuurile, võib-olla veel midagi... Ühesõnaga, nähes, et mul on juba tosin kontakti, viskasin ma kõik kõrvale ja hakkasin siluda. Ja siis algas müstiline: ühendasin RX/TX UART-ilt, laadisin pühendatud tarkvara — see ütleb
INIT
CMD0
ERRORNoh, kõik on loogiline — ma ei ühendanud SD-kaardi moodulit. Parandasin olukorra, laadisin firma… Ja vaikus… Mida ma ainult ei mõelnud, aga probleem oli lihtne: mooduli üks väljund tuli ühendada VCC-le. Minu puhul toetas moodul 5V toite, seega palun, ühendasin kaabli, mis tuli moodulist, plaadi vastaspoolele. Lõpuks, halvasti joodetud pistik nihkus, ja Lihtsalt kaotasin UART-kontakti. facepalm.jpg Lühidalt öeldes, "halb pea ei anna jalgadele rahu", aga kõverad käed — peaga…
Lõpuks nägin Minicomis kauaoodatud
INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
LAADIMINE /Veelgi enam, laadimisnäitaja liikuda. See toob meelde kooliaastad ja aeglane MinuetOS laadimine disketilt. Ainult, et ketas ei kriuksi.
Probleem on selles, et pärast BOOT sõnumit ei juhtu midagi. Seega, on kõige õigem aeg ühendada OpenOCD Raspberryga, GDB-ga hostis ja vaadata, mis see on.
Esiteks, ühendus GDB-ga näitas kohe, et $pc (programmi loendur, jooksva käsu aadress) lendab 0x0 — ilmselt toimub see pärast mitut viga. Seetõttu, kohe pärast sõnumi edastamist BOOT lisame lõputu tsükli. See pidurdab teda natuke…
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;
}Selline kaval kood on kasutusel "usaldusväärsuse" nimel: olen kuulnud, et lõputu tsükkel on Undefined Behavior, ja siin kompilaator tõenäoliselt ei arva (tuletan meelde, et 0x10000 asub BootROM).

Tundub, et mida veel oodata — karm embedded, mis siin mingid lähtekoodid. Aga siiski, autor silus C-koodi... Kreks-feks-meks:
(gdb) file builds/zeowaa-e115/sdboot.elf
Programmi debugeeritakse juba.
Kas olete kindel, et soovite faili muuta? (y või n) y
Symbolite lugemine builds/zeowaa-e115/sdboot.elf...tehtud.
Kuid tuleb laadida mitte MIF-faili ega bin'i, vaid originaalversiooni ELF-formaadis.
Nüüd saab järjekordse katsega aimata aadressi, kus täitmine jätkub (see on veel üks põhjus, miks kompilaator ei pidanud arvama, et tsükkel on lõputu). Käsk
set variable $pc=0xADDRlubab teha registri väärtuse muutmise jooksvalt (antud juhul — jooksva käsu aadress). Sellega saab muuta ka mälu (ja mälu-kaardistatud registreid) väärtusi.
Lõpuks jõudsin järeldusele (ei ole kindel, et õige), et meil on "sd-kaardi pilt vale süsteemi jaoks", ja liikuda tuleks mitte andmete algusesse, vaid 0x89800 baiti edasi:
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 .rodataVõib-olla mõjutas seda ka see, et mul ei olnud käepärast liigset 4Gb kaarti, seega võtsin 2Gb ja asendasin juhuslikult Makefile'is. DEMO_END=11718750 . Tundub, et DEMO_END=3078900 (ärge otsige tähendust konkreetses tähenduses – seda pole, lihtsalt nüüd sobib pilt kaardile).
Teine tase, U-Boot
Nüüd me ikka veel „kukume”, kuid juba õigele aadressile. 0x0000000080089a84. Siin pean tunnistama: tegelikult esitus ei käi „kõikide peatumistega”, vaid on osaliselt kirjutatud juba „hiljem”, seega olen ma siin juba suutnud lisada õige dtb-faili meie SoC-lt, seadistustesse muudatusi teha. HiFive_U-Boot muutuja CONFIG_SYS_TEXT_BASE=0x80089800 (asemel 0x08000000), et laadimisadresse vastaks tegelikule. Laadime nüüd järgmise taseme jaoks teise pildi:
(gdb) file ..\/freedom-u-sdk\/work\/HiFive_U-Boot\/u-boot
(gdb) tui enJa näeme:
│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) │Me hüppame ridade vahel 308 ja 309. Ja ei ole üllatav, arvestades, et $sp sisaldab väärtust 0xfffffffe31cdc0a0. Kahjuks see „põgeneb” pidevalt read 307 tõttu. Seetõttu proovime panna peatuse trap_entry, ja siis minna tagasi 0x80089800 (U-Booti sisenemispunkt), lootes, et see ei nõua registrite õiget seadmist enne üleminekut… Tundub, et see töötab:
(gdb) b trap_entry
Peatus 1 aadressil 0x80089a80: failis \/hdd\/trosinenko\/fpga\/freedom-u-sdk\/HiFive_U-Boot\/arch\/riscv\/cpu\/HiFive\/start.S, rida 308.
(gdb) set variable $pc=0x80089800
(gdb) c
Jätkamine.
Peatus 1, trap_entry () aadressil \/hdd\/trosinenko\/fpga\/freedom-u-sdk\/HiFive_U-Boot\/arch\/riscv\/cpu\/HiFive\/start.S:308
(gdb) p\/x $sp
$4 = 0x81cf950Mitte just parim stack pointer, otse öeldes: osutab üldse mööda RAM-i (kui meil pole veel aadresside tõlget, kuid looda lihtsale variandile).
Proovime asendada pointeri 0x881cf950. Lõpuks jõuame tõdemuseni, et handle_trap kutsutakse ja kutsutakse, samal ajal jõudes _exit_trap argumentidega epc=2148315240 (pärisnumbris):
(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,0Seame breakpoint 'ilma strnlen, then we continue and see:
(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/interr 92
#7 handle_trap (mcause=, epc=, regs=) at arch/riscv/lib/interr 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 PCTundub nagu _exit_trap proovib anda teavet, mis puudutab toimunud erandit, aga tal ei õnnestu. Näib, et meie allikad ei kuvata taas. seadistame kaustad .. /freedom-u-sdk/HiFive_U-Boot/ O! Nüüd kuvatakse!
Noh, käivitame uuesti ja näeme, et virnastuse jälg näitab algse probleemi põhjuse, mis põhjustas esimese vea (mcause == 5). Kui ma õigesti aru sain, mis seal öeldakse lehelt 37, siis see erand tähistab Laadi juurdepääsu viga. Tundub, et põhjus on siin,
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 omab see vale ja sees board_init_f_init_reserve tekib vea. Tundub, et siin on süüdlane: muutuja, millel on selge nimi CONFIG_SYS_INIT_SP_ADDR. See on määratletud failis HiFive_U-Boot/include/configs/HiFive-U540.hMõnes mõttes mõtlesin isegi, et võib-olla ei tasu enam laadijat protsessori jaoks edasi arendada — äkki on lihtsam protsessorit veidi kohandada? Kuid siis märkasin, et see näeb rohkem välja nagu artefakt, mis on tingitud valedest seadistustest teise mälukonfiguratsiooni jaoks, ja on võimalik proovida teha nii:#if 0-
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 */Mõnes mõttes jõudis tugede arv kriitilisse punkti. oli jõudnud kriitilisse punkti. Pärast väikest vaeva jõudsin järeldusele, et on hädavajalik teha korrektne port oma plaadile. Selleks on vaja kopeerida ja kohandada meie konfiguratsiooni mõningaid faile.
Noh, umbes niimoodi
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
Esmane tugi Zeowaa A-E115FB plaadi jaoks
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Üksikasju saab vaadata .
Nagu selgus, on sellel SiFive plaadil mõnede seadmete registrid teistel aadressidel. Samuti selgus, et U-Boot konfigureeritakse juba tuntud Linuxi südamikule sarnase Kconfig mehhanismiga — näiteks on võimalik käskida make menuconfig, ja teie ette kerkib mugav tekstipõhine liides koos parameetrite kirjelduste näitamisega. ? ja jne. Ühesõnaga, kombineerides kahe plaadi kirjelduse, onnistus mul luua kolmanda plaadi kirjeldus, jättes välja kõik uhked PLL ümberkorraldused (ilmselt on see seotud hostarvuti juhtimisega PCIe kaudu, aga ma ei ole selles kindel), sain mingisuguse püsivara, mis õigete tingimuste korral Marsil edastas mulle UARTi kaudu teadet, millise commit'i hash'ist see oli ehitatud, ja kui palju mul DRAM'i on (aga selle info panin ma ise ju pealkirja sisse).
Kahjuks tavaliselt pärast seda plaat enam ei reageeri protsessori JTAG'i kaudu, ja SD-kaardilt bootimine – noh, kahjuks on see minu konfiguratsioonis aeglane. Teisest küljest, mõnikord andis BootROM teada, et VIGA, ei suutnud käivitada, ja kohe hüppas välja U-Boot. Siis taipasin: ilmselt pärast restart'i bitstream'i PLD mälu ei kustutata, ei jõua see "dekonfigureerida" jne. Lühidalt, kui kuulen teadet LAADIMINE \/ , saan debuggeriga ühendada ja käskin set variable $pc=0x80089800, mööndes seega selle pika laadimise (loomulikult eeldusel, et see eelmine kord katkestas piisavalt vara ja ei jõudnud originaalkoodi peale midagi laadida).
Üleüldiselt, kas on normaalne, et protsessor hangub täielikult ja JTAG-debugger ei suuda sellega ühendada, andes teadet
Viga: ei suuda peatada hart 0
Viga: dmcontrol=0x80000001
Viga: dmstatus =0x00030c82Oota! Olen seda juba näinud! Midagi sellist juhtub TileLink'i deadlock'i korral, ja ma ei usalda mälu kontrolleri autorit – kirjutasin selle ju ise... Üks hetk, pärast esimest edukat protsessori ümberkokkuvebemist pärast kontrolleri redigeerimist, märkasin:
INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
LAADIMINE
BOOT
U-Boot 2018.09-g39cd67d-dirty (03. juuli 2019 - 13:50:33 +0300)
DRAM: 1 GiB
MMC:
ENNE KESKKONNA LAADIMISTENNE FDTKONTROLLAADRESSENNE LAADIMISEAADRESSSis: seeria
Välja: seeria
Viga: seeria
Vajuta mis tahes klahvi autobooti peatamiseks: 3Selle kummalise rea peale Sis: seeria Ärge pöörake tähelepanu — see olin mina, kes püüdis külmunud protsessoriga aru saada, kas see töötab keskkonnaga korrektselt. Mis tähendab „Juba kümme minutit on niimoodi kinni”? Kas see suutis vähemalt relokatsiooniga edasi liikuda ja pääseda laadimismenüüsse! Väike kõrvalepõige: kuigi U-Boot laaditakse esimesse 2^24 baiti SD-kaardilt, kopeerib see pärast käivitamist end kaugele aadressile, kas siis konfigureerimispealdise järgi, või lihtsalt operatiivmälu kõrgematesse aadressidesse, teostab ELF-sümbolite relokatsiooni ja edastab juhtimise sinna. Seega: tundub, et see tase on ületatud ja boonuseks saime protsessor, mis ei külmu täielikult pärast seda.
Nii et, miks kell ei tööta? Tundub, et kellad ei liigu üldse…
(gdb) x/x 0x0200bff8
0x200bff8: 0x00000000Aga mis siis, kui näppude abil kella käike keerata?
(gdb) set variable *0x0200bff8=310000000
(gdb) cSiis:
Rohkem kui üks klahv peatab autobooti: 0
MMC_SPI: 0 aadressil 0:1 hz 20000000 režiim 0Kokkuvõte: kellad ei tööta. Tõenäoliselt on see põhjus, miks klaviatuurilt sisend ei tööta:
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:
/* Esimene sümbol ANSI escape'i jada 'e' */
if (c == 'e') {
*esc = 1;
*key = KEY_NONE;
}
break;
case 1:
/* Teine sümbol ANSI '[' */
if (c == '[') {
...Probleem oli selles, et ma natuke liialdaselt muutsin: ma lisasin protsessori konfiguratsioonifaili võtme:
case DTSTimebase => BigInt(0)… lähtudes sellest, et kommentaarides oli öeldud „kui te ei tea — jätke 0“. Ja tõepoolest WithNBigCores asetati see täpselt 1 MHz'iks (nagu muide, oli ka U-Booti konfiguratsioonis näidatud). Kuid ma olin, pagana, hoolikas ja põhjalik: seal ma ei tea, siin 25 MHz! Lõppkokkuvõttes ei toiminud miski. Eemaldasin oma „parandused“ ja...
Rohkem kui üks klahv peatab autobooti: 0
MMC_SPI: 0 aadressil 0:1 hz 20000000 režiim 0
## Tundmatu partitsioonitabeli tüüp 0
libfdt fdt_path_offset() tagastas FDT_ERR_NOTFOUND
** Ei ole partitsioonitabelit - mmc 0 **
## Teave: sisendi andmete suurus = 34 = 0x22
Käivitan uEnv.txt boot2...
## Viga: "boot2" ei ole määratletud
HiFive-Unleashed #Saame isegi käske sisestada! Näiteks, natuke nokitsedes, saame lõpuks aru, et peame sisestama mmc_spi 1 10000000 0; mmc part, vähendades SPI sagedust 20 MHz-lt 10 MHz-le. Miks? Noh, konfiguratsioonis oli kirjutatud maksimaalne sagedus 20 MHz, see on seal ikka veel kirjas. Aga kuidas ma aru saan, et liidesed, vähemalt siin, töötavad nii: kood jagab riistvarakompleksi sageduse (minu puhul — kõikjal 25 MHz) sihtsagedusega ja seadeb saadud väärtuse vastavasse juhtregistrisse jaguriks. Probleem on selles, et kui 115200 Hz UART-ile sobib umbkaudu see, mida on vaja, siis kui jagada 25000000 otse 20000000-ga, saadakse 1, st see töötab 25 MHz-l. Võib-olla on see ka normaalne, aga kui piirangud on seatud, siis peavad need kedagi huvitama (aga see ei pruugi olla kindel)… Ühesõnaga, lihtsam on need näitajad kirja panna ja edasi liikuda — kaugele ja, kahjuks, pikaks ajaks. 25 MHz — see ei ole Core i9.
Konsoli väljund
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 0x82000000Okei, me oleme uuele tasemele üle läinud, kuid see hangub endiselt. Ja vahel viskab ka erandeid. mcause'i näha on võimalik, püüdes koodi antud aadressilt $pc ja pärast si jõuda trap_entry. U-Boot-ile on töötleja, mis suudab välja anda ainult mcause = 0..4, seega valmistuge tsüklisse jääma vale laadimise tõttu. Siin vaatasin konfiguratsioonifaili ja mõtlesin, mida ma muutsin ja meenutasin: seal on ju conf/rvboot-fit.txt kirjutatud:
fitfile=image.fit
# allpool peab vastama sellele, mis on FIT-is (ugha)Mis siis, viime kõik failid kooskõlla, muutame kerneli käsurea umbes nii, kuna on kahtlus, et SIF0 — see on väljund kuhugi PCIe kaudu:
-bootargs=console=ttySIF0,921600 debug
+bootargs=console=ttyS0,125200 debugJa lisaks muudame krüpteerimise algoritmi SHA-256 pealt MD5-ks: krüptograafilist tugevust mul ei ole vaja (veelgi enam, silumise ajal), see tundub äärmiselt aeglane, ja vigade tuvastamiseks laadimise ajal piisab MD5-st. Mis siis lõpuks? Eelmise taseme läbimine muutus märgatavalt kiiremaks (lihtsama krüptimise tõttu) ja järgnev avanes:
...
Verifying Hash Integrity ... md5+ OK
Laadimine 0x90a5a758-st 0x82000000-sse
libfdt fdt_check_header(): FDT_ERR_BADMAGIC
chosen {
linux,initrd-end = ;
linux,initrd-start = ;
riscv,kernel-end = ;
riscv,kernel-start = ;
bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait";
};
libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND
chosen {
linux,initrd-end = ;
linux,initrd-start = ;
riscv,kernel-end = ;
riscv,kernel-start = ;
bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait";
};
Laadimine Kernel Image ... OK
Kernel käivitamine
3Aga kellad ei käi...
(gdb) x/x 0x0200bff8
0x200bff8: 0x00000000Ups, näib, et kella käigu parandamine osutus platseeboks, kuigi mul siis tundus, et see aitas. Ei, muidugi tuleb korda teha, aga alustame sellest, et keerame käepidemed käsitsi ja vaatame, mis juhtub:
0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=1000000
(gdb) c
Jätkab.
^C
Programm sai signaali SIGINT, katkestus.
0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=2000000
(gdb) c
Jätkab.
^C
Programm sai signaali SIGINT, katkestus.
0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=3000000
(gdb) c
Jätkab.Sel ajal...
Laadimine Kernel Image ... OK
Kernel käivitamine
3
2
1
0
## Rakenduse käivitamine 0x80000000-st ...Ei, lähen automatiseerima kellade käiku — võibolla üritab ta seal mingit kalibreerimist teha!
Ja praegune käsu aadress osutab samal ajal kuhugi
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 80001c52laaditud Berkeley Boot Loaderi sees. Isiklikult mind häirib see mainimine htif — host interface, mida kasutatakse tuuma tihendatud käivitamiseks (st koostöös host ARM-iga), ma arvasin, et see on standalone. Siiski, kui leida see funktsioon lähtekoodist, siis ei ole kõik nii hull:
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"); }
}
}Ülesanne: käivita kellad
Registrite otsimine CLINT-is viib meid
val io = IO(new Bundle {
val rtcTick = Bool(INPUT)
})
val time = RegInit(UInt(0, width = timeWidth))
when (io.rtcTick) { time := time + UInt(1) }Mis on ühendatud RTC-sse või salapärasesse MockAON-i, mille kohta ma algselt mõtlesin: "Nii, mis see siin on? Ebaselge? Lülitan välja!" Kuna ma ei saa endiselt aru, mis see takti maagia seal toimub, teen lihtsalt selle loogika uuesti rakenduse System.scala:
val rtcDivider = RegInit(0.asUInt(16.W)) // igaks juhuks toetan kuni 16GHz, olen optimist :)
val mhzInt = p(DevKitFPGAFrequencyKey).toInt
// Oletame, et sagedus on täisarv megaherzhides
rtcDivider := Mux(rtcDivider === (mhzInt - 1).U, 0.U, rtcDivider + 1.U)
outer.clintOpt.foreach { clint =>
clint.module.io.rtcTick := rtcDivider === 0.U
}Liikudes Linux kernel'i suunas
Siin on jutustus juba veninud ja muutunud veidi monotoonseks, seetõttu kirjeldan seda ülevalt alla:
BBL eeldas olemasolu FDT aadressil 0xF0000000, ja ma olen seda juba korrigeerinud! Noh, uurime veel… Leidsin HiFive_U-Boot/arch/riscv/lib/boot.c, asendasin 0x81F00000, mida on näidatud U-Booti laadimisconfiguratsioonis.
Siis BBL kurtis, et mälu pole. Minu tee viis funktsiooni mem_prop, mis riscv-pk/machine/fdt.c: seal sain teada, et fdt ram sõlme tuleb märkida kui device_type = "memory" — siis võibolla tuleb protsessorigeneraatorit hiljem korrigeerida, aga praegu lihtsalt kirjutan käsitsi — olen niikuinii selle faili käsitsi üle kantud.
Nüüd sain sõnumi (esitatud vormindatud kujul, rikka reavahetustega):
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.Tundub, et kõik vajalikud valikud on määratud. riscv,kernel-start ja riscv,kernel-end DTB-s, aga nullid tõlgendatakse valesti. Silumine query_chosen näitas, et BBL proovib tõlgendada 32-bitist aadressi, kuid sellele sattub paar <0x0 0xADDR>, ja esimene väärtus tundub olevat madalamad bitid. Kirjutasin sektsiooni chosen
chosen {
#address-cells = ;
#size-cells = ;
...
}ja parandasin väärtuste genereerimist: mitte lisada 0x0 esimese elemendiga.
Need 100500 lihtsat sammu aitavad sul lihtsalt ja kergelt näha, kuidas pingviin kukub:
Peidetud tekst
Kontrollimine Hash Integrity ... md5+ OK
Laadimine loadables vahemälust 0x90a5a758 kuni 0x82000000
libfdt fdt_check_header(): FDT_ERR_BADMAGIC
valitud {
linux,initrd-lõpp = ;
linux,initrd-alguses = ;
riscv,kerne-lõpp = ;
riscv,kerne-alguses = ;
#aadress-cellerid = ;
#suurus-cellerid = ;
bootargs = "debug console=tty0 console=ttyS0,125200 root=\/dev\/mmcblk0p2 rootwait";
stdout-path = "uart0:38400n8";
};
libfdt fdt_path_offset() tagastas FDT_ERR_NOTFOUND
valitud {
linux,initrd-lõpp = ;
linux,initrd-alguses = ;
riscv,kerne-lõpp = ;
riscv,kerne-alguses = ;
#aadress-cellerid = ;
#suurus-cellerid = ;
bootargs = "debug console=tty0 console=ttyS0,125200 root=\/dev\/mmcblk0p2 rootwait";
stdout-path = "uart0:38400n8";
};
Laadimine Kernel Image ... OK
Käivitamine kernelis
3
2
1
0
## Rakenduse käivitamine aadressil 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 555555 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: Ignores mäluruum 0x80000000 - 0x80200000
[ 0.000000] Linuxi versioon 4.19.0-sifive-1+ (trosinenko@trosinenko-pc) (gcc versioon 8.3.0 (Buildroot 2019.02-07449-g4eddd28f99)) #1 SMP Kolm Juuli 3 21:29:21 MSK 2019
[ 0.000000] bootconsole [early0] lubatud
[ 0.000000] Algne ramdisk aadressil: 0x(____ptrval____) (16777216 baiti)
[ 0.000000] Tsooni vahemikud:
[ 0.000000] DMA32 [mäluaadress 0x0000000080200000-0x00000000bfffffff]
[ 0.000000] Normaalsed [mäluaadress 0x00000000c0000000-0x00000bffffffffff]
[ 0.000000] Liikuv tsoon algab iga sõlme jaoks
[ 0.000000] Varajased mälu sõlmed
[ 0.000000] sõlm 0: [mäluaadress 0x0000000080200000-0x00000000bfffffff]
[ 0.000000] Initmem seadistamine sõlmes 0 [mäluaadress 0x0000000080200000-0x00000000bfffffff]
[ 0.000000] Sõlmes 0 on kokku lehekülgi: 261632
[ 0.000000] DMA32 tsoonis: 3577 lehte kasutatud memmap
[ 0.000000] DMA32 tsoonis: 0 lehte reserveeritud
[ 0.000000] DMA32 tsoonis: 261632 lehte, LIFO partii:63
[ 0.000000] tarkvara IO TLB: kaardistatud [mäluaadress 0xbb1fc000-0xbf1fc000] (64MB)(BBL kuvab embleemi, ja see, mis on ajatempleid sisaldav, on kernel).
Õnneks, ma ei tea, kuidas mujal, kuid RocketChip'is saab JTAG-i kaudu debugeerijat ühendades karistusi otse kinni püüda — debugeerija peatub just selles kohas.
Programm sai signaali SIGTRAP, jälgimise/puruste karistus.
0xffffffe0000024ca in ?? ()
(gdb) bt
#0 0xffffffe0000024ca in ?? ()
Tagasiviide peatub: eelmine raam on sama, mis see raam (rikutud virn?)
(gdb) fail work\/linux\/vmlinux
Programm on juba debugeerimise all.
Kas olete kindel, et soovite faili muuta? (y või n) y
Sümbolite lugemine failist work\/linux\/vmlinux...valmis.
(gdb) bt
#0 0xffffffe0000024ca in setup_smp () at \/hdd\/trosinenko\/fpga\/freedom-u-sdk\/linux\/arch\/riscv\/kernel\/smpboot.c:75
#1 0x0000000000000000 in ?? ()
Tagasiviide peatub: raam ei salvestanud PC-dfreedom-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); // < TE OLETE SIIN
}Nagu öeldakse vanas anekdoodis, CPU ei leitud, käivitamine tarkvaraga emulatsiooniga. Või nagu, ei running. Eksisime protsessori ühes tuumas.
/* The lucky hart to first increment this variable will boot the other cores */
atomic_t hart_lottery;
unsigned long boot_cpu_hartid;Hea kommentaaris linux/arch/riscv/kernel/setup.c — selline Tom Sawyer'i meetodiga aiaga värvimine. Ühesõnaga, täna pole mingil põhjusel võitjaid leidunud, auhind edasilükkub järgmisse loosimisse…
Sellega lõpetan juba niigi veninud artikli.
Jätkub. Selles on lahing kavalate vigadega, mis suudavad peituda, kui sellele ettevaatlikult singlestep-iga läheneda.
Tekstiline skrinnikastimine laadimisest (väline link):
Allikas: habr.com
