Dantist2k17 0 26 июля, 2019 Опубликовано 26 июля, 2019 · Жалоба Приветствую, рассчитываю получить компетентную оценку представленного 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 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Nieve 0 26 июля, 2019 Опубликовано 26 июля, 2019 · Жалоба "частоты приблизительно равны, но асинхронны" - это уже накладывает ограничения по применению, это неправильно. FIFO должна корректно работать при абсолютно разных частотах. Зачем вообще понадобились "error" регистры? Ваша задача не в том чтобы ошибку найти, а в том, чтобы корректно записывать данные в буффер. Из любопытства: что будет если например сработал этот регистр? Смотрю с телефона, поэтому код не удобно читать. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Dantist2k17 0 26 июля, 2019 Опубликовано 26 июля, 2019 · Жалоба 8 minutes ago, Nieve said: "частоты приблизительно равны, но асинхронны" - это уже накладывает ограничения по применению, это неправильно. FIFO должна корректно работать при абсолютно разных частотах. Зачем вообще понадобились "error" регистры? Ваша задача не в том чтобы ошибку найти, а в том, чтобы корректно записывать данные в буффер. Из любопытства: что будет если например сработал этот регистр? Смотрю с телефона, поэтому код не удобно читать. Разумеется что накладывает ограничения, ограничения сознательны. Для начала решил сделать так, следующий вариант уже будет отвязан от соотношения частот. Зачем "error" регистры? Для подстраховки, строгой нужды в них нет, просто пришла в голову идея их реализации вот и сделал. По-поводу задачи я согласен, корректная запись данных, а точнее корректное формирование флагов fifo и empty обеспечивается самой реализацией (логикой ее работы) с использованием кода Грея. Таким образом, способ реализации отвечает за то чтобы fifo работало корректно, а регистры error сигнализируют о том, что реализация не отработала (например под воздействием условий работы: изменение напряжения питания, температуры и т.д.), а следовательно данные нельзя считать достоверными. Что будет если найдена ошибка, это уже решать пользователю fifo, например не воспринимать только что прочитанные данные и подать сигнал сброса на fifo. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RobFPGA 27 26 июля, 2019 Опубликовано 26 июля, 2019 · Жалоба Приветствую! Вы немного не понимаете сути для чего 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. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Dantist2k17 0 26 июля, 2019 Опубликовано 26 июля, 2019 · Жалоба 14 minutes ago, RobFPGA said: Приветствую! Вы немного не понимаете сути для чего grey используется - это лишь упрощает борьбу с метастабильность при распространении сигнала (инкрементального) ко входу регистров приемника. НО ни как не гарантирует что код в регистре на приёмной стороне будет меняться исключительно в одном бите. Это зависит только от соотношения частот на передающей и приемной стороне. Отсюда вытекает что наличие контроля такой ситуации смысла не имеет. Соглашусь, что не гарантирует, но почему же контроль не имеет смысла? Если я знаю, что отличие должно быть только в одном бите, то проверяя этот факт, имею право сделать вывод о возникновении сбоя... Мне видится вполне логичным. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RobFPGA 27 26 июля, 2019 Опубликовано 26 июля, 2019 · Жалоба Приветствую! 7 minutes ago, Dantist2k17 said: Если я знаю, что отличие должно быть только в одном бите, то проверяя этот факт, имею право сделать вывод о возникновении сбоя... Мне видится вполне логичным. Будет ли такая ситуация (изменились 2 бита) ошибкой самого FIFO? Удачи! Rob. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Dantist2k17 0 26 июля, 2019 Опубликовано 26 июля, 2019 · Жалоба 9 minutes ago, RobFPGA said: Приветствую! Будет ли такая ситуация (изменились 2 бита) ошибкой самого FIFO? Удачи! Rob. Не понял вопроса. Сами данные в FIFO никак не пострадают от этого, пострадает логика формирования флагов full и empty. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RobFPGA 27 26 июля, 2019 Опубликовано 26 июля, 2019 · Жалоба Приветствую! Если изменение 2 бит не является ошибкой (логики работы флагов) FIFO при большом соотношении частот, то почему это будет ошибкой и при малом соотношении? Удачи! Rob. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Dantist2k17 0 26 августа, 2019 Опубликовано 26 августа, 2019 · Жалоба 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 нельзя считать достоверными... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RobFPGA 27 26 августа, 2019 Опубликовано 26 августа, 2019 · Жалоба Приветствую! 21 minutes ago, Dantist2k17 said: Вернулся к работе и впал в ступор. Вы не первый "камешек" застывший на краю обрыва понимания при созерцании gray перехода в FIFO Что же вас останавливает? Процитирую себя же On 7/26/2019 at 3:14 PM, RobFPGA said: ... это лишь упрощает борьбу с метастабильностью при распространении сигнала (инкрементального) ко входу регистров приемника ... Обратите внимание на "инкрементального" - может это подтолкнет вас ... Удачи! Rob. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Dantist2k17 0 26 августа, 2019 Опубликовано 26 августа, 2019 · Жалоба 11 minutes ago, RobFPGA said: Приветствую! Вы не первый "камешек" застывший на краю обрыва понимания при созерцании gray перехода в FIFO Что же вас останавливает? Процитирую себя же Обратите внимание на "инкрементального" - может это подтолкнет вас ... Удачи! Rob. Так я и и оперся на то, что сигнал инкрементальный, оперся при обнаружения факта ошибки распространения сигнала. Ошибка есть следствие метастабильности. Или ваша мысль в том, что код Грея лишь снижает вероятность появления ошибки, по той причине что только один бит переключается, а анализировать "историю" переключений особого смысла не имеет? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RobFPGA 27 26 августа, 2019 Опубликовано 26 августа, 2019 · Жалоба Приветствую! 2 minutes ago, Dantist2k17 said: Или ваша мысль в том, что код Грея лишь снижает вероятность появления ошибки, по той причине что только один бит переключается, а анализировать "историю" переключений особого смысла не имеет? Увы это не моя мысль Но так и есть - применение gray code не просто снижает вероятность, а гарантирует что для инкрементального входа возможная ошибка из-за метастабильности не приводить к катастрофическим последствиям для логики FIFO (код на приемной стороне будет меняться только в правильном направлении). Но это никак не гарантирует что код на приемной стороне будет меняться только в одном бите! Удачи! Rob. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dvlwork 0 26 августа, 2019 Опубликовано 26 августа, 2019 · Жалоба 26 минут назад, Dantist2k17 сказал: Ошибка есть следствие метастабильности. Рассматривайте чтение значения со счетчика Грея, как текущее или предыдущее вследствие метастабильности. А предыдущее и текущее могут различаться более чем на единицу. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Viktuar 0 26 августа, 2019 Опубликовано 26 августа, 2019 · Жалоба RobFPGA имеет ввиду, что если выходная частота ниже входной, то за один такт выходной частоты в fifo могут влезть несколько слов и, соответственно, в коде Грея, защелкнутом выходной частотой, изменится сразу несколько бит. Но в этом нет ничего страшного для управляющей логики(флагов), не нужно просто такую ситуацию интерпретировать как ошибку. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Dantist2k17 0 27 августа, 2019 Опубликовано 27 августа, 2019 (изменено) · Жалоба 14 hours ago, Viktuar said: RobFPGA имеет ввиду, что если выходная частота ниже входной, то за один такт выходной частоты в fifo могут влезть несколько слов и, соответственно, в коде Грея, защелкнутом выходной частотой, изменится сразу несколько бит. Но в этом нет ничего страшного для управляющей логики(флагов), не нужно просто такую ситуацию интерпретировать как ошибку. И правда, спасибо. Какие еще общепризнанные методики (кроме использования кода Грея) используются при реализации FIFO? В Google не забанили, просто по запросу dual clock fifo ничего другого найти не удалось. Изменено 27 августа, 2019 пользователем Dantist2k17 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться