О задержке в флип-флопе
Я хочу буферизовать однобитовый сигнал "сделано" двумя однобитными триггерами. Готовый сигнал будет повышаться только в течение одного такта в моем дизайне. Поэтому я написал следующий код.
//first level buffer done signal for one cycle to get ciphertext_reg ready
always @(posedge clk or posedge rst) begin
if(rst)
done_buf_1 = 1'b0;
else
done_buf_1 = done;
end
//second level buffer
always @(posedge clk or posedge rst) begin
if(rst)
done_buf_2 = 1'b0;
else
done_buf_2 = done_buf_1;
end
В функциональном моделировании я обнаруживаю, что done_buf_1 повышается через один цикл после завершения, но done_buf_2 повышается в то же время, что и done_buf_1.
Чем это объясняется?
Спасибо!
3 ответа
У вас уже есть ответы с решением ("используйте неблокирующие назначения"), но вот попытка объяснить, почему вам нужно это сделать.
Оба ваши always
операторы имеют одно и то же событие, поэтому они могут выполняться в любом порядке. Кажется, что происходит то, что первый работает первым. Когда линия...
done_buf_1 = done;
... ударил, он будет блокироваться, пока назначение не будет завершено (это "блокирующее" назначение). Поэтому done_buf_1 немедленно принимает новое значение. Это отличается от неблокирующей версии...
done_buf_1 <= done;
... в котором говорится "дать done_buf_1 значение выполненного (которое я сейчас оценю) в конце временного интервала".
Теперь мы идем дальше, и done_buf_2 назначен.
done_buf_2 = done_buf_1;
Сейчас если done_buf_1
был обновлен с назначением блокировки, он уже имеет текущее значение done
, и вы увидите, как оба сигнала повышаются одновременно. Если это было неблокирующее назначение, то done_buf_1
по-прежнему имеет предыдущее значение done
, так как он не будет обновлен до конца временного интервала, результатом будет задержка в 2 цикла для done_buf_2
,
Но есть и другая проблема. Помните, я говорил, что оператор всегда может выполняться в любом порядке, потому что события были одинаковыми? Хорошо, если второй будет выполнен первым, код будет работать так, как задумано (db2 = db1; db1 = done;
Нет проблем). Поэтому стоит знать, что использование блокирующих назначений, подобных этому, дает ошибочные результаты, особенно между инструментами. Это может привести к некоторым тонким ошибкам.
Вы используете блокирующие назначения =
моделировать синхронную логику. Вам нужно использовать неблокирующие назначения <=
,
Как уже говорили другие: не используйте блокирующие назначения (=
) за это.
Ключевым моментом является то, что "это" - это работа общения между различными процессами. Условия гонки, присущие блокирующим заданиям, делают это непредсказуемым. VHDL относится к этому настолько серьезно, что он разделяет эти типы назначений так, что вы не можете использовать неправильное (если вы держитесь подальше от общих переменных).
Несколько интересных работ на эту тему от Яна Декалуве: