Jump to content

    

Покритикуйте FIFO

Приветствую, рассчитываю получить компетентную оценку представленного verilog описания асинхронного FIFO.

Интересы замечания к аппаратному исполнению, а также к "стилю" описания.

Что касается условий использования такого FIFO: подразумевается что частоты сигналов clk_wr и clk_rd приблизительно равны, но асинхронны. 

Сама по себе идея построения FIFO классическая, от себя добавил два способа обнаружения ошибок, которые могут иметь место при синхронизации.

Способ 1 основан на проверке утверждения о том, что "соседние" состояния счетчика в коде Грея отличаются на один бит, следовательно побитовое исключающее ИЛИ должно принимать значения равные степени двойки.

Способ 2 основан на проверке того, что разность "соседних" значений счетчика в бинарном коде должна быть равна единице.

Кто-нибудь практиковал подобные проверки? Имеет ли смысл тратить на это ресурсы?

`timescale 1ns/1ps
module fifo_tb;
    parameter WIDTH = 8;
    parameter DEPTH = 16;
    
    parameter real PER_clk_wr = 10.0;
    parameter real PER_clk_rd = 11.0;

    wire    [WIDTH-1:0] data_out;
    wire                fifo_empty;
    wire                fifo_full;
    reg     [WIDTH-1:0] data_in;
    reg                 write;
    reg                 read;
    reg                 clk_wr;
    reg                 clk_rd;
    reg                 reset;
    
    fifo    #(WIDTH,DEPTH)  UUT (.*);
    
    reg                 clk;
    reg                 double_read_write;
    
    initial begin
        data_in = '0;
        write   = 1'b0;
        read    = 1'b0;
        clk_wr  = 1'b0;
        clk_rd  = 1'b0;
        reset   = 1'b1;
        //
        clk     = 1'b0;
        double_read_write = 1'b0;
    end
    
    always
        #(PER_clk_wr/2) clk_wr <= ~clk_wr;
    
    always
        #(PER_clk_rd/2) clk_rd <= ~clk_rd;
        
    always
        #(PER_clk_rd >= PER_clk_wr ? PER_clk_rd/2 : PER_clk_wr/2) clk <= ~clk;
        
    initial begin
        repeat(3)   @(posedge clk);
            reset = 1'b0;
        //write data to fifo while !full
        repeat(DEPTH)   begin
            fifo_write;
        end
        wait(fifo_full);
        //read data from fifo while !empty
        repeat(DEPTH)   begin
            fifo_read;
        end
        wait(fifo_empty);
        //write data to fifo, partly
        repeat($urandom_range(DEPTH/2,DEPTH/3))   begin
            fifo_write;
        end
        //write data to fifo, partly
        repeat($urandom_range(DEPTH/3,DEPTH/4))   begin
            fifo_read;
        end
        //write data to fifo, partly
        repeat($urandom_range(DEPTH/2,DEPTH/3))   begin
            fifo_write;
        end
        //read data from fifo while !empty
        while(!fifo_empty) begin
            fifo_read;
            @(posedge clk);
        end
        //write data to fifo, partly
        repeat($urandom_range(DEPTH/2,DEPTH/3))   begin
            fifo_write;
        end
        //"at the same time" write/read
        double_read_write = 1'b1;
        repeat(10)   @(posedge clk);
        //read data from fifo while !empty
        while(!fifo_empty) begin
            fifo_read;
            @(posedge clk);
        end
        $finish;
    end

    task fifo_write;
        begin
            @(posedge clk_wr) begin
                write   <= 1'b1;
                data_in <= $random;
            end
            @(posedge clk_wr)  write <= 1'b0;
        end
    endtask
    
    task fifo_read;
        begin
            @(posedge clk_rd)  read <= 1'b1;
            @(posedge clk_rd)  read <= 1'b0;
        end
    endtask
    
    always@(posedge double_read_write)  fifo_read;
    always@(posedge double_read_write)  fifo_write;
endmodule

`timescale 1ns/1ps
module fifo
    #(
    parameter WIDTH = 8,
    parameter DEPTH = 128)
    (
    output reg  [WIDTH-1:0] data_out,
    output reg              fifo_empty,
    output reg              fifo_full,
    input       [WIDTH-1:0] data_in,
    input                   write,
    input                   read,
    input                   clk_wr,
    input                   clk_rd,
    input                   reset);
    
    reg [$clog2(DEPTH):0]   pointer_write_bin;
    reg [$clog2(DEPTH):0]   pointer_write_gray;
    reg [$clog2(DEPTH):0]   pointer_read_bin;
    reg [$clog2(DEPTH):0]   pointer_read_gray;
    reg [$clog2(DEPTH):0]   register_wr_rd      [2:0];
    reg [$clog2(DEPTH):0]   register_rd_wr      [2:0];
    reg [WIDTH-1:0]         mem                 [DEPTH-1:0];
    integer                 i;
    
    //pointer write
    always@(posedge clk_wr) begin
        if(reset)
            pointer_write_bin <= 0;
        else if(write & !fifo_full)
            pointer_write_bin <= pointer_write_bin + 1'b1;
    end
    always@(posedge clk_wr) begin
        if(reset)
            pointer_write_gray <= 0;
        else 
            pointer_write_gray <= (pointer_write_bin >> 1) ^  pointer_write_bin;
    end
    //pointer read
    always@(posedge clk_rd) begin
        if(reset)
            pointer_read_bin <= 0;
        else if(read & !fifo_empty)
            pointer_read_bin <= pointer_read_bin + 1'b1;
    end
    always@(posedge clk_rd) begin
        if(reset)
            pointer_read_gray <= 0;
        else 
            pointer_read_gray <= (pointer_read_bin >> 1) ^  pointer_read_bin;
    end
    //write and read register  
    always@(posedge clk_wr) begin
        if(write & !fifo_full)
            mem[pointer_write_bin[$clog2(DEPTH)-1:0]] <= data_in; 
    end
    always@(posedge clk_rd) begin
        if(reset)
            data_out <= 0;
        else if(read & !fifo_empty)
            data_out <= mem[pointer_read_bin[$clog2(DEPTH)-1:0]];
    end
    //transmit pointer through clock domain, value have code Gray
    always@(posedge clk_wr) begin
        if(reset)
            for(i = 0;i < 3;i = i + 1) begin
                register_rd_wr[i] <= 0;
            end
        else
            for(i = 0;i < 3;i = i + 1) begin
                if(i != 0)
                    register_rd_wr[i] <= register_rd_wr[i-1];
                else
                    register_rd_wr[i] <= pointer_read_gray;
            end
    end
    always@(posedge clk_rd) begin
        if(reset)
            for(i = 0;i < 3;i = i + 1) begin
                register_wr_rd[i] <= 0;
            end
        else
            for(i = 0;i < 3;i = i + 1) begin
                if(i != 0)
                    register_wr_rd[i] <= register_wr_rd[i-1];
                else
                    register_wr_rd[i] <= pointer_write_gray;
            end
    end
    //register flags full and empty
    always@(posedge clk_rd) begin
        if(reset)
            fifo_empty <= 1'b1;
        else begin
            if(read & (register_wr_rd[1] == (((pointer_read_bin + 1) >> 1) ^  (pointer_read_bin + 1 ))))
                fifo_empty <= 1'b1;
            else if(register_wr_rd[1] != ((pointer_read_bin >> 1) ^  pointer_read_bin))
                fifo_empty <= 1'b0;
        end
    end
    always@(posedge clk_wr) begin
        if(reset)
            fifo_full <= 1'b0;
        else begin
            if(write & ({~register_rd_wr[1][$clog2(DEPTH):$clog2(DEPTH)-1],register_rd_wr[1][$clog2(DEPTH)-2:0]} == (((pointer_write_bin + 1) >> 1) ^  (pointer_write_bin + 1))))
                fifo_full <= 1'b1;
            else if({~register_rd_wr[1][$clog2(DEPTH):$clog2(DEPTH)-1],register_rd_wr[1][$clog2(DEPTH)-2:0]} != (((pointer_write_bin) >> 1) ^  (pointer_write_bin)))
                fifo_full <= 1'b0;
        end
    end
    
    //Detect error, method 1
    reg                         error_writer_m1;
    reg                         error_reader_m1;
    reg     [$clog2(DEPTH):0]   register_compare_writer;
    reg     [$clog2(DEPTH):0]   register_compare_reader;
    integer                     k;
    always@(*) begin
        for(k = 0;k <= $clog2(DEPTH);k = k + 1) begin
            if((register_rd_wr[2] ^ register_rd_wr[1]) == 2**k)
                register_compare_writer[k] <= 1'b1;
            else
                register_compare_writer[k] <= 1'b0;
        end
    end
    always@(*) begin
        for(k = 0;k <= $clog2(DEPTH);k = k + 1) begin
            if((register_wr_rd[2] ^ register_wr_rd[1]) == 2**k)
                register_compare_reader[k] <= 1'b1;
            else
                register_compare_reader[k] <= 1'b0;
        end
    end
    always@(posedge clk_wr) begin
        if(reset)
            error_writer_m1 <= 1'b0;
        else if(register_rd_wr[2] != register_rd_wr[1])
            error_writer_m1 <= !(|register_compare_writer);
    end
    always@(posedge clk_rd) begin
        if(reset)
            error_reader_m1 <= 1'b0;
        else if(register_wr_rd[2] != register_wr_rd[1])
            error_reader_m1 <= !(|register_compare_reader);
    end
    
    //Detect error, method 2
    reg                         error_writer_m2;
    reg                         error_reader_m2;
    reg    [$clog2(DEPTH):0]    grey2bin_writer_old;
    reg    [$clog2(DEPTH):0]    grey2bin_writer_cur;
    reg    [$clog2(DEPTH):0]    grey2bin_reader_old;
    reg    [$clog2(DEPTH):0]    grey2bin_reader_cur;

    always@(*) begin
        for(k = 0; k <= $clog2(DEPTH);k = k + 1)
            grey2bin_writer_old[k] = ^(register_rd_wr[2] >> k);
    end
    always@(*) begin
        for(k = 0; k <= $clog2(DEPTH);k = k + 1)
            grey2bin_writer_cur[k] = ^(register_rd_wr[1] >> k);
    end
    always@(*) begin
        for(k = 0; k <= $clog2(DEPTH);k = k + 1)
            grey2bin_reader_old[k] = ^(register_wr_rd[2] >> k);
    end
    always@(*) begin
        for(k = 0; k <= $clog2(DEPTH);k = k + 1)
            grey2bin_reader_cur[k] = ^(register_wr_rd[1] >> k);
    end
    
    always@(posedge clk_wr) begin
        if(reset)
            error_writer_m2 <= 1'b0;
        else if(register_rd_wr[2] != register_rd_wr[1])
            error_writer_m2 <= (grey2bin_writer_old + 1) != grey2bin_writer_cur;
    
    end
    always@(posedge clk_rd) begin
        if(reset)
            error_reader_m2 <= 1'b0;
        else if(register_wr_rd[2] != register_wr_rd[1])
            error_reader_m2 <= (grey2bin_reader_old + 1) != grey2bin_reader_cur;
    end
