Portanto, no
último artigo, desenvolvemos o sistema de processador mais simples, com o qual planejamos testar o chip de RAM conectado ao FPGA do complexo Redd. Hoje, criaremos um programa C ++ para esse ambiente de hardware e também descobriremos como injetar esse programa e, o mais importante, depurá-lo.

O programa é desenvolvido no ambiente Eclipse. Para iniciá-lo, selecione o item de menu
Ferramentas-> Ferramentas de compilação do software Nios II para Eclipse .

Precisamos criar um projeto e um BSP para ele. Para fazer isso, clique com o botão direito do mouse na área Explorador de Projetos e selecione o item de menu
Novo-> Aplicativo Nios II e BSP no modelo .

O modelo básico com base no qual o projeto será criado foi feito durante a geração do sistema do processador. Portanto, encontramos o arquivo que o contém.

Também daremos um nome ao projeto (eu tenho o
SDRAMtest ) e selecionaremos o tipo de projeto. Eu escolhi
Hello World Small . Gostaria de escolher
Teste de memória , estamos fazendo um teste de memória, mas agora estamos considerando uma maneira geral de criar aplicativos. Portanto, escolhemos a opção geral.

Criamos dois projetos. O primeiro é o nosso projeto, o segundo é o BSP (Board Support Package, grosso modo, bibliotecas para trabalhar com equipamentos).

A primeira coisa que costumo fazer é editar as configurações do BSP. Para fazer isso, seleciono o segundo dos projetos criados, pressione o botão direito do mouse e selecione o item de menu
Nios II -> Editor BSP .

No editor, tudo é dividido em grupos:

Mas, para não correr ao longo deles, selecionarei a raiz da árvore, o elemento
Configurações , e considerarei tudo linearmente. Vou listar o que você deve prestar atenção. Tela de configurações iniciais:

O suporte para saída e o suporte para remoção ao sair está desabilitado, o que libera partes desnecessárias de tamanho enorme do código, mas elas são desabilitadas inicialmente. Essas gralhas foram desligadas, já que eu escolhi a opção mínima
Olá, Mundo . Ao escolher outros tipos de código, é melhor remover esses daws também. Não consigo resistir e ativar o suporte ao C ++. Também é necessário remover a verificação
SysID , caso contrário nada funcionará. O fato é que, ao criar o sistema de hardware, não adicionei o bloco correspondente. As demais configurações são auto-explicativas e não foram alteradas. Na verdade, também não mudei outras configurações. Um pouco mais tarde, voltaremos aqui, mas por enquanto pressionamos o botão Gerar. Estamos criando uma nova versão do BSP com base nas configurações feitas. No final, clique em Sair.
Agora a diversão começa. Como montar um projeto. Pressione o
Ctrl + B usual - obtemos um erro. Erro de decodificação no:

Nada razoável no console:

Selecione o
projeto de construção para o projeto
SDRAMtest , obteremos uma explicação razoável:

De fato, modelos não funcionais são a identidade corporativa da Quartus. Essa é outra razão pela qual não escolhi o
Teste de memória . Há mais e mais corrida. Aqui está claro qual é o problema.
Esta função do arquivo
alt_putstr.c falha :
/* * Uses the ALT_DRIVER_WRITE() macro to call directly to driver if available. * Otherwise, uses newlib provided fputs() routine. */ int alt_putstr(const char* str) { #ifdef ALT_SEMIHOSTING return write(STDOUT_FILENO,str,strlen(str)); #else #ifdef ALT_USE_DIRECT_DRIVERS ALT_DRIVER_WRITE_EXTERNS(ALT_STDOUT_DEV); return ALT_DRIVER_WRITE(ALT_STDOUT_DEV, str, strlen(str), 0); #else return fputs(str, stdout); #endif #endif }
Vamos corrigi-lo de alguma forma mais tarde (para isso, precisamos complicar o sistema de hardware). Hoje nós simplesmente não precisamos disso. Substitua seu corpo pelo
retorno 0 . O projeto começa a se montar.
Ótimo. Já temos um
arquivo elf pronto (mesmo que ele não execute nenhuma função útil) e temos equipamentos nos quais você pode
fazer upload do arquivo hexadecimal (lembre-se de como informamos ao compilador Quartus que os dados de RAM devem ser carregados a partir dele, configurando o Onchip RAM?). Como convertemos
elf em
hex ? Muito simples Iniciamos o projeto de trabalho, pressione o botão direito do mouse, selecione o item de menu
Criar metas-> Construir :

Na janela exibida, tente primeiro selecionar a primeira opção (
mem_init_install ):

Nada vai acontecer. Mas, com a mensagem de erro que nos foi dada, aprendemos como finalizar o projeto para o Quartus:
../SDRAMtest_bsp//mem_init.mk:230: *** Deprecated Makefile Target: 'mem_init_install'. Use target 'mem_init_generate' and then add mem_init/meminit.qip to your Quartus II Project. Stop.
Selecionaremos a compilação com o objetivo de
mem_init_generate , após o qual (logo após !!!) adicionaremos o
arquivo qip especificado ao projeto de hardware (já aprendemos como adicionar arquivos quando adicionamos um sistema de processador ao projeto).

Bem, o próprio
arquivo hexadecimal também pode ser sentido com as mãos. Aqui está:

Ótimo. Temos tudo para começar a encher o programa com funcionalidade real. Vamos para o arquivo
hello_world_small.c . Honestamente, estou um pouco ofendido por trabalhar com C. puro Portanto, vou mudar o nome de cpp. E para que nada dê errado, adicionarei um feitiço ao texto existente:

Mesmo texto: extern "C" { #include "sys/alt_stdio.h" } int main() { alt_putstr("Hello from Nios II!\n"); /* Event loop never exits. */ while (1); return 0; }
É importante executar a operação
Limpar Projeto após alterar o tipo de arquivo; caso contrário, o compilador exibirá mensagens de erro informando que o arquivo
* .c não foi encontrado com base em algumas informações em cache.
Vamos simplificar o teste de memória. Não tenho a tarefa de ensinar o leitor a testar o chip de RAM corretamente. Apenas superficialmente garantimos que o endereço e os barramentos de dados não sejam pegajosos e não tenham interrupções. Ou seja, escrevemos em cada célula todos os zeros e o endereço da célula. Nossa tarefa é criar código de trabalho e tudo o mais (outras constantes de preenchimento e atraso para verificar se os dados estão sendo regenerados) são detalhes de implementação que complicam o texto, mas não alteram sua essência.
Vamos começar com uma pergunta simples e natural: "Onde está localizada a SDRAM no espaço de endereço?" Lembro que chamamos a função de atribuição automática de endereços, mas nem sequer examinamos quais endereços foram realmente atribuídos. De fato, mesmo agora não vamos olhar para lá. Todas as informações necessárias estão no arquivo:
... \ SDRAMtest_bsp \ system.h .
Como resultado, obtemos o seguinte código: extern "C" { #include "sys/alt_stdio.h" #include <stdint.h> #include "../SDRAMtest_bsp/system.h" #include <altera_avalon_pio_regs.h> } int main() { bool bRes = true; volatile static uint32_t* const pSDRAM = (uint32_t*)NEW_SDRAM_CONTROLLER_0_BASE; static const int sizeInDwords = NEW_SDRAM_CONTROLLER_0_SPAN / sizeof (uint32_t); // for (int i=0;i<sizeInDwords;i++) { pSDRAM [i] = 0; } // for (int i=0;i<sizeInDwords;i++) { if (pSDRAM [i] != 0) { bRes = false; } } // for (int i=0;i<sizeInDwords;i++) { pSDRAM [i] = i; } // for (int i=0;i<sizeInDwords;i++) { if (pSDRAM [i] != i) { bRes = false; } } if (bRes) { IOWR_ALTERA_AVALON_PIO_DATA (PIO_0_BASE,0x01); } else { IOWR_ALTERA_AVALON_PIO_DATA (PIO_0_BASE,0x02); } /* Event loop never exits. */ while (1); return 0; }
Coletamos o
arquivo hexadecimal (lembro que através desta caixa de diálogo):

Depois disso, compilamos o equipamento no Quartus e entramos no programador para carregar o "firmware" resultante com o código de programa inicial resultante no chip.

Coloque o “firmware” e receba uma mensagem informando que o sistema permanecerá ativo enquanto estiver conectado ao JTAG (recursos da licença do kernel SDRAM no ambiente de desenvolvimento livre). Na verdade, para o caso de Redd, isso não é crítico. Este JTAG está lá o tempo todo.

Os resultados do teste de RAM podem ser vistos no conector, nos contatos C21 (bem-sucedido) ou B21 (erro). Nós nos conectamos a eles com um osciloscópio.

Ambas as saídas estão em zero. Algo está errado aqui. Resta entender o que exatamente. De fato, um programa inoperante é ótimo, porque agora vamos começar a aprender a depuração JTAG. Nosso objetivo é o projeto, selecione
Debug As-> Nios II Hardware .

A primeira vez que o sistema não encontra o hardware. É assim (observe a cruz vermelha no cabeçalho da guia destacada):

Alterne para a guia
Conexão de destino e marque a caixa de seleção
Ignorar identificação incorreta do sistema . Durante experimentos subsequentes, às vezes também tive que definir o
registro de data e hora de ignorar sistema incompatível . Apenas no caso, destacarei na imagem não com um vermelho, mas com uma moldura amarela para enfatizar que não é necessário instalá-lo agora, mas se o botão
Debug não estiver ativado, talvez seja hora de instalá-lo.

Clique em
Aplicar (
Aplicar ) e clique em
Atualizar conexões (este botão está oculto, é necessário rolar o botão):

Um depurador aparece na lista, você pode clicar em
Depurar ...

Paramos na função
main () . Se desejar, você pode colocar um ponto de interrupção no final do algoritmo e verificar se o programa o alcança:

Execute o programa:

O ponto de interrupção não funcionou. Vamos parar o programa e ver onde ele é executado.

Tudo está ruim. O programa claramente travou. Organizando sucessivamente pontos de interrupção, depois parando (com o botão vermelho "Parar") e reiniciando o programa (usando o botão com o bug), encontramos a área do problema.
Tudo morre quando essa linha é executada para o primeiro elemento:

Mesmo texto: for (int i=0;i<sizeInDwords;i++) { if (pSDRAM [i] != 0) { bRes = false; } }
Do ponto de vista do C ++, tudo está limpo aqui. Mas se você abrir o código desmontado, poderá ver que existem muitos comandos uniformes lá. Parece que o código se apagou. E ele poderia fazer isso se o vinculador o colocasse na SDRAM (cujo conteúdo esse código substitui).

Paramos a depuração, fechamos a perspectiva de depuração.

Vamos ao editor do BSP, no qual estávamos no início deste artigo, mas à guia
Script do
vinculador . Se eu corrigisse essa guia no início, não seria capaz de mostrar a técnica de entrada na depuração JTAG (e enfatizar todo o seu poder em comparação com a saída de depuração simples, porque o fato do código atolado durante a depuração JTAG chamou minha atenção). Assim é. Um bom vinculador coloca tudo na memória, cujo tamanho é maior.

Redirecionamos tudo para
onchip_memory ... Agora temos a SDRAM - é apenas um pedaço de memória cujo desempenho ainda não temos garantia. Você não pode atribuí-lo a nenhuma ação do compilador automatizado.

Nós reconstruímos o
bsp , reconstruímos o projeto. Preciso recriar a imagem da memória e sobrecarregar o FPGA? Então, para um trabalho semi-autônomo, será necessário, mas enquanto a depuração estiver em andamento - não. Uma nova versão do programa será carregada imediatamente na RAM quando uma nova sessão de depuração começar. Mas para que, na próxima inicialização do FPGA, não seja necessário iniciar o depurador, criar um novo
arquivo HEX no final da depuração e montar o “firmware” do FPGA com ele.
Para o novo código, um ponto de interrupção foi atingido, o resultado do teste é
verdadeiro :

O osciloscópio se divertiu: o raio amarelo subiu na unidade.

Teste aprovado. Vamos ser um pouco complicados e, simultaneamente, verificar o desempenho do sistema. Vamos fazer a final assim:
// GPIO if (bRes) { while (true) { IOWR_ALTERA_AVALON_PIO_DATA (PIO_0_BASE,0x01); IOWR_ALTERA_AVALON_PIO_DATA (PIO_0_BASE,0x00); } } else { while (true) { IOWR_ALTERA_AVALON_PIO_DATA (PIO_0_BASE,0x02); IOWR_ALTERA_AVALON_PIO_DATA (PIO_0_BASE,0x00); } }
Como resultado, temos um meandro no osciloscópio (tudo foi conectado através de um loop longo, de modo que um captador capacitivo apareceu na segunda linha, não preste atenção nele):

O resultado é uma frequência de aproximadamente 8,3 MHz a uma frequência de clock de 50 MHz, sem qualquer ajuste fino no núcleo do processador e otimização do código do programa. Ou seja, o acesso às portas passa a uma frequência ligeiramente superior a 16 MHz, o que corresponde a um terço da frequência do sistema. Não que fosse completamente diferente, mas melhor que 4 MHz a uma freqüência de 925 MHz para o Cyclone V SoC ... Não, o próprio processador NIOS II é várias vezes mais lento que o Cyclone ARM do quinto núcleo, mas o processador, como eu disse , temos x64 no sistema, aqui precisamos mais do kernel, que fornece a lógica do ferro. E essa lógica é fornecida precisamente através do trabalho com portas. Se o trabalho com portas for lento, todo o resto ficará ocioso regularmente, aguardando o barramento terminar de funcionar. As características reveladas são o limite de acesso do processador às portas, mas não o hardware como um todo. No entanto, como implementar o trabalho como um todo, consideraremos no próximo artigo.
Conclusão
O artigo mostra como criar e configurar um projeto em C ++ para o ambiente de processador mais simples desenvolvido para o complexo Redd. Os métodos de acesso ao equipamento, bem como a técnica de depuração JTAG, são mostrados. Faça o download do kit de hardware / software obtido ao escrever o artigo
aqui .