Por que precisamos de um programa separado na mesma memória de programa flash de um microcontrolador, especificamente STM32F103, chamado de carregador de inicialização?
O que há de especial nisso para mantê-lo separado do programa aplicativo principal?
De um modo geral, um carregador de inicialização de um sistema baseado em microprocessador (por exemplo, PowerPC MPC8270) faz o mesmo trabalho que um microcontrolador (por exemplo, ARM STM32F103) ou eles executam tarefas fundamentalmente diferentes entre si e, ainda assim, são chamados de 'carregador de inicialização' ?
microcontroller
stm32
programming
flash
bootloader
alt-rose
fonte
fonte
Respostas:
Um carregador de inicialização em um microcontrolador é responsável por atualizar o firmware principal em um canal de comunicação que não seja o cabeçalho de programação. Isso é útil para atualizar o firmware em campo através de cartões BLE, UART, I2C, SD, USB, etc. Seria extremamente inconveniente exigir que os clientes adquirissem programadores apenas para atualizar o firmware em seus dispositivos.
A razão pela qual o gerenciador de inicialização é mantido separado é pela confiabilidade. O carregador de inicialização e o código do aplicativo são colocados em seções separadas do flash, para que o código do aplicativo possa ser apagado e reescrito pelo carregador de inicialização sem alterar nada relacionado ao código do carregador de inicialização.
Se o carregador de inicialização e o aplicativo fossem mantidos juntos, o código do carregador de inicialização precisaria ser copiado para a RAM antes de poder ser executado, pois qualquer atualização de firmware apagaria o código do carregador de inicialização em flash. Se a energia fosse cortada com o código do carregador de inicialização na RAM e o flash apagado, o dispositivo seria bloqueado.
fonte
main()
função de inicialização . Ao ligar, o código de inicialização do carregador de inicialização é executado e chama o carregador de inicializaçãomain()
. O programa do carregador de inicialização verifica se há um aplicativo válido e, em seguida, pula para o código de inicialização do programa que chama o programamain()
. O código de inicialização de cada programa inicializa o ambiente de tempo de execução C para o respectivo programa (ou seja, inicializa variáveis, pilha etc.) e, normalmente, nenhum dos programasmain()
retorna ao código de inicialização.main
.Para que o processo de carregamento possa se recuperar de erros. Suponha que haja um erro de comunicação ou desconexão de energia durante uma atualização. Se o carregador de inicialização fizesse parte do aplicativo que você estava atualizando, o usuário não poderá tentar novamente sem usar hardware especial para atualizar o carregador de inicialização.
Alguns microcontroladores não podem executar código da RAM. Se o carregador de inicialização estivesse misturado com o restante do software, você não conseguiria atualizá-lo, porque não poderá apagar páginas de flash das quais está executando atualmente. A solução é primeiro gravar o novo código na segunda metade do flash e depois pular para ele. O novo código então se copia para a primeira metade do flash. É claro que a desvantagem é que o flash de gravação geralmente é lento e agora que você precisa fazer isso duas vezes, o processo de carregamento pode levar o dobro do tempo. Além disso, essa solução alternativa limita o tamanho do aplicativo a não ser maior que a metade do flash total.
Carregadores de inicialização bem escritos tentam verificar se existe um código válido no dispositivo antes de tentar executá-lo. Se o carregador de inicialização e outro código foram combinados, como você pode ter certeza de que sua rotina de validação funcionaria se todo o código não fosse carregado?
Autenticação. Os carregadores de inicialização seguros tentam verificar se o aplicativo carregado corresponde a uma assinatura digital antes de executar. Mas se o gerenciador de inicialização e outro código foram combinados, não será possível controlar o que é executado no dispositivo, porque, uma vez que o usuário carrega um novo código, não será possível controlar o que acontece na inicialização.
fonte
Eles geralmente estão lá para permitir que você atualize seu programa aplicativo principal.
Você precisa de um código que saiba como apagar e reprogramar parte do flash interno, que não pode ser o programa principal, pois quando ele é apagado, não seria possível reprogramar.
fonte
O carregador de inicialização permite que o MCU se comunique com outra coisa para aceitar um novo programa, armazená-lo e executá-lo após uma redefinição. Se você não possui um gerenciador de inicialização, é necessário um programador para acessar a memória e colocar o programa no lugar.
fonte
Além das outras respostas corretas sobre como permitir a reprogramação do firmware principal a partir do carregador de inicialização, outro benefício de separá-lo é que você pode separar logicamente as tarefas "executar uma vez na inicialização" do código necessário durante o tempo de execução. Então, depois que o carregador de inicialização concluir suas tarefas de configuração inicial, o firmware principal poderá despejar o carregador de inicialização com todo o código que não é mais necessário da memória, economizando espaço significativo na RAM. É possível fazer isso de outras maneiras, mas a divisão do carregador de inicialização / firmware facilita muito em muitas arquiteturas.
fonte
A resposta curta é porque o software é incrível.
Você pode ter tudo o que o gerenciador de inicialização faz com "hardware puro". Mas é muito, muito, muito mais fácil ter as tarefas que o gerenciador de inicialização executa como software e depois interpretadas pelo hardware.
Essas tarefas podem envolver a configuração do hardware para a execução do software "real" (por exemplo, em um Raspberry Pi (via @ErikF)), com um protocolo para substituir o programa "real" antes da execução (verifique um pino, se esse pino é definido e reflash o programa real) ou até mesmo configurando o ambiente de software para o programa "real".
Em um software de menor escala, quando você executa um executável, o carregador de aplicativos move coisas como carregar partes dos dados na memória, às vezes corrige endereços, configura argumentos para coisas principais ou outras coisas globais, gira as bibliotecas fornecidas pelo sistema operacional e depois pula para o início do
_main
código. Algumas dessas coisas podem ser feitas por um gerenciador de inicialização.Em um microcontrolador, algumas das tarefas que um gerenciador de inicialização realiza podem ser divididas no programa. O compilador para sua plataforma pode injetar automaticamente o código de "configuração" em todos os executáveis.
Porém, tê-lo no carregador de inicialização significa que o mesmo compilador pode funcionar em hardware diferente, pois o carregador de inicialização pode "ocultar" a diferença entre as plataformas.
Além disso, o fato de um flash do programa principal não arriscar o gerenciador de inicialização (e a capacidade de atualizar o programa principal), e ter um gerenciador de inicialização não trivial é algo muito bom.
fonte
Uma resposta que não foi abordada é a necessidade de separação de preocupações devido às limitações da linguagem C.
Geralmente, os gerenciadores de inicialização são escritos em uma mistura de Assembly e C, com o estágio de inicialização muito inicial no Assembly.
Isso é feito para configurar certas coisas como:
Essa é uma aproximação muito aproximada das etapas executadas e estou descrevendo o processo de inicialização do ARM; é diferente novamente para x86 e outras arquiteturas.
No entanto, o principal motivo permanece o mesmo: a alocação da pilha C deve ser feita a partir da montagem.
fonte
Uma parte da pergunta que ainda não foi respondida é a diferença entre os gerenciadores de inicialização nos microcontroladores e nos sistemas de microprocessadores.
Microcontrolador
A maioria dos microcontroladores possui memória ROM incorporada que contém o código do programa. A alteração desse código geralmente requer um dispositivo programador que se conecte à interface de programação do microcontrolador (por exemplo, ISP no ATMega). Mas essas interfaces de programação geralmente não são muito convenientes de usar, em comparação com outras interfaces, pois podem não estar prontamente disponíveis no contexto especificado. Por exemplo, enquanto quase todos os computadores possuem portas USB, a interface SPI necessária para o ISP é muito mais rara, e outras interfaces, como a interface PID usada no ATXMega, são suportadas apenas por hardware de programação dedicado.
Portanto, por exemplo, se você deseja atualizar o software de um computador comum sem nenhum hardware externo, pode usar um carregador de inicialização que lê de um tipo diferente de interface (por exemplo, RS232, USB ou RS232 através de USB como no Arduino) para programar o dispositivo sobre interfaces comuns.
Dito isto, se você não precisar dessa funcionalidade, o gerenciador de inicialização é completamente opcional. O microcontrolador ainda pode executar seu código completamente sem o gerenciador de inicialização.
Microprocessador
Em um microprocessador, as coisas são um pouco diferentes. Enquanto a maioria dos microprocessadores possui uma ROM grande o suficiente para um carregador de inicialização, essas ROMs não são grandes o suficiente para suportar um sistema operacional completo. Portanto, o objetivo do carregador de inicialização é inicializar o hardware, procurar um sistema operacional inicializável, carregá-lo e executá-lo. Portanto, o carregador de inicialização é essencial para cada inicialização.
Nos sistemas x86 / x64, esse carregador de inicialização é o BIOS ou o UEFI (basicamente uma versão mais recente de um BIOS).
Às vezes você pode até ter vários gerenciadores de inicialização executando em uma cadeia. Por exemplo, se você possui um sistema de inicialização dupla com Windows e Linux, pode acabar com o seguinte:
Portanto, neste caso, havia três softwares que podem ser considerados um gerenciador de inicialização. O GRUB e o Windows Bootloader estão disponíveis para oferecer ao usuário uma opção de seleção de inicialização mais conveniente do que a BIOS / UEFI daria a eles. Ele também permite o lançamento de vários sistemas operacionais a partir do mesmo disco rígido ou até da mesma partição.
TLDR
Portanto, enquanto nos dois sistemas o gerenciador de inicialização faz coisas semelhantes (ajudando o usuário a escolher qual código inicializar), ambos diferem bastante na maneira como realizam isso e no que fazem exatamente.
fonte