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

Xilinx FIFO Generator (v13.2), некорретное поведение в симуляторе

Столкнулся с неприятностью, где не ожидал. Имею сгенерённую корку Independent Clock Block RAM FIFO, упрощена до предела - 8 бит данных на входе, 8 на выходе. На wr_en (порт записи данных) поступает сигнал:

 

assign wr_en = !full && data_valid; // data_valid поступает синхронно с данными с внешнего мира (из тестбенча).

 

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

 

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

 

assign wr_en = #0.1ns !full && data_valid;

 

то тоже начинает показывать правильную временную диаграмму. Исследование показало, что во всех этих случаях, если смотреть в режиме delta cycles (в квесте), то количество дельтациклов разное. Например, в исходном случае их на сигнале wr_en +11, в случае, если его пропустить через регистр, то +8. Получается, там внутри модели какие-то гонки и отсюда и такое поведение. 

 

В общем, хочется понять, как быть. Конечно, самое простое оставить транспортную задержку в 100 пс, но это пахнет костылями. А хочется сделать нормально. Прошу высказываться.

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


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

я третий день сижу с этой проблемой. Но в альтере. По аналогии wr_en был очень сложный и не успевал временами (слак). Но мне об этом честно сказал Timing Analyzer сразу. Ну и ширина данных у меня 64 бита. Перелопатил всю логику.
Мне кажется у вас нечто похожее, стоит поменять размещение wr_en, поведение меняется.

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


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

Я рассматриваю сугубо функциональную симуляцию, тут нет никаких зависимостей от ширины данных и частот - это логическое моделирование, не временнОе. 

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


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

Just now, dxp said:

Я рассматриваю сугубо функциональную симуляцию

да действительно, не вчитался в название, прошу прощение.

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


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

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

Тут интересно бы знать что конкретно глючит?   На последнем слове нет записи в память но при этом увеличивает указатель и счетчик? Или вообще нет записи? Или пишет 0, или еще как?  

FIFO у Xilinx до сих пор на VHDL значит могут быть глюки/гонки в самом симуляторе на стыке verilog/VHDL или в непосредственно в шедулере сима. У меня было похожее (правда довольно давно уже) - поведение сима менялось от порядка  расположения always блоков в исходнике.  :wacko:  И  вроде было (опять же давно и точно не помню) похожая ситуация с при передаче клока в нижний модуль через несколько промежуточных assign.  

Какая версия сима? Корка из ISE или  Vivado?

Удачи! Rob.

 

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


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

Подробности такие.

 

Инструментарий: Vivado 2018.2, Questasim 10.4e.

 

Исходно у меня в проекте используется вариант с даунсайзом 132:66. Поведение было такое: записываю два слова (по 132 бита), на выходе читаю 4 (по 66), первые два нули, вторые два соответствуют второму 132-разрядному входному. Т.е. как будто первое слово по данным (но не по логике прохождения данных) потерялось. Чтение осуществляется, естественно, по признаку !empty. 

 

Далее последовало изрядное количество экспериментов. Сначала перегенерил корку с портами wr_data_count, rd_data_count. На wr_data_count всегда присутствует ненулевое значение, т.е. если FIFO пустое, то там торчит 1. На rd_data_count при этом честный 0. Запись и чтение показывают, что значения этих сигналов корректны (если считать, что 1 на wr_data_count верна, а это скорее всего так - у правильно работающих вариантов (см. ниже) это всё точно так же). Логика работы full/empty флагов тоже правильная.

 

Затем я просто клонировал FIFO - создал ещё инстанс этой же корки и подал туда сигналы с простого генератора (счётчика). Вижу, то работает правильно. Дальше стал выяснять, по какому же сигналу возникает проблема. Менял местами данные между боевой и тестовой коркой, логика работы не поменялась (т.е. боевая работала так же неправильно, а тестовая - правильно), значит дело не в данных. Потом поменял местами сигналы wr_en. И вот тут оно и проявилось. Т.е. проблема именно в wr_en. Разница между этими сигналами для корок в том, что в боевом варианте этот сигнал формируется на комбинаторной логике (пробовал assign и always_comb, разницы между ними, в общем, нет, что логично), а для тестовой - на флопе. Когда я пропустил тот комбинаторный сигнал через флоп, то правильность работы FIFO восстановилось (естественно, логика работы при этом нарушается из-за сдвига на такт, но в данном случае это не важно). 

 

Всё это выглядит странным - ведь что сигнал с комбинаторной логики, что с флопа поступают одновременно, т.е. при функциональном логическом моделировании все сигналы вычисляются за модельное время 0, и на симуляторе они рисуются в один и тот же момент времени. Очевидно, что несмотря на модельное время 0 в реальности там сигналы вычисляются не одновременно, а в зависимости от сложности логики - это можно увидеть в режиме просмотра дельтациклов. И действительно, например, сигнал с флопа появлялся в том случае после фронта клока через +8 дельтациклов, а с комбинаторной логики через +11.

 

Потом я пробовал менять логику вычисления wr_en, это приводило к изменению результатов по данным на выходе - например, был вариант, когда первое слово выходит правильно, второе - половина текущего, половина следующего, третье - ноль. При этом, опять же, логика работы full/empty (и wr/rd_data_count) всегда правильные, т.е. портятся только данные. Отличия порождаются, полагаю, из-за того, что при изменении логики вычисления wr_en меняется и количество дельтациклов.

 

В процессе экспериментов упростил FIFO до варианта без даунсайза и ширины данных в 8 бит, а логику wr_en до простейшего !full && data_valid (куда уж проще - это минимальная логика работы с FIFO по записи - простой хендшейк), и эффект порчи данных не ушёл. Просто в этом конкретном случае ломается последнее слово - это частный случай всего бесконечного разнообразия вариантов, возникающих при наложении сигналов с разными дельтациклами. Ведь действительно - не может же FIFO "знать", что слово в потоке последнее, значит, это последнее слово чем-то отличается от остальных. И действительно, анализ дельтациклов показал, что у всех слов wr_en  имеет +10, а у последнего +5. Причина в том, что в последнем слове меняется условие формирования data_valid (оно становится не valid).

 

Такова общая картина. Резюме: во всех случаях портятся только данные, логика работы FIFO правильная - количество слов и флаги full/empty правильные. Обратил внимание, что, например, сигнал empty вываливается наружу с задержкой 0.1 нс относительно своего клока. Оную задержку я увидел внутри файла модели fifo_generator_vlog_beh.v (https://github.com/Digilent/Genesys-2-HDMI/blob/master/src/bd/system/ipshared/564d/simulation/fifo_generator_vlog_beh.v) в виде макроса TCQ 100ps. Там внутри куча сигналов, задержанных на эту величину. Зачем они это сделали, не очень ясно, похоже, что какой-то древний легаси код тащат с лохматых времён. Интернет полон возмущённых криков по этому поводу. Ведь в самом деле, зачем на функциональной модели, которая и так должна работать правильно в логике симуляторов, ещё добавлять задержки, имитирующие Tco флопов. 

 

Да, модель, кстати, на верилоге, т.е. тут вряд ли проблема на стыке VHDL<->Verilog. А по поведению с ошибкой данных это похоже как бы на нарушения по холду - и лечится соответственно - введением задержки. 

 

Собственно, меня интересуют ответы на следующие вопросы:

 

1. Это ошибка в модели или это я что-то делаю не так?

 

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

 

3. Как поступить? Хочется сделать правильно. Пока вижу путь добавить задержку в логику формирования wr_en, но это похоже на костыли, а костылей не хочется. К тому же, вот тут я могу пофиксить проблему, но где гарантия, что аналогичное не вылезет на других сигналах корки?

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


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

В 01.03.2019 в 22:31, RobFPGA сказал:

FIFO у Xilinx до сих пор на VHDL значит могут быть глюки/гонки в самом симуляторе на стыке verilog/VHDL или в непосредственно в шедулере сима.

Кстати, да, в директории synth исходников корки таки лежит VHDL'ный файл. Но связи с поведенческим моделированием не просматривается на первый взгляд.

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


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

Странно... В vivado 2017.4 использовал этот блок у себя, симулировал, правда в вивадовом симуляторе, - все сходу взлетело, никаких проблем не было. Скомпилировал в железе - ведет себя точь-в-точь как в симуляторе, все нормально.

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


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

4 часа назад, dxp сказал:

 

Если не сложно скиньте мини-проект. Попробую запустить симуляцю в Modelsim с библиотеками, скопмилированными Vivado 17.4. Может разработчики в библиотеках накосячили для Vivado 18.2

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


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

Был печальный опыт с FIFO в Vivado 2017.3.

По сигналу ресет, при наличии определенной комбинации на других входах, FIFO зависал.

Все время показывал переполнение.

До этого с готовыми FIFO не было проблем ни в Quartus, ни в ISE.

После двухдневных мучений написал свой FIFO и забил на использование готового.

С тех пор готовым коркам от Xilinx я не доверяю. Стараюсь использовать сторонние или полностью свои.

d_c_fifo.sv

d_p_ram_rw.sv

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


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

On 3/2/2019 at 6:44 AM, dxp said:

1. Это ошибка в модели или это я что-то делаю не так?

2. Как такое вообще получается, ведь это не какой-то уникальный случай или эксклюзивный код - тут же простейшая логика работы с очень распространённым элементом цифрового дизайна - FIFO, ведь это по идее используется тысячами неужели мало кто из них на это налетает? Или они в массе не моделируют свои дизайны с двухклоковыми FIFO?

3. Как поступить? Хочется сделать правильно. Пока вижу путь добавить задержку в логику формирования wr_en, но это похоже на костыли, а костылей не хочется. К тому же, вот тут я могу пофиксить проблему, но где гарантия, что аналогичное не вылезет на других сигналах корки?

1.  Не всё так просто. Ошибок в модели нет. И Вы делаете всё правильно.

2. Кто виноват ?  На этот эффект натыкаются все, кто делает более-менее сложный проект. То, что Вам кажется одновременными событиями, для симулятора оказываются разными во времени. Причем это не баг или прихоть языка, а особенность вычислительного ядра. То есть на разных симуляторах один и тот же код будет выдавать РАЗНЫЕ результаты. Например меня в свое время потрясло различие Моделсим и Альдек Актив ХДЛ в моделировании одинакового "чистого" кода (без "костылей")

Яркий пример - простробировать некие данные непосредственно клоком и его копией после двух инверторов. А самая жесть - тот же клок, но через поворитель-буфер. По идее никакой разницы - а в реале второй клок сдвинут на несколько тех дельта-циклов (фемтосекунды?) и выход уже не тот.

3.Что делать?  Выкручиваются, кто как умеет. Я например, моделирую только временную (пост-лейаут) модель. Там уже все реальные задержки присутствуют и описанный эффект не проявляется. Если таки-надо промоделировать модуль до разводки, руками ставлю костыли в виде after 1nS после каждого присваивания.

4. Охи и ахи "да Вы не понимаете VHDL", "это костыли!" пропадают после первого серьезного фэйла "на модели всё работало, а в железе - фигвам".

5. Полное отрезвление наступает, когда проведешь достаточно много времени во временном моделировании - до тебя вдруг доходит, что система построена так, что все триггеры в проекте АВТОМАТОМ подстраиваются на соблюдение условий setup-hold  и что это достигается только при  WrEn <= !full && data_valid after 10 nS  если тактовая 100 МГц. Природу не обманешь.

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


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

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

On 3/2/2019 at 8:44 AM, dxp said:

Далее последовало изрядное количество экспериментов. Сначала перегенерил корку с портами wr_data_count, rd_data_count. На wr_data_count всегда присутствует ненулевое значение, т.е. если FIFO пустое, то там торчит 1 ...

Это уже подозрительно - вы reset правильно делаете для FIFO?  (вроде reset должен быть активный не менее 4-х тактов и wr_en не  должен быть активным при reset)

On 3/2/2019 at 8:44 AM, dxp said:

... Т.е. проблема именно в wr_en. Разница между этими сигналами для корок в том, что в боевом варианте этот сигнал формируется на комбинаторной логике (пробовал assign и always_comb, разницы между ними, в общем, нет, что логично), а для тестовой - на флопе. Когда я пропустил тот комбинаторный сигнал через флоп, то правильность работы FIFO восстановилось (естественно, логика работы при этом нарушается из-за сдвига на такт, но в данном случае это не важно). ...

Потом я пробовал менять логику вычисления wr_en, это приводило к изменению результатов по данным на выходе - например, был вариант, когда первое слово выходит правильно, второе - половина текущего, половина следующего, третье - ноль. При этом, опять же, логика работы full/empty (и wr/rd_data_count) всегда правильные, т.е. портятся только данные. Отличия порождаются, полагаю, из-за того, что при изменении логики вычисления wr_en меняется и количество дельтациклов.

Но при этом - сама модель FIFO не меняется! - а это, IMHO,  на  99%  говорит о том  что проблемы скорее с вашей логикой формирования wr_en или генерацией данных на входе FIFO.  Тут конечно желательно бы тестовый сим проект  посмотреть с воспроизводством проблемы. Чтобы не гадать на FIFOшной гуще. 

On 3/2/2019 at 8:44 AM, dxp said:

Ведь в самом деле, зачем на функциональной модели, которая и так должна работать правильно в логике симуляторов, ещё добавлять задержки, имитирующие Tco флопов. 

Это давняя проблема симуляторов -  так как параллельные события в модели симулируются последовательно.  Отсюда иногда возникают неоднозначности (особенно при 0-вом времени задержек при функ. симуляции) в очередности добавлении событий в очередь исполнения. В старых версиях симов это встречалось довольно часто поэтому и боролись с этим 1ps ... 1ns задержками при присвоении.

 

Удачи! Rob.

 

P.S.  К то муже  если вы посмотрите в саму модель fifo_generator_vlog_beh.v то увидите внутри все ту же комбинаторику  по входу: assign ram_wr_en  = WR_EN & !FULL;

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


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

Выражаю благодарность всем участникам этой темы. И мнения всегда интересны, и предложения наталкивают на новые "ветки" поиска. 

 

10 часов назад, RobFPGA сказал:

Но при этом - сама модель FIFO не меняется! - а это, IMHO,  на  99%  говорит о том  что проблемы скорее с вашей логикой формирования wr_en или генерацией данных на входе FIFO.

Вы как всегда правы! Нашёл причину, похоже! Как обычно, "сам дурак".

 

Таки дело в формировании сигнала wr_en (да и самых данных тоже). А дело было так. Внутри проекта у меня образовался непростой кусок (модуль), который было неудобно моделировать в общем окружении, поэтому я вытащил его в отдельный тестбенч. На модуль приходит AXI4 транзакция, и данные этой транзакции обрабатываются и упаковываются в буфер - то самое FIFO. В боевом проекте данные формируются синтезируемой частью (и там, кстати, такой пакости замечено не было, что теперь вполне понятно), а в тестовом проекте я написал другой генератор транзакций с использованием несинтезируемых конструкций, т.к. это проще и быстрее (а иначе зачем они вообще). И вот тут-то "собака и порылась". Транзакция генерится с помощью task:

 

Спойлер

task automatic axi_transaction(input logic [AXI_ADDR_W-1:0] addr, 
                               input int len);

    logic [AXI_BYTES_W-1:0] offset = addr[AXI_BYTES_W-1:0];
    int                     count = 0;
    logic [ AXI_ADDR_W-1:0] base_addr = addr & '1 << AXI_BYTES_W;
    wdata_t                 wdata = 0;

    // addr channel
    forever begin
        @(posedge clk) begin
            axi4_wr.AWADDR  <= addr;
            axi4_wr.AWLEN   <= get_axi_word_count(addr, len);
            axi4_wr.AWID    <= 1;
            axi4_wr.AWVALID <= 1;
            if(axi4_wr.AWREADY) begin
                break;
            end
        end
    end
    @(posedge clk) begin
        axi4_wr.AWVALID <= 0;
        axi_active      <= 1;
    end
    
    fork 
        begin
            // data
            while(count < axi4_wr.AWLEN)  begin
                @(posedge clk) begin
                    if(enable && axi4_wr.WREADY) begin
                        count++;
                    end
                end
            end
        end
        begin
            forever begin
                wdata = get_wdata(base_addr + count*AXI_BYTES, refmem);
                axi4_wr.WDATA <= wdata;
    
                if(count == 0) begin                              // the first beat
                    axi4_wr.WSTRB <= get_wstrb_first(addr, len);
                end
                else if(count == axi4_wr.AWLEN-1) begin           // the last beat
                    axi4_wr.WSTRB <= get_wstrb_last(addr, len);
                    axi4_wr.WLAST <= 1;
                end
                else begin
                    axi4_wr.WSTRB <= '1;
                    axi4_wr.WLAST <= 0;
                end
                @(posedge clk) ;
            end            
        end
    join_any
    axi_active <= 0;

endtask

 

В этом коде уже всё правильно. А было то же самое, но присваивания были блокирующими. А т.к. синхронизация идёт по событиям @(posedge ...), то и логику присваиваний нужно использовать соответствующую. С блокирующими присваиваниями происходили в полный рост гонки с комбинаторной логикой тестируемого модуля, что и выливалось в описанные глюки. Причину можно легко понять из алгоритма обработки событий виртуальной машиной симуляторе, который упрощено показан на картинке:

 

verilog_event_queue.jpg

 

 

Тут видно, что блокирующие присваивания обрабатываются как Active Events, а порядок их обработки не определён. Вот тут и гонки. 

 

Я же подогнался из-за стереотипного мышления - привязывал логику обработки к типам always блоков, а тут вроде их и нет. Но дело-то не в том, есть такие блоки или нет, а в том, как компилятор симулятора обрабатывает код и строит очереди событий. И если есть привязка к фронту клока, то и всё зависимые от него данные должны обрабатываться соответствующим образом вне зависимости от того, always блок там, task или что-то ещё. Надеюсь, этот эпизод поможет кому-нибудь (послужит уроком или освежит знания по теме).

 

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


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

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

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

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

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

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

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

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

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

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