Modelagem Comportamental e Tempo em Verilog

Os modelos comportamentais em Verilog contêm instruções procedimentais, que controlam a simulação e manipulam variáveis ​​dos tipos de dados. Todas essas declarações estão contidas nos procedimentos. Cada um dos procedimentos possui um fluxo de atividades associado a ele.

Durante a simulação do modelo comportamental, todos os fluxos definidos pelas declarações 'sempre' e 'inicial' começam juntos no tempo de simulação 'zero'. As instruções iniciais são executadas uma vez e as instruções always são executadas repetidamente. Neste modelo, as variáveis ​​de registro aeb são inicializadas para os binários 1 e 0 respectivamente no tempo de simulação 'zero'. A instrução inicial é então concluída e não é executada novamente durante a simulação. Esta instrução inicial contém um bloco de início e fim (também chamado de bloco sequencial) de instruções. Neste bloco de tipo começo-fim, a é inicializado primeiro seguido por b.

Exemplo de modelagem comportamental

module behave; 
reg [1:0]a,b; 

initial 
begin 
   a = ’b1; 
   b = ’b0; 
end 

always 
begin 
   #50 a = ~a; 
end 

always 
begin 
   #100 b = ~b; 
end 
End module

Atribuições Processuais

As atribuições procedurais são para atualizar as variáveis ​​reg, integer, time e memory. Há uma diferença significativa entre a atribuição procedimental e a atribuição contínua, conforme descrito abaixo -

As atribuições contínuas conduzem as variáveis ​​da rede e são avaliadas e atualizadas sempre que um operando de entrada muda de valor.

As atribuições procedurais atualizam o valor das variáveis ​​de registro sob o controle das construções de fluxo procedural que as cercam.

O lado direito de uma atribuição procedural pode ser qualquer expressão avaliada como um valor. No entanto, as seleções de parte do lado direito devem ter índices constantes. O lado esquerdo indica a variável que recebe a atribuição do lado direito. O lado esquerdo de uma atribuição procedimental pode assumir uma das seguintes formas -

  • variável de registro, inteiro, real ou de tempo - Uma atribuição à referência de nome de um desses tipos de dados.

  • bit-select de um registro, inteiro, real ou variável de tempo - Uma atribuição a um único bit que deixa os outros bits intocados.

  • seleção parcial de um registrador, inteiro, real ou variável de tempo - uma seleção parcial de dois ou mais bits contíguos que deixa o resto dos bits intocados. Para o formulário de seleção parcial, apenas expressões constantes são válidas.

  • elemento de memória - uma única palavra de uma memória. Observe que as seleções de bits e partes são ilegais em referências de elementos de memória.

  • concatenação de qualquer uma das opções acima - uma concatenação de qualquer uma das quatro formas anteriores pode ser especificada, o que particiona efetivamente o resultado da expressão do lado direito e atribui as partes da partição, em ordem, às várias partes da concatenação.

Atraso na Atribuição (não para síntese)

Em uma atribuição atrasada Δt unidades de tempo passam antes que a instrução seja executada e a atribuição à esquerda seja feita. Com o atraso intra-atribuição, o lado direito é avaliado imediatamente, mas há um atraso de Δt antes que o resultado seja colocado na atribuição à esquerda. Se outro procedimento alterar um sinal do lado direito durante Δt, ele não afetará a saída. Atrasos não são suportados por ferramentas de síntese.

Sintaxe

  • Procedural Assignmentvariável = expressão

  • Delayed assignment# Δt variável = expressão;

  • Intra-assignment delayvariável = # expressão Δt;

Exemplo

reg [6:0] sum; reg h, ziltch; 
sum[7] = b[7] ^ c[7]; // execute now. 
ziltch = #15 ckz&h; /* ckz&a evaluated now; ziltch changed 
after 15 time units. */ 

#10 hat = b&c; /* 10 units after ziltch changes, b&c is
evaluated and hat changes. */

Bloqueio de atribuições

Uma instrução de atribuição procedural de bloqueio deve ser executada antes da execução das instruções que a seguem em um bloco sequencial. Uma instrução de atribuição procedural de bloqueio não impede a execução de instruções que a seguem em um bloco paralelo.

