Продолжение лабороторной работы, начало
в предыдущей записи << Упражнение 3: D защелка (D latch) на ПЛИС+Verilog
Переходим к более полезной с практической точки зрения реализации защелки - D защелка (D latch). Логическая схема этой защелки выглядит следующим образом:
![](http://img-fotki.yandex.ru/get/6605/161653612.11/0_8e4fd_96cb2053_L)
![](http://img-fotki.yandex.ru/get/6509/161653612.11/0_8e4fe_660e5ca5_M)
Упрощенное обозначение D защелки на схемах
Защелка состоит из 2х операторов AND, одного оператора NOT и одной NOR-RS-защелки. Управляющих входа в схему опять два, но их роль уже другая. Вход D задает значение, которое может быть сохранено в схеме, вход CLK открывает защелку на прием значения с D или закрывает ее так, чтобы она сохранила последнее значение и вход D на него больше не влиял.
Чтобы понять, как это работает, можно опять обратиться к таблице истинности этой схемы:
![](http://img-fotki.yandex.ru/get/6604/161653612.11/0_8e501_47b7b6ac_L)
1. CLK=1, D=0: верхний AND на входе получает 1 и 1 => R получает 1; нижний AND получает 1 и 0 => S пулучает 0. Получаем ситуацию R=1, S=0 => Q=0, ~Q=1 (см объяснения из упражнения 1 с поправкой на таблицу истинности NOR для упражнения 2) => произошла запись в RS защелку значения 0 (будем считать, что текущее значение защелки это значение выхода Q, ~Q - просто инверсия). За текущее значение D защелки принимаем текущее значени RS защелки.
2. CLK=1, D=1: зеркально предыдущему случаю - в D защелку записали значение 1.
3. CLK=0, D=0/1: Т.к. значение CLK=0, очевидно оба значения выходов-входов R и S равны 0 (действие операторов AND), т.е. RS защелка находится в состоянии запоминания последнего установленного в нее значения, новые значения из D просто никак не влияют на схему, т.е. не принимаются.
Итого получаем:
CLK=1: защелка находится в прозрачном (transparent) состоянии - данные идут от D к Q.
CLK=0: защелка находится в непрозрачном (opaque) состоянии - данные от D к Q не идут, защелка сохраняет установленное значение.
Как видно, преимуществом D защелки заключается как минимум уже в том, что для изменения текущего сохраненного значения используется всего один входной сигнал. Второй управляющий сигнал используется для перевода схемы из состояния "открыто для получения нового значения" (прозначна) в состояние "закрыто - последнее значение сохранено" (непрозрачна) и обратно. При этом любые комбинации состояний входных сигналов защелки являются корректными.
Для лучшего визуального понимания описанного выше принципа работы можно посмотреть такой мультик - подъемная дверь (CLK) пускает или не пускает космонавтов снаружи (D) в капсулу (Q):
Попробуем реализовать эту схему на ПЛИС+Verilog и убедиться, что все работает именно так, как описано на расшифровке схемы.
flip_flops.v и
flip_flops_top.v /**
* D latch
*/
module d_latch(
input clk, input d,
output q, output not_q
);
wire r, s;
assign r = clk & ~d;
assign s = clk & d;
rs_latch rs(.r(r), .s(s), .q(q), .not_q(not_q));
endmodule
// D latch test top module
module d_latch_top(
input [0:0] sw,
input [72:72] pio,
output [0:1] ld
);
d_latch d (.clk(sw[0]), .d(pio[72]),
.q(ld[0]), .not_q(ld[1]));
endmodule
модуль rs_latch из предыдущего упражнения.
basys2_d_latch.ucf # D latch
NET "sw<0>" LOC = "p11";
NET "PIO<72>" LOC = "B2";
NET "ld<1>" LOC = "m11" ;
NET "ld<0>" LOC = "m5" ;
На сигнал D назначили свободный провод PIO, на CLK - рычажок SW (рычажок наверху - CLK=HIGH=1, рычажок внизу - CLK=LOW=0), Q и ~Q - две лампочки LED.
Т.е. когда рычажок находится в верхнем положении (CLK=1) - защелка прозрачна и принимает те значения, которые мы подаем ей через свободный провод D (лампочки моргают). Когда рычажок находится в нижнем положении (CLK=0) - защелка непрозрачна - она сохраняет то значение, которое было подано на D в момент перевода рычажка в нижнее положение, подача новых значений на D в такой ситуации роли не играет (лампочки не моргают).
![](http://img-fotki.yandex.ru/get/6506/161653612.11/0_8f5ed_1911f33a_XL)
![](http://img-fotki.yandex.ru/get/6504/161653612.11/0_8f5ee_3e25afc2_XL)
![](http://img-fotki.yandex.ru/get/6507/161653612.11/0_8f5ef_3e33be08_XL)
![](http://img-fotki.yandex.ru/get/6604/161653612.11/0_8f5f0_c9224b45_XL)
![](http://img-fotki.yandex.ru/get/6506/161653612.11/0_8e242_f5a78a67_XL)
![](http://img-fotki.yandex.ru/get/6606/161653612.11/0_8e243_c0791d92_XL)
Упражнение 4: D триггер (D flip-flop) на ПЛИС+Verilog
Еще немного усложняем предыдущую схему в сторону большей практической полезности - добавляем буферную зону.
![](http://img-fotki.yandex.ru/get/6507/161653612.11/0_8e4fa_a4fea943_L)
![](http://img-fotki.yandex.ru/get/6508/161653612.11/0_8e4fc_a079f9a5_M)
Упрощенное обозначение D триггера на схемах
Особым образом совмещаем на схеме 2 D защелки - получаем D триггер (D flip-flop). Левую D защелку назовем хозяин (master), правую - раб (slave).
Сигнал CLK к обеим защелкам подключен один и тот же, только в одну из защелок он поступает в инвертированном состоянии, т.е. в каждый момент времени одна защелка является прозрачной, а вторая непрозрачной и наоборот. Входной сигнал D, несущий внешнее устанавлимое значение, входит в левую D защелку master; выход R левой защелки master подключен к входу D правой защелки slave. За текущее значение D триггера принимаем значение правой защелки slave.
Таким образом, если вникнуть в эту схему, то можно понять, что входной сигнал D может передать свое значение в определяющую значение D триггера защелку slave только пройдя через буферную защелку master. Для этого нужно:
1. Сначала защелку master сделать прозрачной (CLK=0) так, что она начнет принимать входящий сигнал D - защелка slave при этом будет непрозрачной, поэтому дальше защелки master в этот момент сигнал D не проберется.
2. Когда нужное значение в защелку master установлено, делаем прозрачной уже защелку slave (CLK=1) - значение переходит из защелки master в защелку slave - значение триггера установлено. При этом важно, что в этот же момент сама защелка master переходит из прозрачного в непрозрачное состояние, поэтому процессу передачи значения master->slave внешний сигнал D помешать не может.
Таким образом получается, что запись в D триггер происходит только на ребре (edge) сигнала CLK именно в тот момент, когда он переходит из состояния LOW (0) в HIGH (1) - после этого внешний сигнал D никак повлиять на значение триггера не может.
![](http://img-fotki.yandex.ru/get/6607/161653612.11/0_8e4fb_e7842f98_M)
Визуально механизм можно представить в виде переходной камеры на космическом корабле или подводной лодке:
Реализация на ПЛИС+Verilog:
flip_flops.v и
flip_flops_top.v /**
* D flip-flop
*/
module d_flip_flop(
input clk, input d,
output q, output not_q
);
wire n1;
// master
d_latch l1 (.clk(~clk), .d(d), .q(n1));
// slave
d_latch l2 (.clk(clk), .d(n1), .q(q), .not_q(not_q));
endmodule
// D flip-flop test top module
module d_flip_flop_top(
input [0:0] sw,
input [72:72] pio,
output [0:1] ld
);
d_flip_flop d (.clk(sw[0]), .d(pio[72]),
.q(ld[0]), .not_q(ld[1]));
endmodule
basys2_d_flip_flop.ucf # D flip-flop
NET "sw<0>" LOC = "p11";
NET "PIO<72>" LOC = "B2";
NET "ld<1>" LOC = "m11" ;
NET "ld<0>" LOC = "m5" ;
Устройства ввода-вывода назначаем абсолютно аналогично D защелке. D назначили свободный провод PIO, на CLK - рычажок SW (рычажок наверху - CLK=HIGH=1, рычажок внизу - CLK=LOW=0), Q и ~Q - две лампочки LED.
Рычажок внизу - CLK=0 - защелка master прозрачна - промежуточная капсула открыта и готова принимать внешние значения, защелка slave непрозрачна - получаемы в защелку master значения значение триггера не меняют (лампочка сохраняет состояние вне зависимости от того, куда вставлен провод D).
Рычажок вверху - CLK=1 - защелка slave прозрачна, защелка master непрозрачна - последнее значение из master переходит в slave и более не меняется (лампочка меняет состояние на значение сигнала D в момент перевода рычага в верхнее положение и более не меняется вне зависимости от того, куда вставлен провод D после этого).
![](http://img-fotki.yandex.ru/get/6409/161653612.11/0_8f5e8_68b9e331_XL)
![](http://img-fotki.yandex.ru/get/6607/161653612.11/0_8f5e9_208c3031_XL)
![](http://img-fotki.yandex.ru/get/6604/161653612.11/0_8f5ea_426562e4_XL)
![](http://img-fotki.yandex.ru/get/6508/161653612.11/0_8f5eb_500ec86e_XL)
![](http://img-fotki.yandex.ru/get/6608/161653612.11/0_8f5ec_dff0eee8_XL)
Такой механизм позволяет например сделать процесс подачи нового значения в несколько триггеров одновременно более предсказуемым. Допустим, у нас есть несколько однобитных триггеров и мы хотим в них записать какое-то двоичное значение. Конкретный момент появления текущих значений на входных сигналах каждого триггера в общем случае непредсказуем и в случае набора открытых D защелок они могли бы случайным образом асинхронно менять (по сути повредить) их внутреннюю память до того момента, как защелка не захлопнулась. При добавлении буферной зоны из дополнительной D защелки master, непредсказуемость подобного поведения обрезается - если все триггеры подключены к одному и тому же управляющему сигналу (который закрывает и открывает триггер), все значения из буферных защелок отправятся во внутренние значимые защелки одновременно. Также на механизме D триггера основан принцип работы одной из наиболее важной в языке Verilog конструкции 'always @'.
![](http://img-fotki.yandex.ru/get/6606/161653612.11/0_8e246_764c8eea_XL)
Упражнение 5: 0.5 байта (4 бита) из 4х D триггеров на ПЛИС+Verilog
Все элементарно - подключаем 4 модуля D flip-flop к 4м внешним входам и одному тактовому сигналу CLK (в нашем случае - рычажку на плате) - получаем 4хбитный регистр, запись в который происходит при движении рычажка вниз-вверх.
![](http://img-fotki.yandex.ru/get/6409/161653612.11/0_8e248_62e94fea_XL)
Для имплементации на Verilog на этот раз достаточно добавить только новый топ-модуль - все остальное уже есть.
flip_flops_top.v // 4-bit register with 4 D flip-flops test top module
module d_4bit_register_top(
input [0:0] sw,
input [72:75] pio,
output [0:3] ld
);
d_flip_flop d1 (.clk(sw[0]), .d(pio[72]), .q(ld[3]));
d_flip_flop d2 (.clk(sw[0]), .d(pio[73]), .q(ld[2]));
d_flip_flop d3 (.clk(sw[0]), .d(pio[74]), .q(ld[1]));
d_flip_flop d4 (.clk(sw[0]), .d(pio[75]), .q(ld[0]));
endmodule
basys2_d_4bit_regirster.ucf # 4-bit register with 4 D flip-flops
NET "sw<0>" LOC = "p11";
NET "PIO<72>" LOC = "B2";
NET "PIO<73>" LOC = "A3";
NET "PIO<74>" LOC = "J3";
NET "PIO<75>" LOC = "B5";
NET "ld<3>" LOC = "p6" ;
NET "ld<2>" LOC = "p7" ;
NET "ld<1>" LOC = "m11" ;
NET "ld<0>" LOC = "m5" ;
Ввод-вывод - 4 провода по одному на каждый триггер (т.е. на каждый бит регистра) и 4 лампочки на этот раз по одной на каждый триггер. Рычажок SW - общий CLK. Ввод данных - устанавливаем внешний входы на нужные значения и двигаем рычаг вниз-вверх (в точности как для одного D триггера) - 4 бита запасываются в регистр одновременно, о чем сразу сообщают лампочки.
![](http://img-fotki.yandex.ru/get/6604/161653612.11/0_8f5e5_ea9e7d9b_XL)
![](http://img-fotki.yandex.ru/get/6504/161653612.11/0_8f5e6_c7c24c21_XL)
![](http://img-fotki.yandex.ru/get/6508/161653612.11/0_8f5e7_f8903290_XL)
![](http://img-fotki.yandex.ru/get/6608/161653612.11/0_8e247_3cf178e4_XL)
Заключение.
Очевидно, что для реальной работы с памятью внутри ПЛИС использование таких самописных модулей конечно не требуется - для этого там уже есть встроенные более оптимальные и удобные инструменты, которые называются регистры - например, чтобы объявить переменную Verilog, которая будет уметь запоминать любое значение, достаточно объявить ее с ключевым словом "reg". Но после выполнения данной лабоработорной работы становится понятно, какие механизмы могут лежать в основе этих абстракций.
Кроме того, данная лаба закрепляет навыки работы с макетной платой, которые были получены на первой лабораторной работе и навыки работы с модулями Verilog и ПЛИС, которые были получены на второй.
Код лабы на github,
прообраз космонавтов,
подсветка синтаксиса