
Artigos anteriores da série
Como funciona um programa de computador comum? Existe um certo ambiente externo (monitor e teclado com "mouse" são os representantes mais típicos desse próprio ambiente). O programa interage com eles. Ao depurar, você pode fazer influências reais do ambiente externo ou pode emulá-las. Nossos testadores costumam escrever todos os tipos de scripts que apenas emulam influências externas. Depois disso, são iniciados os analisadores de log, que verificam se as respostas estão corretas na quarta-feira.
E se tudo estiver cheio de bugs neste programa de computador? Você pode definir pontos de interrupção e examinar um instantâneo do sistema no momento em que eles atingem. Uma fatia do sistema são os valores das variáveis. Talvez os estados de vários mutexes e outros objetos de sincronização. Em geral, um instantâneo dos parâmetros internos do sistema depurado.
Ao depurar para FPGAs, você pode fazer a mesma coisa. É verdade que, se o ambiente for real, parar e estudar uma seção do sistema é problemático, embora possível. Como parte da história do Redd, continuo promovendo a ideia de que tudo deve ser simples e rápido. Não projetamos sistemas complexos. Estamos fazendo alguns tipos de módulos, como o que foi feito no último artigo . É sofisticado, mas muito, muito descomplicado. Em geral, faremos sua modelagem comportamental.
E aqui surge a questão sobre o ambiente externo. Como simular? Modelos vêm em nosso auxílio. Verilog (assim como VHDL e outros similares) é perfeitamente possível para descrever o comportamento de qualquer coisa. Estamos fazendo um sistema que funciona com o microcircuito ULPI ... Então, para testar seu funcionamento, deve haver algo na outra ponta que se comporte exatamente como o ULPI. Ou seja, o modelo ULPI. Mas isto não é o suficiente. Nosso bloco reage aos comandos do barramento ALAVON_MM. É esse ônibus que dá vida ao quarteirão. Portanto, também precisamos adicionar o modelo de barramento AVALON_MM, e este modelo deve estar ativo. É ela quem apresentará influências de teste.

