O que é um carregador de inicialização e como eu o desenvolverei?

53

Eu conheci muitos projetos nos quais um microcontrolador AVR usa com um gerenciador de inicialização (como o Arduino), mas não entendo muito bem o conceito.

Como posso criar um gerenciador de inicialização (para qualquer microcontrolador)?

Depois de escrever meu carregador de inicialização, como ele é programado para o microcontrolador (como qualquer programa .hex gravado na memória flash do AVR, ou algum outro método)?

mina_g
fonte
10
Você o marcou com a tag bootloader , você leu isso? Em caso afirmativo, você leu as perguntas do Arduino Bootloader , Arduino Bootloader Follow On , Arduino Bootloader , Boas ferramentas ou métodos para entender a estrutura do gerenciador de inicialização e as perguntas Detalhes do Arduino Bootloader ? Todos esses itens tratam de partes da sua pergunta inicial. Eu apara as coisas novas.
Kevin Vermeer
Um artigo sobre o método BootLoader Edifício: beningo.com/wp-content/uploads/images/Papers/...
Yahya Tawil
2
@ KevinVermeer Acho que a pergunta dele é mais direta.
precisa saber é o seguinte

Respostas:

103

Um gerenciador de inicialização é um programa que roda no microcontrolador a ser programado. Ele recebe novas informações do programa externamente através de alguns meios de comunicação e grava essas informações na memória do programa do processador.

Isso contrasta com a maneira normal de colocar o programa no microcontrolador, que é por meio de hardware especial incorporado ao micro para esse fim. Nos PICs, essa é uma interface do tipo SPI. Se bem me lembro, os AVRs usam Jtag, ou pelo menos alguns deles. De qualquer forma, isso requer algum hardware externo que move os pinos de programação corretamente para gravar as informações na memória do programa. O arquivo HEX que descreve o conteúdo da memória do programa é originado em um computador de uso geral; portanto, esse hardware se conecta ao computador de um lado e aos pinos de programação especiais do micro do outro. Minha empresa cria programadores de PIC entre outras coisas como uma linha lateral, por isso estou bastante familiarizado com esse processo nos PICs.

O ponto importante da programação externa via hardware especializado é que ele funciona independentemente do conteúdo existente da memória do programa. Os microcontroladores começam com a memória do programa apagada ou em um estado desconhecido; portanto, a programação externa é o único meio de obter o primeiro programa em um micro.

Se você tiver certeza do programa que deseja carregar no seu produto e seus volumes forem altos o suficiente, poderá ter o fabricante ou um programa de distribuidor para você. O chip é soldado à placa como qualquer outro chip e a unidade está pronta para funcionar. Isso pode ser apropriado para algo como um brinquedo, por exemplo. Uma vez concluído o firmware, está praticamente pronto e será produzido em grandes volumes.

Se seus volumes forem mais baixos, ou mais importante, você espera um desenvolvimento contínuo de firmware e correções de erros, não deseja comprar chips pré-programados. Nesse caso, os chips em branco são montados na placa e o firmware precisa ser carregado no chip como parte do processo de produção. Nesse caso, as linhas de programação de hardware precisam ser disponibilizadas de alguma forma. Isso pode ser feito por meio de um conector explícito ou pogo pin pads, se você desejar criar um dispositivo de teste de produção. Muitas vezes, esses produtos precisam ser testados e talvez calibrados de qualquer maneira, portanto o custo adicional de gravar o programa no processador é geralmente mínimo. Às vezes, quando pequenos processadores são usados, um firmware de teste de produção especial é carregado primeiro no processador. Isso é usado para facilitar o teste e a calibração da unidade, o firmware real é carregado depois que o hardware é considerado bom. Nesse caso, existem algumas considerações sobre o projeto do circuito para permitir o acesso às linhas de programação o suficiente para o processo de programação funcionar, mas também para não incomodar demais o circuito. Para mais detalhes sobre isso, consulte o meugravação de programação em circuito .

