Как известно, все цифровые схемы строятся по общему принципу «машины состояния». Вся схема проекта состоит как-бы из двух частей: регистров, которые хранят текущее состояние системы и комбинационной логики, которая вычисляет из текущего состояния машины ее следующее состояние. Вся схема тактируется некой опорной частотой. Обычно по фронту сигнала тактовой частоты регистры производят запоминание следующего вычисленного состояния, и так происходит каждый такт. Так «машина состояний» работает, переходя из одного состояния в другое, третье и так далее.
Вот меня интересуют такие вопросы:
- Почему цифровые схемы используют только один фронт сигнала тактовой частоты?
- Почему нельзя использовать и фронт и спад?
- Существуют ли в природе регистры, которые могли бы запоминать состояние и по фронту и по спаду одновременно?
- Будет ли выигрыш по быстродействию или по потреблению энергии при использовании таких регистров?
Могу ли я сам ответить хотя бы на некоторые из этих вопросов?
И действительно. Если посмотреть внимательно на свойства языка описания аппаратуры Verilog HDL, то оказывается, что в нем похоже НЕТ конструкций, которые бы описывали триггер работающий по фронту и спаду одновременно. Вот здесь я уже писал про триггера.
Триггер может запоминать по фронту:
always @(posedge clock)
Reg_a <= next_value;
Или триггер может запоминать по спаду:
always @(negedge clock)
Reg_a <= next_value;
А вот почему-то так написать нельзя:
always @(anyedge clock)
Reg_a <= next_value;
Вот так, то же нельзя:
always @(posedge clock or negedge clock)
Reg_a <= next_value;
В этих случаях компилятор выдает ошибку.
Понятно, что Verilog, как язык, отражает текущее состояние технологии производства цифровых микросхем. В современной технологии производства микросхем используются регистры вида DFF – это обычный Data Flip-Flop. Он работает только по фронту или только по спаду – иначе никак. Это SET ( Single Edge Triggered ) Flip-Flop.
Как устроен обычный DFF? На сам деле схем триггера бывает много разных. Одну из них я даже когда-то пытался симулировать еще в среде Quartus 9.
Проще всего DFF представить в виде двух последовательных защелок ( latch ). Вот две схемы защелок:
Первая схема запоминает входные данные при положительном уровне сигнала clock, при нуле на сигнале clock данные свободно проходят со входа D на выход Q.
Вторая схема наоборот, запоминает данные при нуле на сигнале clock, а пропускает со входа D на выход Q при clock=1.
Если теперь соединить эти две схемы последовательно, то получается классический триггер DFF, запоминающий входные данные только по фронту сигнала тактовой частоты:
Принцип действия довольно простой. Когда наступает фронт сигнала CLK, то первая защелка фиксирует входное значение со входа данных D. При этом, вторая защелка свободно пропускает этот сигнал на выход Q. Когда сигнал CLK переходит в «ноль», то уже вторая защелка начинает удерживать хранимое значение, а вот первая защелка начинает передавать сигнал D.
Размышляя о принципе действия обычного триггера ( Single Edge Triggered ) DFF, можно придумать, как реализовать триггер, работающий и по фронту и по спаду. Для этого нужен еще один мультиплексор.
Назовем этот «новый» триггер Double Edge Triggered Flip-Flop – DET FF.
Сделаем вот так:
Эта штука должна работать так. Две первых защелки работают в противофазе. Верхняя держит запомненный по спаду CLK сигнал со входа D, а вторая держит сигнал запомненный по фронту CLK. Выходной мультиплексор выбирает ту защелку, которая в данный момент удерживает запомненное значение.
Давайте попробуем просимулировать такое устройство. Действительно ли оно может работать?
Я описал такую схему DETFF на языке Verilog:
module detff(
input wire d,
input wire c,
output reg q
);
wire nc; assign nc=~c;
//latch0
reg q_0;
always @*
if(c)
q_0 = d;
else
q_0 = q_0;
//latch1
reg q_1;
always @*
if(nc)
q_1 = d;
else
q_1 = q_1;
//output mux
always @*
q = c ? q_1 : q_0;
endmodule
Теперь нам нужен тестбенч, который бы тестировал триггер. В тестбенче, на том же языке Verilog, опишем тактовую частоту и поступающие на экземпляр триггера наши псевдо-случайные данные.
`timescale 1ns / 1ns
module testbench;
//simulate external crystal 100Mhz
reg clk;
initial clk = 1'b0;
always
#20 clk = ~clk;
//generate pseudo-data
reg data;
initial data = 1'b0;
always
#7 data = ~data;
//spy on output q
wire q;
//instance of module being studied
detff tr_inst(
.c(clk),
.d(data),
.q(q)
);
initial
begin
$dumpfile("out.vcd");
$dumpvars(0,testbench);
#1000;
$finish;
end
endmodule
Компилирую и симулирую с помощью простейшего средства Icarus Verilog:
Теперь, просматривая получившиеся выходные данные на триггере с помощью программы gtkwave убеждаюсь, что все работает правильно.
Теперь хорошо бы проверить на реальном железе. Действительно ли это может работать?
Пробовать я буду в плате Марсоход2, но наверняка можно использовать просто Марсоход или любой другой девелопер кит.
Сперва мне нужен модуль многоразрядного регистра DETFF. Я написал его на верилоге и мой модуль принимает параметр – количество разрядов триггера. Я применяю специальную конструкцию языка верилог: generate-endgenerate. Она позволяет создавать экземпляры модулей, например, в цикле, по заданному количеству.
module detffm(
input wire [WIDTH-1:0]d,
input wire c,
output wire [WIDTH-1:0]q
);
parameter WIDTH = 8;
genvar i;
generate
for(i=0; i<WIDTH; i=i+1)
begin:ex
detff x(
.d( d[i] ),
.c( c ),
.q( q[i] )
);
end
endgenerate
endmodule
Теперь можно нарисовать схему всего проекта в графическом редакторе среды Altera Quartus II:
После успешной компиляции я загружаю проект в плату Марсоход2 и вижу как светодиоды платы зажигаются в нужном порядке, так как и должны в двоичном счетчике.
Вы можете и сами убедиться - весь проект можно взять вот здесь:
В самой среде Altera Quartus II после компиляции можно посмотреть что же получилось внутри схемы с помощью RTL Viewer. Вот фрагмент. Видно 32 экземпляра модулей detff, которые сгенерерованы конструкцией Verilog generate-endgenerate:
А каждый модуль выглядит, как и задумывалось:
Итак, похоже все работает! Но можно ли этим пользоваться в реальных проектах?
К сожалению, скорее нет, чем да.
Если внимательно посмотреть на сообщения компилятора Quartus II, то можно увидеть целый ряд неприятных сообщений-предупреждений:
- Verilog HDL Always Construct warning at <location>: inferring latch(es) for variable "<name>", which holds its previous value in one or more paths through the always construct
- Timing Analysis is analyzing one or more combinational loops as latches
Дело в том, что компиляторы очень не любят защелки. Они не могут толком анализировать временные параметры сигналов, проходящих, через latch.
Вот если бы триггеры DETFF были встроены в ПЛИС на системном уровне, да если бы в Verilog была бы родная поддержка этих триггеров, та если бы временные анализаторы понимали бы Double Edge, вот тогда бы можно было бы их использовать.
Почему, же они не применяются в реальных технологиях?
Я думаю причина в том, что реализация DETFF скорее всего требует больше транзисторов, чем обычный SET DFF. А раз больше транзисторов, то больше площадь кристаллов.
Тем не менее, я думаю, может быть скоро все изменится и использование Double Edge будет вполне популярным и традиционным. Поживем - увидим.
PS: Во время написания статьи нашел в google вот такую статью. Здесь автор доказывает, что DETFF может быть и быстрее и экономичней, чем традиционные DFF.
Подробнее...