Регистры и счётчики
Собираем из триггеров два рабочих блока — регистр и счётчик, без которых не обходится ни одна схема.
Счётчик — регистр, который по каждому фронту такта увеличивает (или уменьшает) своё значение на единицу; основа таймеров, делителей частоты и адресации.
Один триггер хранит один бит. Объединив N триггеров под общим тактом, получаем регистр — память на N бит, обновляющуюся синхронно. А если каждый такт прибавлять к регистру единицу, выйдет счётчик — едва ли не самый востребованный блок в цифровой технике. Счётчики измеряют время, формируют адреса, делят частоту, задают развёртку.
Многоразрядный регистр
Регистр — это просто шина reg, защёлкиваемая по такту. Вот 8-битный регистр с разрешением записи en: он обновляется только когда en=1, иначе хранит старое значение:
module reg8(
input wire clk,
input wire en, // разрешение записи
input wire [7:0] d, // вход данных (8 бит)
output reg [7:0] q // хранимое значение
);
always @(posedge clk) begin
if (en)
q <= d; // пишем только при en=1
// если en=0 — q сохраняет значение (триггер «помнит»)
end
endmoduleСчётчик
Счётчик — регистр, к которому каждый такт прибавляется 1. Вот 4-битный счётчик от 0 до 15 с переполнением (после 15 он сам обнуляется, потому что 4 бита хранят только 0..15):
module counter4(
input wire clk,
input wire rst, // сброс в 0
output reg [3:0] count
);
always @(posedge clk) begin
if (rst)
count <= 4'b0000; // сброс
else
count <= count + 1; // инкремент; на 16-м такте 15+1 переполнится в 0
end
endmoduleКак работает под капотом: переполнение по кругу
Поскольку счётчик ограничен разрядностью, он считает «по кругу»: 4-битный идёт 0,1,...,15,0,1,... Это не ошибка, а полезное свойство — на нём строят делители частоты и циклические таймеры. Промоделируем 3-битный счётчик (0..7) на Python и увидим оборачивание:
# 3-битный счётчик: значения 0..7, затем оборачивается в 0
count = 0
WIDTH = 3
MODULO = 2 ** WIDTH # 8
print("такт | count (dec) | count (bin)")
print("-----+-------------+-----------")
for tick in range(10):
print(f" {tick:2} | {count} | {count:03b}")
count = (count + 1) % MODULO # +1 с оборачиванием по модулю 8Вывод:
такт | count (dec) | count (bin) -----+-------------+----------- 0 | 0 | 000 1 | 1 | 001 2 | 2 | 010 3 | 3 | 011 4 | 4 | 100 5 | 5 | 101 6 | 6 | 110 7 | 7 | 111 8 | 0 | 000 9 | 1 | 001
После 7 счётчик возвращается к 0 — ровно как 4-битный count + 1 после 15. В железе переполнение происходит само: старший бит просто теряется, и значение оборачивается.
Частые ошибки
- Забыть про переполнение. Если нужен счёт до конкретного числа (например, до 9), добавьте условие сброса:
if (count == 9) count <= 0;, иначе счётчик дойдёт до предела разрядности. - Использовать блокирующее
=в счётчике. В тактируемом блоке нужно<=; иначе цепочки счётчиков посчитают неверно. - Недостаточная разрядность. Чтобы досчитать до миллиона, нужно минимум 20 бит (2^20 ≈ 1.05 млн); узкий счётчик переполнится раньше.
Итог
- Регистр — N триггеров под общим тактом; хранит многоразрядное значение.
- Счётчик прибавляет 1 каждый такт и оборачивается по разрядности.
- Разрешение записи
enи сбросrstуправляют поведением регистра. - Для счёта до нужного предела добавляйте явное условие сброса.