Até agora tudo bem, e nenhum gerenciador de inicialização é necessário. No entanto, considere um produto com firmware relativamente complexo que você deseja atualizar em campo ou até permita que o cliente final atualize. Você não pode esperar que o cliente final tenha um gadget de programador ou saiba como usá-lo adequadamente, mesmo que você o tenha fornecido. Na verdade, um dos meus clientes faz isso. Se você comprar a opção de personalização de campo especial, você terá um dos meus programadores com o produto.

No entanto, na maioria dos casos, você apenas deseja que o cliente execute um programa em um PC e tenha o firmware atualizado magicamente. É aqui que entra o carregador de inicialização, especialmente se o seu produto já tiver uma porta de comunicação que possa interagir facilmente com um PC, como USB, RS-232 ou ethernet. O cliente executa um programa de PC que já fala com o gerenciador de inicialização no micro. Isso envia o novo binário para o carregador de inicialização, que o grava na memória do programa e faz com que o novo código seja executado.

Parece simples, mas não é, pelo menos não, se você deseja que esse processo seja robusto. E se ocorrer um erro de comunicação e o novo firmware estiver corrompido quando chegar ao gerenciador de inicialização? E se a energia for interrompida durante o processo de inicialização? E se o gerenciador de inicialização tiver um bug e um dado em si mesmo?

Um cenário simplista é que o carregador de inicialização sempre é executado a partir da redefinição. Ele tenta se comunicar com o host. Se o host responder, ele informa ao carregador de inicialização que não tem nada de novo ou envia um novo código. À medida que o novo código chega, o código antigo é substituído. Você sempre inclui uma soma de verificação com o código enviado, para que o carregador de inicialização possa saber se o novo aplicativo está intacto. Caso contrário, ele permanece no carregador de inicialização solicitando um upload constantemente até que algo com uma soma de verificação válida seja carregado na memória. Isso pode ser aceitável para um dispositivo que está sempre conectado e possivelmente onde uma tarefa em segundo plano é executada no host que responde às solicitações do carregador de inicialização. Esse esquema não é bom para unidades amplamente autônomas e que ocasionalmente se conectam a um computador host.

Normalmente, o carregador de inicialização simples, como descrito acima, não é aceitável, pois não há segurança contra falhas. Se uma nova imagem do aplicativo não for recebida intacta, você deseja que o dispositivo continue executando a imagem antiga e não fique morto até que um upload bem-sucedido seja realizado. Por esse motivo, geralmente existem dois módulos especiais no firmware, um carregador e um carregador de inicialização. O remetente faz parte do aplicativo principal. Como parte das comunicações regulares com o host, uma nova imagem do aplicativo pode ser carregada. Isso requer memória separada da imagem principal do aplicativo, como uma EEPROM externa ou usar um processador maior, para que metade do espaço da memória do programa possa ser alocado para armazenar a nova imagem do aplicativo. O remetente apenas grava a nova imagem do aplicativo recebida em algum lugar, mas não a executa. Quando o processador é redefinido, o que pode ocorrer sob comando do host após um upload, o carregador de inicialização é executado. Agora, este é um programa totalmente independente que não precisa de capacidade de comunicação externa. Ele compara as versões atuais e carregadas do aplicativo, verifica suas somas de verificação e copia a nova imagem na área de aplicativos se as versões diferirem e a nova soma de verificação de imagem verificar. Se a nova imagem estiver corrompida, ela simplesmente executará o aplicativo antigo como antes.

Eu fiz muitos gerenciadores de inicialização e não há dois iguais. Não existe um gerenciador de inicialização de uso geral, apesar do que algumas empresas de microcontroladores querem que você acredite. Cada dispositivo tem seus próprios requisitos e circunstâncias especiais ao lidar com o host. Aqui estão apenas algumas das configurações do carregador de inicialização e, às vezes, do upload que eu usei:

  1. Carregador de inicialização básico. Este dispositivo tinha uma linha serial e seria conectado a um host e ligado conforme necessário. O carregador de inicialização foi executado a partir da redefinição e enviou algumas respostas à solicitação de upload para o host. Se o programa de upload estivesse em execução, ele responderia e enviaria uma nova imagem do aplicativo. Se não respondesse dentro de 500 ms, o gerenciador de inicialização desistiria e executaria o aplicativo existente. Para atualizar o firmware, é necessário executar o aplicativo atualizador no host primeiro, depois conectar e ligar o dispositivo.

  2. Carregador de memória do programa. Aqui usamos o PIC de tamanho seguinte que tinha o dobro de memória de programa. A memória do programa foi dividida aproximadamente em 49% do aplicativo principal, 49% da nova imagem do aplicativo e 2% do carregador de inicialização. O carregador de inicialização seria executado a partir da redefinição e copiaria a nova imagem do aplicativo na imagem atual do aplicativo, nas condições corretas.

  3. Imagem EEPROM externa. Como o nº 2, exceto que uma EEPROM externa foi usada para armazenar a nova imagem do aplicativo. Nesse caso, o processador com mais memória também teria sido fisicamente maior e em uma subfamília diferente que não possuía a mistura de periféricos de que precisávamos.

  4. Carregador de inicialização TCP. Este foi o mais complexo de todos. Foi utilizado um grande PIC 18F. Os últimos 1/4 de memória continham o gerenciador de inicialização, que possuía sua própria cópia completa de uma pilha de rede TCP. O carregador de inicialização foi executado a partir da redefinição e tentou se conectar a um servidor de upload especial em uma porta conhecida em um endereço IP configurado anteriormente. Isso era para instalações grandes, onde havia sempre uma máquina de servidor dedicada para todo o sistema. Cada dispositivo pequeno faria check-in com o servidor de upload após a redefinição e receberia uma nova cópia do aplicativo, conforme apropriado. O carregador de inicialização substituirá o aplicativo existente pela nova cópia, mas somente o executará se a soma de verificação estiver marcada. Caso contrário, ele retornaria ao servidor de upload e tentaria novamente.

    Como o próprio gerenciador de inicialização era um pedaço de código complicado que continha uma pilha de rede TCP completa, ele também precisava ser atualizado em campo. Da maneira que fizemos, o servidor de upload alimentou um aplicativo especial cujo único objetivo era sobrescrever o gerenciador de inicialização assim que ele foi executado e, em seguida, redefiniu a máquina para que o novo gerenciador de inicialização fosse executado, o que levaria o servidor de upload a enviar o última imagem principal do aplicativo. Tecnicamente, uma falha de energia durante os poucos milissegundos necessários para o aplicativo especial copiar uma nova imagem no gerenciador de inicialização seria uma falha irrecuperável. Na prática, isso nunca aconteceu. Estávamos bem com a chance muito improvável disso, já que esses dispositivos faziam parte de grandes instalações onde já havia pessoas que faziam manutenção no sistema, o que ocasionalmente significava substituir os dispositivos incorporados por outros motivos.

Espero que você possa ver que existem várias outras possibilidades, cada uma com suas próprias vantagens e desvantagens de risco, velocidade, custo, facilidade de uso, tempo de inatividade etc.

Olin Lathrop
fonte
11
Todos os AVRs, exceto a família Xmega (que possui uma nova interface de 2 fios), usam uma interface SPI enquanto mantidos em redefinição. Os maiores também têm JTAG, alguns têm programação paralela e os menores podem exigir alta voltagem se a redefinição tiver sido reconfigurada como E / S. Alguns MCUs, como as famílias Parallax Propeller e Motorola / Freescale 68HC08, não precisam de hardware de programação mínimo, mas de gerenciadores de inicialização em ROM.
Yann Vernier
Ele compara as versões atuais e carregadas do aplicativo, verifica suas somas de verificação e copia a nova imagem na área de aplicativos se as versões diferirem e a nova soma de verificação de imagem verificar. Sim, mas e se a energia for interrompida no meio desta ação, haveria uma imagem corrompida na "área de aplicativos". Suponho que seja melhor ter um aplicativo bootloader-failafe-app que grave o novo aplicativo no flash da mcu e, se a soma de verificação for válida, ele também escreverá em algum lugar "ok para inicializar o novo aplicativo". Então, no início, ele verifica se é ok para inicializar o novo aplicativo, se não é ele continua executando-se (à prova de falhas app)
Ervadac
@Erv: se a energia falhar no meio da cópia da nova versão para a atual, a soma de verificação da versão atual falhará quando a energia voltar e o carregador de inicialização for executado novamente. Normalmente, coloco a palavra soma de verificação no final da imagem, para que qualquer gravação parcial tenha uma chance muito boa de falha na soma de verificação.
Olin Lathrop
Oi. Posso recomendar o carregador de inicialização tipo 5 - em vez da pilha TCP, você pode implementar o UDP. Em seguida, use o TFTP para baixar a atualização ou o protocolo nativo.
I486
22

