Parte 3: Quase carregando o Linux de um cartão SD para o RocketChip

Na parte anterior , um controlador de memória de trabalho mais ou menos foi implementado, ou melhor, um wrapper sobre o IP Core da Quartus, que é um adaptador para o TileLink. Hoje, sob o título “Portamos o RocketChip para uma placa-mãe chinesa pouco conhecida com o Cyclone”, você verá um console em funcionamento. O processo se arrastou um pouco: eu já pensei em lançar rapidamente o Linux e seguir em frente, mas não estava lá. Nesta parte, proponho examinar o processo de inicialização das tentativas tímidas de inicialização do U-Boot, BBL e do kernel Linux. Mas há um console - U-Boot-ovsky, e bastante avançado, com muito do que você espera de um console completo.


No hardware, um cartão SD conectado via SPI, bem como UART, será adicionado. Na parte do software, o BootROM será substituído de xip para sdboot e, de fato, os seguintes estágios de inicialização (no cartão SD) serão adicionados.


Dopando o hardware


Portanto, a tarefa: você precisa mudar para o núcleo "grande" e conectar o UART (da Raspberry) e o adaptador SD (foi utilizado algum tipo de placa Catalex com seis pinos: GND, VCC, MISO, MOSI, SCK, CS).


Em princípio, tudo era bastante simples. Mas antes de perceber isso, fui jogado um pouco de um lado para o outro: depois do tempo anterior, decidi que novamente você só precisa misturar algo como HasPeripheryUART no System (e na implementação, respectivamente), o mesmo para o cartão SD - e é isso estará pronto. Então eu decidi ver como isso foi implementado em um design "sério". Então, o que tiramos de sério? Arty, aparentemente, não se encaixa - o monstro permanece sem unleahshed.DevKitConfigs . E, de repente, descobriu-se que havia em toda parte algum tipo de sobreposição que era adicionada através dos parâmetros pelas teclas. Eu acho que isso provavelmente é muito flexível e configurável, mas eu gostaria de começar algo para começar ... Mas você não tem o mesmo, apenas muletas mais fáceis? .. Então me deparei com vera.iofpga.FPGAChip para FPGAs Microsemi e imediatamente puxado entre aspas Tentei fazer minha implementação por analogia, o benefício aqui é mais ou menos todo o "layout da placa-mãe" em um arquivo.


Na verdade, você só precisa adicionar linhas ao System.scala


 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 ... 

Uma linha no corpo da classe System adiciona informações sobre a frequência com que essa parte do nosso SoC trabalha no arquivo dts. Até onde eu entendi, o DTS / DTB é um análogo estático da tecnologia plug-and-play para dispositivos incorporados: a árvore de descrição do dts é compilada em um arquivo dtb binário e transmitida pelo carregador ao kernel para que ele possa configurar corretamente o hardware. Curiosamente, sem uma linha com tlclock tudo é perfeitamente sintetizado, mas não funcionará para compilar o BootROM (lembrarei agora que será sdboot ) - durante a compilação, ele analisa o arquivo dts e cria um cabeçalho com a macro TL_CLK , graças ao qual ele pode configurar corretamente os divisores de freqüência para interfaces externas.


Você também precisará consertar levemente a “fiação”:


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 } 

As cadeias de registradores, francamente, foram adicionadas simplesmente por analogia com alguns outros lugares no código-fonte. Provavelmente, eles devem se proteger contra a metaestabilidade . Talvez alguns blocos já tenham sua própria proteção, mas primeiro quero executar pelo menos "em um nível alto". Uma pergunta mais interessante para mim é por que o MISO e o MOSI dependem de diferentes dq ? Ainda não encontrei a resposta, mas parece que o restante do código conta exatamente com essa conexão.


Fisicamente, acabei de atribuir as conclusões do projeto aos contatos soltos no bloco e reorganizei o jumper de seleção de tensão em 3,3V.


Adaptador SD

Vista superior:



Vista inferior:



Depurando a parte do software: tools


Primeiro, vamos falar sobre as ferramentas de depuração disponíveis e suas limitações.


Minicom


Primeiro, precisamos ler de alguma forma o que o gerenciador de inicialização e o kernel produzem. Para fazer isso, no Linux (neste caso, no RaspberryPi), precisamos do programa Minicom. De um modo geral, qualquer programa para trabalhar com uma porta serial é adequado.


Observe que na inicialização, o nome do dispositivo da porta deve ser especificado como -D /dev/ttyS0 - após a opção -D . Bem, a principal informação: para sair, use Ctrl-A, X Eu realmente tive um caso em que essa combinação não funcionou - então você pode simplesmente dizer killall -KILL minicom de uma sessão SSH adjacente.


