Un bon style de codage facilite la lecture, le débogage et la modification du code. Bien que le code Verilog puisse être écrit de manière arbitraire tant que la syntaxe est correcte, un style de codage bâclé est souvent une affaire sans lendemain. Parfois, lorsque vous regardez votre propre code, vous ne pouvez ni voir la signification des signaux ni comprendre la fonctionnalité des modules, et vous devez analyser la logique étape par étape. Cela prend beaucoup de temps et d'énergie à digérer, ce qui affecte sérieusement l'avancement de la conception.

Pour éviter que les autres ou nous-mêmes s'exclament sincèrement : « Quel petit idiot a écrit ce fichu code ! » Voici quelques suggestions sur le style de codage.

À propos du nommage

Les variables de signal, les modules, etc. doivent utiliser des noms significatifs, et les noms de signal doivent rester inchangés lors du passage entre modules, afin que le code lui-même porte des informations descriptives claires et une lisibilité accrue.

Lorsqu'un nom contient trop de mots, vous pouvez les concaténer en utilisant des initiales majuscules ou des traits de soulignement "_". Je préfère personnellement ce dernier car il est plus clair.

  reg     DataToDestinationClock ;
  reg     data_to_destination_clock ; // Recommandé

Il est recommandé d'utiliser des abréviations de mots pour nommer les signaux, et de savoir quoi conserver ou écarter afin d'éviter des noms de signaux trop longs. Par exemple, clock est abrégé en clk, destination en dest, source en src, etc.

reg data_to_destination_clock ; reg des_data ; // Recommandé

Utilisez astucieusement les chiffres pour représenter des lettres anglaises. Par exemple, 2 représente « to » et 4 représente « for », ce qui peut économiser un tout petit peu d'espace de code.

  reg     clk_for_test, sig_uart_to_spi ;
  reg     clk4test, sig_uart2spi ; //推荐

Bien que Verilog soit sensible à la casse, il est recommandé d'utiliser tout en minuscules pour les noms de modules fonctionnels généraux, les ports, les variables de signal, etc., d'utiliser des majuscules pour les paramètres, et d'utiliser des majuscules pour certains ports spéciaux tels que l'alimentation et pad. Ce n'est que pour faciliter l'écriture du code, ce qui permet de distinguer facilement les constantes des variables et d'éviter la différence entre des variables de signal qui ont le même nom mais une casse différente.

  parameter         DW = 8 ; //常量
  reg [DW-1 : 0]    wdata ;  //变量

Les variables de registre ajoutent généralement un suffixe_r, les variables retardées de cycles d'horloge ajoutent un suffixe_r1、_r2, etc. Il y a deux avantages principaux. Premièrement, pendant la conception RTL, les données peuvent être facilement manipulées selon le type de variable. Deuxièmement, les noms de signaux dans la netlist après synthèse changent souvent ; ajouter des suffixes permet de trouver facilement les variables de signal correspondantes dans la netlist après synthèse qui correspondent à celles du RTL.

  wire      dout_en ;
  reg       dout_en_r ;
  ...  //dout_en_r 的逻辑
  assign    dout_en = dout_en_r ;

Autres suffixes :_dPeut indiquer un signal retardé,_tPeut indiquer un signal stocké temporairement,_nPeut indiquer un signal actif à l'état bas,_sPeut indiquer un signal esclave,_mPeut indiquer un signal maître, etc.

Évitez d'utiliser des mots-clés pour nommer les signaux, par exemplein, out, x, zne sont pas recommandés comme variables.

Les noms de fichiers doivent rester cohérents avec le nom demodulede la conception, et essayez de n'inclure qu'un seul module de conception par fichier.

À propos des commentaires

Chaque module de conception doit commencer par des informations de description de fichier, notamment les droits d'auteur, le nom du module, l'auteur, la date, le résumé, l'historique des modifications, etc. Par exemple :

/**********************************************************
// Copyright 1891.06.02-2017.07.14
// Contact with [email protected]
================ example.v ======================
>> Author       : willrious
>> Date         : 1995.09.07
>> Description  : Welcome
>> note         : (1)To 
>>              : (2)My
>> V180121      : World.
************************************************************/

Les commentaires doivent exprimer de manière concise le sens décrit par le code. Les commentaires courts sont ajoutés après une ligne de code, tandis que les commentaires longs doivent être écrits une ligne au-dessus.

   //输出位宽小于输入位宽,求取缩小的倍数及对应的位数
   parameter       SHRINK       = DWI/DWO ;
   reg [AWI-1:0]         ADDR_WR ; //写地址

Les commentaires doivent être écrits en anglais autant que possible afin de garantir leur affichage correct sur différents systèmes d'exploitation et éditeurs.

Parmi les signaux de port, à l'exception des signaux d'horloge et de réinitialisation courants, il est préférable de commenter également les autres signaux.

Les commentaires sont très puissants. Vous pouvez utiliser les informations des commentaires pour dessiner des diagrammes temporels, et même utiliser les commentaires pour dessiner des schémas de structure de circuits numériques.

À propos de l'optimisation

Utilisez des parenthèses pour déterminer la priorité ou la structure logique du programme. Pour éviter les erreurs de conception causées par des problèmes de priorité des opérateurs, il est recommandé d'utiliser fréquemment des parenthèses. En même temps, une utilisation astucieuse des parenthèses peut parfois optimiser la structure après la synthèse logique. Par exemple :

    // souvent synthétisé en 3 additionneurs en série
    assign F = A + B + C + D ;
    // souvent synthétisé en 2 additionneurs parallèles et 1 additionneur en cascade, avec des contraintes de timing plus souples
    assign F = (A + B) + (C + D) ;
 
    // Non recommandé
    assign flag = cnt == 4'd2 && mode == 2'b01;
    // Recommandé
    assign flag = (cnt == 4'd2) && (mode == 2'b01);  

Pour les instructions conditionnelles, essayez d'utiliser des instructions case au lieu d'instructions if. Lorsqu'il y a trop d'instructions de test conditionnel au même niveau, la structure matérielle synthétisée à partir d'instructions case consomme souvent moins de ressources et a un meilleur timing que celle issue d'instructions if.

Lorsque vous écrivez des machines d'état, essayez d'utiliser le style en 3 segments pour garantir que le code est propre et sûr.

Lors de la conception d'un système, essayez d'adopter la méthode consistant à découper les modules par fonction, puis à instancier les modules. Par rapport à l'intégration de milliers de lignes de code dans un seul fichier, le découpage en modules est bénéfique pour la conception en équipe et facilite la mise à jour et la maintenance.

À propos de la propreté

Veillez à ce qu'il y ait un signal par ligne pour les signaux de port, avec la virgule immédiatement après la déclaration du port. Ceux qui ont des TOC, veuillez également garder les virgules alignées.

// Non recommandé
module even_divisor (input rstn, clk, output clk_div2, clk_div4, clk_div10) ;

// Recommandé
module even_divisor    (
    input               rstn     ,
    input               clk      ,
    output              clk_div2 ,
    output              clk_div4 ,
    output              clk_div10
    );

Lorsqu'une ligne de code est trop longue, essayez de la renvoyer à la ligne sans utiliser de caractère de continuation de ligne, par exemple :

      assign rempty    = (rover_flag == rq2_wptr_decode[AWI]) &&
                         (raddr_ex >= rq2_wptr_decode[AWI-1:0]);

Essayez d'utiliser begin + end pour garantir la relation de cascade entre les instructions d'exécution. begin doit être sur la même ligne que le mot-clé, et end doit être sur une nouvelle ligne. Par exemple, lors de l'utilisation d'un bloc always, ou lorsqu'une instruction conditionnelle ne comporte qu'une seule instruction d'exécution, les mots-clés begin + end peuvent être omis. Toutefois, pour garantir l'intégrité structurelle et faciliter le débogage et la modification futurs, il est recommandé d'ajouter ces mots-clés.

   always @(posedge dout_clk or negedge rstn) begin
      if (!rstn) begin
         dout_en_r       <= 1'b0 ;
      end
      else begin
         dout_en_r       <= rd_en_wir ;
      end
   end

Essayez d'utiliser la touche Tab et des espaces pour garantir que les instructions sont alignées selon la structure hiérarchique. Il doit également y avoir des espaces entre les variables, les mots-clés et les opérateurs pour faciliter le jugement logique.

   generate
      if (DWO >= DWI) begin
         reg [DWI-1:0]         mem [(1<<AWI)-1 : 0] ;
         always @(posedge CLK_WR) begin
            if (WR_EN) begin
               mem[ADDR_WR]  <= D ;
            end
         end
      end
   endgenerate

Lors de l'instanciation de modules, essayez de séparer les signaux de port des signaux de connexion et alignez-les respectivement. Lorsque le signal de connexion est un vecteur, indiquez sa largeur de bits pour faciliter la lecture et le débogage.

    ram u_ram(
        .CLK_WR          (clk),
        .WR_EN           (wren), // Ne pas écrire lorsque plein
        .ADDR_WR         (addr),
        .D               (wdata[9:0]),
        .Q               (rdata[31:0])
        );

Lors de l'instanciation de plusieurs modules identiques, essayez d'utiliser des instructions generate pour éviter des descriptions de code d'instanciation trop longues.