endmodule

 

Share this post


Link to post
Share on other sites

"частоты приблизительно равны, но асинхронны" -  это уже накладывает ограничения по применению, это неправильно. FIFO должна корректно работать при абсолютно разных частотах.

Зачем вообще понадобились "error" регистры? Ваша задача не в том чтобы ошибку найти, а в том, чтобы корректно записывать данные в буффер. 

Из любопытства: что будет если например сработал этот регистр? Смотрю с телефона, поэтому код не удобно читать.

Share this post


Link to post
Share on other sites
8 minutes ago, Nieve said:

"частоты приблизительно равны, но асинхронны" -  это уже накладывает ограничения по применению, это неправильно. FIFO должна корректно работать при абсолютно разных частотах.

Зачем вообще понадобились "error" регистры? Ваша задача не в том чтобы ошибку найти, а в том, чтобы корректно записывать данные в буффер. 

Из любопытства: что будет если например сработал этот регистр? Смотрю с телефона, поэтому код не удобно читать.

Разумеется что накладывает ограничения, ограничения сознательны. Для начала решил сделать так, следующий вариант уже будет отвязан от соотношения частот.

 

Зачем "error" регистры? Для подстраховки, строгой нужды в них нет, просто пришла в голову идея их реализации вот и сделал. По-поводу задачи я согласен, корректная запись данных, а точнее корректное формирование флагов fifo и empty обеспечивается самой реализацией (логикой ее работы) с использованием кода Грея. Таким образом, способ реализации отвечает за то чтобы fifo работало корректно, а регистры error сигнализируют о том, что реализация не отработала (например под воздействием условий работы: изменение напряжения питания, температуры и т.д.), а следовательно данные нельзя считать достоверными.

 

