A interface de um module
A lista de ports de um module é sua interface - tudo que o mundo externo pode ver e tocar. Dentro do module, o corpo calcula saídas a partir de entradas. Os ports são os wires que cruzam o limite.
A declaração moderna no estilo ANSI coloca direção, tipo e largura inline:
module my_module(
input wire clk,
input wire reset,
input wire [7:0] data_in,
output reg [7:0] data_out,
output wire valid
);
// corpo
endmodule
Essa é a forma inteira. Cada port tem:
- Uma direção:
input,outputouinout. - Um tipo:
wireoureg(oulogicem SystemVerilog). - Uma largura: um range como
[7:0], ou um único bit se omitido. - Um nome: o identificador que você vai usar dentro do corpo.
As três direções
input - Conduzido de fora
Inputs são sempre wire. O driver está fora do module - ou um sinal de um module de nível superior ou o reg do testbench. Dentro deste module, você pode ler inputs em expressões mas nunca atribuir a eles:
module reader(input wire [7:0] data);
initial $display("data = %h", data);
endmodule
Escrever data = ... dentro do module seria um erro.
output - Conduzido de dentro
Outputs são conduzidos por algo dentro deste module. Podem ser wire (conduzidos por assign ou por uma saída de submódulo) ou reg (conduzidos a partir de always/initial).
module driver(
input wire a,
input wire b,
input wire clk,
output wire y, // wire - conduzido por assign
output reg q // reg - conduzido por always
);
assign y = a & b; // OK porque y é wire
always @(posedge clk)
q <= a; // OK porque q é reg
endmodule
Tentar atribuir a y dentro do bloco always, ou conduzir q com um assign, seria um erro de compilação. A escolha da palavra-chave precisa combinar com o driver.
inout - Bidirecional
Ports inout são wires que podem ser conduzidos alternadamente pelo module ou pelo que estiver conectado por fora. Aparecem em fronteiras de chip - linhas SDA I²C, pinos GPIO bidirecionais, barramentos de dados compartilhados. Dentro do module, você controla a direção com um padrão tri-state:
module bidir_pin(
input wire data_out,
input wire output_enable,
output wire data_in,
inout wire pin
);
assign pin = output_enable ? data_out : 1'bz;
assign data_in = pin;
endmodule
pin é o wire compartilhado. Quando output_enable é alto, o module aciona pin para data_out. Quando baixo, ele libera o pino para alta impedância (z), deixando um driver externo usá-lo. data_in sempre observa o que está atualmente no pino.
inout é raro em lógica puramente interna. Se você está construindo um controlador de memória, um núcleo de CPU, um pipeline de processamento de imagem, talvez nunca precise dele. É um recurso para o anel de I/O nos limites do chip.
Ports de bit único vs multi-bit
O range de largura é opcional - omita-o para um port de bit único:
input wire valid, // 1 bit
input wire [7:0] data, // 8 bits
input wire [31:0] addr, // 32 bits
Você pode usar parameters para larguras, que é como modules parametrizados funcionam:
module bus #(
parameter WIDTH = 32
)(
input wire [WIDTH-1:0] in,
output wire [WIDTH-1:0] out
);
assign out = in;
endmodule
Estilos ANSI vs Verilog-1995
Você vai ver ocasionalmente código antigo que divide a lista de ports e as declarações:
// Estilo antigo Verilog-1995 - NÃO faça isso em código novo
module foo(clk, data_in, data_out);
input clk;
input [7:0] data_in;
output reg [7:0] data_out;
// ...
endmodule
A lista de ports contém apenas nomes; cada port é então declarado separadamente dentro do corpo. É verboso, propenso a erros do tipo "nomeado na lista de ports mas nunca declarado" e o dobro do que digitar. O estilo ANSI-2001 (o mostrado em todos estes docs) substitui ambos:
module foo(
input wire clk,
input wire [7:0] data_in,
output reg [7:0] data_out
);
// ...
endmodule
Use o estilo ANSI. Ferramentas o suportam desde 2001.
Um exemplo completo
Esse module único tem:
- Um parameter (
WIDTH). - Inputs para clock, reset, load enable, data e shift enable.
- Uma saída
reg(data_out) conduzida dentro de um blocoalways. - Uma saída
wire(msb_out) conduzida por uma continuous assignment. - Uma declaração ANSI completa que lista direção, tipo e largura de cada port inline.
É assim que quase todo module sintetizável que você vai escrever é moldado.
O que vem a seguir
O próximo doc - Module Instantiation - mostra como pegar este module e usá-lo dentro de um design maior. O shifter que você acabou de escrever não é útil sozinho; ele ganha o pão quando algo o instancia e o conecta a um sistema.
Perguntas frequentes
Quais são as três direções de port em Verilog?
input, output e inout. input é conduzido de fora do module. output é conduzido de dentro do module. inout é bidirecional - útil para barramentos tri-state onde o module ao mesmo tempo aciona e lê o mesmo pino. A grande maioria de ports internos é input ou output; inout só aparece em pinos de chip.
Qual a diferença entre output wire e output reg?
output wire significa que a saída é conduzida por uma continuous assignment ou pela saída de um submódulo - tudo fora de um bloco always. output reg significa que é conduzida de dentro de um bloco procedural (initial ou always). A palavra-chave controla se você pode mirar o sinal de dentro de um always, não se o hardware sintetizado contém um flip-flop.
Qual é o estilo ANSI para ports em Verilog?
O estilo ANSI declara direção e tipo de cada port inline na lista de ports: module foo(input wire [7:0] data, output reg [7:0] result);. O estilo Verilog-1995 mais antigo só listava nomes na lista de ports e os redeclarava dentro do module. Use sempre o estilo ANSI em código novo - é mais curto, menos sujeito a erros e padrão em qualquer lugar moderno.
Posso ter múltiplos inputs e outputs em um module Verilog?
Sim - um module pode ter quantos ports você precisar, em qualquer combinação de direções. Separe-os com vírgulas na lista de ports. A ordem na lista de ports determina a ordem de conexão posicional na instanciação, mas você quase sempre usa conexões nomeadas (.port(signal)) para evitar depender da ordem.