I Der blev implementeret en mere eller mindre fungerende hukommelseskontrol, nærmere bestemt en wrapper over IP Core fra Quartus, som fungerer som en adapter til TileLink. I dag i kategorien "Portering af RocketChip til et mindre kendt kinesisk board med Cyclone" vil du se en fungerende konsol. Processen tog lidt længere tid end forventet; jeg troede, at jeg hurtigt ville kunne starte Linux og komme videre, men sådan gik det ikke. I denne del foreslår jeg, at vi ser på processen med at starte U-Boot, BBL, og de forsigtige forsøg på, at Linux kernen initialiserer sig. Men der er en konsol — U-Boot-konsollen, og den er ret avanceret, med meget af det, man forventer fra en fuldgyldig konsol.
I hardware-delen vil der blive tilføjet et SD-kort via SPI-interface samt UART. I software-delen vil BootROM blive erstattet med xip og få en fungerende kommando. sdboot og faktisk tilføjes følgende boot-faser (på SD-kortet).
Finjustering af hardware-delen
Så opgaven: Vi skal overgå til det "store" kerne og forbinde UART (fra Raspberry) og SD-adapter (der blev brugt et eller andet board fra Catalex med seks pins: GND, VCC, MISO, MOSI, SCK, CS).
Generelt set var det ret simpelt. Men før jeg indså det, blev jeg lidt kastet frem og tilbage; efter sidste gang besluttede jeg, at jeg bare skulle blande ind i System noget som HasPeripheryUART (og i implementeringen selvfølgelig), det samme gælder for SD-kortet — og så ville alt være klart. Senere besluttede jeg at se, hvordan det var implementeret i et "seriøst" design. Så, hvad har vi her af det seriøse? Arty ser ud til ikke at passe — der er kun monstret unleahshed.DevKitConfigs. Og pludselig opdagede jeg, at der overalt var nogle overlays, der tilføjes gennem parametre ved hjælp af nøgler. Jeg gætter på, at det sandsynligvis er meget fleksibelt og konfigurerbart, men jeg ville bare have noget, jeg kunne starte ... Har I ikke noget tilsvarende, bare lettere og mere skrøbeligt? Her stødte jeg på vera.iofpga.FPGAChip til Microsemi FPGA og straks splittede jeg det op i citater og prøvede at lave min egen implementering på samme måde, heldigvis er her mere eller mindre al "ledningsføring af systemkortet" i én fil.
Det viste sig, at man faktisk bare skulle tilføje til System.scala linjerne
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
...Linjen i klassens krop System tilføjer information om den frekvens, som denne del af vores SoC fungerer på, til dts-filen. Så vidt jeg forstår, er DTS/DTB en statisk analog til plug-and-play-teknologien for indlejrede enheder: dts-beskrivelsestræet kompileres til en binær dtb-fil og sendes til loaderen, så den kan konfigurere hardware korrekt. Interessant nok, uden linjen med tlclock siger, at alt syntetiseres perfekt, men BootROM kan ikke kompileres (lad mig minde om, at dette nu vil være sdboot) — under kompileringen parser den dts-filen og opretter en header med makroen TL_CLK, hvilket gør det muligt for den korrekt at justere frekvensdelene for de eksterne grænseflader.
Det vil også kræve lidt justering af "fordelingen":
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
}Registreringskæderne er ærligt talt kun tilføjet som analogi med nogle andre steder i den oprindelige kode. De skal sandsynligvis beskytte mod . Måske er der allerede beskyttelse i nogle blokke, men i første omgang ønsker jeg at få det til at køre i det mindste "på et kvalitetsniveau". Et mere interessant spørgsmål for mig er, hvorfor MISO og MOSI er tilknyttet forskellige dq? Ответа я пока так и не нашёл, но, похоже, остальной код рассчитывает именно на такое подключение.
Fysisk har jeg bare tildelt designpins til de ledige kontakter på stikket og flyttet spændingsvalgsjumperen til 3,3V.
SD-adapter
Set fra oven:

Set fra neden:

Debugging af softwaren: værktøjer
Lad os først tale om de tilgængelige debug-værktøjer og deres begrænsninger.
Minicom
For det første skal vi på en eller anden måde læse, hvad loaderen og kernen viser. Til dette i Linux (i dette tilfælde på den, der kører på RaspberryPi) har vi brug for programmet Minicom. Generelt vil ethvert program til at arbejde med den serielle port fungere.
Bemærk, at når programmet starter, skal portens enhedsnavn angives som -D /dev/ttyS0 — efter indstillingen. -DOg den vigtigste oplysning: brug Ctrl-A, X. Jeg har faktisk haft en situation, hvor denne kombination ikke fungerede — så kan du fra en tilstødende SSH-session bare sige killall -KILL minicom.
Der er også en anden funktion. Specifikt på RaspberryPi er der to UART'er, og begge porte kan allerede være tilpasset til noget: den ene til Bluetooth, mens den anden som standard viser kernens konsol. Heldigvis kan denne adfærd reconfigureres. .
Omdannelse af hukommelse
Når jeg fejlretter, måtte jeg nogle gange laste bootloaderen (undskyld) ind i RAM direkte fra værten. Måske kan det gøres direkte fra GDB, men jeg valgte den enkle vej: jeg kopierede den nødvendige fil til Raspberry, åbnede også port 4444 (telnet fra OpenOCD) via SSH og brugte kommandoen load_image. Når du udfører det, ser det ud som om alt er frosset, men i virkeligheden «det sover ikke, det blinker bare langsomt»: det indlæser filen, men gør det med en hastighed på et par kilobyte per sekund.
Egenskaber ved at sætte breakpoints
Mange har sandsynligvis ikke tænkt over dette, når de fejlretter almindelige programmer, men breakpoints sættes ikke altid hårdt. Nogle gange involverer opsætning af et breakpoint midlertidig skrivning af en speciel instruktion til det ønskede sted direkte i maskinkoden. For eksempel virkede standardkommandoen b i GDB sådan:
- man kan ikke sætte et breakpoint inden i BootROM, fordi ROM
- man kan sætte et breakpoint på koden, der er indlæst i RAM fra SD-kortet, men man skal vente, indtil den er indlæst. Ellers er det ikke os, der overskriver en del af koden, men bootloaderen, der overskriver vores breakpoint.
Jeg er sikker på, at man klart kan bede om at bruge hardware breakpoints, men de er i hvert fald begrænsede i antal.
Hurtig erstatning af BootROM
I begyndelsen af fejlfinding opstår der ofte et ønske om at ændre BootROM og prøve igen. Men der er et problem: BootROM er en del af designet, der indlæses i FPGA'en, og dens syntese tager et par minutter (og det efter næsten øjeblikkelig kompilering af selve BootROM-billedet fra C og assembler…). Heldigvis er alt faktisk meget hurtigere: rækkefølgen af handlinger er sådan:
- gengenerere bootrom.mif (jeg skiftede til MIF i stedet for HEX, fordi jeg konstant havde problemer med HEX, og MIF er den oprindelige Altera-format)
- i Quartus sige
Processing -> Update Memory Initialization File - på et punkt Assembler (i venstre kolonne Tasks) befale Start again
Alt i alt tager det — et par ti sekunder.
Forberedelse af SD-kortet
Her er alt relativt simpelt, men man skal have tålmodighed og omkring 14 Gb plads på disken:
git clone https://github.com/sifive/freedom-u-sdk
git submodule update --recursive --init
makeDerefter skal du indsætte et rent, nærmere bestemt et, der ikke indeholder noget nødvendigt, SD-kort og udføre
sudo make DISK=/dev/sdX format-boot-loader… hvor sdX — er enheden tildelt kortet. ADVARSEL: Dataene på kortet vil blive slettet, overskrevet og helt væk! Det er næppe en god idé at udføre hele byggeriet fra sudo, fordi så vil alle byggeartefakter tilhøre root, og byggeriet skal gøres fra sudo hele tiden.
Resultatet er et kort formateret i GPT med fire partitioner, hvoraf den ene har FAT med uEnv.txt og en bootbar image i FIT-format (det indeholder flere underbilleder, hver med sin egen opstartadresse), den anden partition er ren, og den forventes at blive formateret til Ext4 for Linux. De to andre partitioner er mystiske: på den ene bor U-Boot (dets offset er, så vidt jeg forstår, hardkodet i BootROM), og på den anden ser det ud til, at dets miljøvariabler bor, men jeg bruger dem ikke endnu.
Niveau et, BootROM
Folkefærdets visdom siger: "Hvis der i programmering er danse med tamburiner, så er der i elektronik også danse med brandslukkere." Det handler ikke engang om, at jeg en gang næsten brændte styrenheden af, fordi jeg troede, at "Nå, GND — det er jo lavt niveau" (tilsyneladende ville en modstand stadig ikke være skadelig…) Det handler snarere om, at hvis hænderne ikke vokser det rigtige sted fra, så holder elektronik ikke op med at overraske: mens jeg lod forbindelsen til styrenheden, kunne jeg ikke få kontaktpunkterne til at lodde ordentligt — på videoer viser de, hvordan loddetinnet direkte spreder sig over forbindelsen, så snart loddekolben berører det, mens mit bare "klistrer" på en tilfældig måde. Nå, måske passende loddetin ikke kunne håndtere loddekolbens temperatur, måske andet… Alt i alt, da jeg så, at jeg allerede havde et dusin kontaktpunkter, gav jeg op og begyndte at fejlsøge. Og her begyndte det mystiske: tilsluttede RX/TX fra UART, indlæste firmware — og den skriver
INIT
CMD0
ERRORNå, alt er logisk — jeg havde ikke tilsluttet SD-kortmodulet. Lader os rette op på situationen, indlæser firmware... Og der er stilhed... Hvad jeg ikke har overvejet, men kisten åbnede sig simpelthen: en af moduls benene skulle tilsluttes til VCC. I mit tilfælde støttede modulet 5V til strømforsyning, så jeg, uden at tøve, tilsluttede ledningen, der kom fra modulet, til den modsatte side af styrenheden. Resultatet var, at den dårligt loddet forbindelse blev skæv, og jeg mistede bare UART-forbindelsen. facepalm.jpg Generelt set, "En dum hoved giver ikke fred til benene", og klodsede hænder - til hovedet...
Til sidst så jeg det ventede i Minicom
INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
LOADING /Desuden bevæger og drejer indlæsnings indikatoren. Det minder mig om skoleårene og den langsomme indlæsning af MinuetOS fra diskette. Bare disketten knirker ikke.
Problemet er, at efter BOOT-beskeden sker der ikke noget. Så det er på tide at tilslutte via OpenOCD på Raspberry, til GDB på værten, og se hvad det er.
Først viste forbindelsen med GDB straks, at $pc (program counter, adressen for den aktuelle instruktion) flyver til 0x0 — sandsynligvis sker det efter en multiple fejl. Så straks efter at have udgivet beskeden BOOT tilføjer vi en uendelig løkke. Det vil forsinke det lidt...
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;
}Sådan en smart kode bruges "for pålidelighed": jeg har hørt, at en uendelig løkke skulle være Undefined Behavior, men her vil compileren næppe gætte det (Jeg minder om, at i 0x10000 ligger BootROM).