Что будет если найдена ошибка, это уже решать пользователю fifo, например не воспринимать только что прочитанные данные и подать сигнал сброса на fifo.

Share this post


Link to post
Share on other sites

Приветствую!

Вы немного не понимаете сути для чего grey используется -  это лишь упрощает борьбу с метастабильность при распространении сигнала (инкрементального) ко входу регистров приемника.  НО ни как не гарантирует что  код в регистре на приёмной стороне будет меняться исключительно в одном бите.  Это зависит только от соотношения частот на передающей и приемной стороне. Отсюда вытекает что наличие контроля такой ситуации смысла не имеет.  

По стилю:  я бы выделял все вычисляемые константы в отдельные смысловые localparam, типа -

localparam WR_PTR_WH = $clog2(DEPTH) ;    

И избегал  использование  в операциях сравнения вычисляемых переменных, например тут:

... if(read & (register_wr_rd[1] == (((pointer_read_bin + 1) >> 1) ^ (pointer_read_bin + 1 ))))  ...

Это сильно портит времянку при синтезе.  Выгоднее для этого иметь второй/третий   pointer_*_bin который  уже на 1 и 2 больше чем основной.   

Так же gray переход  удобно выделить в отдельный модуль - сильно упростить задание constrains при синтезе. 

Удачи! Rob.

 

Share this post


Link to post
Share on other sites
14 minutes ago, RobFPGA said:

Приветствую!

Вы немного не понимаете сути для чего grey используется -  это лишь упрощает борьбу с метастабильность при распространении сигнала (инкрементального) ко входу регистров приемника.  НО ни как не гарантирует что  код в регистре на приёмной стороне будет меняться исключительно в одном бите.  Это зависит только от соотношения частот на передающей и приемной стороне. Отсюда вытекает что наличие контроля такой ситуации смысла не имеет.  

Соглашусь, что не гарантирует, но почему же контроль не имеет смысла? Если я знаю, что отличие должно быть только в одном бите, то проверяя этот факт, имею право сделать вывод о возникновении сбоя... Мне видится вполне логичным.

Share this post


Link to post
Share on other sites

Приветствую!

7 minutes ago, Dantist2k17 said:

Если я знаю, что отличие должно быть только в одном бите, то проверяя этот факт, имею право сделать вывод о возникновении сбоя... Мне видится вполне логичным.

Будет ли такая ситуация (изменились 2 бита) ошибкой самого FIFO? 

Удачи! Rob.

 

Share this post


Link to post
Share on other sites
9 minutes ago, RobFPGA said:

Приветствую!

Будет ли такая ситуация (изменились 2 бита) ошибкой самого FIFO? 

Удачи! Rob.

 

Не понял вопроса. 

Сами данные в FIFO никак не пострадают от этого, пострадает логика формирования флагов full и empty.

Share this post


Link to post
Share on other sites

Приветствую!

Если  изменение 2 бит не является ошибкой (логики работы флагов) FIFO при большом соотношении частот, то почему это будет  ошибкой и при малом соотношении?

Удачи! Rob.

  

Share this post


Link to post
Share on other sites
On 7/26/2019 at 5:00 PM, Dantist2k17 said:

Не понял вопроса. 

