Golikov 0 June 18, 2014 Posted June 18, 2014 · Report post Всем привет! Будет много текста, прошу отнестись с пониманием... Есть схемка, сегодня все работает, но есть большое подозрение что я просто загнал ошибку куда - то в глубину. И хочется понять как ее найти. В схеме 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 клока, так же близость фронта сигнала и клока неважны, ведь сигнал однобитный и он либо захватится либо захватиться на следующем фронту. Почему-то это изменение починило оба интерфейса и теперь все работает. И главный мой вопрос: я реально что-то исправил, или оба изменения просто меняли схему разводки, и ошибка куда-то уползла и вылезет чуть по позже при очередной модификации схемы? Чувство очень неприятное и главное не вижу путей решения... прокомментируйте пожалуйста... Quote Share this post Link to post Share on other sites More sharing options...
Alex11 8 June 18, 2014 Posted June 18, 2014 · Report post Про первую половину. То, что сигнал находится только в одном if не означает, что он дальше будет влиять на изменеие состояния только одного триггера. Поэтому, если не защелкнуть его хотя бы один раз, то последующие триггера могут использовать (в зависимости от длины пути) разные значения сигнала. После чего встанут они в произвольные значения. И если эти триггера определяют, например, состояние машины, то оно может оказаться сильно не тем, какое хочется. Причем это будет не состояние при сигнале 0 и не состояние при сигнале 1. На следующем такте машина, скорее всего, не придет в правильное состояние, хотя сигнал уже будет стабильный - предыдущее состояние машины некорректно. Про метастабильность отдельный разговор. Для ее устранения лучше защелкивать сигнал два раза по цепочке. Quote Share this post Link to post Share on other sites More sharing options...
Golikov 0 June 18, 2014 Posted June 18, 2014 · Report post Убрал сделанные изменения, то есть 2 триггера на чипселекте и третий триггер на сигнале готовности. Синтезировал, залил, запустил... И что? все опять работает.... И что это было? И как с этим жить? Мог интересно синтезатор вчера дурака валять? Или там есть просто какой-то случайный момент, в ходе которого схема собирается то так, то сяк, и у меня эта ошибка будет через раз всплывать? 4 что-то у меня вообще нет идей как эту ошибку отследить.... Quote Share this post Link to post Share on other sites More sharing options...
SM 9 June 18, 2014 Posted June 18, 2014 · Report post И что? все опять работает.... И что это было? Погода поменялась... Похолодало. Вот и заработало. Можно, например, сделать как-то так, чтобы SPI блок по своему клоку устанавливал флаг готовности данного в 1, а блок, который забирает данное, асинхронно сбрасывал бы его, когда забрал данное (при условии, разумеется, что асинхронный сброс гарантировано снимается задолго до теоретической возможности следующей установки). И CS не протаскивать в другой домен вообще. Quote Share this post Link to post Share on other sites More sharing options...
Golikov 0 June 18, 2014 Posted June 18, 2014 · Report post чип селект он больше как детекция начала, чтобы схему можно было в любой момент ввести в начальное состояние, я его ставлю и жду несколько клоков, думаю если и будет какая мета стабильность, все пройдет... влияние внешних факторов на схему не похоже.... разница между неработающим - полуработающим - и полностью работающим вариантом время синтеза, минуты какие - то. Причем как с холодного старта, так и с долгой работы. Удивляет что убрав последние изменения я в точности, с точностью до комментария пришел к исходному не работающему тексту. Мне казалось один и тот же текст, в тех же настройках должен давать одну и ту же раскладку... больше похоже что когда я в ночи делал эти не влияющие изменения, пересохранял файлы, что-то пробудил совесть синтезатора и он сгенерил работающий вариант. А так как изменения были не важными то и с ними и без них все работает, потому отменив их я опять получил работающий вариант. Странно все как-то, осадок остался что есть какая-то плавающая ошибка, и она будет как кровавое пятно всплывать. Неприятно работать не пределе ресурсов, надо уговорить поставить плисинку побольше... Кроме долгих тестов есть еще какие-то методологии поиска такого рода ошибок? В симуляторе код делает ровно то что я хочу, похоже на какие-то не синхронные сигналы, но вроде они за блок 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)) ? я уже начинаю проверять все что можно, отдохнуть что ли... Quote Share this post Link to post Share on other sites More sharing options...
SM 9 June 18, 2014 Posted June 18, 2014 · Report post Кроме долгих тестов есть еще какие-то методологии поиска такого рода ошибок? Есть, но они Вам скорее всего не подойдут. Надо нарисовать блок-схему получившегося междоменного перехода, и при помощи ручки, бумажки и собственного мозга, проанализировать все возможные варианты влияния относительных положений фронтов всех там участвующих сигналов на состояние схемы. Междоменные переходы всегда проектируются так, что сигналов там мало, и их анализ "вручную" не сложен. А вот тесты в симуляторе, напротив, ничего не выявят, так как метастабильности в стандартных либах вообще не анализируются и не генерируются, в лучшем случае, можно получить расползание "x"-ов по схеме, которое ничего не покажет, кроме того, что в этот момент была высокая вероятность метастабильности. PS Да, в begin-end последнее присвоение присвоит окончательное значение, и можно последовательно сколько хотите присваивать одной переменной. PPS Конструкции, если конечно "NeedWrite" однобитный, равносильны. UPD: И, вдогонку... В междоменных переходах надо запретить всякие там репликации и дупликации критических триггеров. А то синтезаторы умные слишком стали, и могут напакостить, не зная, что там этого делать нельзя. Quote Share this post Link to post Share on other sites More sharing options...
rsv2007 0 June 18, 2014 Posted June 18, 2014 · Report post Опыт использования показал, что даже стандартные ip ядра SPI из EDK от Xilinx тоже сбоят, справда довольно редко, и очень похоже на метастабильность. Там получалось что-то около одного сбоя на 1000-5000 транзакций, причем с повышением тактовой частоты SPI количество сбоев увеличивалось. Так что попробуйте еще на официальном форуме посмотреть, проблема получается не новая Quote Share this post Link to post Share on other sites More sharing options...
Dr.Alex 0 June 18, 2014 Posted June 18, 2014 · Report post Опыт использования показал, что даже стандартные ip ядра SPI из EDK от Xilinx тоже сбоят, справда довольно редко, и очень похоже на метастабильность. Там получалось что-то около одного сбоя на 1000-5000 транзакций Бредятина. Всегда завидую гражданам, налетающим на метастабильность и затем героически с нею борящимся. Очевидно, все они делают устройства, способные (и обязанные) как минимум несколько месяцев (а скорее лет) работать без перезагрузки, и каждый сбой - ЧП масштаба перезапуска 500-мегаваттного генератора Красноярской ГЭС. Потому что чаще метастабильность не проявляется. Quote Share this post Link to post Share on other sites More sharing options...
Golikov 0 June 19, 2014 Posted June 19, 2014 · Report post Есть, но они Вам скорее всего не подойдут. Ну вот зачем вы меня обижаете:)... я так уже сделал, и вроде бы все проверил, никуда там асинхронщина дальше не прет, посли приема данные передаются в основной клоковый домен и в схему идут синхронные сигналы 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 обращения иногда в левый адрес, с повышением плотности обмена повышаются и сбои). И адреса были нового модуля. Все таки я грешу на то что синтезатор что-то не то построил, надо среду почаще перегружать что ли... Бредятина. Всегда завидую гражданам, налетающим на метастабильность и затем героически с нею борящимся Хорошо если это метастабильность, ее победить нельзя, надо смериться, принять меры и успокоится... А если это какая-то реальная ошибка, которая то есть, то нет... Она ведь может в температуре вылезти и получите прибор который раз в день перегружается, и кого это обрадует? Если прибор сбоит на столе, то в поле он точно будет висеть через раз. В поле все всегда хуже чем дома... Quote Share this post Link to post Share on other sites More sharing options...
dxp 112 June 19, 2014 Posted June 19, 2014 · Report post Возможно, в сторону от обсуждения указанной проблемы, но если речь о конечном результате, то в случае SPI мы никогда не использовали SCK в качестве клока, а только лишь в качестве сигнала битовой синхронизации. Таким образом, протаскиваем все входные сигналы через двухтриггерные синхронизаторы на внутренний системный клок, который гораздо более высокочастоный (типовые значения: SPI - 10 MHz, clk - 100 MHz), и дальше уже спокойно работаем в основном клоковом домене, не имея необходимости в дальнейшей синхронизации с остальной логикой. Вообще, тема интересная поднята. Давно хотел поднять обсуждение про приём данных в ПЛИС по внешнему клоку. Создам новую тему, чтобы тут не офтопить. :) Quote Share this post Link to post Share on other sites More sharing options...
Maverick_ 16 June 19, 2014 Posted June 19, 2014 · Report post можно по SPI принимать данные и отправлять в двуклоковое фифо (в "простом" случае), также можно организовать запись/чтение в/из фифо. Можно в фифо поставить несколько указателей на некоторую "пустоту/заполненость" данными - для удобства... Далее работа с фифо с "другого конца". В данном случае мне кажется абсолютно коректное решение для клокового перехода. Достоинство данного решения, что можно сделать запись по N разрядов, а вычитывать по М разрядов (это про фифо). Дополнительные сигналы управления провести через цепочку синхронизирующих регистров - в случае необходимости... Также можно вспомнить и про Flancter (вложение) Flancter_App_Note.pdf Quote Share this post Link to post Share on other sites More sharing options...
andrew_b 23 June 19, 2014 Posted June 19, 2014 · Report post можно по SPI принимать данные и отправлять в двуклоковое фифоВ SPI клок есть только тогда, когда передаются данные. А для FIFO он нужен всегда. Quote Share this post Link to post Share on other sites More sharing options...
blackfin 59 June 19, 2014 Posted June 19, 2014 · Report post Хорошо если это метастабильность, ее победить нельзя, надо смериться, принять меры и успокоится... Да, пора уже принять меры: Multi-Asynchronous Clock Designs. :rolleyes: Quote Share this post Link to post Share on other sites More sharing options...
Golikov 0 June 19, 2014 Posted June 19, 2014 · Report post то в случае 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 единичка, никакая мета стабильность не может ее сдвинуть, ведь так? Quote Share this post Link to post Share on other sites More sharing options...
GAYVER 2 June 19, 2014 Posted June 19, 2014 · Report post Убрал сделанные изменения, то есть 2 триггера на чипселекте и третий триггер на сигнале готовности. Синтезировал, залил, запустил... И что? все опять работает.... И что это было? И как с этим жить? Мог интересно синтезатор вчера дурака валять? Или там есть просто какой-то случайный момент, в ходе которого схема собирается то так, то сяк, и у меня эта ошибка будет через раз всплывать? 4 что-то у меня вообще нет идей как эту ошибку отследить.... переразвели проект - по новой в плис все легло. блок мог компактненько уложиться и задержки на распространение асинхронки оказались минимальны - куак следствие ошибки нет. в следующй раз блок размажется по всей плисине на предельных значениях распространения синхронных сигналов, время "добегания" асинхронки к разным блокам увеличивается - нестабильность Quote Share this post Link to post Share on other sites More sharing options...