Conception d'une FIFO asynchrone avec Verilog

Le module Verilog ci-dessous définit une FIFO asynchrone avec des paramètres configurables pour la largeur des données et la profondeur. Le mode FWFT (First Word Fall Through) est activé, et la conversion en code Gray est employée pour gérer les pointeurs entre domaines d'horloge. La mémoire est implémentée comme une RAM à double port, avec des signaux de statut "plein" et "vide" générés par comparaison des pointeurs synchronisés.

// Module de FIFO asynchrone en Verilog `timescale 1ns/1ps module Async_FIFO_Module #( parameter DATA_WIDTH = 8, // Largeur des données parameter DEPTH_POWER = 3 // Exposant pour la profondeur : 2**DEPTH_POWER ) ( input async_reset, // Réinitialisation asynchrone input write_clock, // Horloge d'écriture input write_enable, // Requête d'écriture input [DATA_WIDTH-1:0] data_in, // Données à écrire output reg full_flag, // Indicateur FIFO pleine output reg [DEPTH_POWER-1:0] write_count, // Compteur d'écriture input read_clock, // Horloge de lecture input read_enable, // Requête de lecture output [DATA_WIDTH-1:0] data_out, // Données lues output reg empty_flag, // Indicateur FIFO vide output reg [DEPTH_POWER-1:0] read_count // Compteur de lecture );

// Signaux internes
wire [DEPTH_POWER-1:0]   addr_write, addr_read;
wire [DEPTH_POWER:0]     bin_next_write, gray_next_write;
wire [DEPTH_POWER:0]     bin_next_read, gray_next_read;
wire                     full_condition, empty_condition;
reg [DATA_WIDTH-1:0]     memory [0:(1<<depth_power addr_write="bin_counter_write[DEPTH_POWER-1:0];" address_diff="" always="" assign="" async_reset="" begin="" bin_counter_read="" bin_counter_write="" bin_next_write="bin_counter_write" dans="" data_in="" data_out="memory[addr_read];" des="" donn="" du="" else="" end="" g="" gray_next_write="bin_next_write" gray_pointer_read="" gray_pointer_write="" if="" la="" m="" memory="" negedge="" or="" pointeurs="" read_clock="" reg="" signal="" sortie="" sync_read_ptr1="" sync_read_ptr2="" sync_write_ptr1="" sync_write_ptr2="" synchronisation="" tampon="" write_clock="" write_enable="">> 1);
assign full_condition = (gray_next_write == {~sync_write_ptr2[DEPTH_POWER:DEPTH_POWER-1], sync_write_ptr2[DEPTH_POWER-2:0]});

always @(posedge write_clock or negedge async_reset) begin
    if (!async_reset) begin
        bin_counter_write <= 0;
        gray_pointer_write <= 0;
    end else begin
        bin_counter_write <= bin_next_write;
        gray_pointer_write <= gray_next_write;
    end
end

always @(posedge write_clock or negedge async_reset) begin
    if (!async_reset) begin
        full_flag <= 0;
    end else begin
        full_flag <= full_condition;
    end
end

// Génération du signal "vide"
assign addr_read = bin_counter_read[DEPTH_POWER-1:0];
assign bin_next_read = bin_counter_read + (!empty_flag && read_enable);
assign gray_next_read = bin_next_read ^ (bin_next_read >> 1);
assign empty_condition = (gray_next_read == sync_read_ptr2);

always @(posedge read_clock or negedge async_reset) begin
    if (!async_reset) begin
        bin_counter_read <= 0;
        gray_pointer_read <= 0;
    end else begin
        bin_counter_read <= bin_next_read;
        gray_pointer_read <= gray_next_read;
    end
end

always @(posedge read_clock or negedge async_reset) begin
    if (!async_reset) begin
        empty_flag <= 1;
    end else begin
        empty_flag <= empty_condition;
    end
end

// Calcul des compteurs
always @(*) begin
    if (!async_reset) begin
        address_diff = 0;
    end else if (addr_write > addr_read) begin
        address_diff = addr_write - addr_read;
    end else begin
        address_diff = (1 << (DEPTH_POWER+1)) - addr_read + addr_write;
    end
end

always @(posedge write_clock or negedge async_reset) begin
    if (!async_reset) begin
        write_count <= 0;
    end else begin
        write_count <= address_diff[DEPTH_POWER-1:0];
    end
end

always @(posedge read_clock or negedge async_reset) begin
    if (!async_reset) begin
        read_count <= 0;
    end else begin
        read_count <= address_diff[DEPTH_POWER-1:0];
    end
end

endmodule </depth_power>


</div>Le fichier de test ci-dessous simule le comportement de la FIFO asynchrone avec des horloges indépendantes et des scénarios d'écriture/lecture. Les signaux de contrôle sont générés pour tester les états plein, vide, et les opérations simultanées. Le compteur de test définit des périodes d'écriture seule, suivies de lecture, puis de lecture/écriture concurrentes pour valider la robustesse de la conception.

<div>```

// Banc de test pour FIFO asynchrone
`timescale 1ns/1ps
module Async_FIFO_Testbench();

    // Signaux de test
    reg clk_write, clk_read, reset_n;
    reg [7:0] data_to_write;
    reg wr_req, rd_req;

    // Génération des horloges et réinitialisation
    initial begin
        reset_n = 0;
        #2000 reset_n = 1;
    end

    initial begin
        clk_write = 0;
        clk_read = 0;
    end

    always #5 clk_write = ~clk_write;
    always #10 clk_read = ~clk_read;

    // Instance du module FIFO
    Async_FIFO_Module #(
        .DATA_WIDTH(8),
        .DEPTH_POWER(4)
    ) fifo_inst (
        .async_reset(reset_n),
        .write_clock(clk_write),
        .write_enable(wr_req),
        .data_in(data_to_write),
        .full_flag(),
        .write_count(),
        .read_clock(clk_read),
        .read_enable(rd_req),
        .data_out(),
        .empty_flag(),
        .read_count()
    );

    // Logique de contrôle des écritures
    reg [10:0] counter_write;
    always @(posedge clk_write or negedge reset_n) begin
        if (!reset_n) begin
            counter_write <= 0;
        end else if (counter_write == 2000) begin
            counter_write <= 0;
        end else begin
            counter_write <= counter_write + 1;
        end
    end

    always @(posedge clk_write or negedge reset_n) begin
        if (!reset_n) begin
            data_to_write <= 0;
            wr_req <= 0;
        end else if ((counter_write >= 1 && counter_write <= 21) || (counter_write >= 1000 && counter_write <= 1090)) begin
            data_to_write <= counter_write[7:0];
            wr_req <= 1;
        end else begin
            data_to_write <= 0;
            wr_req <= 0;
        end
    end

    // Logique de contrôle des lectures
    always @(posedge clk_read or negedge reset_n) begin
        if (!reset_n) begin
            rd_req <= 0;
        end else if (counter_write[9]) begin
            rd_req <= 1;
        end else begin
            rd_req <= 0;
        end
    end
endmodule

Étiquettes: Verilog FIFO FPGA conception numérique code Gray

Publié le 10 juillet à 20h23