Jump to content
    

Снова про метастабильность, цепочки триггеров и т.д.

Всем привет! Будет много текста, прошу отнестись с пониманием...

 

Есть схемка, сегодня все работает, но есть большое подозрение что я просто загнал ошибку куда - то в глубину. И хочется понять как ее найти.

В схеме 2 интерфейса, сначала работали оба, с изменением схемы сломался второй, первый работал, после модификаций первый сломался, но починился второй, сейчас работают оба.

 

Интерфейсы SPI - режим слейв, клок внешний, есть сигнал чип селекта. SPI асинхронно работает по своему клоку, он сдвигает данные и считает контрольную сумму. После того как набрал нужное количество данных он ставит флаг что готовы данные. Схема уже по своему клоку проверяет что контрольная сумма сошлась и забирает данные.

 

Критически осматривая интерфейсы нашел 2 места с которыми не все очевидно:

 

первое место

   always @(posedge clk)
      begin
         if(spi_cs == 1'b1)
           begin
             ... подготовка к обмену

           end
       ....
      end

 

идея этого места такова. Пока нет клоков SPI он ничего делать не может, потому по основному клоку проверяется сигнал spi_cs, если он вверху, то обмена быть не может и ставиться флаг что это начало обмена. С первым клоком SPI, тот увидит флаг начало обмена и поймет что надо начинать, дальнейшие клоки будут просто принимать данные.

 

spi_cs - это асинхронный внешний сигнал для clk и любой скажет что надо его пропустить через 2 триггера, но хочется подробнее разобрать это место

 

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

2. Значит если он измениться близко к фронту клока то самое плохое что может быть это что до части элементов что под if(spi_cs == 1) он дойдет как единица, а для какой-то части еще останется нулем.

3. Значит если после установки cs выждать паузу не менее 2 клоков, то можно с уверенностью сказать что все элементы под if(spi_cs == 1) его получили, и приняли задаваемое значение

 

Один момент - метастабильность, но как понимаю если какой-то элемент схватит сигнал spi_cs в среднем положении и подвиснет на первом фронте, то после второго фронта он уже получит четкий вход и сможет занять правильное положение, или я не прав? Что кардинально меняется в этом месте с добавлением 2 триггеров на входе cs. Это было первое изменение схемы которое починило второй интерфейс, но почему то напрочь сломало первый.

 

 

 

второе место схемы

 

always @(posedge spi_clk)
  begin
     .....
     if(DataCounter == 0)
        CollectAllMessage <= 1;
     ....
  end


always @(posedge clk)
  begin
      CollectAllMessage1<=CollectAllMessage;
      CollectAllMessage2<=CollectAllMessage1;

     ....
     if((CollectAllMessage2 == 1'b1)&&(NotProcessData == 1'b0))
       NotProcessData <= 1; 
  end

 

 

 

идея этого места такова, по клоку SPI принимаются данные и считается число оставшихся бит. Если все биты приняты, ставиться флаг что данные готовы. По основному клоку при наличии этого флага и отсутствию начала обработки, ставиться флаг что обработка начата, а данные идут дальше.

 

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

Нужна какая-то корреляция числа триггеров и частот сигналов? У меня SPI в 2 раза медленнее основного клока, и формально данные которые он выставляет могут до след фронта быть меняющимися (ПЛИС забита под завязку, пути могут быть и длиннющими). Значит 2 такта их использовать не корректно, и нужен еще один такт на то если вдруг сигнал готовности будет близко к фронту.

Руководствуясь такими соображениями я удлинил цепочку на CollectAllMessage3<=CollectAllMessage2; Но сейчас я понимаю что это дает задержку в 4 клока, так же близость фронта сигнала и клока неважны, ведь сигнал однобитный и он либо захватится либо захватиться на следующем фронту.

 

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

 

Чувство очень неприятное и главное не вижу путей решения... прокомментируйте пожалуйста...

 

 

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Убрал сделанные изменения, то есть 2 триггера на чипселекте и третий триггер на сигнале готовности. Синтезировал, залил, запустил... И что? все опять работает.... И что это было? И как с этим жить? Мог интересно синтезатор вчера дурака валять? Или там есть просто какой-то случайный момент, в ходе которого схема собирается то так, то сяк, и у меня эта ошибка будет через раз всплывать? 4

 

что-то у меня вообще нет идей как эту ошибку отследить....

Share this post


Link to post
Share on other sites

И что? все опять работает.... И что это было?

 

Погода поменялась... Похолодало. Вот и заработало.

 

Можно, например, сделать как-то так, чтобы SPI блок по своему клоку устанавливал флаг готовности данного в 1, а блок, который забирает данное, асинхронно сбрасывал бы его, когда забрал данное (при условии, разумеется, что асинхронный сброс гарантировано снимается задолго до теоретической возможности следующей установки). И CS не протаскивать в другой домен вообще.

Share this post


Link to post
Share on other sites

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

 

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

 

Удивляет что убрав последние изменения я в точности, с точностью до комментария пришел к исходному не работающему тексту. Мне казалось один и тот же текст, в тех же настройках должен давать одну и ту же раскладку... больше похоже что когда я в ночи делал эти не влияющие изменения, пересохранял файлы, что-то пробудил совесть синтезатора и он сгенерил работающий вариант. А так как изменения были не важными то и с ними и без них все работает, потому отменив их я опять получил работающий вариант.

 

Странно все как-то, осадок остался что есть какая-то плавающая ошибка, и она будет как кровавое пятно всплывать. Неприятно работать не пределе ресурсов, надо уговорить поставить плисинку побольше...

 

Кроме долгих тестов есть еще какие-то методологии поиска такого рода ошибок? В симуляторе код делает ровно то что я хочу, похоже на какие-то не синхронные сигналы, но вроде они за блок SPI у меня не выходят, дальше внутри ПЛИС все синхронно...

 

 

 

два момента у меня много тасков (так было удобнее описывать) и получается что они по очереди ставят и отменяют один параметр

 

ну типа

task MyTask;
NeedStoreReg <= 1'b0;
endtask

а в тексте
always @(posedge clk) 
  begin
     MyTask;

     .....

     if(HaveReg)
       NeedStoreReg <= 1'b1;

  end

 

ведь нижнее присвоение перегрузит присвоение таска, ведь правда? И так можно перегружать сколько хочешь? то есть если будет еще таск ниже в котором опять что-то меняется, оно отменит все верхние? Так по стандарту

 

и еще один момент который меня беспокоит

 

if(NeedWrite && (Addr == REG_ADDR1)) - этак конструкция равносильна конструкции if((NeedWrite == 1'b1) && (Addr == REG_ADDR1)) ?

 

я уже начинаю проверять все что можно, отдохнуть что ли...

Share this post


Link to post
Share on other sites

Кроме долгих тестов есть еще какие-то методологии поиска такого рода ошибок?

 

Есть, но они Вам скорее всего не подойдут. Надо нарисовать блок-схему получившегося междоменного перехода, и при помощи ручки, бумажки и собственного мозга, проанализировать все возможные варианты влияния относительных положений фронтов всех там участвующих сигналов на состояние схемы. Междоменные переходы всегда проектируются так, что сигналов там мало, и их анализ "вручную" не сложен.

 

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

 

PS

Да, в begin-end последнее присвоение присвоит окончательное значение, и можно последовательно сколько хотите присваивать одной переменной.

 

PPS

Конструкции, если конечно "NeedWrite" однобитный, равносильны.

 

UPD:

И, вдогонку...

 

В междоменных переходах надо запретить всякие там репликации и дупликации критических триггеров. А то синтезаторы умные слишком стали, и могут напакостить, не зная, что там этого делать нельзя.

Share this post


Link to post
Share on other sites

Опыт использования показал, что даже стандартные ip ядра SPI из EDK от Xilinx тоже сбоят, справда довольно редко, и очень похоже на метастабильность. Там получалось что-то около одного сбоя на 1000-5000 транзакций, причем с повышением тактовой частоты SPI количество сбоев увеличивалось. Так что попробуйте еще на официальном форуме посмотреть, проблема получается не новая

Share this post


Link to post
Share on other sites

Опыт использования показал, что даже стандартные ip ядра SPI из EDK от Xilinx тоже сбоят, справда довольно редко, и очень похоже на метастабильность. Там получалось что-то около одного сбоя на 1000-5000 транзакций

 

Бредятина. Всегда завидую гражданам, налетающим на метастабильность и затем героически с нею борящимся. Очевидно, все они делают устройства, способные (и обязанные) как минимум несколько месяцев (а скорее лет) работать без перезагрузки, и каждый сбой - ЧП масштаба перезапуска 500-мегаваттного генератора Красноярской ГЭС. Потому что чаще метастабильность не проявляется.

 

Share this post


Link to post
Share on other sites

Есть, но они Вам скорее всего не подойдут.

Ну вот зачем вы меня обижаете:)...

 

я так уже сделал, и вроде бы все проверил, никуда там асинхронщина дальше не прет, посли приема данные передаются в основной клоковый домен и в схему идут синхронные сигналы

 

Addr (one hot), Data, RW, RD. в схеме проверка

 

if(RD & Addr[REG_ADDR])

Out <=Data;

if(WR & Addr[REG_ADDR])

Data <= In;

 

И в момент когда схема работала со сбоями было обращение в левые адреса, в которые я не обращался... Как такое может быть если все констраины прошли и схема синхронная? Причем в 95% процентов адресов обращение было верное, то есть куда писал-читал там и работал, а на 2 адреса обращения не проходили точно (то есть 7 из 10 записей верно а 3 обращения иногда в левый адрес, с повышением плотности обмена повышаются и сбои). И адреса были нового модуля. Все таки я грешу на то что синтезатор что-то не то построил, надо среду почаще перегружать что ли...

 

 

Бредятина. Всегда завидую гражданам, налетающим на метастабильность и затем героически с нею борящимся

Хорошо если это метастабильность, ее победить нельзя, надо смериться, принять меры и успокоится... А если это какая-то реальная ошибка, которая то есть, то нет... Она ведь может в температуре вылезти и получите прибор который раз в день перегружается, и кого это обрадует?

Если прибор сбоит на столе, то в поле он точно будет висеть через раз. В поле все всегда хуже чем дома...

Share this post


Link to post
Share on other sites

Возможно, в сторону от обсуждения указанной проблемы, но если речь о конечном результате, то в случае SPI мы никогда не использовали SCK в качестве клока, а только лишь в качестве сигнала битовой синхронизации. Таким образом, протаскиваем все входные сигналы через двухтриггерные синхронизаторы на внутренний системный клок, который гораздо более высокочастоный (типовые значения: SPI - 10 MHz, clk - 100 MHz), и дальше уже спокойно работаем в основном клоковом домене, не имея необходимости в дальнейшей синхронизации с остальной логикой.

 

Вообще, тема интересная поднята. Давно хотел поднять обсуждение про приём данных в ПЛИС по внешнему клоку. Создам новую тему, чтобы тут не офтопить. :)

Share this post


Link to post
Share on other sites

можно по SPI принимать данные и отправлять в двуклоковое фифо (в "простом" случае), также можно организовать запись/чтение в/из фифо. Можно в фифо поставить несколько указателей на некоторую "пустоту/заполненость" данными - для удобства...

Далее работа с фифо с "другого конца". В данном случае мне кажется абсолютно коректное решение для клокового перехода. Достоинство данного решения, что можно сделать запись по N разрядов, а вычитывать по М разрядов (это про фифо). Дополнительные сигналы управления провести через цепочку синхронизирующих регистров - в случае необходимости...

Также можно вспомнить и про Flancter (вложение)

Flancter_App_Note.pdf

Share this post


Link to post
Share on other sites

можно по SPI принимать данные и отправлять в двуклоковое фифо
В SPI клок есть только тогда, когда передаются данные. А для FIFO он нужен всегда.

Share this post


Link to post
Share on other sites

то в случае SPI мы никогда не использовали SCK в качестве клока

Это слишком строгое утверждение... это хорошо, если

(типовые значения: SPI - 10 MHz, clk - 100 MHz)

 

в Моем случае SPI 50 МГц, А клок 100, SPI слейв не выйдет в таком раскладе без использования клока.

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

 

 

принимать данные и отправлять в двуклоковое фифо

Хорошее решение (правда не думал о нем), но зараза клок SPI пропадает по приему слова (как уже сказали выше). Фактически я и горожу 1 словное фифо со всеми вытекающими. Принимаю данные в буфер, и ставлю сигнал есть данные.

 

 

Также можно вспомнить и про Flancter

Спасибо погляжу, сейчас бегло поглядев, мне кажется что я так и сделал

 

у меня в 2 доменах свои сигналы DataRecive и DataReciveAck.

когда прием закончен DataRecive <= ~ DataReciveAck. Этот дата ресив через 2 триггера идет в первый домен, и там глядят

если DataRecive2 != DataReciveAck - значит есть данные, и DataReciveAck <= DataRecive2;

Почитаю поподробнее может оно там хитрее придумано. В своей связке между изменениями сигналов по 2 клока, мне кажется что точно все должно устаканится...

 

 

Да, пора уже принять меры: Multi-Asynchronous Clock Designs.

Это мне кажется слишком обще, почитаю спасибо, но статья большая, не быстро это будет

 

 

на самом деле волную частности типа

 

always @(posedge clk)
begin
    if(spi_cs == 1'b1)
      begin
         WR <= 1'b0;    
         RD <= 1'b0;

         Data <= 0;
         Addr <= 0;
      end
end

 

 

spi_cs - внешний сигнал, который не синхронизирован с клоком и не пропущен через 2 триггера. Addr - это one hot адрес, то есть там все нули и всего одна единичка. Так вот мне кажется ни при каких раскладах не может так получится что выполняя эту конструкцию в адресе загорится другая единичка. То есть может ли 001 снимаясь перейти в 010 или 011?

Мне кажется не может, если бы в адресе было много бит, то да, часть снялась, часть не снялась, сигнал RW мог тоже не снятся и фактически произошла бы запись в другой адрес. Но в случае если в векторе всего 1 единичка, никакая мета стабильность не может ее сдвинуть, ведь так?

Share this post


Link to post
Share on other sites

Убрал сделанные изменения, то есть 2 триггера на чипселекте и третий триггер на сигнале готовности. Синтезировал, залил, запустил... И что? все опять работает.... И что это было? И как с этим жить? Мог интересно синтезатор вчера дурака валять? Или там есть просто какой-то случайный момент, в ходе которого схема собирается то так, то сяк, и у меня эта ошибка будет через раз всплывать? 4

 

что-то у меня вообще нет идей как эту ошибку отследить....

 

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

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...