Há outro recurso. Especificamente, existem dois UARTs no RaspberryPi, e ambas as portas já podem ser adaptadas para algo: um para Bluetooth, e outro, o console do kernel é exibido por padrão. Felizmente, esse comportamento pode ser reconfigurado de acordo com este manual .


Reescrevendo a memória


Ao depurar, para testar a hipótese, às vezes eu tinha que carregar o carregador de inicialização (desculpe) na RAM diretamente do host. Talvez isso possa ser feito diretamente do GDB, mas no final fui pelo caminho simples: copiei o arquivo necessário para o Raspberry, também enviei a porta 4444 (telnet do OpenOCD) através do SSH e usei o comando load_image . Quando você o executa, parece que tudo está congelado, mas, na realidade, “ele não dorme, apenas pisca lentamente” : carrega o arquivo, apenas a uma velocidade de alguns kilobytes por segundo.


Recursos de instalação de pontos de interrupção


Provavelmente, muitos não precisaram pensar nisso ao depurar programas regulares, mas os pontos de interrupção nem sempre são definidos no hardware. Às vezes, definir um ponto de interrupção envolve escrever temporariamente uma instrução especial no lugar certo diretamente no código da máquina . Por exemplo, é assim que meu comando b padrão no GDB funcionou. Aqui está o que se segue:


  • você não pode colocar um ponto dentro do BootROM, porque a ROM
  • Você pode definir um ponto de interrupção no código carregado na RAM do cartão SD, mas precisa aguardar até que ele seja carregado. Caso contrário, não reescreveremos um pedaço de código, mas o carregador reescreverá nosso ponto de interrupção

Tenho certeza de que você pode pedir explicitamente o uso de pontos de interrupção de hardware, mas há um número limitado deles de qualquer maneira.


Troca rápida de BootROM


No estágio inicial da depuração, geralmente existe o desejo de corrigir o BootROM e tentar novamente. Mas há um problema: o BootROM faz parte do design carregado no FPGA, e sua síntese é uma questão de vários minutos (e isso é depois de compilar quase instantaneamente a própria imagem do BootROM do C e do Assembler ...). Felizmente, de fato, tudo é muito mais rápido : a sequência de ações é a seguinte:


  • regenere o bootrom.mif (mudei para MIF em vez de HEX, porque com HEX sempre tive alguns problemas e MIF é o formato Alter nativo)
  • em Quartus dizem Processing -> Update Memory Initialization File
  • no Assembler (na coluna esquerda de Tarefas), comando Iniciar novamente

Para tudo sobre tudo - algumas dezenas de segundos.


Preparação do cartão SD


Tudo aqui é relativamente simples, mas você precisa ser paciente e ter cerca de 14 GB de espaço em disco:


 git clone https://github.com/sifive/freedom-u-sdk git submodule update --recursive --init make 

Em seguida, você precisa inserir um cartão SD limpo, ou melhor, que não contenha o que precisa, e executar


 sudo make DISK=/dev/sdX format-boot-loader 

... onde sdX é o dispositivo atribuído ao cartão. ATENÇÃO: os dados no cartão serão apagados, substituídos e em geral! Dificilmente vale a pena fazer a montagem inteira a partir do sudo , porque todos os artefatos da montagem serão de propriedade da root , e a montagem terá que ser feita a partir do sudo tempo todo.


O resultado é um cartão marcado na GPT com quatro seções, uma das quais é FAT com uEnv.txt e uma imagem para download no formato FIT (contém várias sub-imagens, cada uma com seu próprio endereço de download), a outra seção está em branco e deve ser formatada no Ext4 para Linux. Mais duas seções são enigmáticas : o U-Boot vive em um (seu deslocamento, como eu o entendo, está conectado no BootROM); por outro, parece que suas variáveis ​​de ambiente estão ativas, mas ainda não as uso.


Nível Um, BootROM


A sabedoria popular diz: "Se na programação há danças com um pandeiro, então na eletrônica - também com um extintor de incêndio". Nem que uma vez eu quase queimei o quadro, decidindo que "Bem, GND é o mesmo nível baixo" (aparentemente, o resistor ainda não faria mal ...) É mais sobre isso se as mãos não crescem a partir daí, os componentes eletrônicos não param de trazer surpresas: quando eu soldava o conector na placa, ainda não conseguia dissolver os contatos normalmente - o vídeo mostra como a solda se espalha diretamente pela conexão, basta colocar o ferro de soldar e cair sobre mim de qualquer maneira. Bem, talvez a solda não fosse adequada para a temperatura do ferro de soldar, talvez outra coisa ... Em geral, quando vi que já tinha uma dúzia de contatos, cuspi e comecei a depurar. E então uma coisa misteriosa começou: conectei o RX / TX do UART, carrego o firmware - ele escreve


 INIT CMD0 ERROR 

