Tempo de software vs tempo de hardware
Em um programa, o tempo avança uma instrução por vez. A CPU termina a linha 1, então executa a linha 2. Se você quer que duas coisas aconteçam ao mesmo tempo, você recorre a threads, async ou a uma segunda máquina.
Em hardware, tudo acontece ao mesmo tempo. Um circuito não se reveza. O somador está sempre somando, o multiplexador está sempre selecionando, o flip-flop está sempre observando o clock. Não há program counter. Não há "linha atual".
Verilog tem que descrever esse mundo em texto. A forma como faz isso é a fonte de toda confusão neste capítulo.
Duas camadas de concorrência
Quando você lê um módulo Verilog, está olhando para duas coisas diferentes ao mesmo tempo:
- A descrição estática do circuito. Wires, registers, instâncias de gates, instâncias de submódulos, continuous assignments. Tudo isso existe simultaneamente. A ordem no arquivo não importa.
- Blocos procedurais -
initialealways- que parecem pequenos programas pelos quais o simulador caminha. Dentro de um desses blocos, declarações sim executam em algum tipo de ordem. Mas vários blocosalwayspodem estar ativos ao mesmo tempo, cada um em sua própria pequena thread de tempo simulado.
module example(input wire a, input wire b, output wire y, output wire z);
assign y = a & b; // existe em todos os momentos
assign z = a | b; // também existe em todos os momentos, em paralelo
always @(posedge a) begin
// um reator "sempre ativo" separado que acorda
// a cada borda de subida de `a`
end
endmodule
As duas linhas assign não são uma sequência. Elas descrevem duas peças de lógica combinacional que a ferramenta de síntese pode dispor lado a lado. O bloco always é uma terceira coisa acontecendo em paralelo.
O que "sinal" significa
Em software, uma variável guarda um valor até você mudá-lo. Em Verilog, um sinal mantém um valor continuamente - e a cada momento ele depende do que quer que o esteja conduzindo.
Um wire é conduzido de fora (um assign, a porta de saída de um submódulo, uma conexão inout). Um reg é conduzido de dentro de um bloco procedural. Ambos têm um valor em todos os momentos. Não existe "não inicializado" no sentido do C - sinais são ou conduzidos a um valor definido, o x especial (desconhecido), ou z (alta impedância). Cobrimos os dois últimos em X e Z Values.
Clocks mudam tudo
Assim que você traz um clock, o tempo começa a importar. Um flip-flop é uma pequena peça de hardware que captura o valor de sua entrada na borda de subida (ou de descida) de um clock e o mantém até a próxima borda. O que permite construir contadores, máquinas de estado, pipelines - qualquer coisa que tenha memória - é o clock.
Note q <= d em vez de q = d. Essa é uma non-blocking assignment - o burro de carga da lógica clocked. Ela diz "na próxima borda de clock, agende q para se tornar o que quer que d seja agora". Vamos detalhar as regras em Blocking vs Non-blocking; por enquanto, reconheça que a atribuição não está fingindo ser uma declaração de software.
RTL: O modelo mental de transferência entre registers
A maior parte do Verilog sintetizável é escrita em um estilo chamado Register Transfer Level, ou RTL. A ideia é simples:
- Decida qual estado seu circuito precisa (os registers).
- Para cada register, descreva duas coisas: o que o reseta e qual lógica combinacional calcula seu próximo valor.
- Ligue saídas de lógica combinacional a entradas de register e você tem um circuito funcionando.
always @(posedge clk) begin
if (reset) state <= IDLE;
else state <= next_state;
end
always @(*) begin
case (state)
IDLE: next_state = start ? RUNNING : IDLE;
RUNNING: next_state = done ? IDLE : RUNNING;
default: next_state = IDLE;
endcase
end
Essa é uma máquina de dois estados. O primeiro bloco always é clocked - é um flip-flop. O segundo é puramente combinacional - é apenas uma equação. Quase toda máquina de estado, contador e pipeline que você vai escrever segue essa forma.
Os hábitos que travam você
Se você vem de software, aqui está uma lista curta de hábitos a deixar de lado:
- "Variáveis atualizam quando eu atribuo a elas." Não em uma borda de clock - uma non-blocking assignment agenda a atualização para o final do time step.
- "Declarações executam de cima para baixo." Fora de blocos procedurais, não. Dentro de um bloco clocked, mais ou menos - mas blocking vs non-blocking muda o que "em ordem" realmente significa.
- "Vou alocar isso quando precisar." Hardware não aloca. Cada register e gate precisa existir no momento da síntese. O tamanho de cada vetor é fixo.
- "Esse loop é rápido - é só uma operação." Um loop
forem Verilog sintetizável é desenrolado em hardware paralelo. Um loop de 64 iterações se torna 64 cópias do corpo, não uma instrução de CPU que executa 64 vezes.
Você não está aprendendo a escrever um programa. Você está aprendendo a descrever um circuito. O instinto de ler de cima para baixo é exatamente o errado e leva um tempo para reprogramar.
O que vem a seguir
Os próximos docs caminham por obter um toolchain local (opcional - o editor do navegador está ótimo), depois escrever seu primeiro módulo do zero. Voltaremos ao contraste hardware-vs-software toda vez que algo parecer estranho, porque a maioria das partes "estranhas" tem a mesma raiz: isso não é software.
Perguntas frequentes
Qual a diferença entre hardware e software em termos de Verilog?
Software é uma sequência de instruções executadas por uma CPU, uma depois da outra. Hardware - o que o Verilog descreve - é uma rede de gates e wires que carregam sinais ao mesmo tempo. Um arquivo Verilog descreve essa rede. O simulador imita o comportamento paralelo; uma ferramenta de síntese o transforma em silício real.
Código Verilog executa de cima para baixo como C?
Não - e tratá-lo assim é o erro mais comum de iniciante. Declarações de nível superior (assigns, instâncias de módulo, blocos always) todas 'existem' ao mesmo tempo. Só dentro de blocos procedurais - initial e always - acontece algo parecido com execução sequencial, e mesmo lá, non-blocking assignments quebram a ilusão.
O que 'concorrente' significa em Verilog?
Significa que múltiplas declarações descrevem partes de um circuito que operam ao mesmo tempo. Duas declarações assign no mesmo módulo não são 'linha 1 depois linha 2' - são duas peças de hardware funcionando em paralelo, ambas reagindo às suas entradas continuamente.
O que é RTL design?
RTL significa Register Transfer Level. É um estilo de escrever Verilog onde você descreve o circuito como registers (flip-flops) e a lógica combinacional que calcula seus próximos valores. A maior parte do Verilog sintetizável é RTL. O nível acima é comportamental; o nível abaixo é gate-level.