В интернете существует большое количество статей про FIFO, и когда то мне попалась очень хорошая и толковая статья. К сожалению, сейчас я её не нашёл. Далее – мой личный опыт по созданию и применению компонента FIFO. Готовый элемент находится на Github в проекте fpga_components. Свой компонент потребовался по нескольким причинам:
- FIFO XIlinx не умеет работать в режиме ретрансмита – это главная причина
- FIFO Xilinx требует создания компонента с заданными параметрами – у нас развелось слишком много разных компонентов.
- FIFO Xilinx содержит ошибку – если приходит сигнал сброса одновременно с сигналом записи данных, то в FIFO застревает одно слово. Это мы конечно обошли, но всё равно неприятно.
Итак, что такое FIFO. В общем случае это двухпортовая память, два счётчика адреса и два автомата – для чтения и записи данных.
Одно из главных применений FIFO это перевод данных с одной тактовой частоты на другую. Этим определяется такая схема. При одной тактовой частоте на запись и чтение автоматы можно упростить.
Давайте рассмотрим внешние порты компонента FIFO:
component cl_fifo_m12 is
generic(
FIFO_WIDTH : in integer:=64; -- ширина FIFO
FIFO_SIZE : in integer:=4096; -- размер FIFO
FIFO_PAF : in integer:=16; -- уровень срабатывания флага PAF
FIFO_PAE : in integer:=544 -- уровень срабатывания флага PAE
);
port(
-- сброс
reset_p : in std_logic; -- 1 - сброс
-- запись
clk_wr : in std_logic; -- тактовая частота
data_in : in std_logic_vector( FIFO_WIDTH-1 downto 0 ); -- данные
data_en : in std_logic; -- 1 - запись в fifo
flag_wr : out bl_fifo_flag; -- флаги fifo, синхронно с clk_wr
cnt_wr : out std_logic_vector( 15 downto 0 ); -- счётчик слов
-- чтение
clk_rd : in std_logic; -- тактовая частота
data_out : out std_logic_vector( FIFO_WIDTH-1 downto 0 ); -- данные
data_rd : in std_logic:='0'; -- 1 - чтение из fifo, данные на втором такте
flag_rd : out bl_fifo_flag; -- флаги fifo, синхронно с clk_rd
cnt_rd : out std_logic_vector( 15 downto 0 ); -- счётчик слов
rt : in std_logic:='0'; -- 1 - переход на начало в произвольный момент
rt_mode : in std_logic:='0' -- 1 - переход на начало после чтения всего содержимого FIFO
);
end component;
Настройка компонента:
- FIFO_WIDTH – ширина FIFO, может быть любая.
- FIFO_SIZE – число слов в FIFO, это степень двойки, от 64 до 65536. Если нужен больший размер то надо делать составное FIFO.
- FIFO_PAF – уровень срабатывания флага почти полного FIFO.
- FIFO_PAE – уровень срабатывания флага почти пустого FIFO, о флагах будет дальше.
Названия портов вполне очевидные, несколько комментариев по флагам:
Флаги FIFO передаются типом bl_fifo_flag; Определение типа:
type bl_fifo_flag is record
ef : std_logic; -- 0 - FIFO пустое
pae : std_logic; -- 0 - FIFO почти пустое
hf : std_logic; -- 0 - FIFO заполнено наполовину
paf : std_logic; -- 0 - FIFO почти полное
ff : std_logic; -- 0 - FIFO полное
ovr : std_logic; -- 1 - запись в полное FIFO
und : std_logic; -- 1 - чтение из пустого FIFO
end record;
Обратите внимание, используется отрицательная логика. Узнали? Да, я ещё из тех динозавров кто работал с TTL на сериях 155, 533, 1533 и отдельными микросхемами FIFO. Так что эти флаги мне привычны, они были сделаны много лет назад и до сих пор используются.
Флаг ef – сигнализирует что FIFO пустое. Если ef=1, то из FIFO можно прочитать одно слово.
Флаг pae – сигнализирует, что FIFO почти пустое. На сколько почти определяет параметр FIFO_PAE. Если pae=1, то из FIFO можно прочитать не более чем FIFO_PAE слов.
Флаг hf – сигнализирует что FIFO заполнено наполовину.
Флаг paf – сигнализирует, что FIFO почти полное. На сколько почти определяет параметр FIFO_PAF. Если paf=1, то в FIFO можно записать не более чем FIFO_PAF слов
Флаг ff – FIFO полное. Если ff=0, то в FIFO записывать нельзя.
Флаг ovr – переполнение. Если ovr=1, то это значит что произошла запись в полное FIFO
Флаг und – underflow. Если und=1, то это значит что произошло чтение из пустого FIFO.
Вполне очевидно, что при записи в FIFO мы должны записать слово в двухпортовую память и увеличить счётчик записи. Или сначала увеличить, а потом записать. А при операции чтения надо зафиксировать данные на выходе и увеличить счётчик чтения. А вот дальше требуется решить следующие вопросы:
- Как определить что FIFO полное или не полное, т.е. можно ли в него записывать ?
- Как определить что FIFO пустое или не пустое? Т.е. можно ли из него читать ?
- Как правильно сформировать флаги PAE, PAF, HF ?
- Что такое число слов в FIFO ?
Вполне очевидно, что ответы на все эти вопросы в анализе счётчиков адреса для записи и чтения. Но эти счётчики работают на разных частотах. Вот здесь начинаются различия в реализациях. Я применил симметричную схему передачи значений счётчиков на другой тактовый домен. В результате получилось, что каждый из автоматов чтения и записи имеет значение своего счётчика и задержанное значение другого счётчика. Из этих значений автоматы формируют свои флаги и значение количества слов в FIFO. Это можно представить на структурной схеме:
Надо ясно понимать, что узел перетактирования (в проекте это компонент ctrl_retack_counter_m12) передаёт данные с задержкой на несколько тактов. Поэтому состояния FIFO также изменяются с задержкой. Например, если FIFO пустое и него записано одно слово, то флаг ef=1 появится с некоторой задержкой. Это же относится к выходам количества слов в FIFO. Например, если в пустое FIFO будет записано 16 слов, то в процессе записи выход cnt_wr будет принимать значения 0,1,2,3, … 16 (это если не производится чтение из FIFO), а вот выход cnt_rd будет принимать значения например такие: 0, 5, 8, 12, 16. Точный порядок будет зависеть от соотношения частот и не может быть предсказан. Это принципиальное свойство FIFO которое работает на разных частотах. Хотя в зависимости от схемы синхронизации могут быть различные нюансы.
Определение пустого и полного FIFO производится на анализе счётчиков адресов. Причём у меня есть два адреса для записи (текущий и следующий) и два адреса для чтения, также текущий и следующий. В компоненте cl_fifo_control_m12 это сигналы w_adr, w_next_adr и r_adr, r_next_adr; Соотношение адресов в различных состояниях представлено на рисунках ниже.
В исходном состоянии w_adr=0, r_adr=0, w_next_adr=1, r_next_adr=1. Если w_adr=r_adr, то FIFO пустое.
При записи слово данных записывается по адресу w_adr и адрес записи увеличивается.
Через несколько таков значение w_adr будет передано в w_adr_to_rd (перейдёт в тактовый домен clk_rd) и по факту не совпадения r_adr и w_adr_to_rd будет установлен флаг ef=1, т.е. из FIFO можно будет считать слово данных. Однако одно слово это мало, для получения высокой скорости передачи надо работать с блоком данных. И здесь требуется использовать флаг PAE. Когда в FIFO будет записано FIFO_PAE слов, будет установлен флаг pae=1 и можно будет прочитать сразу блок данных. Это основной режим работы с DMA каналом.
Если скорость записи больше чем скорость чтения, то адрес записи догонит адрес чтения:
В этом случае w_next_adr будет равен r_adr, а точнее r_adr_to_wr (мы можем сравнивать только значения на своём тактовом домене). Это означает, что FIFO полное и записывать дальше нельзя, что бы не испортить уже записанные данные. Надо отметить, что для подключения АЦП это обычная ситуация. У нас такой режим называется однократный сбор через FIFO. В этом режиме АЦП записывает данные на большой скорости в FIFO, а медленный процессор эти данные считывает. При этом мы знаем, что действительными будет только блок данных который соответствует размеру FIFO. Обычно на этот размер как раз и программируется канал DMA. После чтения данных FIFO сбрасывается и всё повторяется снова. Вот в этом режиме принципиально важно, что бы запись в полное FIFO не портила предыдущие данные.
Если требуется записывать данные блоками, то надо использовать флаг PAF. Если paf=1, то в FIFO можно записать FIFO_PAF слов.
Значения флагов PAE и PAF надо выбирать из требований DMA контроллера к которому подключено FIFO. Например, для PCI Express у нас используется блок данных размером 4 кБ. Это 256 слов по 128 разрядов. Размер флага PAE я устанавливаю в 272. Т.е. чуть больше чем 256. Это я делаю намеренно, что бы не допускать опустошения FIFO. Ну не доверяю я схемам формирования флагов.
А как производится определение количества слов в FIFO? Всё достаточно просто – из адреса записи надо вычесть адрес чтения. Адрес кратен степени 2, поэтому вычитание будет идти по модулю 2^N; Поскольку у нас есть две пары адресов, то у нас получится и два значения количества слов в одном FIFO (может это как то связано с квантовой механикой?).
Значения флагов PAE и HF (по чтению) формируются из r_cnt. Значения PAF и HF(по записи) формируются из w_cnt.
Основной причиной, по которой пришлось разрабатывать свой компонент FIFO, является потребность в реализации циклического режима для работы на ЦАП. В этом режиме производится запись блока данных, он может быть любого размера, разумеется не превышая размера FIFO. А затем начинается чтение, причём после выдачи последнего записанного слова сразу происходит переход на первое слово. Это позволяет подключить медленный процессор к быстрому ЦАП. Компонент FIFO имеет два входа для циклического режима. rt_mode=1 означает, что после выдачи последнего записанного слова надо перейти на нулевой адрес.
А вот вход rt нужен немного для другого. Наличие rt=1 позволяет перевести FIFO на нулевой адрес в произвольный момент времени. Иногда это у нас тоже используется.
В проекте fpga_components представлены два FIFO:
- cl_fifo_x64_v7
- cl_fifo_m12
cl_fifo_x64_v7 разработан и опубликован достаточно давно. Также он давно используется и доказал свою работоспособность. Он в качестве двухпортовой памяти использует компонент сформированный Core Generator. Для разных размеров FIFO требуются свои компоненты, например в каталоге fpga_components\src\fifo\fifo_v7\coregen находятся четыре компонента
- ctrl_dpram512x64_v7
- ctrl_dpram1024x64_v7
- ctrl_dpram8192x64_v7
- ctrl_dpram32768x64_v7
И это всё только для шины с шириной 64 разряда. Для других шин и других размеров требуются свои компоненты. Мы их потихоньку делали и к настоящему моменту у нас есть большая куча, с которой работать уже неудобно. Александр Капитанов ( capitanov ) обратил на это внимание и предложил элегантное решение — сделать полностью синтезируемое FIFO. Он это реализовал в своём проекте: github.com/capitanov/adc_configurator Компонент: ctrl_fifo_config. Основная идея в том, что бы применить вот такую конструкцию VHDL:
type RAM is array (integer range <>) of std_logic_vector(DATA_WIDTH-1 downto 0);
signal Mem : RAM (0 to DATA_DEPTH-1);
Это конструкция будет синтезирована в двухпортовую память. Идея красивая и в результате доработки cl_fifo_x64_v7 получилось FIFO cl_fifo_m12.
Недостаточно написать FIFO, надо ещё проверить его работу. Для проверки используется подход принятый при разработке PROTEQ, о котором можно прочитать в моей предыдущей статье.
Существует компонент tb_00 который имеет настраиваемые параметры.
component tb_00 is
generic(
max_time : in time:=100 us; -- максимальное время теста
period_wr : in time; -- период частоты записи
period_rd : in time; -- период частоты чтения
fifo_size : in integer; -- размер FIFO
FIFO_PAF : in integer; -- уровень срабатывания флага PAF
FIFO_PAE : in integer; -- уровень срабатывания флага PAE
max_fifo0_pkg : in integer -- число пакетов для приёма
);
end component;
Он позволяет проверить прохождение потока данных через FIFO при различных соотношениях тактовых частот и уровнях срабатывания флагов PAE и PAF. Также существуют компоненты тестовых случаев:
- tc_00_01 – проверят случай, когда скорость записи больше скорости чтения.
- tc_00_02 – а это когда скорость чтения больше чем скорость записи.
В результате формируется вот такой отчёт о запуске тестов:
Global fifo_12 TC log:
tc_00_01 PASSED
tc_00_02 PASSED
Конечно, для каждого теста сохраняется и свой отчёт.
Например такой:
# KERNEL: FIFO 0 - PKG= 1 6310 ns 0 ns ERROR: 0 SPEED: 0
# KERNEL: FIFO 0 - PKG= 2 12022 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 3 17734 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 4 23446 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 5 29158 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 6 34870 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 7 40582 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 8 46294 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 9 52006 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 10 57718 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 11 63430 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 12 69142 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 13 74854 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 14 80566 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 15 86278 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 16 91990 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 17 97702 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 18 103414 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 19 109126 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 20 114838 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 21 120550 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 22 126262 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 23 131974 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 24 137686 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 25 143398 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 26 149110 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 27 154822 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 28 160534 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 29 166246 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 30 171958 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 31 177670 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 32 183382 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 33 189094 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 34 194806 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 35 200518 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 36 206230 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 37 211942 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 38 217654 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 39 223366 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 40 229078 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 41 234790 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 42 240502 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 43 246214 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 44 251926 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 45 257638 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 46 263350 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 47 269062 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 48 274774 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 49 280486 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 50 286198 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 51 291910 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 52 297622 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 53 303334 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 54 309046 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 55 314758 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 56 320470 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 57 326182 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 58 331894 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 59 337606 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 60 343318 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 61 349030 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 62 354742 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 63 360454 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 64 366166 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 65 371878 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 66 377590 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 67 383302 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 68 389014 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 69 394726 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 70 400438 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 71 406150 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 72 411862 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 73 417574 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 74 423286 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 75 428998 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 76 434710 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 77 440422 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 78 446134 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 79 451846 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 80 457558 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 81 463270 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 82 468982 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 83 474694 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 84 480406 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 85 486118 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 86 491830 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 87 497542 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 88 503254 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 89 508966 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 90 514678 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 91 520390 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 92 526102 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 93 531814 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 94 537526 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 95 543238 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 96 548950 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 97 554662 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 98 560374 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG= 99 566086 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=100 571798 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=101 577510 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=102 583222 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=103 588934 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=104 594646 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=105 600358 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=106 606070 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=107 611782 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=108 617494 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=109 623206 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=110 628918 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=111 634630 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=112 640342 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=113 646054 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=114 651766 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=115 657478 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=116 663190 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=117 668902 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=118 674614 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=119 680326 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=120 686038 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=121 691750 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=122 697462 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=123 703174 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=124 708886 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=125 714598 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=126 720310 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=127 726022 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=128 731734 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=129 737446 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=130 743158 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=131 748870 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=132 754582 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=133 760294 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=134 766006 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=135 771718 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=136 777430 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=137 783142 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=138 788854 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=139 794566 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=140 800278 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=141 805990 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=142 811702 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=143 817414 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=144 823126 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=145 828838 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=146 834550 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=147 840262 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=148 845974 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=149 851686 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=150 857398 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=151 863110 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=152 868822 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=153 874534 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=154 880246 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=155 885958 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=156 891670 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=157 897382 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=158 903094 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=159 908806 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=160 914518 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=161 920230 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=162 925942 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=163 931654 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=164 937366 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=165 943078 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=166 948790 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=167 954502 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=168 960214 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=169 965926 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=170 971638 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=171 977350 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=172 983062 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=173 988774 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=174 994486 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=175 1000198 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=176 1005910 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=177 1011622 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=178 1017334 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=179 1023046 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=180 1028758 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=181 1034470 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=182 1040182 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=183 1045894 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=184 1051606 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=185 1057318 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=186 1063030 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=187 1068742 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=188 1074454 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=189 1080166 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=190 1085878 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=191 1091590 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=192 1097302 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=193 1103014 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=194 1108726 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=195 1114438 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=196 1120150 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=197 1125862 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=198 1131574 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=199 1137286 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=200 1142998 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=201 1148710 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=202 1154422 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=203 1160134 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=204 1165846 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=205 1171558 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=206 1177270 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=207 1182982 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=208 1188694 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=209 1194406 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=210 1200118 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=211 1205830 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=212 1211542 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=213 1217254 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=214 1222966 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=215 1228678 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=216 1234390 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=217 1240102 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=218 1245814 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=219 1251526 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=220 1257238 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=221 1262950 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=222 1268662 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=223 1274374 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=224 1280086 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=225 1285798 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=226 1291510 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=227 1297222 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=228 1302934 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=229 1308646 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=230 1314358 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=231 1320070 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=232 1325782 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=233 1331494 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=234 1337206 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=235 1342918 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=236 1348630 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=237 1354342 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=238 1360054 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=239 1365766 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=240 1371478 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=241 1377190 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=242 1382902 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=243 1388614 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=244 1394326 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=245 1400038 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=246 1405750 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=247 1411462 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=248 1417174 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=249 1422886 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=250 1428598 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=251 1434310 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=252 1440022 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=253 1445734 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=254 1451446 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=255 1457158 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: FIFO 0 - PKG=256 1462870 ns 5712 ns ERROR: 0 SPEED: 1368
# KERNEL: Завершён приём данных: 1463200 ns
# KERNEL: FIFO 0
# KERNEL: Принято пакетов: 256
# KERNEL: Правильных: 256
# KERNEL: Ошибочных: 0
# KERNEL: Общее число ошибок: 0
# KERNEL: Скорость передачи: 1368 МБайт/с
# KERNEL:
# KERNEL:
# KERNEL:
# KERNEL: TEST finished successfully
# KERNEL:
При необходимости тесты будут дополняться. Хочу обратить внимание, что для вывода текста в консоль я использую пакет PCK_FIO. Он резко упрощает вывод текста.
Например, вывод результатов выглядит так:
fprint( output, L, "Завершён приём данных: %r ns\n", fo(now) );
fprint( output, L, "FIFO 0 \n" );
fprint( output, L, " Принято пакетов: %d\n", fo( rx0_result.pkg_rd ) );
fprint( output, L, " Правильных: %d\n", fo( rx0_result.pkg_ok ) );
fprint( output, L, " Ошибочных: %d\n", fo( rx0_result.pkg_error ) );
fprint( output, L, " Общее число ошибок: %d\n", fo( rx0_result.total_error ) );
fprint( output, L, " Скорость передачи: %r МБайт/с\n\n", fo( integer(rx0_result.velocity) ) );
Это похоже на Си.
В итоге я считаю что получился элегантный компонент, достаточно удобный для практической работы.
Комментарии (41)
Daffodil
13.02.2017 12:13+1Рекомендую ознакомиться http://www.zimmerdesignservices.com/mydownloads/no_mans_land_20130328.pdf В некоторых случаях с большим clock skew даже правильно написанное асихронное фифо не будет правильно работать без timing constraints
bluetooth
13.02.2017 14:03+3Это память типа «первым вошёл-первым ушёл» (first input – first output).
Вообще-то «first in — first out».
Daffodil
13.02.2017 14:06А почему вы у себя в коде пишете:
flag2to1 <= flag2 after 1 ns when rising_edge( clk1 );
А если кому-то потребуется использовать ваше fifo в дизайне который работает на частоте больше 1 ГГц, оно нормально будет симулироваться?dsmv2014
13.02.2017 14:10Ну если на 1 ГГц то надо написать after 0.1 ns, или меньше.
Я всегда пишу after, так лучше видно сигналы на временной диаграмме и предохраняет от расхождения с работой в реальной аппаратуре.Daffodil
13.02.2017 14:21Эти задержки накапливаются. Где-то в другом месте кто-то может написать еще одно присвоение, потом еще.
В общем весь продуктовый RTL который я видел был написан без задержек. Если хочется увидеть какие-то «мнимые» задержки на временной диаграмме, досточно просто включить отображение дельта-циклов.capitanov
13.02.2017 14:26Мы пишем задержки только после синхронных присвоений под CLK, поэтому они не накапливаются. При следующем защелкивании — она снова будет 1нс. Про 1ГГц — верно подмечено, нужно весь код заменить на что-то типа 0.1нс. Поэтому я в своих проектах ввел переменную td, которой присваивается значение этой задержки.
nerudo
13.02.2017 17:15«Мы» это стандарт на кодирование в компании или просто чья-то светлая идея?
capitanov
13.02.2017 17:29-3Изначально — это «светлая идея», которая затем переросла в негласный стандарт компании.
К сожалению, он не всеми соблюдается, но мы стремимся к единообразию и соблюдению определенных правил написания кода. В свое время я написал такой документ, но сами понимаете — протащить его наверх и сделать это стандартом крайне непросто. Несмотря на то, что многие поддержали эту идею, а коллективные обсуждения по этому вопросу возникали неоднократно. :)
dsmv2014
13.02.2017 17:53Это всё на основе реального опыта. Было несколько случаев, когда результаты моделирования резко отличались от реальной работы. Разбор выявил узкое место. Это присваивание тактовых частот. Присваивание тактовых частот также производится с дельта-задержкой и это приводит к трудно обнаруживаемым проблемам. В итоге — проще написать aftger 1 ns.
Daffodil
13.02.2017 18:04+2Обычно для моделирования всяких clock-гейтов и прочей логики на клоках используются присвоения с immediate notification, тогда event скедулиться в текущий дельта-цикл. Не помню деталей VHDL, нужно спеку смотреть. Последнее время пользуюсь только SystemC.
В общем использование задержек в синтезируемом коде приводит к путтанице. Особенно если у вас в перемешку RTL с gate-level с timing аннотациями. В общем я за свою жизнь уже настродался с интеграцией кода вроде вашего, поэтому у меня на него больная реакция.dsmv2014
13.02.2017 18:18По стандарту языка конструкцию after синтезатор игнорирует. Так что никаких проблем нет.
Да, и моделированием после трассировки мы не занимаемся.
ilynxy
14.02.2017 01:04+2Шёл 2017 год. Люди изобретали двухклоковое фифо, открывая попутно для себя свойства кода Грея, допуская ошибки связанные с метастабильностью в цепях сброса и пересечении клоковых доменов.
Clock domain crossing: http://www.sunburst-design.com/papers/CummingsSNUG2008Boston_CDC.pdf
Async fifo design: http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf
Это не фундаментальные труды по схемотехнике и разработке цифровых схем, но дают представление о предмете.
И еще, боюсь показаться грубым или заносчивым, но скажите пожалуйста, что подвигает Вас писать статьи? Что решения описанные в этой, что в предыдущей статье содержат классические ошибки начинающих (начиная от неглубокой проработки теории и специфики задач и, вследствии, принятие и реализация несоответствующих схемных решений, щедро приправленых синдромом NIH). И все бы ничего, если бы это был студенческий энтузиазм, но в профиле ж написано «инженер-разработчик».dsmv2014
14.02.2017 11:56+1Век живи — век учись (правда всё равно не поможет)
При всех недостатках — у меня есть о чём рассказать и показать.
Хотя бы для организации дискуссии.ilynxy
14.02.2017 13:50Тогда нужно поддержать и «афтар пешы ещо» =).
Реально же много толковых людей, но они предпочитают «набигать и критиковать» (прямо как я), а вот написать грамотную и связную статью — выше сил.
nckma
Хм… а почему Вы не стали использовать код грея для передачи указателей фифо через домен? Я думал это стандартное, проверенное временем решение, которое используют все.
dsmv2014
Код Грея может спасти только при очень близких частотах. А вот при переходе от 250 МГц к 100 МГц он уже не поможет.
Daffodil
Поясните кейс с 250 / 100 МГц
dsmv2014
Счётчик на 250 МГц успеет насчитать несколько значений за один такт 100 МГц. И я не понимаю как здесь может помочь год Грея.
GolikovAndrey
Это не важно сколько он насчитает.
В любой момент времени вы всегда будите видеть либо текущее значение, либо прошлое. Только если у вас шина со значением настолько расползается по времени, что по одним битам успеют придти два значения пока по другим ползет одно. Но это нереальный случай.
А имея в любой момент времени либо старое либо новое значение, при это за счет счетчика грея отличающиеся только на 1 бит, вы не можете неправильно обработать заполнение фифо.
dsmv2014
| Только если у вас шина со значением настолько расползается по времени
При асинхронных сигналах тактовой частоты всегда будут ситуации когда разные разряды шины будут защёлкнуты в разное время. Т.е. в другом домене будет часть нового значения а часть старого. А часть вообще будет в метастабильном состоянии. И в этом случае год Грея не поможет.
Daffodil
Ваша реализация точно так же не дает никаких гарантий. В теории сдвиг фазы может превышать может превышать любое число тактовых периодов, если вы не написали constraint синтезатору. В общем реализация с Греем лучше т.к. требует меньше тактов на пересинхронизацию.
dsmv2014
Это не так. У меня есть компонент ctrl_retack_counter_m12 Он корректно переводит шину data_in на data_out за несколько тактов. В нём сначала фиксируется значение на data_in, взводится флаг и фиксируется значение на data_out. Далее всё повторяется.
Daffodil
ну смотрите, у вас есть вектор сигналов data который пишется на одном клоке, а читается на другом. Если вы не напишете constraint на этот сигнал, то разные сигналы в этом векторе могут прийти с разной задержкой. На низкой частоте в маленьком дизайне скорей всего все будет правильно работать. Но на высоких частотах задержка может превышать несколько тактов и вы рискуете получить неправильное значение. Я в другом комменте скинул ссылку на статью про этот кейс. Т.е. constraint нужно писать всегда. А раз пишем constraint, то проще делать на грее, т.к. его можно пересинхронизировать обычным двойным флопом
dsmv2014
Теперь я понял что вы опасаетесь. Но смотрите что происходит.
Автомат pr_st1 фиксирует data и взводит flag1
flag1 переводится в другой домен — flag1to2
flag1to2 обнаруживается автоматом pr_st2
автомат pr_st2 переходит в состояние s2 и фиксирует данные на выходе.
Т.е. есть как минимум два такта на прохождение данных от data к data_out.
Хотя для высоких частот это может быть стать проблемой.
Спасибо за указание на узкое место.
P.S. Но код Грея всё равно не спасёт. Или здесь я тоже не прав?
nckma
Хм… а по моему, при передаче кодом грея в метастабильном состоянии может оказаться только один бит, тот который в данный момент меняется. Остальные же изменились раньше и всегда верные.
GolikovAndrey
Я не спорю как будет:) я знаю ответ:).
Код грея делает так что одно значение от другого будет отличаться ровно 1 битом. В любой момент времени вы можете получить либо новое, либо старое значение. Даже в случае мета-стабильности у вас в итоге триггер свалиться к новому или старому значению и это всегда даст правильный ответ.
Вам не нужно обеспечить чтобы счетчик фифо из одного домена в другом менялся последовательно, он может перепрыгивать через значение. Вам важно обеспечить чтобы вы из одного домена в другой из-за гонки не передали значение которого не было. А с кодом грея и полным счетчиком это возможно только если счетчик смениться несколько раз за врем фронта. Подчеркиваю фронта, а не периода.
nerudo
Это все правильно, но вовсе не так радужно в случае (крайне маловероятном, но в жизни всякое бывает), когда период изменения счетчика меньше чем возможная задержка сигнала по пути в другой тактовый домен (подразумевая, что по каждому биту задержки могут быть разными).
Ну то есть в реальной жизни я сомневаюсь что такое может приключиться, но о чем говорит коллега понятно.
GolikovAndrey
Это не верно.
Не важно сколько у вас идет сигнал из одного домена в другой и сколько раз за это время изменится счетчик. Важно только насколько расходиться шина, если у вас разница между приходами самого быстрого бита и самого медленного больше периода клока изменяющего счетчик то будет беда, но это как же надо развестись чтобы такое получилось:)
вот у вас есть счетчик который меняется
0 1 2 3 4 5 6 7 8 9 10
пусть он доходит в другой домен с задержкой в 2 такта
0 0 0 1 2 3 4 5 6 7 8 9
пусть другой домен выбирает каждый 10 отсчет
0 0 0 0 0 0 0 0 0 7 7 7 7…
но обратите внимание что когда мы меняли 0 на 7, мы на входе который защелкивает данные имели то 6 7 8, То есть мы могли защелкнуть 6, или переход 6-7 или 7 или переход 7-8, мы не могли видеть перехода 6 — 8, мы не могли видеть часть от 6 и часть от 7, так как считаем что период изменения больше разбежки шины.
так вот 6, 6-7, 7, 7-8, в коде Грея все эти ситуации будут превращены в 6 или 7 или 8, 6-7 и 7-8 свалиться в одно из устойчивых.
И не важно с какой частотой вы выбираете и с какой общей задержкой приходят данные
dsmv2014
Давайте рассмотрим такую ситацию: клок записи 250 МГц, клок чтения 100 МГц.
Задержки на шине передачи от от одного счётчика к другому распределились так:
bit0 — 1.1 ns
bit1 — 1.2 ns
bit2 — 1.4 ns
Это всё законно для частоты 250 МГц.
Теперь случай 1: фронт клока чтения отстал на 1.0 ns — все три бита получат старое значение
Следующий случай: фронт отстал на 1.3 ns — bit0 и bit1 — новое, bit 2 — старое
Ещё случай: фронт отстал ровно на 1.2 ns — bit2 — старое, bit0 — новое, bit1 — в метастабильном.
А эти случаи будут идти постоянно.
GolikovAndrey
Отлично, теперь мы к этому добавляем кодирование кодом грея
старое значение {bit0, bit1, bit2} отличается от нового {bit0, bit1, bit2} только одним битом
то есть у нас 3 варианта
{bit0, bit1, bit2} -> {New, bit1, bit2}
{bit0, bit1, bit2} -> {bit0, New, bit2}
{bit0, bit1, bit2} -> {bit0, bit1, New}
для первого случая мы получим
{bit0, bit1, bit2} -> {bit0, bit1, bit2}
второго случая
{bit0, bit1, bit2} -> {New, bit1, bit2}
{bit0, bit1, bit2} -> {bit0, New, bit2}
{bit0, bit1, bit2} -> {bit0, bit1, bit2}
то есть 2 варианта изменились, а третий передался просто старым
для 3 случая
{bit0, bit1, bit2} -> {New, bit1, bit2}
{bit0, bit1, bit2} -> {bit0, New или bit1, bit2}
{bit0, bit1, bit2} -> {bit0, bit1, bit2}
то есть первый вариант прошел, второй вариант либо прошол либо остался старым
третий вариант остался старым
dsmv2014
Не так. Обратите внимание, что частота записи 250 МГц, а частота чтения 100 МГц. Т.е. новое значение изменилось больше чем на один бит.
GolikovAndrey
сие невозможно:) разбежка между битами 0.3 нСек, а период задающего клока 4 нСек. Ни при каких условиях вы не можете на другом конце шины в один момент времени получить не соседние значения, которые отличаются не более чем на 1 бит.
то что шина в 100 МГц видит данные в 2.5 раза реже не означает что данные в шине пропадают в промежутки между клоками…
Постройте симуляцию и посмотрите.
dsmv2014
Внимательно посмотрел и признаю — я не прав. Действительно год Грея можно использовать при переходе между тактовыми доменами.
Благодарю за советы.
Дальше можно обсудить будет ли реализация на коде Грея более эффективной.
GolikovAndrey
есть ограничения: код Грея будет работать только для FIFO глубиной степени 2^n, иначе при переходе максимум-минимум будут многобитывае изменения.
второе ограничение: если вы хотите иметь флаги частичного заполнения, то вам придется сделать обратные преобразователи код Грея — обычный код, чтобы сравнивать уровни заполнения.
Пересинхронизация будет удобнее, но чуть объемнее. Надо все биты через 2 триггера пропускать, а не один флаг, с другйо стороны нет логики на флаге. Расширенный бит счетчика делает очень удобный анализ полно-пусто, без частичных уровней заполнения можно все сделать в коде Грея без обратного преобразования.
dsmv2014
Похоже код Грея мне не поможет. Настройка флагов обязательно нужна.
GolikovAndrey
преобразователь в код грея и обратно — это небольшая комбинаторная схема. Скорее всего она имеет меньший или сравнимый размер с логикой формирования флагов синхронизации.
Внутри домена у вас нормальный счетчик адреса, в другой домен счетчик идет через преобразователь в код Грея, переходит в этом виде между доменами и на той стороне восстанавливается опять в нормальный вид. Так решено пожалуй 99% FIFO через которые делают пересинхронизацию. Это стандартное и облизанное со всех сторон решение.
Des333
Счётчик Грея как раз и нужен для того, чтобы у Вас изменялся только 1 бит.
И тогда, очевидно, возможны только 2 ситуации:
1) Бит уже успел измениться (будет защёлкнуто новое значение счётчика)
2) Бит ещё не успел измениться (будет защёлкнуто предыдущее значение счётчика)