sonycman 0 26 октября, 2016 Опубликовано 26 октября, 2016 · Жалоба Доброго времени суток! Есть проектик (не мой) для CycloneII(III), который я потихоньку запускаю на CycloneV SoC. Пользую Квартус 16.0. В нём есть простой модуль SPI: //////////////////////////////////////// // SPI // //////////////////////////////////////// // this is SPI mode 3 (CPOL=1, CPHA=1) // clock default state is HI, data are captured on clock's rising edge and data are propagated on a falling edge reg spi_cs_n_en; reg spi_act; reg spi_act_d; reg [ 6-1:0] spi_div; reg [ 6-1:0] spi_div_r; reg spi_div_en; reg [ 4-1:0] spi_cnt; reg [ 8-1:0] spi_dat_w; reg spi_dat_en; reg [ 8-1:0] spi_dat_r; reg spi_block_en; reg [ 10-1:0] spi_block; // SPI chip-select (active low) // masked set : if any of the upper 4 bits of first byte are set, only bits with same position in lower four bits are changed // unmasket set: if none of the upper 4 bits are set, all four lower bits get overwritten always @ (posedge clk, posedge rst) begin if (rst) spi_cs_n <= 4'b1111; else if (spi_cs_n_en) spi_cs_n <= ~((|dat_w[7:4]) ? ((dat_w[7:4] & dat_w[3:0]) | (~dat_w[7:4] & ~spi_cs_n)) : dat_w[3:0]); end // SPI active always @ (posedge clk, posedge rst) begin if (rst) spi_act <= 1'b0; else if (spi_act && (~|spi_cnt) && (~|spi_div) && (~|spi_block)) spi_act <= 1'b0; else if (spi_dat_en && !spi_act_d) spi_act <= 1'b1; end // SPI active - last cycle always @ (posedge clk, posedge rst) begin if (rst) spi_act_d <= 1'b0; else if (spi_act && (~|spi_cnt) && (~|spi_div) && (~|spi_block)) spi_act_d <= 1'b1; else if (spi_act_d && (~|spi_div)) spi_act_d <= 1'b0; end // SPI clock divider register always @ (posedge clk, posedge rst) begin if (rst) spi_div_r <= SPI_CNT - 6'd1; else if (spi_div_en && !(spi_act || spi_act_d)) spi_div_r <= dat_w[5:0]; end // SPI clock divider always @ (posedge clk, posedge rst) begin if (rst) spi_div <= SPI_CNT - 6'd1; else if (spi_div_en && !(spi_act || spi_act_d)) spi_div <= dat_w[5:0]; else if (spi_act && (~|spi_div)) spi_div <= spi_div_r; else if ((spi_act || spi_act_d) && ( |spi_div)) spi_div <= spi_div - 6'd1; end // SPI counter always @ (posedge clk, posedge rst) begin if (rst) spi_cnt <= 4'b1111; else if (spi_act && (~|spi_div)) spi_cnt <= spi_cnt - 4'd1; end // SPI clock assign spi_clk = spi_cnt[0]; // SPI write data always @ (posedge clk) begin if (spi_dat_en && !(spi_act || spi_act_d)) spi_dat_w <= dat_w[7:0]; else if (spi_act && spi_clk && (~|spi_div) && (~(&spi_cnt))) spi_dat_w <= {spi_dat_w[6:0], 1'b1}; end // SPI data out assign spi_do = spi_dat_w[7]; // SPI read data always @ (posedge clk) begin if (spi_act && !spi_clk && (~|spi_div)) spi_dat_r <= {spi_dat_r[6:0], spi_di}; end // SPI block count always @ (posedge clk, posedge rst) begin if (rst) spi_block <= 10'd0; else if (spi_block_en && !(spi_act || spi_act_d)) spi_block <= dat_w[9:0]; else if (spi_act && (~|spi_div) && (~|spi_cnt) && (|spi_block)) spi_block <= spi_block - 10'd1; end //////////////////////////////////////// // register enable // //////////////////////////////////////// always @ (*) begin if (cs && we) begin sys_rst_en = 1'b0; ctrl_en = 1'b0; sys_stat_en = 1'b0; tx_en = 1'b0; rx_en = 1'b0; uart_stat_en = 1'b0; timer_en = 1'b0; spi_div_en = 1'b0; spi_cs_n_en = 1'b0; spi_dat_en = 1'b0; spi_block_en = 1'b0; case(adr[RAW+2-1:2]) REG_RST : sys_rst_en = 1'b1; REG_CTRL : ctrl_en = 1'b1; REG_SYS_STAT : sys_stat_en = 1'b1; REG_UART_TX : tx_en = 1'b1; REG_UART_RX : rx_en = 1'b1; REG_UART_STAT : uart_stat_en = 1'b1; REG_TIMER : timer_en = 1'b1; REG_SPI_DIV : spi_div_en = 1'b1; REG_SPI_CS : spi_cs_n_en = 1'b1; REG_SPI_DAT : spi_dat_en = 1'b1; REG_SPI_BLOCK : spi_block_en = 1'b1; default : begin sys_rst_en = 1'b0; ctrl_en = 1'b0; sys_stat_en = 1'b0; tx_en = 1'b0; rx_en = 1'b0; uart_stat_en = 1'b0; timer_en = 1'b0; spi_div_en = 1'b0; spi_cs_n_en = 1'b0; spi_dat_en = 1'b0; spi_block_en = 1'b0; end endcase end else begin sys_rst_en = 1'b0; ctrl_en = 1'b0; sys_stat_en = 1'b0; tx_en = 1'b0; rx_en = 1'b0; uart_stat_en = 1'b0; timer_en = 1'b0; spi_div_en = 1'b0; spi_cs_n_en = 1'b0; spi_dat_en = 1'b0; spi_block_en = 1'b0; end end //////////////////////////////////////// // register read // //////////////////////////////////////// always @ (*) begin case(adr_r[RAW+2-1:2]) REG_CTRL : dat_r = {28'h0000000, ctrl_cfg}; REG_SYS_STAT : dat_r = {28'h0000000, sys_status}; REG_UART_RX : dat_r = {24'h000000, rx_reg}; REG_UART_STAT : dat_r = {28'h0000000, tx_ready, rx_ready, rx_miss, rx_valid}; REG_TIMER : dat_r = {16'h0000, timer}; REG_SPI_DIV : dat_r = {26'h0000000, spi_div_r}; REG_SPI_DAT : dat_r = {24'h000000, spi_dat_r}; default : dat_r = 32'hxxxxxxxx; endcase end //////////////////////////////////////// // ack & err // //////////////////////////////////////// // ack always @ (*) begin case(adr[RAW+2-1:2]) REG_UART_TX : ack = tx_ready; REG_SPI_DIV, REG_SPI_CS, REG_SPI_DAT, REG_SPI_BLOCK : ack = !(spi_act | spi_act_d); default : ack = 1'b1; endcase end // err assign err = 1'b0; Клок используется от внешнего генератора на 50 МГц (плата DE1-SoC от Terasic). Вроде бы всё нормально, синхронная логика, SPI работает межмодульно внутри чипа, наружу его пины не выводятся. Но как только я подключил пины SPI в SignalTap, чтобы уточнить, как именно идёт передача данных - он "посыпался" :( Сигнал spi_clk стал менять длительность уровней (к примеру верхний уровень формируется длительностью в 5 тактов, а нижний - всего в один), счётчик spi_cnt вообще работает как попало, считает то вниз, то вверх (!), в общем полезла какая то асинхронщина до полного зависания процессора (так как не всегда взводится ack), который работает с этим SPI. Стабильно считает только spi_div, в остальном какой-то бардак. Понять не могу, как может нарушиться времянка в таком синхронном коде? Да на такой низкой частоте? Подскажите, плиз, в чём может быть проблема? Неужели неграмотно написан модуль? Может ли быть проблема в том, что код разбит на куски с отдельными always для каждого регистра? Вроде бы коллеги говорят, что это наоборот - грамотно? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
yuravg 0 26 октября, 2016 Опубликовано 26 октября, 2016 · Жалоба Может ли быть проблема в том, что код разбит на куски с отдельными always для каждого регистра? Вроде бы коллеги говорят, что это наоборот - грамотно? На мой взгляд код написан грамотно, даже через чур (ситраксис "posedge clk, posedge rst", 1'b0, выравнивание и т.д.). Отдельные always или общий always для разных регистров - никак не влияет на работу Подскажите, плиз, в чём может быть проблема? Проблема может быть в самом подключении signaltap, посмотрите в timequest, до и после подключения signaltap, может увидите проблему(если timequest используется) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
bogaev_roman 0 26 октября, 2016 Опубликовано 26 октября, 2016 · Жалоба Вы не привели временные ограничения, помимо самого кода. 99% что есть временные ошибки, смотрите timequest - все ограничения должны быть прописаны и выполнены, только в этом случае все будет гарантированно работать. Тут ведь как может быть - пины и сигналы, которые вытягиваете на signaltap расположены в одном конце кристалла, а сам jtag в диаметрально противоположном, вот и не вытягивается даже вроде бы и огромные 20нс. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sonycman 0 26 октября, 2016 Опубликовано 26 октября, 2016 · Жалоба Констрейн на клок прописан стандартный, вида: create_base_clock -name "clk_50" [get_ports "CLOCK_50"] А какие ещё констрейны нужны именно этому простому куску кода? Он больше ни от чего не зависит. И проблема не только в том, что в сигнал тапе видно асинхронщину, а в том, что сам SPI работать перестаёт. В таймквест заглянуть смогу только вечером, надо бы посмотреть, нет ли большого сдвига по времени клока между регистрами модуля. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
bogaev_roman 0 26 октября, 2016 Опубликовано 26 октября, 2016 · Жалоба Он больше ни от чего не зависит. И проблема не только в том, что в сигнал тапе видно асинхронщину, а в том, что сам SPI работать перестаёт. У вашего куска кода есть входные/выходные данные? Вроде бы всё нормально, синхронная логика, SPI работает межмодульно внутри чипа, наружу его пины не выводятся. Извиняюсь, невнимательно прочитал, если частота задана и вся логика работает внутри одного клокового домена, то тут надо только timequest открывать и анализировать. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sonycman 0 26 октября, 2016 Опубликовано 26 октября, 2016 · Жалоба Вот взять, к примеру, регистр spi_cnt. Он считает на уменьшение, когда модуль активен и когда регистр предделителя равен нулю. Но в сигналтапе я вижу, что он дополнительно переключается ещё и в момент, когда spi_div равен двум. То есть явно нарушены тайминги между регистрами, и переключение идёт в момент, когда сигналы ещё не устоялись. Но почему квартус устроил такую кашу? У вашего куска кода есть входные/выходные данные? Извиняюсь, невнимательно прочитал, если частота задана и вся логика работает внутри одного клокового домена, то тут надо только timequest открывать и анализировать. Данные записываются через порт авалон процессором, встроенным в SoC. Да, сам порт и модуль spi тактируются одним клоком. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
bogaev_roman 0 26 октября, 2016 Опубликовано 26 октября, 2016 · Жалоба Вот взять, к примеру, регистр spi_cnt. Он считает на уменьшение, когда модуль активен и когда регистр предделителя равен нулю. Ну я уже пытался описать возможную ситуация - Вы вывели этот сигнал на ST, который, возможно, физически находится в противоположном конце кристалла. Раньше разрядик счетчика использовался только для внутренней логики, а теперь еще и привязан к ST, фиттер по времянке не может его вытащить на ST и пытается физически подвинуть поближе к этой логике, но в результате рушится внутренняя. В худшем случае неадекватно работает и тап и сама логика. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
yuravg 0 26 октября, 2016 Опубликовано 26 октября, 2016 · Жалоба Но почему квартус устроил такую кашу? Кстати, убедитесь, что стробирован rst, из приведенного кода это не ясно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sonycman 0 26 октября, 2016 Опубликовано 26 октября, 2016 · Жалоба Ну я уже пытался описать возможную ситуация - Вы вывели этот сигнал на ST, который, возможно, физически находится в противоположном конце кристалла. Раньше разрядик счетчика использовался только для внутренней логики, а теперь еще и привязан к ST, фиттер по времянке не может его вытащить на ST и пытается физически подвинуть поближе к этой логике, но в результате рушится внутренняя. В худшем случае неадекватно работает и тап и сама логика. Понял вас. В проекте есть коммутируемые клоки на софтпроцессоре, таймквест не врубается и ругается на них, якобы времянка не выдержана. Но на самом деле там все в норме. А вот времянки по spi не видно, надо будет хорошенько поискать в таймквесте именно их... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
iosifk 3 26 октября, 2016 Опубликовано 26 октября, 2016 · Жалоба Доброго времени суток! Есть проектик (не мой) В нём есть простой модуль SPI: Неужели неграмотно написан модуль? Может ли быть проблема в том, что код разбит на куски с отдельными always для каждого регистра? Вроде бы коллеги говорят, что это наоборот - грамотно? У меня просьба. Пришлите пожалуйста мне этот файл на почту или напишите, где Вы его взяли. Буду его использовать как пример "тупого" кодирования... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
bogaev_roman 0 26 октября, 2016 Опубликовано 26 октября, 2016 · Жалоба В проекте есть коммутируемые клоки на софтпроцессоре, таймквест не врубается и ругается на них, якобы времянка не выдержана. Правильно делает, нужно описывать все, а то в результате на одной плате работает, на другой нет, в помещении работает/на улице нет и т.д. Но на самом деле там все в норме. С чего Вы взяли, то что тест прошел? А вот времянки по spi не видно, надо будет хорошенько поискать в таймквесте именно их... Сгенерируйте отчеты по всем клокам и найдите требуемый. Если требуемого не увидели, то сгенерируйте отчет по unconstraint, может быть он будет в списе игнорируемых, в общем для начала убедитесь, что он есть на самом деле. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sonycman 0 26 октября, 2016 Опубликовано 26 октября, 2016 · Жалоба У меня просьба. Пришлите пожалуйста мне этот файл на почту или напишите, где Вы его взяли. Буду его использовать как пример "тупого" кодирования... В смысле - с кодом всё таки что-то не так? Файл отправил вам на почту. Правильно делает, нужно описывать все, а то в результате на одной плате работает, на другой нет, в помещении работает/на улице нет и т.д. С чего Вы взяли, то что тест прошел? Сгенерируйте отчеты по всем клокам и найдите требуемый. Если требуемого не увидели, то сгенерируйте отчет по unconstraint, может быть он будет в списе игнорируемых, в общем для начала убедитесь, что он есть на самом деле. Я совершенно с вами согласен, так оно и получается, я уже на своей шкуре и именно на этом проекте это испытываю. Но я новичок, и пока что не представляю, как сделать констрейны на сложные места. Тут вот с простым SPI не так всё просто получается... :( Тест прошёл потому, что проект работает, это проект ретрокомпьютера Amiga - Minimig. Я его портирую на плату DE1-SoC, весьма интересное в целях обучения занятие. Сейчас вот меняю софтпроцессор на встроенный в CycloneV ARM, приходится перепахивать прошивку ну и параллельно знакомиться поближе с различными узлами проекта... Спасибо за советы, буду искать этот клок в таймквесте. ЗЫ: как то обидно, что стомегагерцовые связи SDRAM и её контроллер получилось увидеть в сигналтап, а медленный и простой SPI нет... :( Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
bogaev_roman 0 26 октября, 2016 Опубликовано 26 октября, 2016 · Жалоба ЗЫ: как то обидно, что стомегагерцовые связи SDRAM и её контроллер получилось увидеть в сигналтап, а медленный и простой SPI нет... :( Не знаю насчет конкретно Вашего контроллера, но для DDR3 или ethernet гигабитного, визард на корки автоматически герерирует все ограничения (и грамотную пересинхронизацию) и пользователю достаточно запустить tcl-скрипт, чтобы все это подключить. К контроллеру Вы скорее всего цепляетесь на типовые регистровые точки, не критические, типа init_done, calibration_sucсs, или входную/выходную шины данных. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
spectr 0 26 октября, 2016 Опубликовано 26 октября, 2016 · Жалоба А никого не смущает куча вот таких always'ов, где работа ведется с сигналами, отсутствующими в списке чувствительности: // SPI active always @ (posedge clk, posedge rst) begin if (rst) spi_act <= 1'b0; else if (spi_act && (~|spi_cnt) && (~|spi_div) && (~|spi_block)) spi_act <= 1'b0; else if (spi_dat_en && !spi_act_d) spi_act <= 1'b1; end Разве не должно быть как минимум так? // SPI active always @ (posedge clk, posedge rst) begin if (rst) spi_act <= 1'b0; else begin if (spi_act && (~|spi_cnt) && (~|spi_div) && (~|spi_block)) spi_act <= 1'b0; else if (spi_dat_en && !spi_act_d) spi_act <= 1'b1; end end Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
iosifk 3 26 октября, 2016 Опубликовано 26 октября, 2016 · Жалоба А никого не смущает куча вот таких always'ов, где работа ведется с сигналами, отсутствующими в списке чувствительности: Меня не смущает. Это значит, что результат изменения этих сигналов будет зафиксирован под клоки. Это и называется "синхронное проектирование"... А вот там, где always @ (*) begin и далее мешанина асинхронного коммутатора - смущает... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться