МАРСОХОД

Open Source Hardware Project

Проекты Altera Quartus II для платы Марсоход2

Проект-исследование счетчика на DET Flip-Flop

Типичная машина состояний
Как известно, все цифровые схемы строятся по общему принципу «машины состояния». Вся схема проекта состоит как-бы из двух частей: регистров, которые хранят текущее состояние системы и комбинационной логики, которая вычисляет из текущего состояния машины ее следующее состояние. Вся схема тактируется некой опорной частотой. Обычно по фронту сигнала тактовой частоты регистры производят запоминание следующего вычисленного состояния, и так происходит каждый такт. Так «машина состояний» работает, переходя из одного состояния в другое, третье и так далее.

Вот меня интересуют такие вопросы:

  1. Почему цифровые схемы используют только один фронт сигнала тактовой частоты?
  2. Почему нельзя использовать и фронт и спад? 
  3. Существуют ли в природе регистры, которые могли бы запоминать состояние и по фронту и по спаду одновременно?
  4. Будет ли выигрыш по быстродействию или по потреблению энергии при использовании таких регистров?

Могу ли я сам ответить хотя бы на некоторые из этих вопросов?



И действительно. Если посмотреть внимательно на свойства языка описания аппаратуры 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 ). Вот две схемы защелок:

Latches, защелка на мультиплексоре

Первая схема запоминает входные данные при положительном уровне сигнала clock, при нуле на сигнале clock данные свободно проходят со входа D на выход Q.

Вторая схема наоборот, запоминает данные при нуле на сигнале clock, а пропускает со входа D на выход Q при clock=1.

Если теперь соединить эти две схемы последовательно, то получается классический триггер DFF, запоминающий входные данные только по фронту сигнала тактовой частоты:

Схема триггера DFF

Принцип действия довольно простой. Когда наступает фронт сигнала CLK, то первая защелка фиксирует входное значение со входа данных D. При этом, вторая защелка свободно пропускает этот сигнал на выход Q. Когда сигнал CLK переходит в «ноль», то уже вторая защелка начинает удерживать хранимое значение, а вот первая защелка начинает передавать сигнал D.

Размышляя о принципе действия обычного триггера ( Single Edge Triggered ) DFF, можно придумать, как реализовать триггер, работающий и по фронту и по спаду. Для этого нужен еще один мультиплексор.

Назовем этот «новый» триггер Double Edge Triggered Flip-Flop – DET FF.

Сделаем вот так:

Double Edge Triggered Flip-Flop

Эта штука должна работать так. Две первых защелки работают в противофазе. Верхняя держит запомненный по спаду 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:

Симуляция с Icarus Verilog

Теперь, просматривая получившиеся выходные данные на триггере с помощью программы gtkwave убеждаюсь, что все работает правильно.

Симуляция Double Edge Triggered Flip-Flop

Теперь хорошо бы проверить на реальном железе. Действительно ли это может работать?

Пробовать я буду в плате Марсоход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:

Проект в среде Altera Quartus II

 

После успешной компиляции я загружаю проект в плату Марсоход2 и вижу как светодиоды платы зажигаются в нужном порядке, так как и должны в двоичном счетчике.

Вы можете и сами убедиться - весь проект можно взять вот здесь:

В самой среде Altera Quartus II после компиляции можно посмотреть что же получилось внутри схемы с помощью RTL Viewer. Вот фрагмент. Видно 32 экземпляра модулей detff, которые сгенерерованы конструкцией Verilog generate-endgenerate:

DETFF в RTLViewer

А каждый модуль выглядит, как и задумывалось:

DETFF, Double Edge Triggered Flip-Flop

Итак, похоже все работает! Но можно ли этим пользоваться в реальных проектах?

К сожалению, скорее нет, чем да.

Если внимательно посмотреть на сообщения компилятора Quartus II, то можно увидеть целый ряд неприятных сообщений-предупреждений:

  1. 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
  2. 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.

 

Комментарии  

+1 #23 Алексей 26.03.2013 14:38
Применял подобную конструкцию. В ней имел место один неприятный момент. Задержка на выходном мультиплексоре была меньше чем на триггере, поэтому в момент переключения возникали пики(риски). Попробую пояснить "рисунком":