Qual é o conceito do gerenciador de inicialização?

Imagine este cenário: Você tem uma quantidade razoável de armazenamento em seu microcontrolador - o suficiente para armazenar mais de 2 a 3 programas ou aplicativos que são independentes um do outro. Suponha que, ao inicializar seu dispositivo, você possa escolher qual deles executar. Então, o que você precisaria para apoiar isso? Você precisaria de um programa inicial que permita escolher entre os outros no momento da inicialização.

Como funciona?

Um gerenciador de inicialização é esse programa - é a primeira coisa a ser executada e pode carregar outros aplicativos em locais específicos da memória (persistentes como FLASH ou voláteis como RAM) e, em seguida, salta para o programa desejado, onde assumirá a execução a partir daí .

Como criar um gerenciador de inicialização avr (ou para qualquer microcontrolador)?

Nunca criei um gerenciador de inicialização, mas é assim que penso em fazê-lo: comece a escrever um programa de firmware como faria normalmente - mas verifique se ele é colocado em uma área que seja sempre a primeira coisa a ser executada quando o dispositivo inicializa. Em primeiro lugar, algumas das funcionalidades que eu gostaria deste pequeno programa: capacidade de fazer upload de um novo programa para um local disponível na memória, apagar um programa enviado anteriormente, escolher qual programa executar (se houver mais de um) e possui algum tipo de estrutura de dados de armazenamento (tabela de salto expansível?) para poder lembrar onde estão os outros programas e pular para eles. A interação pode ser feita através do UART, onde pode apresentar um menu de terminal muito simples e a capacidade de fazer upload de firmware nesse mesmo canal.

Como é programado para o microcontrolador (como qualquer programa .hex gravado no flash rom do avr ou algum outro método)?

Se for um chip completamente em branco, sem um gerenciador de inicialização existente que possa se atualizar, será necessário gravar no FLASH da mesma forma que você descreveu, usando qualquer técnica necessária para fazer isso (ICSP no caso do AVR).

Isso não é de forma alguma abrangente sobre o que são "gerenciadores de inicialização". Dependendo do que você deseja de um ou do sistema para o qual eles foram projetados, você pode criar um para fazer upload de itens para um local especificado na RAM, em vez de FLASH, e iniciar a execução em qualquer local de memória arbitrário. Ou talvez você queira um que seja capaz de escolher qual sistema operacional carregar quando o seu PC inicializar (veja o grub, por exemplo). Carregadores de inicialização para microcontroladores de 8 bits tendem a ser muito simples.

Uma observação sobre o Arduino: Este gerenciador de inicialização gerencia apenas um programa AFAIK, mas também assume a porta serial para gerenciar o upload de firmware e outras coisas .

Jon L
fonte
Para sua informação, foi escrita uma resposta para abordar os pontos originais que foram editados.
Jon G
3

O conceito de um carregador de "inicialização" é semelhante ao conceito de "escorvar" uma bomba. Em outras palavras, você precisa de "algo" que carregue um programa em um determinado endereço e, em seguida, comece a executar o programa nesse endereço. Isso é o carregador de inicialização. No caso mais simples, o carregador de inicialização "aparece" no endereço inicial designado da CPU (zero, provavelmente), carrega o programa no segmento de memória necessário, transfere o controle para ele e "desaparece". A aparência e o desaparecimento são controlados por hardware "externo". Uma implementação possível seria usar uma ROM ativada por meio de uma redefinição de "hardware" e desativada com uma redefinição de "software". O carregador na ROM pode ser tão simples ou tão complexo quanto necessário, e precisa ser escrito na forma binária que a CPU em particular entende. Se o espaço de endereço usado pela ROM não for necessário, a desativação da ROM não será necessária. Obviamente, EEPROM, ePROM, flash PROM etc. podem ser usados ​​no lugar da ROM.

Guill
fonte