Em última análise, temos que fazer exatamente esse sistema. E então seremos capazes de gravar diagramas de tempo de sinais em todos os seus barramentos e até mesmo dentro de qualquer um de seus módulos. Se ocorrer um erro, podemos definir pontos de interrupção e examinar instantâneos do sistema para encontrar o inimigo. Embora, pessoalmente, eu geralmente não defina esses pontos de interrupção, na maioria das vezes a análise de gráficos de tempo é suficiente. O fato é que os sinais podem ser vistos não apenas os sinais da interface, mas também os internos. Puxando uma dúzia ou dois sinais internos no gráfico, você geralmente pode adivinhar o que está implementado de forma errada na lógica.
O objetivo do artigo de hoje não é falar sobre o que é modelagem em geral (esta é uma longa história), mas mostrar como realizar essa modelagem mais rapidamente. E vamos considerar isso não em uma missão de combate, mas em um exemplo simples. Faremos um sistema de teste muito simples para que no próximo artigo já entendamos de onde crescem as pernas de uma versão mais complexa dele, pois ao ler é mais conveniente não sentar e se perguntar: “Por que ele faz isso?”, Mas conhecer todos os princípios básicos, dos quais já decorrem complicações ... A propósito, recentemente descobri que um dos meus conhecidos, embora possua habilidade para modelagem, não sabia que o ambiente Quartus possui mecanismos embutidos que permitem que você faça isso com facilidade e naturalidade. Ele despendeu muito mais esforço do que o necessário. Então, talvez alguém também aprenda algo novo para si mesmo sobre as possibilidades inerentes ao Quartus. Então,vamos começar.
Verilog
As pessoas se enquadram em duas categorias. Quem gosta de criar tudo do zero com as mãos e quem gosta de fazer com o mouse. Criar tudo com as mãos é mais correto. Você pode controlar cada ação e fazer tudo o que sabe perfeitamente. Mas a memória não é confiável. Se ela está fazendo a mesma coisa o tempo todo, ela mantém os detalhes em mente, e se ela tem que alternar entre os idiomas o tempo todo, depois de um mês ou dois ela tem que se lembrar do que precisa ser feito lá. Portanto, trabalhar através da opção “mexer com o mouse” tem direito de existir, nem que seja por isso. Novamente, se o módulo que está sendo depurado tiver uma dúzia ou mais de sinais de interface, sempre fico entediado de fazer o trabalho de rotina de declará-los e encaminhá-los. Portanto, agora veremos como fazer um modelo usando o mouse. E então - cada um vai decidir por si mesmo se isso é suficiente para ele ou se ele deve mudar para o trabalho manual.
Então, queremos simular o módulo. O que é "simular" está além do escopo de nosso ciclo, você pode escrever um grande ciclo separado sobre este tópico. Ou seja, dentro da estrutura desta seção, presumimos que você esteja familiarizado com a metodologia para desenvolver um modelo. Mas então tudo precisa ser incluído no projeto ... Ou não? Curiosamente, você nem mesmo precisa criar seu próprio projeto para modelar um módulo. Podemos anexar-se como um parasita a qualquer projeto, sem incluir nada de novo nele, mas apenas criando uma suíte de testes que não participará da montagem principal de forma alguma.
Por uma questão de interesse, vamos anexar ao nosso projeto ULPI um módulo tão engraçado no SystemVerilog, escrito por mim especificamente para ilustração e que não tem nada a ver com o analisador desenvolvido. Há apenas algum tempo, eu tinha muita dificuldade em calcular somas de verificação, então me veio à cabeça.
module sum(
input clk,
input [7:0] data,
input we,
input sof,
output [15:0] sum
);
logic [15:0] temp;
always @ (posedge clk)
begin
if (we)
begin
if (sof)
temp <= data;
else
temp <= temp + data;
end
end
// -
//assign sum = (~temp)+1;
// :
assign sum = temp;
endmodule
Pode-se ver que os dados chegam a ele por meio de um barramento, que lembra muito remotamente o AVALON_MM, e são simplesmente produzidos em código paralelo.
Vamos colocar o arquivo resultante no diretório com nosso projeto, mas não vamos incluí-lo no projeto no Quartus. Em vez disso, criaremos um conjunto de testes especificamente para ele. Para fazer isso, selecione o item de menu Assignments—> Settings:

e na árvore que aparece, procure o item EDA Tools Settings—> Simulation:

A propósito, sobre o tipo de simulação destacado pelo quadro verde. Talvez alguém se lembre que nos primeiros artigos eu disse que na hora de criar um projeto, puramente por hábito, escolho ModelSim Altera? Foi a própria arma no palco que mais cedo ou mais tarde teve que disparar. No entanto, se você não selecionou um tipo de modelagem ao criar o projeto, pode selecioná-lo ou alterá-lo aqui.
Continuamos a criar um conjunto de testes. Mude o botão de opção para Compilar bancada de teste (a propósito, como este termo se traduz perfeitamente em russo? Não consigo escrever "bancada de teste", pois não vejo nenhuma bancada) e pressione o botão Bancadas de teste :

Na caixa de diálogo que abrir, pressione Novo :

Se fazer caso de teste manualmente, você pode preencher os campos em uma passagem. Mas como fazemos tudo com o mouse, agora preenchemos apenas parte dos campos, e preencheremos o resto depois. No campo de nome da bancada de testeDigitei a palavra Parazit (o que mais chamar de teste que apenas parasita o projeto?). A palavra Parazit abaixo foi preenchida automaticamente. Não vamos mudar agora, mas no futuro ainda temos que fazer isso. Além disso, usando o botão "...", selecionei o arquivo sum.sv com o código do somador a ser depurado e, em seguida, usando o botão Adicionar , coloquei-o na lista de arquivos de teste. Por enquanto, é isso. Fechando a caixa de diálogo ... A

seguir, continuaremos a formar o teste no ambiente ModelSim. Para fazer isso, selecione o item de menu Tools—> Run Simulation Tools—> RTL Simulation:

A janela ModelSim é aberta. Talvez erros sejam encontrados no código Verilog, então você precisa fechar o ModelSim, corrigir os erros e reabri-lo. Mas, mais cedo ou mais tarde, a lista de erros se tornará puramente organizacional. É assim para mim:

Nenhum módulo de nível superior encontrado. Isto é normal. Ainda não o criamos de forma simples. Portanto, vamos trabalhar na lista de bibliotecas e abri-la. Aqui está, nosso adicionador.

Passe o mouse sobre ele, pressione o botão direito do mouse e selecione o item de menu Criar Wave. Isso tudo é tão chato no texto, se eu estivesse gravando um vídeo, todo o processo levaria dezenas de segundos, então não se assuste, mas preste atenção em suas mãos. Portanto, crie uma onda ...

Os sinais de interface do módulo foram movidos automaticamente para o gráfico:

É necessário atribuir um valor a um deles. Não importa qual, é importante nomear. O ambiente de modelagem muito antigo do Quartus era bom para gerar sinais de clock. Infelizmente, ele foi retirado da entrega há muito tempo, desde que começaram a anexar o ModelSim, e aqui nem tudo fica tão bonito com algo assim. Não vi sentido em gerar um gerador aqui, então nem vou mostrar. Então ... Bem, vamos definir a linha para zero. Visamos o sinal, pressione o botão direito, selecione o item de menu Edit—> Wave Editor—> Create / Modify WaveForm.

Na caixa de diálogo que aparece, selecione Constante . E, ao mesmo tempo, alteraremos o tempo, digamos, em 100 microssegundos:

Em seguida, indicamos o valor 0:

Criamos o conjunto de dados mínimo necessário e o resto será mais fácil de fazer com canetas. Exportamos o arquivo. Para fazer isso, selecione o item de menu File—> Export—> Waveform:

Selecione o tipo de arquivo Verilog Testbench (a propósito, é uma pena que não seja SystemVerilog, mas no futuro será possível corrigi-lo com canetas). Também definimos o nome do arquivo. Eu chamei de parazit_tb , seguindo o "porque não?"

Isso é tudo, o ModelSim pode ser fechado, enquanto a casa temporária não precisa ser salva.
O que fazer com o próximo modelo
Aqui está um arquivo Verilog torto, mas ainda assim feito, o sistema criado para nós:
`timescale 1ns / 1ns
module parazit_tb ;
reg sof ;
reg we ;
wire [15:0] sum ;
reg [7:0] data ;
reg clk ;
sum
DUT (
.sof (sof ) ,
.we (we ) ,
.sum (sum ) ,
.data (data ) ,
.clk (clk ) );
// "Constant Pattern"
// Start Time = 0 ns, End Time = 100 us, Period = 0 ns
initial
begin
end
initial
#0 $stop;
endmodule
A automação nos salvou de escrever blocos de construção. Além disso, se houvesse mais sinais de interface, a automação registraria e conectaria obedientemente todos os circuitos. Pessoalmente, quando eu crio conjuntos de testes manualmente, é o processo de descrição de sinais e seu encaminhamento que é deprimente. Agora, neste arquivo, criaremos um modelo de ambiente que afetará o módulo de soma depurado .
Como você pode ver, não faz sentido configurar as constantes feitas pelo oscilador. Mesmo assim, todos os circuitos foram criados, o módulo a ser testado está conectado, até a seção inicial foi criada. Vamos refinar o código. A primeira é remover o ponto de interrupção excluindo as linhas:
initial
#0 $stop;
A seguir, vamos adicionar um modelo de gerador de relógio (que saudades de um gerador maravilhoso, que foi feito pelos antigos Quartuses! Lá você poderia definir a frequência em megahertz e não pensar em recalcular em um período, e mais ainda - meio período).
always
begin
clk = 0;
#5;
clk = 1;
#5;
end
Agora precisamos enviar alguns bytes de dados. A maneira mais fácil de fazer isso é direto na seção inicial , mas se eu escrever cada fase de acesso ao barramento lá, o código nesta seção ficará confuso. Portanto, farei a seguinte tarefa (é ela quem atua como modelo de pneu):
task SendByte (input reg[7:0] D);
begin
data = D;
we = 1;
@(posedge clk);
#1
we = 0;
end
endtask
Bem, vou escrever o propósito das constantes e a chamada de ciclos para trabalhar com o barramento no bloco inicial . Deixe-me lembrá-lo de que o tipo de registro # 123 significa "esperar 123 unidades de tempo". Temos em nanossegundos. Também lembro que, como as atribuições são sequenciais, usamos a operação "igual", não a "seta". Portanto, temos o seguinte código de teste principal:
Assista aqui
initial
begin
sof = 0;
we = 0;
data = 0;
#13;
//
sof = 1;
SendByte (1);
//
sof = 0;
SendByte (5);
SendByte (1);
//
#20;
SendByte (1);
end
No total, nosso código de módulo completo se parece com isto:
Visualize o código completo do módulo.
`timescale 1ns / 1ns
module parazit_tb ;
reg sof ;
reg we ;
wire [15:0] sum ;
reg [7:0] data ;
reg clk ;
sum
DUT (
.sof (sof ) ,
.we (we ) ,
.sum (sum ) ,
.data (data ) ,
.clk (clk ) );
always
begin
clk = 0;
#5;
clk = 1;
#5;
end
task SendByte (input reg[7:0] D);
begin
data = D;
we = 1;
@(posedge clk);
#1
we = 0;
end
endtask
// "Constant Pattern"
// Start Time = 0 ns, End Time = 100 us, Period = 0 ns
initial
begin
sof = 0;
we = 0;
data = 0;
#13;
//
sof = 1;
SendByte (1);
//
sof = 0;
SendByte (5);
SendByte (1);
//
#20;
SendByte (1);
end
endmodule
Concluindo a preparação do caso de teste
É hora de adicionar este texto ao conjunto de testes. Para fazer isso, vá para a caixa de diálogo que já conhecemos. Mas

agora não criamos nosso conjunto, mas o selecionamos da lista. No futuro, a lista irá crescer à medida que os conjuntos são adicionados ... Depois de selecionar, pressione o botão Editar. Fiz três edições nas configurações:
- Adicionado o arquivo parazit_tb.v à lista.
- Como no arquivo parazit_tb.v , o módulo de nível superior tem o nome parazit_tb (você pode ter certeza olhando a fonte da seção anterior), inseri esse nome no módulo de nível superior na linha de bancada de teste .
- Eu disse para executar a simulação por 10 microssegundos e depois fazer uma pausa. Se qualquer coisa, farei isso pressionando os botões de controle manual.

Total
Fechamos tudo. Execute o ModelSim novamente. Vemos que tudo está funcionando corretamente. Os dados entram e são contados no valor. Se não houver dados no relógio ( nós somos zero), a quantidade não aumenta.

Como usar o próprio ambiente de modelagem é um tópico para vários artigos. E sim em formato de vídeo. Mas, em geral, nos familiarizamos com o método de preparação e execução rápida de testes na linguagem Verilog do ambiente Quartus.
Agora que sabemos como executar a simulação rapidamente, podemos esboçar um modelo de ambiente para nosso analisador USB e testar seu funcionamento. Ao mesmo tempo, não memorizamos um único feitiço ModelSim, já que Quartus permite configurar tudo usando o "mouse". Ele mesmo gera todos os scripts necessários e chama ele mesmo o ambiente ModelSim. Também criamos a base para o modelo no modo automático, embora tenhamos que modificá-la manualmente.
Ai e ah. Um dos elementos do ambiente externo é o módulo ULPI. Para desenvolver seu próprio modelo, você deve, em primeiro lugar, entender cuidadosamente a lógica de funcionamento desse microcircuito. E no artigo anterior eu disse que é muito complicado. E, em segundo lugar, você precisa gastar muito tempo desenvolvendo o código do modelo. E a eliminação de erros nele ... É claro que é mais fácil encontrar algo pronto. Mas o modelo pronto foi encontrado apenas na linguagem SystemC. Portanto, no próximo artigo, aprenderemos a modelar um sistema usando essa linguagem.