Сами данные в FIFO никак не пострадают от этого, пострадает логика формирования флагов full и empty.

On 7/26/2019 at 5:06 PM, RobFPGA said:

Приветствую!

Если  изменение 2 бит не является ошибкой (логики работы флагов) FIFO при большом соотношении частот, то почему это будет  ошибкой и при малом соотношении?

Удачи! Rob.

  

Вернулся к работе и впал в ступор.

Изменение 2 бит приведет к ошибочному формированию флагов FIFO, независимо от соотношения частот, ведь это фактически нарушение работы счетчика (чтения или записи) при синхронизации, следовательно флаги fifo нельзя считать достоверными...

Share this post


Link to post
Share on other sites

Приветствую!

21 minutes ago, Dantist2k17 said:

Вернулся к работе и впал в ступор.

Вы не первый "камешек" застывший на краю обрыва понимания при созерцании gray перехода в FIFO :wacko2: Что же вас останавливает? 

Процитирую себя же  

On 7/26/2019 at 3:14 PM, RobFPGA said:

... это лишь упрощает борьбу с метастабильностью при распространении сигнала (инкрементального) ко входу регистров приемника ...

Обратите внимание на "инкрементального"  - может это подтолкнет вас ... :smile:

Удачи! Rob.

Share this post


Link to post
Share on other sites
11 minutes ago, RobFPGA said:

Приветствую!

Вы не первый "камешек" застывший на краю обрыва понимания при созерцании gray перехода в FIFO :wacko2: Что же вас останавливает? 

Процитирую себя же  

Обратите внимание на "инкрементального"  - может это подтолкнет вас ... :smile:

Удачи! Rob.

Так я и и оперся на то, что сигнал инкрементальный, оперся при обнаружения факта ошибки распространения сигнала. Ошибка есть следствие метастабильности.

Или ваша мысль в том, что код Грея лишь снижает вероятность появления ошибки, по той причине что только один бит переключается, а анализировать "историю" переключений особого смысла не имеет?

 

Share this post


Link to post
Share on other sites

Приветствую!

2 minutes ago, Dantist2k17 said:

Или ваша мысль в том, что код Грея лишь снижает вероятность появления ошибки, по той причине что только один бит переключается, а анализировать "историю" переключений особого смысла не имеет?

Увы  это не моя мысль :unknw: Но так и есть - применение gray code не просто снижает вероятность, а гарантирует  что для инкрементального входа  возможная ошибка из-за метастабильности не приводить к катастрофическим последствиям для логики FIFO (код на приемной стороне  будет меняться только в правильном направлении).  Но это никак не гарантирует что код на приемной стороне будет меняться только в одном бите!

Удачи! Rob.

Share this post


Link to post
Share on other sites
26 минут назад, Dantist2k17 сказал:

Ошибка есть следствие метастабильности.

Рассматривайте чтение значения со счетчика Грея, как текущее или предыдущее вследствие метастабильности. А предыдущее и текущее могут различаться более чем на единицу.

Share this post


Link to post
Share on other sites

RobFPGA имеет ввиду, что если выходная частота ниже входной, то за один такт выходной частоты в fifo могут влезть несколько слов и, соответственно, в коде Грея, защелкнутом выходной частотой, изменится сразу несколько бит. Но в этом нет ничего страшного для управляющей логики(флагов), не нужно просто такую ситуацию интерпретировать как ошибку.

Share this post


Link to post
Share on other sites

 

14 hours ago, Viktuar said:

RobFPGA имеет ввиду, что если выходная частота ниже входной, то за один такт выходной частоты в fifo могут влезть несколько слов и, соответственно, в коде Грея, защелкнутом выходной частотой, изменится сразу несколько бит. Но в этом нет ничего страшного для управляющей логики(флагов), не нужно просто такую ситуацию интерпретировать как ошибку.

И правда, спасибо.

 

 

 

Какие еще общепризнанные методики (кроме использования кода Грея) используются при реализации FIFO?

В Google не забанили, просто по запросу dual clock fifo ничего другого найти не удалось.

Edited by Dantist2k17

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this