CLK 0000011111000
Q_POS 1111111000000
Q_NEG 0000000000000
OUT 0000011000000

Поэтому перед мультиплексором ставил штук 6-8 инверторов, чтобы отодвинуть клоковый сигнал приходящий на мультиплексор.
+2 #22 ReAl 23.11.2012 14:42
Цитирую Alex:
вот по теме http://bsvi.ru/dual-edge-flip-flop/

Ну это практически то же самое, что у меня.
Если подать на оба d0, d1 одно и то же, на два клока взаимно-инверсн ые и учесть, что
if (in == 1) ff_rise = !ff_fall;
else ff_rise = ff_fall;
это то же, что
ff_rise = in ^ ff_fall;
то разница останется в отсутствии у меня асинхронных сброса/установк и, а «клоковое» ядро будет 1:1
+1 #21 ReAl 23.11.2012 14:36
Цитирую afad:
обрезает на знаке "меньше", может так пройдет:
Когда у меня порещало на «меньше» -- я просто в тексте html-код знака набирал,
& l t ;
(пробелы убрать)
+2 #20 afad 23.11.2012 09:02
У[censored] обрезает на знаке "меньше", может так пройдет:
module detff(
output q,
input d, c );

reg qq0, qq1;
assign q = qq1 ^ qq0;
always @(posedge c) qq0 "меньше"= d ^ qq1;
always @(negedge c) qq1 "меньше"= d ^ qq0;
endmodule
+1 #19 afad 23.11.2012 08:58
Вот описание целиком:
module detff(
output q,
input d, c );

reg qq0, qq1;
assign q = qq1 ^ qq0;
always @(posedge c) qq0
+1 #18 afad 23.11.2012 08:56
Вот еще вариант защелки по фронту и спаду, сделан на основе двухклокового триггера, предложенного ReAl:
module detff(
output q,
input d, c );

reg qq0, qq1;
assign q = qq1 ^ qq0;
always @(posedge c) qq0
+2 #17 Alex 23.11.2012 07:32
вот по теме http://bsvi.ru/dual-edge-flip-flop/
+1 #16 afad 22.11.2012 08:58
Да, это работает. Замечательно, что схема симметричная. Спасибо ReAl!
+2 #15 ReAl 21.11.2012 22:10
Цитирую ReAl:
Если этому «триггеру»
Поломалось на знаке «меньше»
Обсуждение там:
http://electronix.ru/forum?s=&showtopic=80167&view=findpost&p=831264
Если на вход d0 подать 0, а на d1 подать 1, то будет то, что Вы хотите:

module ff2chan (
output q,
input c0, d0, e0, c1, d1, e1 );

reg qq0, qq1;
assign q = qq1 ^ qq0;
always @(posedge c0) if( e0 ) qq0 <= d0 ^ qq1;
always @(posedge c1) if( e1 ) qq1 <= d1 ^ qq0;
endmodule
+1 #14 ReAl 21.11.2012 22:06
Цитирую afad:
сделать триггер, который бы по фронту одного сигнала устанавливался в "1", а по фронту другого - в "0".
Используя два триггера, каждый из которых переключается по своему сигналу. Если этому «триггеру»
module ff2chan (
output q,
input c0, d0, e0, c1, d1, e1);
reg qq0, qq1;
assign q = qq1 ^ qq0;
always @(posedge c0) if( e0 ) qq0
+1 #13 ReAl 21.11.2012 21:56
Цитирую Саша:
always @ (CLK)
и синтезер квартус это понимает и альторовские ПЛИСы это выполняют
И что он при этом синтезирует?
+1 #12 саша 21.11.2012 18:18
если ты хочешь и по фронту и по спаду то запись такова

always @ (CLK)always @ (CLK)

и синтезер квартус это понимает и альторовские плисы это выполняюти синтезер квартус это понимает и альторовские ПЛИСы это выполняют
+1 #11 Саша 21.11.2012 18:17
если ты хочешь и по фронту и по спаду то запись такова

always @ (CLK)