Sintaxe

A sintaxe para uma atribuição de procedimento de bloqueio é a seguinte -

<lvalue> = <timing_control> <expression>

Onde, lvalue é um tipo de dados que é válido para uma instrução de atribuição procedural, = é o operador de atribuição e o controle de tempo é o atraso opcional intra-atribuição. O atraso de controle de tempo pode ser um controle de atraso (por exemplo, # 6) ou um controle de evento (por exemplo, @ (clk posedge)). A expressão é o valor do lado direito que o simulador atribui ao lado esquerdo. O operador de atribuição = usado para bloquear atribuições procedurais também é usado por atribuições contínuas procedurais e atribuições contínuas.

Exemplo

rega = 0; 
rega[3] = 1;            // a bit-select 
rega[3:5] = 7;          // a part-select 
mema[address] = 8’hff;  // assignment to a memory element 
{carry, acc} = rega + regb;  // a concatenation

Atribuições não bloqueadoras (RTL)

A atribuição de procedimento sem bloqueio permite que você agende atribuições sem bloquear o fluxo de procedimento. Você pode usar a instrução de procedimento sem bloqueio sempre que quiser fazer várias atribuições de registro no mesmo intervalo de tempo, sem levar em conta a ordem ou dependência umas das outras.

Sintaxe

A sintaxe para uma atribuição procedural sem bloqueio é a seguinte -

<lvalue> <= <timing_control> <expression>

Onde lvalue é um tipo de dados válido para uma instrução de atribuição procedural, <= é o operador de atribuição sem bloqueio e o controle de tempo é o controle de tempo opcional intra-atribuição. O atraso de controle de tempo pode ser um controle de atraso ou um controle de evento (por exemplo, @ (clk posedge)). A expressão é o valor do lado direito que o simulador atribui ao lado esquerdo. O operador de atribuição sem bloqueio é o mesmo operador que o simulador usa para o operador relacional menor que ou igual. O simulador interpreta o operador <= como um operador relacional quando você o usa em uma expressão e interpreta o operador <= como um operador de atribuição quando você o usa em uma construção de atribuição procedural sem bloqueio.

Como o simulador avalia atribuições de procedimento sem bloqueio Quando o simulador encontra uma atribuição de procedimento sem bloqueio, o simulador avalia e executa a atribuição de procedimento sem bloqueio em duas etapas da seguinte forma -

  • O simulador avalia o lado direito e programa a atribuição do novo valor para ocorrer em um momento especificado por um controle de tempo de procedimento. O simulador avalia o lado direito e programa a atribuição do novo valor para ocorrer em um momento especificado por um controle de tempo de procedimento.

  • No final da etapa de tempo, na qual o atraso determinado expirou ou o evento apropriado ocorreu, o simulador executa a atribuição atribuindo o valor ao lado esquerdo.

Exemplo

module evaluates2(out); 
output out; 
reg a, b, c; 
initial 

begin 
   a = 0; 
   b = 1; 
   c = 0; 
end 
always c = #5 ~c; 
always @(posedge c) 

begin 
   a <= b; 
   b <= a; 
end 
endmodule

Condições

A instrução condicional (ou instrução if-else) é usada para decidir se uma instrução é executada ou não.

Formalmente, a sintaxe é a seguinte -

<statement> 
::= if ( <expression> ) <statement_or_null> 
||= if ( <expression> ) <statement_or_null> 
   else <statement_or_null> 
<statement_or_null> 

::= <statement> 
||= ;

A <expressão> é avaliada; se for verdadeiro (ou seja, tiver um valor conhecido diferente de zero), a primeira instrução será executada. Se for falso (tem valor zero ou o valor é x ou z), a primeira instrução não é executada. Se houver uma instrução else e <expression> for falsa, a instrução else é executada. Visto que o valor numérico da expressão if é testado para ser zero, certos atalhos são possíveis.

Por exemplo, as duas declarações a seguir expressam a mesma lógica -

if (expression) 
if (expression != 0)

Visto que a parte else de um if-else é opcional, pode haver confusão quando um else é omitido de uma sequência if aninhada. Isso é resolvido associando sempre o else ao anterior mais próximo, se esse não tiver um else.

Exemplo

if (index > 0) 
if (rega > regb) 
   result = rega; 
   else // else applies to preceding if 
   result = regb; 

If that association is not what you want, use a begin-end block statement 
to force the proper association 

if (index > 0) 
begin 

if (rega > regb) 
result = rega; 
end 
   else 
   result = regb;

Construção de: if- else- if

A construção a seguir ocorre com tanta frequência que vale a pena uma breve discussão separada.

Example

if (<expression>) 
   <statement> 
   else if (<expression>) 
   <statement> 
   else if (<expression>) 
   <statement> 
   else  
   <statement>

Essa sequência de if (conhecida como construção if-else-if) é a maneira mais geral de escrever uma decisão de múltiplas vias. As expressões são avaliadas em ordem; se alguma expressão for verdadeira, a instrução associada a ela é executada e termina toda a cadeia. Cada declaração é uma declaração única ou um bloco de declarações.

A última parte else da construção if-else-if lida com 'nenhum dos anteriores' ou caso padrão em que nenhuma das outras condições foi satisfeita. Às vezes, não há uma ação explícita para o padrão; nesse caso, o else posterior pode ser omitido ou pode ser usado para a verificação de erros para capturar uma condição impossível.

Declaração do Caso

A declaração de caso é uma declaração de decisão multi-vias especial que testa se uma expressão corresponde a uma de várias outras expressões e se ramifica de acordo. A instrução case é útil para descrever, por exemplo, a decodificação de uma instrução de microprocessador. A instrução case tem a seguinte sintaxe -

Example

<statement> 
::= case ( <expression> ) <case_item>+ endcase 
||= casez ( <expression> ) <case_item>+ endcase 
||= casex ( <expression> ) <case_item>+ endcase 
<case_item> 
::= <expression> <,<expression>>* : <statement_or_null> 
||= default : <statement_or_null> 
||= default <statement_or_null>

As expressões de caso são avaliadas e comparadas na ordem exata em que são fornecidas. Durante a procura linear, se uma das expressões de item de caso corresponder à expressão entre parênteses, a instrução associada a esse item de caso será executada. Se todas as comparações falharem e o item padrão for fornecido, a instrução do item padrão será executada. Se a instrução padrão não for fornecida e todas as comparações falharem, nenhuma das instruções do item de caso será executada.

Além da sintaxe, a instrução case difere da construção if-else-if multi-way de duas maneiras importantes -

  • As expressões condicionais na construção if-else-if são mais gerais do que comparar uma expressão com várias outras, como na instrução case.

  • A instrução case fornece um resultado definitivo quando há valores x e z em uma expressão.

Demonstrações de Looping

Existem quatro tipos de instruções de loop. Eles fornecem um meio de controlar a execução de uma instrução zero, uma ou mais vezes.

  • para sempre executa continuamente uma instrução.

  • repeat executa uma instrução um número fixo de vezes.

  • while executa uma instrução até que uma expressão se torne falsa. Se a expressão começar com falsa, a instrução não será executada.

  • para a execução de controles de suas instruções associadas por um processo de três etapas, como segue -

    • Executa uma atribuição normalmente usada para inicializar uma variável que controla o número de loops executados

    • Avalia uma expressão - se o resultado for zero, o loop for termina, e se não for zero, o loop for executa sua (s) instrução (ões) associada (s) e, em seguida, realiza a etapa 3

    • Executa uma atribuição normalmente usada para modificar o valor da variável loopcontrol e, em seguida, repete a etapa 2

A seguir estão as regras de sintaxe para as instruções de loop -

Example

<statement> 
::= forever <statement> 
||=forever 
begin 
   <statement>+ 
end  

<Statement> 
::= repeat ( <expression> ) <statement> 
||=repeat ( <expression> ) 
begin
   <statement>+ 
end  

<statement> 
::= while ( <expression> ) <statement> 
||=while ( <expression> ) 
begin 
   <statement>+ 
end  
<statement> 
::= for ( <assignment> ; <expression> ; <assignment> ) 
<statement> 
||=for ( <assignment> ; <expression> ; <assignment> ) 
begin 
   <statement>+ 
end

Controles de atraso

Controle de Atraso

A execução de uma instrução de procedimento pode ser controlada por atraso usando a seguinte sintaxe -

<statement> 
::= <delay_control> <statement_or_null> 
<delay_control> 
::= # <NUMBER> 
||= # <identifier> 
||= # ( <mintypmax_expression> )

O exemplo a seguir atrasa a execução da atribuição em 10 unidades de tempo -

# 10 rega = regb;

Os próximos três exemplos fornecem uma expressão após o sinal de número (#). A execução da atribuição atrasa pelo tempo de simulação especificado pelo valor da expressão.

Controle de Eventos

A execução de uma instrução procedural pode ser sincronizada com uma mudança de valor em uma rede ou registro, ou a ocorrência de um evento declarado, usando a seguinte sintaxe de controle de evento -

Example

<statement> 
::= <event_control> <statement_or_null> 

<event_control> 
::= @ <identifier> 
||= @ ( <event_expression> ) 

<event_expression> 
::= <expression> 
||= posedge <SCALAR_EVENT_EXPRESSION> 
||= negedge <SCALAR_EVENT_EXPRESSION> 
||= <event_expression> <or <event_expression>>

* <SCALAR_EVENT_EXPRESSION> é uma expressão que resolve para um valor de um bit.

As alterações de valor em redes e registradores podem ser usadas como eventos para disparar a execução de uma instrução. Isso é conhecido como detecção de um evento implícito. A sintaxe Verilog também permite detectar mudanças com base na direção da mudança - isto é, em direção ao valor 1 (posedge) ou em direção ao valor 0 (negedge). O comportamento de posedge e negedge para valores de expressão desconhecidos é o seguinte -

  • um negedge é detectado na transição de 1 para desconhecido e de desconhecido para 0
  • um posedge é detectado na transição de 0 para desconhecido e de desconhecido para 1

Procedimentos: Sempre e Blocos Iniciais

Todos os procedimentos em Verilog são especificados em um dos quatro blocos a seguir. 1) Blocos iniciais 2) Blocos sempre 3) Tarefa 4) Função

