Modules, ports et hiérarchie
Déclarer une interface, choisir entre wire et reg, puis relier plusieurs modules proprement.
Le rôle d'un module
Le module est le bloc de base de Verilog. Il peut représenter une petite fonction, comme un comparateur, ou le niveau supérieur d'un FPGA complet.
module channel_guard (
input wire i_valid,
input wire i_ready,
output wire o_fault
);
assign o_fault = i_valid & ~i_ready;
endmoduleLa déclaration placée entre parenthèses donne le nom, le sens et le type de chaque port. Cette forme dite ANSI est disponible en Verilog-2001.
Entrées, sorties et ports bidirectionnels
Un port peut avoir trois directions :
| Direction | Rôle |
|---|---|
input | valeur reçue par le module |
output | valeur produite par le module |
inout | liaison bidirectionnelle |
inout est surtout utile au niveau des broches physiques, par exemple pour un bus réellement tristate. À l'intérieur d'un FPGA, il vaut mieux séparer les chemins d'entrée, de sortie et d'activation.
Comprendre wire et reg
Un wire représente un réseau piloté par une affectation continue, une primitive ou la sortie d'un autre module. Il ne mémorise pas une valeur par lui-même.
Un reg est une variable que l'on peut affecter dans un bloc procédural always ou initial. Malgré son nom, reg ne signifie pas automatiquement « registre physique ». Un reg affecté dans un bloc combinatoire complet devient de la logique combinatoire.
module threshold_flag (
input wire [7:0] i_level,
output reg o_high
);
always @* begin
o_high = 1'b0;
if (i_level >= 8'd200)
o_high = 1'b1;
end
endmoduleIci, o_high doit être déclaré reg parce qu'il est affecté dans always. Le bloc attribue une valeur dans tous les cas, donc il ne demande aucune mémoire.
Instancier un module
Une définition de module décrit un type de bloc. Une instance est une utilisation concrète de ce bloc dans un autre module.
module dual_channel_status (
input wire [1:0] i_valid,
input wire [1:0] i_ready,
output wire [1:0] o_fault,
output wire o_any_fault
);
wire w_fault_0;
wire w_fault_1;
channel_guard u_guard_0 (
.i_valid(i_valid[0]),
.i_ready(i_ready[0]),
.o_fault(w_fault_0)
);
channel_guard u_guard_1 (
.i_valid(i_valid[1]),
.i_ready(i_ready[1]),
.o_fault(w_fault_1)
);
assign o_fault =
u_guard_0 et u_guard_1 sont deux circuits distincts construits à partir du même module. Chacun possède ses propres connexions.
Relier les ports par nom
La notation .nom_du_port(signal) relie un port par son nom. Elle reste lisible si l'ordre des ports change et réduit les erreurs sur les modules qui ont plusieurs signaux de même largeur.
La connexion positionnelle existe aussi :
channel_guard u_guard_0 (i_valid[0], i_ready[0], w_fault_0);Elle est courte, mais une inversion entre deux positions peut passer inaperçue. Pour le RTL du site, les connexions nommées sont préférées.
Pilotes et conflits
Un signal ne doit généralement avoir qu'un seul pilote RTL. Deux assign qui pilotent le même wire peuvent produire un conflit et une valeur inconnue en simulation.
Les bus tristate sont un cas particulier. Sur FPGA, les tristates internes sont souvent remplacés par de la logique. Il est donc plus simple de choisir explicitement la source avec un multiplexeur.
À retenir
- Un module définit une interface et un circuit.
- Un
wireest piloté de façon continue ; unregest affecté dans un bloc procédural. - Le mot
regne garantit pas l'inférence d'une bascule. - Une instance est une copie matérielle d'un module.
- Les connexions nommées rendent la hiérarchie plus facile à relire.
📝 Tester mes connaissances - Quiz du chapitre