и синтезер квартус это понимает и альторовские ПЛИСы это выполняют
+1 #10 afad 21.11.2012 15:10
Цитирую afad:
Verilog, естественно, отказывается это синтезировать.
Имелось ввиду, что написанное на Verilog-e (по фрону одного сигнала устанавливаем reg в "1", по фронту другого - этот же reg в "0") не синтезируется Quartus-ом.
+1 #9 afad 21.11.2012 14:46
Интересовал вопрос, а можно ли сделать триггер, который бы по фронту одного сигнала устанавливался в "1", а по фронту другого - в "0". Verilog, естественно, отказывается это синтезировать. Реализовал так: один из фронтов превратил в в узкий импульс и подал его на асинхронный вход триггера. Но это не совсем то, что хотелось бы получить в идеале.
+1 #8 ReAl 20.11.2012 15:34
Из документации Xilinx CoolRunner-II :

Each macrocell flip-flop is configurable for either single edge or DualEDGE clocking, providing either double data rate capability or the ability to distribute a slower clock (thereby saving power).

Кажется, и у первого CoolRunner-а и даже у того филипсовского проеекта, который xilinx выкупила, это было.
+1 #7 Alex 20.11.2012 09:39
Цитирую nckm:
Цитирую Прохожий:
Значит, память DDR, работающая по обоим фронтам синхросигнала, проектируется не на Verilog?..

Да нет, проектируют на Verilog, но это не очень просто - приходится всякими констрэйнами проекта добиваться компактности расположения логики возле выходав данных идущих на DDR


AFAIK, в DDR одни тригеры работаю по фронту, другие по спаду затем объединяются в более широкую шину 2х16 => 1x32
+1 #6 s0upau1t 19.11.2012 09:11
4. Будет ли выигрыш по быстродействию или по потреблению энергии при использовании таких регистров?

Уточните вопрос.

*. Касательно DDR памяти - посмотрите в IP-ядрах Quartus есть модуль, который как раз реализует механизм работы по двум фронтам. В документации на него все очень доступно.
+1 #5 s0upau1t 19.11.2012 09:10
3. Существуют ли в природе регистры, которые могли бы запоминать состояние и по фронту и по спаду одновременно?

Зачем? В своей сути мы можем заменить схему с таким триггером на логическую функцию. Вы говорили о разделении цифровой схему на логику и память - так не нужно их мешать.

+ мне кажется, Вы не разделяете след.понятия: есть триггеры, которые работают по фронту - flip-flop, а есть, которые по уровню - latch.
+1 #4 s0upau1t 19.11.2012 09:09
2. Почему нельзя использовать и фронт и спад?

Можно. :) Но нужно учитывать, что временные характеристики FET-структур при переключении 0 -> 1 и 1 -> 0 немного отличаются. При проектировании микросхем и переходе от netlist'а к топологии и технологическом у базису есть возможно выбрать из элементов разных типов. Как раз на этом этапе существует специальная группа триггеров с более симметричными временными свойствами.
+1 #3 s0upau1t 19.11.2012 09:08
1. Почему цифровые схемы используют только один фронт сигнала тактовой частоты?

Неправда. Возьмите, например, схему деления частоты на 3 (с результатом в виде меандра). Осуществляется деление на 6, задержка на 1.5 такта в одном канале и xor (вроде) чистого канала и задержанного. Задержка в 1.5 такта реализуется как раз за счет триггеров, один из которых работает по инверсному фронту.
+1 #2 nckm 19.11.2012 06:11
Цитирую Прохожий:
Значит, память DDR, работающая по обоим фронтам синхросигнала, проектируется не на Verilog?..

Да нет, проектируют на Verilog, но это не очень просто - приходится всякими констрэйнами проекта добиваться компактности расположения логики возле выходав данных идущих на DDR
+2 #1 Прохожий 17.11.2012 12:43
Значит, память DDR, работающая по обоим фронтам синхросигнала, проектируется не на Verilog?..

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


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


GitHub YouTube Twitter
Вы здесь: Начало Проекты Проект Марсоход2 Проект-исследование счетчика на DET Flip-Flop