Dominando Instanciação de Módulos Verilog: Boas Práticas para Projeto Hierárquico
Como vimos em um artigo anterior, projetos maiores e complexos são construídos integrando múltiplos módulos de forma hierárquica. Módulos podem ser instanciados dentro de outros módulos e portas destas instâncias podem ser conectadas com outros sinais dentro do módulo pai.
Estas conexões de porta podem ser feitas via lista ordenada ou por nome.
Conexão de Porta por Lista Ordenada
Um método de fazer a conexão entre as expressões de porta listadas em uma instanciação de módulo com os sinais dentro do módulo pai é pela lista ordenada.
mydesign é um module instanciado com o nome d0 em outro módulo chamado tb_top. Portas são conectadas em uma certa ordem que é determinada pela posição daquela porta na lista de portas da declaração do módulo. Por exemplo, b no testbench é conectado a y do design simplesmente porque ambos estão na segunda posição na lista de portas.
module mydesign (input x, y, z, output o); endmodule
module tb_top;
wire [1:0] a; wire b, c;
mydesign d0 (a[0], b, a[1], c); // a[0] at position 1 -> x, b at position 2 -> y, etc.
endmodule
Isso é muito inconveniente porque a ordem pode mudar se uma nova porta for adicionada à lista ou quando o número de portas no design é muito grande.
Conexão de Porta por Nome
Uma maneira melhor de conectar portas é ligando explicitamente portas em ambos os lados usando seus nomes de porta.
O ponto . indica que o nome da porta seguinte ao ponto pertence ao design. O nome do sinal ao qual a porta do design deve ser conectado é dado a seguir entre parênteses ( ).
module design_top;
wire [1:0] a; wire b, c;
mydesign d0 (
.x (a[0]), // signal "x" in mydesign connects to "a[0]" in design_top
.y (b), // signal "y" in mydesign connects to "b" in design_top
.z (a[1]), .o (c)
);
endmodule
É recomendado codificar cada conexão de porta em uma linha separada para que qualquer mensagem de erro de compilação apontará corretamente para o número da linha onde o erro ocorreu. Isso é muito mais fácil de depurar e resolver comparado a não saber qual porta criou o erro se todas estivessem na mesma linha.
Como estas conexões são feitas por nome, a ordem em que aparecem é irrelevante. Múltiplas conexões de porta de instância de módulo não são permitidas.
Portas Desconectadas/Flutuantes
Portas que não são conectadas a nenhum fio no módulo instanciador terão um valor de alta impedância.
module design_top;
mydesign d0 (
// x is an input and not connected, hence a[0] will be Z
.y (a[1]), .z (a[1]), .o ()
); // o has valid value in mydesign but since it is not connected to "c" in design_top, c will be Z
endmodule
Exemplo
Vamos pegar o exemplo de shift register que vimos antes, e deixar algumas portas desconectadas.
module shift_reg (input d, input clk, input rstn, output q);
wire [2:0] q_net;
dff u0 (.d(d), .clk(clk), .rstn(rstn), .q(q_net[0]));
dff u1 (.d(q_net[0]), .clk(clk), .rstn(rstn), .q()); // Output q left floating
dff u2 (.d(q_net[1]), .clk(clk), .rstn(rstn), .q()); // Output q left floating
dff u3 (.d(q_net[2]), .clk(clk), .rstn(rstn), .q(q));
endmodule
Note que saídas das instâncias u1 e u2 são deixadas desconectadas no esquemático RTL obtido após síntese. Como a entrada d para instâncias u2 e u3 agora estão conectadas a nets que não estão sendo conduzidas por nada, elas são aterradas.
Em simulações, tais portas desconectadas serão denotadas como alta impedância ('hZ) tipicamente mostradas em formas de onda como uma linha laranja verticalmente alinhada no meio.
Todas as declarações de porta são implicitamente declaradas como wire e portanto a direção da porta é suficiente naquele caso. No entanto portas output que precisam armazenar valores devem ser declaradas como tipo de dado reg e podem ser usadas em um bloco procedural como always e initial apenas.
Portas do tipo input ou inout não podem ser declaradas como reg porque elas estão sendo conduzidas de fora continuamente e não devem armazenar valores, mas sim refletir as mudanças nos sinais externos o mais rápido possível. É perfeitamente legal conectar duas portas com tamanhos de vetor variados, mas a de menor tamanho de vetor prevalecerá e os bits restantes da outra porta com largura maior serão ignorados.
Verilog
- Domine o Verilog: Tutorial Completo para Projeto de Circuitos Digitais
- Dominando Concatenação Verilog: Guia Prático
- Verilog Essencial: Visão Geral Concisa
- Entendendo Atribuições em Verilog: Tipos, Sintaxe e Boas Práticas
- Verilog Blocking vs Non-Blocking: Diferenças Essenciais Explicadas
- Dominando Funções em Verilog para Design RTL Eficiente
- Entendendo Tasks em Verilog: Funções vs Tasks, Sintaxe e Uso
- Dominando Referência Hierárquica em Verilog: Evite Colisão de Nomes em Módulos e Blocos
- Gerador de Clock Verilog: Guia de Design e Implementação
- Dominando Funções Matemáticas do Verilog para Design de Hardware Eficiente