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

Доброго времени суток!

 

Есть проектик (не мой) для 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 для каждого регистра?

Вроде бы коллеги говорят, что это наоборот - грамотно?

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


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

Может ли быть проблема в том, что код разбит на куски с отдельными always для каждого регистра?

Вроде бы коллеги говорят, что это наоборот - грамотно?

На мой взгляд код написан грамотно, даже через чур (ситраксис "posedge clk, posedge rst", 1'b0, выравнивание и т.д.).

Отдельные always или общий always для разных регистров - никак не влияет на работу

 

Подскажите, плиз, в чём может быть проблема?

Проблема может быть в самом подключении signaltap, посмотрите в timequest, до и после подключения signaltap, может увидите проблему(если timequest используется)

 

 

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


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

Вы не привели временные ограничения, помимо самого кода. 99% что есть временные ошибки, смотрите timequest - все ограничения должны быть прописаны и выполнены, только в этом случае все будет гарантированно работать. Тут ведь как может быть - пины и сигналы, которые вытягиваете на signaltap расположены в одном конце кристалла, а сам jtag в диаметрально противоположном, вот и не вытягивается даже вроде бы и огромные 20нс.

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


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

Констрейн на клок прописан стандартный, вида: create_base_clock -name "clk_50" [get_ports "CLOCK_50"]

А какие ещё констрейны нужны именно этому простому куску кода?

 

Он больше ни от чего не зависит.

И проблема не только в том, что в сигнал тапе видно асинхронщину, а в том, что сам SPI работать перестаёт.

 

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

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


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

Он больше ни от чего не зависит.

И проблема не только в том, что в сигнал тапе видно асинхронщину, а в том, что сам SPI работать перестаёт.

У вашего куска кода есть входные/выходные данные?

Вроде бы всё нормально, синхронная логика, SPI работает межмодульно внутри чипа, наружу его пины не выводятся.

Извиняюсь, невнимательно прочитал, если частота задана и вся логика работает внутри одного клокового домена, то тут надо только timequest открывать и анализировать.

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


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

Вот взять, к примеру, регистр spi_cnt. Он считает на уменьшение, когда модуль активен и когда регистр предделителя равен нулю.

Но в сигналтапе я вижу, что он дополнительно переключается ещё и в момент, когда spi_div равен двум.

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

Но почему квартус устроил такую кашу?

 

У вашего куска кода есть входные/выходные данные?

 

Извиняюсь, невнимательно прочитал, если частота задана и вся логика работает внутри одного клокового домена, то тут надо только timequest открывать и анализировать.

Данные записываются через порт авалон процессором, встроенным в SoC.

Да, сам порт и модуль spi тактируются одним клоком.

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


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

Вот взять, к примеру, регистр spi_cnt. Он считает на уменьшение, когда модуль активен и когда регистр предделителя равен нулю.

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

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


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

Но почему квартус устроил такую кашу?

Кстати, убедитесь, что стробирован rst, из приведенного кода это не ясно.

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


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

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

Понял вас.

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

Но на самом деле там все в норме.

 

А вот времянки по spi не видно, надо будет хорошенько поискать в таймквесте именно их...

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


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

Доброго времени суток!

 

Есть проектик (не мой)

В нём есть простой модуль SPI:

 

Неужели неграмотно написан модуль?

 

Может ли быть проблема в том, что код разбит на куски с отдельными always для каждого регистра?

Вроде бы коллеги говорят, что это наоборот - грамотно?

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

Буду его использовать как пример "тупого" кодирования...

 

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


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

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

Правильно делает, нужно описывать все, а то в результате на одной плате работает, на другой нет, в помещении работает/на улице нет и т.д.

Но на самом деле там все в норме.

С чего Вы взяли, то что тест прошел?

А вот времянки по spi не видно, надо будет хорошенько поискать в таймквесте именно их...

Сгенерируйте отчеты по всем клокам и найдите требуемый. Если требуемого не увидели, то сгенерируйте отчет по unconstraint, может быть он будет в списе игнорируемых, в общем для начала убедитесь, что он есть на самом деле.

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


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

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

Буду его использовать как пример "тупого" кодирования...

В смысле - с кодом всё таки что-то не так?

Файл отправил вам на почту.

 

Правильно делает, нужно описывать все, а то в результате на одной плате работает, на другой нет, в помещении работает/на улице нет и т.д.

 

С чего Вы взяли, то что тест прошел?

 

Сгенерируйте отчеты по всем клокам и найдите требуемый. Если требуемого не увидели, то сгенерируйте отчет по unconstraint, может быть он будет в списе игнорируемых, в общем для начала убедитесь, что он есть на самом деле.

Я совершенно с вами согласен, так оно и получается, я уже на своей шкуре и именно на этом проекте это испытываю.

Но я новичок, и пока что не представляю, как сделать констрейны на сложные места. Тут вот с простым SPI не так всё просто получается... :(

 

Тест прошёл потому, что проект работает, это проект ретрокомпьютера Amiga - Minimig.

Я его портирую на плату DE1-SoC, весьма интересное в целях обучения занятие.

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

 

Спасибо за советы, буду искать этот клок в таймквесте.

 

ЗЫ: как то обидно, что стомегагерцовые связи SDRAM и её контроллер получилось увидеть в сигналтап, а медленный и простой SPI нет... :(

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


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

ЗЫ: как то обидно, что стомегагерцовые связи SDRAM и её контроллер получилось увидеть в сигналтап, а медленный и простой SPI нет... :(

Не знаю насчет конкретно Вашего контроллера, но для DDR3 или ethernet гигабитного, визард на корки автоматически герерирует все ограничения (и грамотную пересинхронизацию) и пользователю достаточно запустить tcl-скрипт, чтобы все это подключить. К контроллеру Вы скорее всего цепляетесь на типовые регистровые точки, не критические, типа init_done, calibration_sucсs, или входную/выходную шины данных.

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


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

А никого не смущает куча вот таких 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

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


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

А никого не смущает куча вот таких always'ов, где работа ведется с сигналами, отсутствующими в списке чувствительности:

Меня не смущает. Это значит, что результат изменения этих сигналов будет зафиксирован под клоки. Это и называется "синхронное проектирование"...

А вот там, где

always @ (*) begin

и далее мешанина асинхронного коммутатора - смущает...

 

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


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

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

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

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

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

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

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

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

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

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