Jump to content

    

Dantist2k17

Участник
  • Content Count

    37
  • Joined

  • Last visited

Community Reputation

0 Обычный

About Dantist2k17

  • Rank
    Участник

Recent Profile Visitors

442 profile views
  1. Не понял вопроса. Сами данные в FIFO никак не пострадают от этого, пострадает логика формирования флагов full и empty.
  2. Соглашусь, что не гарантирует, но почему же контроль не имеет смысла? Если я знаю, что отличие должно быть только в одном бите, то проверяя этот факт, имею право сделать вывод о возникновении сбоя... Мне видится вполне логичным.
  3. Разумеется что накладывает ограничения, ограничения сознательны. Для начала решил сделать так, следующий вариант уже будет отвязан от соотношения частот. Зачем "error" регистры? Для подстраховки, строгой нужды в них нет, просто пришла в голову идея их реализации вот и сделал. По-поводу задачи я согласен, корректная запись данных, а точнее корректное формирование флагов fifo и empty обеспечивается самой реализацией (логикой ее работы) с использованием кода Грея. Таким образом, способ реализации отвечает за то чтобы fifo работало корректно, а регистры error сигнализируют о том, что реализация не отработала (например под воздействием условий работы: изменение напряжения питания, температуры и т.д.), а следовательно данные нельзя считать достоверными. Что будет если найдена ошибка, это уже решать пользователю fifo, например не воспринимать только что прочитанные данные и подать сигнал сброса на fifo.
  4. Приветствую, рассчитываю получить компетентную оценку представленного 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
  5. В моем случае была "свой атмосфера" схема описывалась на verilog, при этом разводилась руками. Была необходимость не плодить модули, а всегда обращаться к одному. О том, что нет информации по окружению пока не сделан полный синтез, я тогда по правде сказать и не задумывался.
  6. Насчет Genus не скажу, в DC делал следующим образом: #register_write current_design register_data characterize reg_dev_low current_design register_write compile_ultra -no_autoungroup #register_data current_design $top_level current_design mem characterize gen_reg[0].register_data current_design register_data set_dont_touch {reg_dev_low reg_dev_most} compile_ultra -no_autoungroup #MEM current_design mem set_dont_touch {gen_reg[*].register_data} compile_ultra -no_autoungroup #fdt current_design $top_level characterize gen_fdt[0].fdt current_design fdt compile_ultra -no_autoungroup #register_synch_negedge current_design $top_level current_design spi characterize synch_ncs current_design register_synch_negedge compile_ultra -no_autoungroup current_design $top_level set_dont_touch {spi/synch_ncs spi/synch_sclk gen_fdt[*].fdt mem} compile_ultra -no_autoungroup Т.е. изначально характеризуем модуль ссылаясь на любой instance, затем делаем этот модуль текущим дизайном и компилируем. А перед синтезом верхнего уровня ставим dont touch. В принципе это тоже самое что посоветовал Alex. Я делал это процессе, не напрягало. Однако, encounter вероятней всего выдаст сообщение о том что netlist не уникален.
  7. Спасибо за проделанную работу, на "кошках" как и вы проделал, все верно отрабатывает. Буду искать где собака порылась.
  8. Будет, но это никак не относится к синтезу в DC без clock tree.
  9. Я работаю в DC 2014.09. От частоты зависимости не наблюдаю, также как и от наличия multicycle_path (схема выглядит примерно одинаково). multicycle_path применился, расчет timing по пути, осуществляется на 4 периодах. Сейчас проделал тоже самое в DC 2010.12 все аналогично, чудес не бывает, видимо что-то делаю не так. Спасибо.
  10. Спасибо, такой подход имеет право на жизнь, однако подобными костылями не хочется пользоваться. Если не смогу разобраться то просто вынесу формирование функции в отдельный модуль при поведенческом описании. На синтез запускаю командой compile_ultra -no_autoungroup -no_boundary_optimization.
  11. multicycle_path по hold_time не указывал, речь идет про логический синтез без clock tree, анализа и оптимизации по времени удержания не происходит
  12. В данном случае без него никак. Если убрать multicycle то логическая схема получается абсолютно такой же как и при его наличии. Я могу заблуждаться, но не могу согласиться с какой-либо оптимизацией по hold time, так как на данном этапе синтеза нет никакого clock tree, идет оценка только по setup time. Логики по входу мало, но она не способна отработать на требуемой частоте за один период, задача из разряда "впихнуть невпихуемое". За недостаток спасибо, на этапе физ синтеза необходимо будет задавать ограничения и на hold. Как я понимаю, задача multicycle указать синтезатору места, в которых у логики есть больше одного такта на обработку, дабы он не занимался оптимизацией там, где это не столь критично (с учетом увеличенных запасов по времени, которые определяются схемотехникой/verilog описанием).
  13. Мне не нравится как он разложил, функционально все верно. Просто в данном случае я очень сильно зажат по быстродействию, и на путь от data_a до data есть multicycle_path которого хватает чтобы получить положительный slack, а по пути от flag до data всего один период, которого к сожалению не хватает при такой результирующей схеме, однако вполне будет хватать если схема будет в таком виде, как я ожидаю увидеть. Я разрабатываю под ASIC, синтез через DC. Добавить в код соответствующие примитивы конечно можно, но хотелось бы поизящней что ли. По-поводу (* keep =1 *) wire flag_f = flag[3] & function; не понимаю что вы подразумеваете под (* keep =1 *), добавлять в код промежуточный wire не пробовал. Спасибо за совет.
  14. Приветствую. Имеется verilog, в общих чертах выглядит следующим образом. Исходный код выкладывать не стал дабы не нагружать восприятие. reg [31:0] data_a; reg [31:0] data_b; reg [3:0] flag; //счетчие в коде 1 из N always@(posedge clk or negedge rst) begin if(!rst) flag <= 4'd1; else flag <= flag << 1 | flag[3]; end //данные обновляются раз в 4 такта, т.е. на функцию по факту отводится 4 периода тактовой частоты //о чем я сообщаю синтезатору посредством //set_multicycle_path 4 -end -setup -from {data_a[*]} -to {data[*]} //set_multicycle_path 4 -end -setup -from {data_b[*]} -to {data[*]} assign function = комбинационная функция от(data_a, data_b); always@(posedge clk or negedge rst) begin if(!rst) data <= 1'b0; else data <= data | flag[3] & function; end В результате синтеза получаем следующий путь. flag_reg_0_/C (FDP) flag_reg_0_/Q (FDP) U212/O (NOR3B3) U213/O (O21AI1) U214/O (AND2B2) U5/O (NAN3B1) U215/O (AND2B2) U216/O (OR2) data/D (FDC) Однако по пути от flag до data я планировал увидеть что-то вроде этого. flag_reg_0_/C (FDP) flag_reg_0_/Q (FDP) U212/O (AND2) U213/O (OR2) data/D (FDC) Как я понимаю синтезатор просто замешивает flag_reg в логику формирования функции в процессе оптимизации, вот только с какой целью, делал синтез на разных частотах, результат не меняется, наличие/отсутствие multicycle_path в sdc тоже влияния не оказывает. В данном случае принципиально, чтобы по данному пути было минимальное количество элементов. Пробовал записывать function предварительно в триггер, при этом изменив multicycle_path с 4 на 3, это исправило ситуацию, однако зачем ставить дополнительный триггер, если можно обойтись без него (теоретически). Но на практике это сделать не получается, что посоветуете?
  15. Да спасибо, дошел до этого, опробовал. Не понравилось. Поправил verilog описание и уложился в частоту. Однако ведь можно столкнуться с задачей, когда необходимо разложить по триггерам БОЛЬШОЙ дешифратор, который описан с помощью case. Переписать его как-то алгоритмически не представляется возможным. Как быть в такой ситуации, не сталкивались?