Регистры и счётчики

Собираем из триггеров два рабочих блока — регистр и счётчик, без которых не обходится ни одна схема.

Счётчик — регистр, который по каждому фронту такта увеличивает (или уменьшает) своё значение на единицу; основа таймеров, делителей частоты и адресации.

Один триггер хранит один бит. Объединив 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 управляют поведением регистра.
  • Для счёта до нужного предела добавляйте явное условие сброса.
Проверьте себя
1. Что произойдёт с 4-битным счётчиком count <= count + 1 после значения 15?
AОн застрянет на 15
BОн переполнится и станет 0 (счёт по кругу)
CВозникнет ошибка
DОн перейдёт к 16
2. Сколько бит нужно счётчику, чтобы досчитать как минимум до миллиона?
A10 бит
B16 бит
C20 бит
D8 бит
3. Как заставить счётчик считать только до 9, а затем начинать заново?
AУменьшить тактовую частоту
BДобавить условие: если count == 9, сбросить в 0, иначе инкремент
CИспользовать тип wire вместо reg
DЭто невозможно