МАРСОХОД

Open Source Hardware Project

Язык описания аппаратуры Verilog HDL

Счетчики.

В этой статье я постараюсь рассказать про счетчики, про их описание на Verilog и их схемотехническое представление в RTLViever.

Счетчики широко применяются везде, где нужно посчитать число некоторых событий, да и не только для этого Smile

Двоичный счетчик.

Вот это просто двоичный счетчик. На входе данных группы триггеров стоит сумматор. Одно из слагаемых для сумматора - это предыдущее значение счетчика, а второе слагаемое - константа "единица".


reg [3:0]counter;
always @(posedge clk)
  counter <= counter + 1'd1;


Вот представление этого счетчика в RTLViewer:

схема двоичного счетчика

К сожалению симулировать такой счетчик не получится, так как симулятору не известно начальное значение регистров counter[3:0], значит он не сможет вычислить и все последующие значения счетчика. Чтобы провести симуляцию нам нужен двоичный счетчик с ассинхронным сбросом.

Двоичный счетчик с асинхронным сбросом.


reg [3:0]counter;
always @(posedge clk or posedge reset)
  if(reset)
    counter <= 4'd0;
  else
    counter <= counter + 1'd1;


Вот его схемотехническое представление в RTLViewer:

схема двоичного счетчика с ассинхронным сбросом

И вот его временная диаграмма:

временная диаграмма схемы двоичного счетчика

Видно, что по фронту тактовой частоты в регистры счетчика будет записываться очередное значение, на единицу большее, чем предыдущее. Если в коде Verilog использовать минус, вместо плюса, то  счетчик будет считать в обратную сторону.

Еще раз подчеркну, что ассинхронный сброс или установка как правило используются только в самом начале работы устройства.

Двоичный счетчик с синхронным сбросом.

Здесь обнуление регистров счетчика происходит по фронту тактовой частоты. В схеме появляется мультиплексор, который выбирает для записи в регистры счетчика либо следующее значение, либо ноль.


always @(posedge clk)
  if(reset)
    counter <= 4'd0;
  else
    counter <= counter + 1'd1;


Вот представление в RTL:

схема двоичного счетчика с синхронным сбросом

И его временная диаграмма:

временная диаграмма счетчика с синхронным сбросом

Счетчик с асинхронным сбросом и входом разрешения и сигналом загрузки.
Чтобы как-то разнообразить описание счетчиков я решил в этом примере дать более "осмысленные" имена сигналам. Представим себе, что мы разрабатываем свой процессор:

  • В процессоре есть указатель на исполняемую инструкцию instr_ptr[15:0].
  • После сброса системы по сигналу reset этот указатель instr_ptr устанавливается на инструкцию по адресу ноль.
  •  С каждым тактом исполнение программы движется вперед - значение instr_ptr увеличивается и каждый раз выбирается следующая команда.
  • Предположим, что некоторые инструкции исполняются дольше других. Это могут быть какие нибудь сложные команды типа умножения или деления. В этом случае АЛУ процессора (арифметико-логическое устройство) выдает нам сигнал cpu_wait вставляя такты ожидания. В эти такты ожидания instr_ptr не изменяется.
  • Если же в какой-то момент времени дешифратор команд увидит команду безусловного перехода (jmp) или условного перехода (jz, jnz, jc и т.д.) и нужно перейти на другой адрес, то нам приходит сигнал branch_cond и в счетчик команд загружается адрес перехода branch_addr.

Вот так это все может быть записано на Verilog:


always @(posedge clk or posedge reset)
begin
 if
(reset)
   instr_ptr <= 16'd0;
 else
 if
(~cpu_wait)
 begin
  if
(branch_cond)
   instr_ptr <= branch_addr;
  else
   instr_ptr <= instr_ptr + 1'd1;
 end
end


Давайте рассмотрим, что представляет из себя этот код в RTLViewer:

Счетчик с загрузкой начального значения счета

А вот временная диаграмма:

Счетчик команд процессора

На этой диаграмме, как пример,  видно такт ожидания на инструкции процессора по адресу 3 и переход с адреса 6 на адрес 35.

Еще счетчики могут быть не только двоичными. Вот, например:

Счетчик по модулю 10.

В этом примере максимальное число в регистре счетчика - это девять. Сигнал cout активен в этот последний такт счета. Следующий такт записывает в регистр ноль. Всего тактов, включая нулевой, получается десять. Отсюда и название - по модулю 10.

Понятно, что аналогично можно строить счетчики по любому модулю.


wire cout;
reg [3:0]counter;

always @(posedge clk or posedge reset)
begin
 if
(reset)
  counter <= 4'd0;
 else
 if
