Перейти к содержанию
    

Приветствую, рассчитываю получить компетентную оценку представленного 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

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 minutes ago, Nieve said:

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

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

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

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

 

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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Вы немного не понимаете сути для чего 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.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

14 minutes ago, RobFPGA said:

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

7 minutes ago, Dantist2k17 said:

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

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

Удачи! Rob.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 minutes ago, RobFPGA said:

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

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

Удачи! Rob.

 

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

Удачи! Rob.

  

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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 нельзя считать достоверными...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

21 minutes ago, Dantist2k17 said:

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

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

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

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

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

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

Удачи! Rob.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

11 minutes ago, RobFPGA said:

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

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

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

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

Удачи! Rob.

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

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

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

2 minutes ago, Dantist2k17 said:

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

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

Удачи! Rob.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

26 минут назад, Dantist2k17 сказал:

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

 

14 hours ago, Viktuar said:

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

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

 

 

 

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

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

Изменено пользователем Dantist2k17

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...