Man skulle tro, hvad kan man forvente — hård embedded, hvilke kilder kan der være. Men faktisk forfatteren debuggede C-koden... Kreks-feks-peks:
(gdb) file builds/zeowaa-e115/sdboot.elf
Et program bliver allerede debuggede.
Er du sikker på, at du vil ændre filen? (y eller n) y
Læsning af symboler fra builds/zeowaa-e115/sdboot.elf...færdig.
Det er kun nødvendigt at indlæse ikke en MIF-fil eller bin, men den originale version i ELF-format.
Nu kan man med mange forsøg gætte adressen, hvor udførelsen skal fortsætte (det er også en grund til, at compileren ikke burde have gættet, at løkken er uendelig). Kommandoen
set variable $pc=0xADDRgør det muligt at ændre registreringsværdien undervejs (i dette tilfælde adressen for den aktuelle instruktion). Med det samme kan man også ændre værdier gemt i hukommelsen (og memory-mapped registre).
I sidste ende kom jeg til den konklusion (jeg er ikke sikker på, at den er korrekt), at vi har "billedet af SD-kortet for det forkerte system", og at vi skal gå til ikke i starten af de indlæste data, men til 0x89800 nogle bytes videre:
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 .rodataMåske påvirkede det også, at jeg uden at have et unødvendigt kort på 4Gb, tog et på 2Gb og ved at metode af prøven og fejl ændrede det i Makefile. DEMO_END=11718750 og få en fungerende kommando. DEMO_END=3078900 (led ikke efter mening i den specifikke betydning – der er ingen, bare nu passer billedet på kortet).
Niveau to, U-Boot
Nu "falder" vi stadig, men vi befinder os allerede på adressen 0x0000000080089a84. Her må jeg indrømme: faktisk er fremstillingen ikke "med alle stop", men delvist skrevet "efterfølgende", så her har jeg allerede fået lagt den rigtige dtb-fil fra vores SoC, rettet indstillingerne. HiFive_U-Boot variabel CONFIG_SYS_TEXT_BASE=0x80089800 (i stedet for 0x08000000), så indlæsningens adresse matcher den faktiske. Vi indlæser nu allerede kortet til næste niveau som et andet billede:
(gdb) file ..\/freedom-u-sdk\/work\/HiFive_U-Boot\/u-boot
(gdb) tui enOg vi ser:
│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) │Og vi hopper mellem linje 308 og 309. Og ikke overraskende, givet at i $sp ligger værdien 0xfffffffe31cdc0a0. Desværre "løber" den også konstant væk på grund af linje 307. Så lad os prøve at sætte et breakpoint på trap_entry, og derefter gå tilbage til 0x80089800 (indgangspunktet for U-Boot), og vi håber, at det ikke kræver korrekt opsætning af registrene før overgangen... Det ser ud til at fungere:
(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
Fortsætte.
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 = 0x81cf950Ikke en god stack pointer, for at sige det mildt: den peger overhovedet væk fra RAM (hvis vi selvfølgelig ikke har adresstransliteration, men lad os håbe på den enkle mulighed).
Lad os prøve at ændre pointeren til 0x881cf950. I sidste ende kommer vi frem til, at handle_trap bliver ved med at blive kaldt, mens vi går ind i _exit_trap med argumentet epc=2148315240 (i decimal):
(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,0Sætter breakpoint på strnlen, fortsætter og ser:
(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 PCDet ser ud til, _exit_trap at den prøver at udgive fejlfindingsinformation om den opståede undtagelse, men det lykkes ikke. Så, vores kildekoder vises igen ikke. set directories ../freedom-u-sdk/HiFive_U-Boot/ Åh! Nu vises de!
Lad os køre det igen, og se på staktracen for årsagen til det oprindelige problem, der forårsagede den første fejl (mcause == 5). Hvis jeg forstår korrekt, hvad der står på side 37, så betyder denne undtagelse Load access fault. Årsagen er åbenbart, at her
arch/riscv/cpu/HiFive/start.S:
call_board_init_f:
li t0, -16
li t1, CONFIG_SYS_INIT_SP_ADDR
and sp, t1, t0 /* tving 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 /* spring til board_init_f() */
$sp har den samme inkorrekte værdi, og inden for board_init_f_init_reserve opstår der en fejl. Det ser ud til, at det er synderen: variabelen med det entydige navn CONFIG_SYS_INIT_SP_ADDR. Den er defineret i filen HiFive_U-Boot/include/configs/HiFive-U540.h. På et tidspunkt overvejede jeg endda, om det ville være lettere at justere processoren lidt i stedet for at færdiggøre bootloaderen til processoren. Men så indså jeg, at det mere lignede et artefakt fra ufuldstændigt indstillede parametre til en anden hukommelseskonfiguration, og jeg kunne prøve at gøre det sådan her:#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 */På et tidspunkt nåede antallet af workarounds en kritisk grænse. Efter at have kæmpet lidt, indså jeg, at det var nødvendigt at lave en korrekt port til mit kort. For dette er det nødvendigt at kopiere og tilpasse et antal filer til vores konfiguration.
Nå, cirka, her er en smule
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
Første støtte til Zeowaa A-E115FB-boardet
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.hDetaljer kan ses i .
Som det viste sig, har registrene for nogle enheder på dette SiFive-kort andre adresser. Det viste sig også, at U-Boot konfigureres via den velkendte mekanisme Kconfig fra Linux-kernen — for eksempel kan man kommmandere make menuconfig, og en praktisk tekstgrænseflade vises med beskrivelser af parametrene. ? Og generelt, ved at sammenflette beskrivelserne af to kort til beskrivelsen af det tredje, og fjerne alle de pompøse PLL-omkonfigurationer (som åbenbart har noget at gøre med styringen fra værtscomputeren via PCIe, men det er ikke helt sikkert), endte jeg med en firmware, der under de rette forhold på Mars sendte mig en meddelelse via UART om, hvilken commit-hash den var bygget fra, og hvor meget DRAM jeg havde (men disse oplysninger havde jeg selv angivet i headeren).
Det er bare ærgerligt, at kortet normalt holdt op med at svare på processorens JTAG, og at boot fra SD-kortet desværre ikke er hurtigt i min konfiguration. På den anden side viste BootROM af og til, at FEJL, det kunne ikke starte, og U-Boot dukker straks op. Her gik det op for mig: åbenbart slettes bitstrømmen ikke i FPGA'en efter en genstart, den når ikke at "reinitialiseres" osv. Kort sagt, man kan bare, når meddelelsen dukker op, INDLÆSNING / tilslutte sig debuggeren og give kommandoen set variable $pc=0x80089800, og dermed springe den lange boot over (naturligvis forudsat at det brød ned tidligt sidste gang og ikke nåede at indlæse noget over originalkoden).
For øvrigt, er det normalt, at processoren låser fuldstændigt, så JTAG-debbugeren ikke kan forbinde med meddelelserne
Fejl: kunne ikke bremse hart 0
Fejl: dmcontrol=0x80000001
Fejl: dmstatus =0x00030c82Vent lige lidt! Jeg har set det før! Noget lignende sker ved deadlock i TileLink, og jeg stoler ikke rigtigt på forfatteren af hukommelseskontrolleren - han skrev det selv... Pludselig, efter den første vellykkede ombygning af processoren efter redigering af kontrolleren, så jeg:
INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
INDLÆSNING
BOOT
U-Boot 2018.09-g39cd67d-dirty (03. jul 2019 - 13:50:33 +0300)
DRAM: 1 GiB
MMC:
FØR INDLÆSNING AF ENVFØR FDTCONTROLADDRFØR INDLÆSNING AF ADDRInd: serial
Out: serial
Err: serial
Tryk på en vilkårlig tast for at stoppe autoboot: 3På denne mærkelige linje før Ind: serial Lad ikke dette irritere dig – jeg prøvede på en hangende processor at forstå, om det virker korrekt med miljøet. Hvad betyder det, "Det har hængt i ti minutter"? Har det i det mindste formået at relo-kere og gå til opstartsmenuen! En lille bemærkning: Selv om U-Boot indlæses blandt de første 2^24 byte fra SD-kortet, kopierer det sig selv til en sikrere adresse, som enten er gemt i konfigurationsheaderen eller blot i de øverste adresser i RAM,producerer relokering af ELF-symboler og overgiver kontrollen dertil. Så vidt jeg kan se, har dette niveau været gennemført, og som bonus har vi fået en processor, der ikke hænger stenhårdt bagefter.
Så hvorfor fungerer timeren ikke? Det ser ud til, at klokken af en eller anden grund slet ikke tæller...
(gdb) x/x 0x0200bff8
0x200bff8: 0x00000000Hvad hvis vi drejer hændernes (timere)?
(gdb) set variable *0x0200bff8=310000000
(gdb) cSå:
Tryk på en vilkårlig tast for at stoppe autoboot: 0
MMC_SPI: 0 ved 0:1 hz 20000000 mode 0Konklusion: Uret fungerer ikke. Sandsynligvis skyldes det også, at der ikke er indtastning fra tastaturet:
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:
/* Første tegn i ANSI escape-sekvensen 'e' */
if (c == 'e') {
*esc = 1;
*key = KEY_NONE;
}
break;
case 1:
/* Andet tegn i ANSI '[' */
if (c == '[') {
...Problemet viste sig at være, at jeg havde gjort det lidt rigeligt: jeg tilføjede en nøgle i processorens konfiguration:
case DTSTimebase => BigInt(0)... baseret på hvad der stod i kommentaren "hvis du ikke ved det, så lad det være 0". Og så WithNBigCores fik den faktisk indstillet til 1MHz (som det faktisk også var angivet i U-Boot konfigurationen). Men jeg var, for pokker, omhyggelig og grundig: her ved jeg ikke, men her er 25MHz! I sidste ende virker intet. Jeg fjernede mine "forbedringer" og...
Tryk på en vilkårlig tast for at stoppe autoboot: 0
MMC_SPI: 0 ved 0:1 hz 20000000 mode 0
## Ukendt partitionstabeltype 0
libfdt fdt_path_offset() returnerede FDT_ERR_NOTFOUND
** Ingen partitionstabel - mmc 0 **
## Info: input datastørrelse = 34 = 0x22
Kører uEnv.txt boot2...
## Fejl: "boot2" ikke defineret
HiFive-Unleashed #Man kan endda indtaste kommandoer! For eksempel, efter lidt graven, kan man endelig gætte at indtaste mmc_spi 1 10000000 0; mmc part, hvilket reducerer SPI-frekvensen fra 20MHz til 10MHz. Hvorfor? Nå, i konfigurationen var den maksimale frekvens skrevet som 20MHz, og den står der stadig. Men efter hvad jeg forstår, fungerer grænsefladerne her, i hvert fald, sådan: koden deler hardwareblokfrekvensen (her er den 25MHz) med den ønskede, og sætter det resulterende værdi som divider i den relevante kontrolregister. Problemet er, at hvis for en UART ved 115200Hz det ville være nogenlunde korrekt, så vil en direkte deling af 25000000 med 20000000 give 1, dvs. det vil fungere på 25MHz. Måske er det okay, men hvis der er begrænsninger, så skal det være nødvendigt for nogen (men det er ikke sikkert)... I det hele taget er det nemmere at sætte det og komme videre — langt væk og, desværre, i lang tid. 25MHz er ikke det samme som en Core i9.
Konsoloutput
HiFive-Unleashed # env edit mmcsetup
edit: mmc_spi 1 10000000 0; mmc part
HiFive-Unleashed # boot
MMC_SPI: 1 ved 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 0x82000000Okay, vi har nået et nyt niveau, men det hænger stadig. Nogle gange spammer det også med undtagelser. Du kan se mcause ved at overvåge koden på den angivne adresse $pc og efter si at være på trap_entry. Selve behandleren fra U-Boot kan kun vise for mcause = 0..4, så forbered dig på at komme i en løkke ved forkert indlæsning. Her dykker jeg ind i konfigurationsfilen, ser på hvad jeg har ændret, og husker: der står jo i conf/rvboot-fit.txt det skrevet:
fitfile=image.fit
# nedenunder skal meget matche, hvad der er i FIT (ugha)Nå, lad os bringe alle filer i overensstemmelse, ændre kommando-strengen for kernen omtrent således, da der er mistanker om, at SIF0 — det er output et eller andet sted via PCIe:
-bootargs=console=ttySIF0,921600 debug
+bootargs=console=ttyS0,125200 debugOg i samme omgang ændrer vi hash-algoritmen fra SHA-256 til MD5: kryptostyrken har jeg ikke brug for (især under debugging), det siges at tage forfærdeligt lang tid, og for at fange integritetsfejl ved opstart er MD5 mere end tilstrækkeligt. Hvad er så resultatet? Vi går betydeligt hurtigere gennem det forrige niveau (takket være den enklere hashing), og det næste niveau er blevet åbnet:
...
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 = ;
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";
};
Loading Kernel Image ... OK
Booting kernel in
3Det er bare ikke urene, der tikker…
(gdb) x/x 0x0200bff8
0x200bff8: 0x00000000Ups, det ser ud til, at justeringen af uret var et placebo, selvom det dengang virkede, som om det hjalp. Nej, det skal selvfølgelig fixes, men lad os først dreje viserne manuelt og se, hvad der sker:
0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=1000000
(gdb) c
Fortsætter.
^C
Programmet modtog signal SIGINT, Interrupt.
0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=2000000
(gdb) c
Fortsætter.
^C
Programmet modtog signal SIGINT, Interrupt.
0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=3000000
(gdb) c
Fortsætter.I mellemtiden…
Loading Kernel Image ... OK
Booting kernel in
3
2
1
0
## Starting application at 0x80000000 ...Nej, jeg går i gang med at automatisere urens gang — for måske overvejer det at kalibrere timeren!
Og adressen på den nuværende instruktion peger i mellemtiden et eller andet sted hen
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 80001c52inden for Berkeley Boot Loader er startet. Det bekymrer mig personligt, at der nævnes htif — host interface, der anvendes til tethered opstart af kernen (det vil sige i samarbejde med host ARM), jeg havde forventet standalone. Men hvis man finder denne funktion i kildekoden, er det ikke så slemt:
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 ("wfi"); }
}
}Quest: start klokken
Søgning efter registre i CLINT fører os til
val io = IO(new Bundle {
val rtcTick = Bool(INPUT)
})
val time = RegInit(UInt(0, width = timeWidth))
when (io.rtcTick) { time := time + UInt(1) }Som tilsluttes RTC, eller i den mystiske MockAON, som jeg først tænkte: «Okay, hvad er det her? Uforståeligt? Slukker!» Da jeg stadig ikke forstår, hvilken taktmagi der sker der, vil jeg bare reproducere denne logik i System.scala:
val rtcDivider = RegInit(0.asUInt(16.W)) // for en sikkerheds skyld understøtter jeg op til 16GHz, jeg er optimist :)
val mhzInt = p(DevKitFPGAFrequencyKey).toInt
// Lad os antage, at frekvensen er et helt antal megahertz
rtcDivider := Mux(rtcDivider === (mhzInt - 1).U, 0.U, rtcDivider + 1.U)
outer.clintOpt.foreach { clint =>
clint.module.io.rtcTick := rtcDivider === 0.U
}På vej mod Linux-kernen
Her er fortællingen allerede strukket lidt for meget og blevet en smule ensformig, så jeg vil beskrive den på overfladen:
BBL forventede tilstedeværelsen af FDT på adressen 0xF0000000, og jeg havde allerede rettet det! Nå, lad os lede videre… Jeg fandt det i HiFive_U-Boot/arch/riscv/lib/boot.c, ændret det til 0x81F00000, som angivet i U-Boot's bootkonfiguration.
Derefter klagede BBL over, at der ikke var noget hukommelse. Min vej gik til funktionen mem_prop, viser at riscv-pk/machine/fdt.c: derfra fandt jeg ud af, at noden fdt ram skal mærkes som device_type = "memory" — måske skal jeg senere justere processegeneratoren, men for nu skriver jeg det bare ind manuelt — jeg har trods alt overført denne fil med hånden.
Nu har jeg fået en besked (vist i formateret format med linjeskift):
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.Det lader til, at de nødvendige indstillinger angives korrekt riscv,kernel-start og riscv,kernel-end i DTB, men nulene parses. Debugging query_chosen viste, at BBL prøver at parse en 32-bit adresse, men den får en par <0x0 0xADDR>, og den første værdi ser ud til at være de lavere bits. Jeg tilføjede til sektionen chosen
chosen {
#address-cells = ;
#size-cells = ;
...
}og justerede værdigenereringen: ikke tilføje 0x0 som første element.
Disse 100500 enkle trin vil let og enkelt vise, hvordan pingvinen falder:
Skjult tekst
Verificerer hash-integritet ... md5+ OK
Indlæser loadables fra 0x90a5a758 til 0x82000000
libfdt fdt_check_header(): FDT_ERR_BADMAGIC
valgt {
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() returnerede FDT_ERR_NOTFOUND
valgt {
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";
};
Indlæser kernelbilledet ... OK
Starter kernel i
3
2
1
0
## Starter applikation ved 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: Ignorerer hukommelsesområde 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 ons. jul 3 21:29:21 MSK 2019
[ 0.000000] bootconsole [early0] aktiveret
[ 0.000000] Initial ramdisk ved: 0x(____ptrval____) (16777216 bytes)
[ 0.000000] Zonesvarianter:
[ 0.000000] DMA32 [mem 0x0000000080200000-0x00000000bfffffff]
[ 0.000000] Normal [mem 0x00000000c0000000-0x00000bffffffffff]
[ 0.000000] Flytbar zone-start for hver node
[ 0.000000] Tidlig hukommelsenoden ranges
[ 0.000000] node 0: [mem 0x0000000080200000-0x00000000bfffffff]
[ 0.000000] Initmem opsætning node 0 [mem 0x0000000080200000-0x00000000bfffffff]
[ 0.000000] På node 0 totalpages: 261632
[ 0.000000] DMA32 zone: 3577 sider brugt til memmap
[ 0.000000] DMA32 zone: 0 sider reserveret
[ 0.000000] DMA32 zone: 261632 sider, LIFO batch:63
[ 0.000000] software IO TLB: kortlagt [mem 0xbb1fc000-0xbf1fc000] (64MB)(emblemet vises af BBL, og det med tidsstempler — kernen).
Heldigvis ved jeg ikke, hvordan det er overalt, men på RocketChip kan man fange traps fra æsken ved at tilslutte debuggere via JTAG — debuggeren stopper præcist på dette punkt.
Programmet modtog signal SIGTRAP, Trace/breakpoint-trap.
0xffffffe0000024ca in ?? ()
(gdb) bt
#0 0xffffffe0000024ca in ?? ()
Tilbagekaldning stoppet: tidligere ramme identisk med denne ramme (korrupt stak?)
(gdb) file work/linux/vmlinux
Et program bliver allerede debugget.
Er du sikker på, at du vil ændre filen? (y eller n) y
Læser symboler fra work/linux/vmlinux...færdig.
(gdb) bt
#0 0xffffffe0000024ca in setup_smp () at /hdd/trosinenko/fpga/freedom-u-sdk/linux/arch/riscv/kernel/smpboot.c:75
#1 0x0000000000000000 in ?? ()
Tilbagekaldning stoppet: ramme gemte ikke 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); // < DU ER HER
}Som det blev sagt i den gamle vits, CPU ikke fundet, kører software-emulering. Eller ikke running. Gik forkert i den eneste CPU-kerne.
/* The lucky hart to first increment this variable will boot the other cores */
atomic_t hart_lottery;
unsigned long boot_cpu_hartid;En god kommentar i linux/arch/riscv/kernel/setup.c — dette er at male et hegn efter Tom Sawyers metode. Generelt er der i dag ikke fundet nogen vindere, præmien flyttes til næste udgave...
Jeg foreslår, at vi slutter denne allerede for lange artikel.
Fortsættelse følger. I den vil der være en kamp med en snedig fejl, som kan gemme sig, hvis man nærmer sig den langsomt med singlestep.
Tekstbaseret skærmoptagelse af opstart (ekstern link):
Kilde: habr.com