(counter==4'd9)
  counter <= 4'b0;
 else
  counter <= counter + 1'd1;
end

assign cout = (counter==4'd9);


В представлении RTL видно, что кроме собственно регистра у нас есть еще сумматор и компаратор. Сумматор подготавливает для записи следующее значение счетчика, то есть counter+1. Компаратор сравнивает текущее значение счетчика с последним в последовательности (у нас это число 9).

десятичный счетчик

Считает эта схема вот так:

временная диаграмма для десятичного счетчика

Сигнал cout удобно использовать, например, для каскадного соединения счетчиков как сигнал разрешения счета для старших десятичных разрядов.

В следующей статье я хотел рассказать про сдвиговые регистры.

 

 

Комментарии  

+1 #14 son313 13.03.2015 12:35
Всем привет. Спасибо что прошлый раз помогли с шинником 74245 . ) Если кто знает, подскажите как на verilog реализовать К155АГ3 . За ранее благодарен. )
+2 #13 nckm 26.03.2014 11:17
Цитирую newbee:
Не могу сообразить, как сделать такой сброс у счетчика, который реагирует ТОЛЬКО на фронт сброса? То есть, вот пришел сброс, и держится на постоянном уровне. Счетчик должен сброситься по его пришедшему фронту, а потом продолжать считать, хотя уровень сброса активен.

у стандартного триггера есть только один вход чувствительный к фронту, поэтому не получается написать то, что вы хотите.
Вообще, по хорошему, такие конструкции как вы хотите лучше и не пытаться делать. В идеале схема тактируется только одной тактовой частотой. В этом случае все будет работать понятно и предсказуемо.
Внешние входные воздействия лучше сразу оцифровать тактовой частотой и потом выделять фронты примерно вот так: https://marsohod.org/index.php/verilog/157-verilogedges
+1 #12 newbee 25.03.2014 18:24
Не могу сообразить, как сделать такой сброс у счетчика, который реагирует ТОЛЬКО на фронт сброса? То есть, вот пришел сброс, и держится на постоянном уровне. Счетчик должен сброситься по его пришедшему фронту, а потом продолжать считать, хотя уровень сброса активен.
+2 #11 ohmjke 02.08.2013 02:29
Цитирую shlash:
Ну вот, и здесь ничего нет.
Дополните, пожалуйста, вычитающими, реверсивными и многоразрядными (десятичные), формирование сигнала cin для старших разрядов.

Не поймите меня неправильно, но, я считаю, что если человек занялся изучением программируемой логики, то он достаточно хорошо разбирается в "обычной" цифровой технике, следовательно, для Вас не должно быть проблемой самостоятельно написать то, что Вы перечислили, опираясь на этот пример.
+1 #10 shlash 21.09.2012 09:55
Ну вот, и здесь ничего нет.
Дополните, пожалуйста, вычитающими, реверсивными и многоразрядными (десятичные), формирование сигнала cin для старших разрядов.
+1 #9 Alexey 28.04.2011 13:34
Петр если триггеров хватит! Вы размещаете 1001 триггер таким объявлением [1000:0]reg.
+1 #8 Петр 28.04.2011 05:52
Не понял, почему весь комментарий не отобразился?
0 #7 Петр 28.04.2011 05:50
Еще вопрос. Может не по теме, если что - смело удаляйте.
Скачал Ваши уроки по Verilog - осилил с пятого раза, вроде как разъясняется, споткнулся на следующем.
На странице 27 Вы объясняете блокирующие и неблокирующие присваивания. Вроде как понятно. НО!
На странице 28 Вы используете неблокирующее присваивание
+1 #6 Петр 28.04.2011 05:36
Все понятно!
Спасибо!
А я могу объявить reg[1000:0]?
+1 #5 nckm_ 28.04.2011 05:22
Цитирую Петр:
Попытаюсь правильно сформулировать вопрос.
Вы используете переменную для хранения
counter типа reg[3:0].
В данной переменной мы можем хранить 2^3=8.
Какова наибольшая размерность reg может быть объявлена?

объявление "reg [3:0]counter" обозначает 4х битный регистр. Биты нумеруются от 0 до 3 включительно.
Значит максимальное хранимое число будет 15.
В двоичном виде это 4'b1111 или в десятичном 4'd15 или в шестнадцатеричн ом виде 4'hf
+1 #4 nckm_ 28.04.2011 05:19
Цитирую dm.urievich:
Что-то картинки не показываются :sad:

странно - у меня все видно..
+1 #3 Петр 28.04.2011 04:32
Что значит 4'b0, 4'd9
0 #2 Петр 28.04.2011 04:27
Попытаюсь правильно сформулировать вопрос.
Вы используете переменную для хранения
counter типа reg[3:0].
В данной переменной мы можем хранить 2^3=8.
Какова наибольшая размерность reg может быть объявлена?
0 #1 dm.urievich 27.04.2011 17:40
Что-то картинки не показываются :sad:

Добавить комментарий


Защитный код
Обновить


GitHub YouTube Twitter
Вы здесь: Начало Verilog Счетчики.