Возможно, то, о чем я расскажу, где-то подробно описано. Может это "основы основ", которые я ввиду своей неопытности проморгал.
С другой стороны, из этого поста, наверное, можно сделать небольшую статейку-иллюстрацию, ибо пример довольно поучителен.
Все описанное ниже проводилось в среде Quartus II 13.0sp1, железо - плата DE0, плис Cyclone III (
www.terasic.com.tw/cgi-bin/page/archive....guage=English&No=364
)
Тактовая частота - всего лишь 50 МГц, а в программе SignalTap - 100МГц.
Потребовалось мне как-то сделать совершенно примитивный модуль - одновибратор со счетчиком. По сигналу он выдает импульс заданной длины и счетчик, который считает, пока этот импульс держится. Также мне нужен был сигнал сброса. По привычке сделал этот сброс асинхронным.
Получилось что-то такое:
always @(posedge clk or negedge reset_n)
begin
if(~reset_n)
begin
pulseR <= 0;
q_pulseR <= 0;
end
else
begin
if(start)
pulseR <= 1;
if(pulseR)
q_pulseR <= q_pulseR + 1;
else
q_pulseR <= 0;
if(q_pulseR + 1 == width)
pulseR <= 0;
end
end
Ну и, чтобы проверить работу прямо в железе, в программе signaltap, я создал длинный счетчик. примерно так:
reg [31:0] cnt_max;
always @(posedge clk)
cnt_max<=cnt_max+1;
// далее где-то в коде я подаю на вход своего модуля сигнал start:
.start(cnt_max == 15)
Я думаю, бывалые плисоводы сразу догадаются, что из такого может получиться.
Запускаю, смотрю. Не работает: импульс не появляется, счетчик не считает, крокодил не ловится. При том, что в signaltap-e этот самый start виден.
Почему-то не срабатывает условие if(start).
Провожу несколько экспериментов. В частности, когда я сделал сброс синхронным, убрав "or negedge reset_n", все по волшебству заработало.
Ниже скриншот двух случаев: с асинхронным ресетом и с синхронным.
Тактовая частота делается в PLL, все констрейны, связанные с клоками в sdc-файле прописаны. Критических предупреждений не было.
Я очень надеюсь, что бывалые плисоводы смогут в комментариях дать подробный ответ, почему так получается и укажут мне, что я упустил из виду.
Однако, я провел исследования и пришел к следующим выводам:
1) очевидно, конструкция с асинхронным ресетом синтезируется так, что в некоторых случаях при несоблюдении каких-то правил, возникают подобные эффекты.
решение 1: сделать ресет синхронным.
2) комбинационная логика в условии.
решение 2: можно ее предварительно защелкнуть в регистр, и все заработает.
reg start;
always @(posedge clk)
start <= cnt_max == 14; // когда счетчик будет равен 15 на следующем такте, start выставится в 1.
можно также сделать запись в регистр в самом модуле, но тогда появится задержка в 1 такт.
3) использование длинного счетчика. тут на выбор аж три варианта
решение 3: использовать короткий счетчик. Если сделать 8 бит вместо 32 - все будет работать.
решение 4: сделать длинный счетчик с переносом (это я подсмотрел в руководстве "TimeQuest для чайников")
always @(posedge clk)
begin
cnt_max[7:0] <= cnt_max[7:0] + 1;
if(cnt_max[7:0] == 8'hFF)
begin
cnt_max[15:8] <= cnt_max[15:8] + 1;
... // и так далее до старших разрядов
end
end
решение 5: использовать мегафункцию lpm_counter -- не проверял, но по идее она сама должна в зависимости от ПЛИС правильно синтезировать счетчик.