As declarações inicial e sempre são ativadas no início da simulação. Os blocos iniciais são executados apenas uma vez e sua atividade termina quando a instrução termina. Em contraste, os blocos always são executados repetidamente. Sua atividade morre apenas quando a simulação é encerrada. Não há limite para o número de blocos iniciais e sempre que podem ser definidos em um módulo. Tarefas e funções são procedimentos que são ativados em um ou mais locais em outros procedimentos.

Blocos Iniciais

A sintaxe para a declaração inicial é a seguinte -

<initial_statement> 
::= initial <statement>

O exemplo a seguir ilustra o uso da instrução inicial para inicialização de variáveis ​​no início da simulação.

Initial 
Begin 
   Areg = 0; // initialize a register 
   For (index = 0; index < size; index = index + 1) 
   Memory [index] = 0; //initialize a memory 
   Word 
End

Outro uso típico dos blocos iniciais é a especificação de descrições de forma de onda que são executadas uma vez para fornecer estímulo à parte principal do circuito que está sendo simulado.

Initial 
Begin 
   Inputs = ’b000000; 
   // initialize at time zero 
   #10 inputs = ’b011001; // first pattern 
   #10 inputs = ’b011011; // second pattern 
   #10 inputs = ’b011000; // third pattern 
   #10 inputs = ’b001000; // last pattern 
End

Sempre bloqueia

A declaração 'sempre' se repete continuamente durante toda a execução da simulação. A sintaxe para a instrução always é fornecida abaixo

<always_statement> 
::= always <statement>

A instrução 'always', por causa de sua natureza de loop, só é útil quando usada em conjunto com alguma forma de controle de tempo. Se uma declaração 'sempre' não fornece meios para o tempo avançar, a declaração 'sempre' cria uma condição de impasse de simulação. O código a seguir, por exemplo, cria um loop infinito de atraso zero -

Always areg = ~areg;

Fornecer um controle de tempo para o código acima cria uma descrição potencialmente útil - como no exemplo a seguir -

Always #half_period areg = ~areg;