Bem, tudo é lógico - eu não conectei o módulo do cartão SD. Corrigimos a situação, carregamos o firmware ... E silêncio ... O que eu simplesmente não mudei de idéia, e o caixão acabou de abrir: uma das saídas do módulo tinha que ser conectada ao VCC. No meu caso, o módulo suportava 5V de potência; portanto, sem pensar duas vezes, prendi um fio que se estendia do módulo para o lado oposto da placa. Como resultado, o conector soldado torcido torceu e o contato UART foi simplesmente perdido. facepalm.jpg Em geral, “uma cabeça ruim não dá descanso para as pernas” e mãos tortas - para a cabeça ...


No final, vi o tão esperado Minicom


 INIT CMD0 CMD8 ACMD41 CMD58 CMD16 CMD18 LOADING / 

Além disso, move o indicador de carregamento está girando. Lembro-me diretamente dos anos escolares e da inicialização do MinuetOS a partir de um disquete. A menos que a unidade mole.


O problema é que nada acontece após a mensagem de inicialização. Então, é hora de conectar-se através do OpenOCD no Raspberry, GDB no host e ver o que é.


Primeiramente, a conexão com o GDB mostrou imediatamente que $pc (contador de programa, endereço da instrução atual) voa para 0x0 - isso provavelmente acontece após um erro múltiplo. Portanto, imediatamente após a mensagem BOOT ser emitida, adicionamos um loop infinito. Isso vai atrasá-lo por um tempo ...


 diff --git a/bootrom/sdboot/sd.cb/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; } 

Esse código complicado é usado "por confiabilidade": ouvi em algum lugar que, ao que parece, um loop infinito é um comportamento indefinido, e aqui é improvável que o compilador adivinhe (lembro que o BootROM está localizado em 0x10000 ).


Existe um depurador, nenhuma fonte


Parece, mas o que mais se pode esperar - um ambiente hostil, que tipo de fonte existe. Mas afinal, nesse artigo, o autor depurou o código ... Krex-fex-pex:


 (gdb) file builds/zeowaa-e115/sdboot.elf A program is being debugged already. Are you sure you want to change the file? (y or n) y Reading symbols from builds/zeowaa-e115/sdboot.elf...done. 

Existem códigos-fonte!


Somente você precisa carregar não o arquivo MIF e não a lixeira, mas a versão original no formato ELF.


Agora, com a enésima tentativa de adivinhar o endereço onde a execução continuará (esse é outro motivo pelo qual o compilador não deveria ter adivinhado que o loop é infinito). A equipe


 set variable $pc=0xADDR 

permite alterar o valor do registro em movimento (nesse caso, o endereço da instrução atual). Com sua ajuda, você pode alterar os valores registrados na memória (e nos registros mapeados na memória).


No final, cheguei à conclusão (não tenho certeza se está correto) de que temos uma "imagem do cartão sd do sistema errado" e precisamos ir não até o início dos dados baixados, mas para 0x89800 bytes:


 diff --git a/bootrom/sdboot/head.S b/bootrom/sdboot/head.S index 14fa740..2a6c944 100644 --- a/bootrom/sdboot/head.S +++ b/bootrom/sdboot/head.S @@ -13,7 +13,7 @@ _prog_start: smp_resume(s1, s2) csrr a0, mhartid la a1, dtb - li s1, PAYLOAD_DEST + li s1, (PAYLOAD_DEST + 0x89800) jr s1 .section .rodata 

Talvez isso também tenha sido afetado pelo fato de eu não ter um cartão 4Gb desnecessário disponível, eu o levei para 2Gb e o substituí no Makefile por DEMO_END=11718750 por DEMO_END=3078900 (não procure o significado em um valor específico - ele não existe, agora a imagem é colocada para o cartão).


Nível Dois, Inicialização em U


Agora ainda estamos "caindo", mas já estamos no endereço 0x0000000080089a84 . Aqui tenho que admitir: na verdade, a apresentação não é "com todas as paradas", mas é parcialmente escrita "depois", então aqui eu já consegui colocar o arquivo dtb correto em nosso SoC, ajuste a variável CONFIG_SYS_TEXT_BASE=0x80089800 nas HiFive_U-Boot do HiFive_U-Boot (em vez de 0x08000000 ) para que o endereço de download corresponda ao endereço real. Faça o download agora mapa do próximo nível outra imagem:


 (gdb) file ../freedom-u-sdk/work/HiFive_U-Boot/u-boot (gdb) tui en 

E nós vemos:


  │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) │ 

E pulamos entre as linhas 308 e 309. E não é de surpreender, já que $sp contém o valor 0xfffffffe31cdc0a0 . Infelizmente, ele também “foge” constantemente devido à linha 307. Portanto, tentaremos definir um ponto de interrupção em trap_entry e depois mudar novamente para 0x80089800 (ponto de entrada U-Boot), e esperamos que ele não exija a configuração correta de registros antes da transição ... Parece funcionar:


 (gdb) b trap_entry Breakpoint 1 at 0x80089a80: file /hdd/trosinenko/fpga/freedom-u-sdk/HiFive_U-Boot/arch/riscv/cpu/HiFive/start.S, line 308. (gdb) set variable $pc=0x80089800 (gdb) c Continuing. Breakpoint 1, trap_entry () at /hdd/trosinenko/fpga/freedom-u-sdk/HiFive_U-Boot/arch/riscv/cpu/HiFive/start.S:308 (gdb) p/x $sp $4 = 0x81cf950 

Então, o ponteiro da pilha, digamos sem rodeios: geralmente aponta para além da RAM (a menos que, é claro, ainda tenhamos tradução de endereço, mas esperemos uma opção simples).


Vamos tentar substituir o ponteiro por 0x881cf950 . Como resultado, chegamos ao fato de que handle_trap é chamado e chamado, deixando em _exit_trap o argumento epc=2148315240 (em decimal):


 (gdb) x/10i 2148315240 0x800cb068 <strnlen+12>: lbu a4,0(a5) 0x800cb06c <strnlen+16>: bnez a4,0x800cb078 <strnlen+28> 0x800cb070 <strnlen+20>: sub a0,a5,a0 0x800cb074 <strnlen+24>: ret 0x800cb078 <strnlen+28>: addi a5,a5,1 0x800cb07c <strnlen+32>: j 0x800cb064 <strnlen+8> 0x800cb080 <strdup>: addi sp,sp,-32 0x800cb084 <strdup+4>: sd s0,16(sp) 0x800cb088 <strdup+8>: sd ra,24(sp) 0x800cb08c <strdup+12>: li s0,0 

Definimos o ponto de interrupção em strnlen , continuamos e vemos:


 (gdb) bt #0 strnlen (s=s@entry=0x10060000 "", count=18446744073709551615) at lib/string.c:283 #1 0x00000000800cc14c in string (buf=buf@entry=0x881cbd4c "", end=end@entry=0x881cc15c "", s=0x10060000 "", field_width=<optimized out>, precision=<optimized out>, flags=<optimized out>) at lib/vsprintf.c:265 #2 0x00000000800cc63c in vsnprintf_internal (buf=buf@entry=0x881cbd38 "exception code: 5 , ", size=size@entry=1060, fmt=0x800d446e "s , epc %08x , ra %08lx\n", fmt@entry=0x800d4458 "exception code: %d , %s , epc %08x , ra %08lx\n", args=0x881cc1a0, args@entry=0x881cc188) at lib/vsprintf.c:619 #3 0x00000000800cca54 in vsnprintf (buf=buf@entry=0x881cbd38 "exception code: 5 , ", size=size@entry=1060, fmt=fmt@entry=0x800d4458 "exception code: %d , %s , epc %08x , ra %08lx\n", args=args@entry=0x881cc188) at lib/vsprintf.c:710 #4 0x00000000800cca68 in vscnprintf (buf=buf@entry=0x881cbd38 "exception code: 5 , ", size=size@entry=1060, fmt=fmt@entry=0x800d4458 "exception code: %d , %s , epc %08x , ra %08lx\n", args=args@entry=0x881cc188) at lib/vsprintf.c:717 #5 0x00000000800ccb50 in printf (fmt=fmt@entry=0x800d4458 "exception code: %d , %s , epc %08x , ra %08lx\n") at lib/vsprintf.c:792 #6 0x000000008008a9f0 in _exit_trap (regs=<optimized out>, epc=2148315240, code=<optimized out>) at arch/riscv/lib/interrupts.c:92 #7 handle_trap (mcause=<optimized out>, epc=<optimized out>, regs=<optimized out>) at arch/riscv/lib/interrupts.c:55 #8 0x0000000080089b10 in trap_entry () at /hdd/trosinenko/fpga/freedom-u-sdk/HiFive_U-Boot/arch/riscv/cpu/HiFive/start.S:343 Backtrace stopped: frame did not save the PC 

Parece que _exit_trap deseja fornecer informações de depuração sobre a exceção que ocorreu, mas falha . Portanto, algo com o nosso código fonte não é exibido novamente. set directories ../freedom-u-sdk/HiFive_U-Boot/ Oh! Agora exibido!


Bem, execute-o novamente e veja na pilha rastrear a causa do problema original que causou o primeiro erro ( mcause == 5 ). Se eu entendi corretamente o que está escrito aqui na página 37, essa exceção significa Load access fault . A razão, aparentemente, é porque aqui


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 tem o mesmo valor incorreto e ocorre um erro dentro de board_init_f_init_reserve . Esse parece ser o culpado: uma variável com o nome explícito CONFIG_SYS_INIT_SP_ADDR . Está definido no arquivo HiFive_U-Boot/include/configs/HiFive-U540.h . Em algum momento, eu até pensei, ou talvez, bem, termine o gerenciador de inicialização do processador - talvez seja mais fácil consertar levemente o processador? Mas então vi que era mais um artefato das configurações não completamente #if 0 para uma configuração de memória diferente e você pode tentar fazer isso:


 diff --git a/include/configs/HiFive-U540.hb/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\0" /* 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 */ 

Em algum momento, a quantidade muletas fixadores tecnológicos chegaram a um ponto crítico. Depois de um pequeno tormento, cheguei à necessidade de fazer a porta correta na minha placa. Para fazer isso, você precisa copiar e corrigir um certo número de arquivos para nossa configuração.


Bem, aproximadamente, aqui está uma pequena mesa
 trosinenko@trosinenko-pc:/hdd/trosinenko/fpga/freedom-u-sdk/HiFive_U-Boot$ git show --name-status commit 39cd67d59c16ac87b46b51ac1fb58f16f1eb1048 (HEAD -> zeowaa-1gb) Author: Anatoly Trosinenko <anatoly.trosinenko@gmail.com> Date: Tue Jul 2 17:13:16 2019 +0300 Initial support for Zeowaa A-E115FB board M arch/riscv/Kconfig A arch/riscv/cpu/zeowaa-1gb/Makefile A arch/riscv/cpu/zeowaa-1gb/cpu.c A arch/riscv/cpu/zeowaa-1gb/start.S A arch/riscv/cpu/zeowaa-1gb/timer.c A arch/riscv/cpu/zeowaa-1gb/u-boot.lds M arch/riscv/dts/Makefile A arch/riscv/dts/zeowaa-1gb.dts A board/Zeowaa/zeowaa-1gb/Kconfig A board/Zeowaa/zeowaa-1gb/MAINTAINERS A board/Zeowaa/zeowaa-1gb/Makefile A board/Zeowaa/zeowaa-1gb/Zeowaa-A-E115FB.c A configs/zeowaa-1gb_defconfig A include/configs/zeowaa-1gb.h 

Detalhes podem ser encontrados no repositório .


Como se viu, nesta placa SiFive, os registros de alguns dispositivos têm endereços diferentes. Também se descobriu que o U-Boot é configurado pelo mecanismo Kconfig, já familiar no kernel do Linux - por exemplo, você pode comandar make menuconfig e verá uma interface de texto conveniente com descrições de parâmetros mostradas em ? etc. Em geral, tendo cegado a descrição da terceira a partir das descrições de duas placas, descartando qualquer reconfiguração de PLL a partir daí (aparentemente, isso está de alguma forma conectado ao controle PCIe do computador host, mas isso não é exato), obtive um firmware, que, se o tempo estiver bom em Marte Ele me deu uma mensagem do UART sobre qual hash de confirmação foi coletado e quanto DRAM eu tenho (mas também registrei essas informações no cabeçalho).


É uma pena que, depois disso, a placa normalmente pare de responder com o processador JTAG e a inicialização a partir do cartão SD não seja, infelizmente, rápida na minha configuração. Por outro lado, às vezes o BootROM exibia uma mensagem de que ERROR não pôde inicializar e o U-Boot apareceu imediatamente. Foi então que me dei conta: aparentemente, depois de reiniciar o fluxo de bits, a memória não está desgastada no FPGA, não há tempo para "ficar destruído", etc. Em resumo, você pode simplesmente quando a mensagem LOADING / message aparecer, conectar-se a um depurador e set variable $pc=0x80089800 , ignorando esse longo download (é claro, assumindo que a última set variable $pc=0x80089800 foi muito cedo e não havia tempo para o código original download).


