always-блоки, тактовый сигнал и posedge
Вводим время в схему: учимся описывать элементы, которые меняются по фронту тактового сигнала.
Триггер (flip-flop) — элемент памяти на один бит, который запоминает значение входа в момент фронта тактового сигнала и хранит его до следующего фронта.
Комбинационная логика не имеет памяти — она лишь функция входов. Но любая интересная схема (счётчик, автомат, процессор) должна помнить состояние и менять его во времени. Память во времени приносит последовательностная логика, а её сердце — триггер, синхронизированный тактовым сигналом (clock).
Тактовый сигнал — пульс схемы
Тактовый сигнал — это прямоугольная волна, которая ритмично переключается 0→1→0→1 миллионы раз в секунду. Каждый переход из 0 в 1 называют передним фронтом (posedge), из 1 в 0 — задним (negedge). Триггеры «защёлкивают» вход именно по фронту — это превращает непрерывное время в дискретные такты, по которым «шагает» вся схема:
clk: __|‾‾|__|‾‾|__|‾‾|__|‾‾|__
^ ^ ^ ^
posedge — в эти моменты триггеры захватывают входСинхронный дизайн (все триггеры от одного такта) — основа надёжной цифровой схемотехники: события происходят строго по фронтам, и поведение схемы предсказуемо.
always-блок и рождение триггера
Последовательностную логику описывают процедурным блоком always со списком чувствительности. Запись always @(posedge clk) означает: «выполни этот блок на каждом переднем фронте clk». Внутри присваивания создают триггеры:
module dff(
input wire clk, // тактовый сигнал
input wire d, // вход данных
output reg q // выход (хранит бит) — обязательно reg
);
always @(posedge clk) begin
q <= d; // по фронту clk защёлкиваем d в q
end
endmoduleЭто классический D-триггер. Читаем как описание железа: «есть триггер, который на каждом переднем фронте clk копирует d в q». Между фронтами q держит своё значение, что бы ни делал d. Обратите внимание: выход q объявлен reg, потому что ему присваивают внутри always. И присваивание здесь — неблокирующее <= (почему именно оно — тема следующего урока, для тактируемой логики это правильный выбор).
Как работает под капотом
Промоделируем поведение D-триггера во времени на Python: вход d меняется произвольно, но q обновляется только в моменты фронтов такта. Это показывает дискретизацию времени:
# Эмулируем D-триггер: q обновляется только на фронте такта
d_sequence = [1, 0, 0, 1, 1, 0] # значения d перед каждым фронтом
q = 0 # начальное состояние триггера
print("фронт | d (до) | q (после фронта)")
print("------+--------+----------------")
for cycle, d in enumerate(d_sequence):
q = d # защёлкиваем d в q ровно на фронте
print(f" {cycle} | {d} | {q}")Вывод:
фронт | d (до) | q (после фронта) ------+--------+---------------- 0 | 1 | 1 1 | 0 | 0 2 | 0 | 0 3 | 1 | 1 4 | 1 | 1 5 | 0 | 0
Значение q повторяет d, но «снимок» берётся лишь в моменты фронтов. В реальной схеме это и даёт память: между тактами выход стабилен, что позволяет строить конвейеры и автоматы.
Частые ошибки
- Забыть объявить выход как
reg. Сигнал, которому присваивают вalways, должен бытьreg, иначе ошибка. - Смешивать
posedgeразных тактов в одном блоке. Одинalwaysдолжен тактироваться от одного фронта одного сигнала — иначе получится нереализуемое или ненадёжное железо. - Управлять схемой не от такта, а от данных. В синхронном дизайне всё «шагает» от тактового сигнала; самодельные «такты» из данных приводят к глитчам.
Итог
- Последовательностная логика добавляет память во времени через триггеры.
- Тактовый сигнал дискретизирует время; триггеры срабатывают по фронту (posedge).
always @(posedge clk)создаёт тактируемое железо; выходы —reg.- Синхронный дизайн (один такт на всё) — основа надёжной схемы.