Utilisation pratique des tableaux à deux dimensions en Verilog : de l'arithmétique matricielle à l'optimisation de la mémoire (avec exemples complets de code)

La manipulation de matrices est un besoin courant dans les projets d'accélération matérielle, notamment pour le traitement d'images ou les réseaux de neurones. L'utilisation de tableaux à deux dimensions en Verilog permet de simplifier la gestion de ces structures de données tout en optimisant l'utilisation des ressources matérielles.

Définition et initilaisation des tableaux 2D

La syntaxe de base pour déclarer un tableau 2D en Verilog est :

reg [DATA_WIDTH-1:0] nom_tableau [LIGNES-1:0][COLONNES-1:0];

Exemple d'initialisation

Voici trois méthodes pour initialiser un tableau 2D :

  • Initialisation manuelle : Idéale pour les petites matrices
  • Intiialisation par boucle : Utile pour les matrices régulières
  • Importation depuis un fichier : Adaptée aux données volumineuses

Optimisation des accès mémoire

Le mode d'accès aux éléments du tableau influence directement les performances. Voici deux approches :

// Accès ligne par ligne
always @(posedge clk) begin
    for (int indexLigne = 0; indexLigne < LIGNES; indexLigne++) begin
        for (int indexColonne = 0; indexColonne < COLONNES; indexColonne++) begin
            // Traitement de la donnée
        end
    end
end

// Accès colonne par colonne
always @(posedge clk) begin
    for (int indexColonne = 0; indexColonne < COLONNES; indexColonne++) begin
        for (int indexLigne = 0; indexLigne < LIGNES; indexLigne++) begin
            // Traitement de la donnée
        end
    end
end

Multiplication matricielle optimisée

Voici une implémentation pipelinée pour la multiplication de matrices :

module multiplicateurMatrices #(
    parameter TAILLE = 8,
    parameter LARGEUR_DONNEES = 16,
    parameter ETAPES_PIPELINE = 3
) (
    input wire clock,
    input wire reset_n,
    input wire [LARGEUR_DONNEES-1:0] matriceA [0:TAILLE-1][0:TAILLE-1],
    input wire [LARGEUR_DONNEES-1:0] matriceB [0:TAILLE-1][0:TAILLE-1],
    output reg [LARGEUR_DONNEES*2-1:0] resultat [0:TAILLE-1][0:TAILLE-1]
);

reg [LARGEUR_DONNEES-1:0] tamponA [0:ETAPES_PIPELINE-1][0:TAILLE-1][0:TAILLE-1];
reg [LARGEUR_DONNEES-1:0] tamponB [0:ETAPES_PIPELINE-1][0:TAILLE-1][0:TAILLE-1];
reg [LARGEUR_DONNEES*2-1:0] sommePartielle [0:TAILLE-1][0:TAILLE-1];

always @(posedge clock or negedge reset_n) begin
    if (!reset_n) begin
        // Initialisation du résultat
        for (int indexLigne = 0; indexLigne < TAILLE; indexLigne++) begin
            for (int indexColonne = 0; indexColonne < TAILLE; indexColonne++) begin
                resultat[indexLigne][indexColonne] <= 0;
                sommePartielle[indexLigne][indexColonne] <= 0;
            end
        end
        // Initialisation des tampons
        for (int etape = 0; etape < ETAPES_PIPELINE; etape++) begin
            for (int indexLigne = 0; indexLigne < TAILLE; indexLigne++) begin
                for (int indexColonne = 0; indexColonne < TAILLE; indexColonne++) begin
                    tamponA[etape][indexLigne][indexColonne] <= 0;
                    tamponB[etape][indexLigne][indexColonne] <= 0;
                end
            end
        end
    end else begin
        // Logique de pipeline
    end
end

endmodule

Étiquettes: Verilog FPGA mémoire Matrices

Publié le 8 octobre à 16h11