A propósito, e geralmente é normal que o processador seja interrompido completamente e o depurador JTAG com mensagens não consiga se conectar a ele


 Error: unable to halt hart 0 Error: dmcontrol=0x80000001 Error: dmstatus =0x00030c82 

Então espere! Eu já vi isso! - TileLink, - — … , :


 INIT CMD0 CMD8 ACMD41 CMD58 CMD16 CMD18 LOADING BOOT U-Boot 2018.09-g39cd67d-dirty (Jul 03 2019 - 13:50:33 +0300) DRAM: 1 GiB MMC: BEFORE LOAD ENVBEFORE FDTCONTROLADDRBEFORE LOADADDRIn: serial Out: serial Err: serial Hit any key to stop autoboot: 3 

In: serial — , environment. , « »? ! : U-Boot 2^24 SD-, , , , , ELF-, . : , , .


, ? , - ...


 (gdb) x/x 0x0200bff8 0x200bff8: 0x00000000 

, ?


 (gdb) set variable *0x0200bff8=310000000 (gdb) c 

:


 Hit any key to stop autoboot: 0 MMC_SPI: 0 at 0:1 hz 20000000 mode 0 

: . , - :


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 == '[') { ... 

, : :


  case DTSTimebase => BigInt(0) 

… , « — 0». WithNBigCores 1MHz (, , U-Boot). , , : , 25MHz! . «» ...


 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 # 

! , , , , mmc_spi 1 10000000 0; mmc part , SPI 20MHz 10MHz. Porque , 20MHz, . , , , , : ( — 25MHz) , . , 115200Hz UART- , , 25000000 20000000 1, .. 25MHz. , , , , - ( )… , — , , . 25MHz — Core i9.


 HiFive-Unleashed # env edit mmcsetup edit: mmc_spi 1 10000000 0; mmc part HiFive-Unleashed # boot MMC_SPI: 1 at 0:1 hz 10000000 mode 0 Partition Map for MMC device 0 -- Partition Type: EFI Part Start LBA End LBA Name Attributes Type GUID Partition GUID 1 0x00000800 0x0000ffde "Vfat Boot" attrs: 0x0000000000000000 type: ebd0a0a2-b9e5-4433-87c0-68b6b72699c7 type: data guid: 76bd71fd-1694-4ff3-8197-bfa81699c2fb 2 0x00040800 0x002efaf4 "root" attrs: 0x0000000000000000 type: 0fc63daf-8483-4772-8e79-3d69d8477de4 type: linux guid: 9f3adcc5-440c-4772-b7b7-283124f38bf3 3 0x0000044c 0x000007e4 "uboot" attrs: 0x0000000000000000 type: 5b193300-fc78-40cd-8002-e86c45580b47 guid: bb349257-0694-4e0f-9932-c801b4d76fa3 4 0x00000400 0x0000044b "uboot-env" attrs: 0x0000000000000000 type: a09354ac-cd63-11e8-9aff-70b3d592f0fa guid: 4db442d0-2109-435f-b858-be69629e7dbf libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND 2376 bytes read in 0 ms Running uEnv.txt boot2... 15332118 bytes read in 0 ms ## Loading kernel from FIT Image at 90000000 ... Using 'config-1' configuration Trying 'bbl' kernel subimage Description: BBL/SBI/riscv-pk Type: Kernel Image Compression: uncompressed Data Start: 0x900000d4 Data Size: 74266 Bytes = 72.5 KiB Architecture: RISC-V OS: Linux Load Address: 0x80000000 Entry Point: 0x80000000 Hash algo: sha256 Hash value: 28972571467c4ad0cf08a81d9cf92b9dffc5a7cb2e0cd12fdbb3216cf1f19cbd Verifying Hash Integrity ... sha256+ OK ## Loading fdt from FIT Image at 90000000 ... Using 'config-1' configuration Trying 'fdt' fdt subimage Description: unavailable Type: Flat Device Tree Compression: uncompressed Data Start: 0x90e9d31c Data Size: 6911 Bytes = 6.7 KiB Architecture: RISC-V Load Address: 0x81f00000 Hash algo: sha256 Hash value: 10b0244a5a9205357772ea1c4e135a4f882409262176d8c7191238cff65bb3a8 Verifying Hash Integrity ... sha256+ OK Loading fdt from 0x90e9d31c to 0x81f00000 Booting using the fdt blob at 0x81f00000 ## Loading loadables from FIT Image at 90000000 ... Trying 'kernel' loadables subimage Description: Linux kernel Type: Kernel Image Compression: uncompressed Data Start: 0x900123e8 Data Size: 10781356 Bytes = 10.3 MiB Architecture: RISC-V OS: Linux Load Address: 0x80200000 Entry Point: unavailable Hash algo: sha256 Hash value: 72a9847164f4efb2ac9bae736f86efe7e3772ab1f01ae275e427e2a5389c84f0 Verifying Hash Integrity ... sha256+ OK Loading loadables from 0x900123e8 to 0x80200000 ## Loading loadables from FIT Image at 90000000 ... Trying 'ramdisk' loadables subimage Description: buildroot initramfs Type: RAMDisk Image Compression: gzip compressed Data Start: 0x90a5a780 Data Size: 4467411 Bytes = 4.3 MiB Architecture: RISC-V OS: Linux Load Address: 0x82000000 Entry Point: unavailable Hash algo: sha256 Hash value: 883dfd33ca047e3ac10d5667ffdef7b8005cac58b95055c2c2beda44bec49bd0 Verifying Hash Integrity ... sha256+ OK Loading loadables from 0x90a5a780 to 0x82000000 

, , . . mcause , $pc si trap_entry . U-Boot mcause = 0..4, . , , , : conf/rvboot-fit.txt :


 fitfile=image.fit # below much match what's in FIT (ugha) 

, , , , SIF0- PCIe :


 -bootargs=console=ttySIF0,921600 debug +bootargs=console=ttyS0,125200 debug 

SHA-256 MD5: ( , ), , MD5 — . Qual é o resultado? ( ), :


 ... Verifying Hash Integrity ... md5+ OK Loading loadables from 0x90a5a758 to 0x82000000 libfdt fdt_check_header(): FDT_ERR_BADMAGIC chosen { linux,initrd-end = <0x00000000 0x83000000>; linux,initrd-start = <0x00000000 0x82000000>; riscv,kernel-end = <0x00000000 0x80a00000>; riscv,kernel-start = <0x00000000 0x80200000>; bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait"; }; libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND chosen { linux,initrd-end = <0x00000000 0x83000000>; linux,initrd-start = <0x00000000 0x82000000>; riscv,kernel-end = <0x00000000 0x80a00000>; riscv,kernel-start = <0x00000000 0x80200000>; bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait"; }; Loading Kernel Image ... OK Booting kernel in 3 

...


 (gdb) x/x 0x0200bff8 0x200bff8: 0x00000000 

, , , , . , , , , :


 0x00000000bff6dbb0 in ?? () (gdb) set variable *0x0200bff8=1000000 (gdb) c Continuing. ^C Program received signal SIGINT, Interrupt. 0x00000000bff6dbb0 in ?? () (gdb) set variable *0x0200bff8=2000000 (gdb) c Continuing. ^C Program received signal SIGINT, Interrupt. 0x00000000bff6dbb0 in ?? () (gdb) set variable *0x0200bff8=3000000 (gdb) c Continuing. 

...


  Loading Kernel Image ... OK Booting kernel in 3 2 1 0 ## Starting application at 0x80000000 ... 

, — , , !


-


 0000000080001c20 <poweroff>: 80001c20: 1141 addi sp,sp,-16 80001c22: e022 sd s0,0(sp) 80001c24: 842a mv s0,a0 80001c26: 00005517 auipc a0,0x5 80001c2a: 0ca50513 addi a0,a0,202 # 80006cf0 <softfloat_countLeadingZeros8+0x558> 80001c2e: e406 sd ra,8(sp) 80001c30: f7fff0ef jal ra,80001bae <printm> 80001c34: 8522 mv a0,s0 80001c36: 267000ef jal ra,8000269c <finisher_exit> 80001c3a: 00010797 auipc a5,0x10 80001c3e: 41e78793 addi a5,a5,1054 # 80012058 <htif> 80001c42: 639c ld a5,0(a5) 80001c44: c399 beqz a5,80001c4a <poweroff+0x2a> 80001c46: 72c000ef jal ra,80002372 <htif_poweroff> 80001c4a: 45a1 li a1,8 80001c4c: 4501 li a0,0 80001c4e: dc7ff0ef jal ra,80001a14 <send_ipi_many> 80001c52: 10500073 wfi 80001c56: bff5 j 80001c52 <poweroff+0x32> 

Berkeley Boot Loader. htif — host interface, tethered- ( ARM), - standalone. , , , :


 void poweroff(uint16_t code) { printm("Power off\r\n"); finisher_exit(code); if (htif) { htif_poweroff(); } else { send_ipi_many(0, IPI_HALT); while (1) { asm volatile ("wfi\n"); } } } 

:


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 kernel


, :


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" — , , , — .


( , ):


 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. 

, riscv,kernel-start riscv,kernel-end DTB, . query_chosen , BBL 32- , <0x0 0xADDR> , , , . chosen


 chosen { #address-cells = <1>; #size-cells = <0>; ... } 

: 0x0 .


100500 , :


  Verifying Hash Integrity ... md5+ OK Loading loadables from 0x90a5a758 to 0x82000000 libfdt fdt_check_header(): FDT_ERR_BADMAGIC chosen { linux,initrd-end = <0x83000000>; linux,initrd-start = <0x82000000>; riscv,kernel-end = <0x80a00000>; riscv,kernel-start = <0x80200000>; #address-cells = <0x00000001>; #size-cells = <0x00000000>; bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait"; stdout-path = "uart0:38400n8"; }; libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND chosen { linux,initrd-end = <0x83000000>; linux,initrd-start = <0x82000000>; riscv,kernel-end = <0x80a00000>; riscv,kernel-start = <0x80200000>; #address-cells = <0x00000001>; #size-cells = <0x00000000>; bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait"; stdout-path = "uart0:38400n8"; }; Loading Kernel Image ... OK Booting kernel in 3 2 1 0 ## Starting application at 0x80000000 ... bbl loader SIFIVE, INC. 5555555555555555555555555 5555 5555 5555 5555 5555 5555 5555 5555555555555555555555 5555 555555555555555555555555 5555 5555 5555 5555 5555 5555 5555555555555555555555555555 55555 55555 555555555 55555 55555 55555 55555 55555 5 55555 55555 55555 55555 55555 55555 55555 55555 55555 55555 55555 555555555 55555 5 SiFive RISC-V Core IP [ 0.000000] OF: fdt: Ignoring memory range 0x80000000 - 0x80200000 [ 0.000000] Linux version 4.19.0-sifive-1+ (trosinenko@trosinenko-pc) (gcc version 8.3.0 (Buildroot 2019.02-07449-g4eddd28f99)) #1 SMP Wed Jul 3 21:29:21 MSK 2019 [ 0.000000] bootconsole [early0] enabled [ 0.000000] Initial ramdisk at: 0x(____ptrval____) (16777216 bytes) [ 0.000000] Zone ranges: [ 0.000000] DMA32 [mem 0x0000000080200000-0x00000000bfffffff] [ 0.000000] Normal [mem 0x00000000c0000000-0x00000bffffffffff] [ 0.000000] Movable zone start for each node [ 0.000000] Early memory node ranges [ 0.000000] node 0: [mem 0x0000000080200000-0x00000000bfffffff] [ 0.000000] Initmem setup node 0 [mem 0x0000000080200000-0x00000000bfffffff] [ 0.000000] On node 0 totalpages: 261632 [ 0.000000] DMA32 zone: 3577 pages used for memmap [ 0.000000] DMA32 zone: 0 pages reserved [ 0.000000] DMA32 zone: 261632 pages, LIFO batch:63 [ 0.000000] software IO TLB: mapped [mem 0xbb1fc000-0xbf1fc000] (64MB) 

( BBL, — ).


, , , RocketChip JTAG trap- — .


 Program received signal SIGTRAP, Trace/breakpoint trap. 0xffffffe0000024ca in ?? () (gdb) bt #0 0xffffffe0000024ca in ?? () Backtrace stopped: previous frame identical to this frame (corrupt stack?) (gdb) file work/linux/vmlinux A program is being debugged already. Are you sure you want to change the file? (y or n) y Reading symbols from work/linux/vmlinux...done. (gdb) bt #0 0xffffffe0000024ca in setup_smp () at /hdd/trosinenko/fpga/freedom-u-sdk/linux/arch/riscv/kernel/smpboot.c:75 #1 0x0000000000000000 in ?? () Backtrace stopped: frame did not save the PC 

freedom-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); // <    } 

, CPU not found, running software emulation . running. .


 /* The lucky hart to first increment this variable will boot the other cores */ atomic_t hart_lottery; unsigned long boot_cpu_hartid; 

linux/arch/riscv/kernel/setup.c — . , - , ...


.


. , , singlestep-.


( ):
asciicast

Source: https://habr.com/ru/post/pt458482/


All Articles