1i7

Лабораторная работа 3: делаем память (защелки и триггеры) (2)

Aug 21, 2012 04:05

Продолжение лабороторной работы, начало в предыдущей записи <<



Упражнение 3: D защелка (D latch) на ПЛИС+Verilog

Переходим к более полезной с практической точки зрения реализации защелки - D защелка (D latch). Логическая схема этой защелки выглядит следующим образом:






Упрощенное обозначение D защелки на схемах

Защелка состоит из 2х операторов AND, одного оператора NOT и одной NOR-RS-защелки. Управляющих входа в схему  опять два, но их роль уже другая. Вход D задает значение, которое может быть сохранено в схеме, вход CLK открывает защелку на прием значения с D или закрывает ее так, чтобы она сохранила последнее значение и вход D на него больше не влиял.

Чтобы понять, как это работает, можно опять обратиться к таблице истинности этой схемы:




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 в такой ситуации роли не играет (лампочки не моргают).























Упражнение 4: D триггер (D flip-flop) на ПЛИС+Verilog

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






Упрощенное обозначение 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 никак повлиять на значение триггера не может.




Визуально механизм можно представить в виде переходной камеры на космическом корабле или подводной лодке:



Реализация на ПЛИС+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 после этого).


















Такой механизм позволяет например сделать процесс подачи нового значения в несколько триггеров одновременно более предсказуемым. Допустим, у нас есть несколько однобитных триггеров и мы хотим в них записать какое-то двоичное значение. Конкретный момент появления текущих значений на входных сигналах каждого триггера в общем случае непредсказуем и в случае набора открытых D защелок они могли бы случайным образом асинхронно менять (по сути повредить) их внутреннюю память до того момента, как защелка не захлопнулась. При добавлении буферной зоны из дополнительной D защелки master, непредсказуемость подобного поведения обрезается - если все триггеры подключены к одному и тому же управляющему сигналу (который закрывает и открывает триггер), все значения из буферных защелок отправятся во внутренние значимые защелки одновременно. Также на механизме D триггера основан принцип работы одной из наиболее важной в языке Verilog конструкции 'always @'.






Упражнение 5: 0.5 байта (4 бита) из 4х D триггеров на ПЛИС+Verilog

Все элементарно - подключаем 4 модуля D flip-flop к 4м внешним входам и одному тактовому сигналу CLK (в нашем случае - рычажку на плате) - получаем 4хбитный регистр, запись в который происходит при движении рычажка вниз-вверх.




Для имплементации на 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 бита запасываются в регистр одновременно, о чем сразу сообщают лампочки.

















Заключение.

Очевидно, что для реальной работы с памятью внутри ПЛИС использование таких самописных модулей конечно не требуется - для этого там уже есть встроенные более оптимальные и удобные инструменты, которые называются регистры - например, чтобы объявить переменную Verilog, которая будет уметь запоминать любое значение, достаточно объявить ее с ключевым словом "reg". Но после выполнения данной лабоработорной работы становится понятно, какие механизмы могут лежать в основе этих абстракций.

Кроме того, данная лаба закрепляет навыки работы с макетной платой, которые были получены на первой лабораторной работе и навыки работы с модулями Verilog и ПЛИС, которые были получены на второй.

Код лабы на github, прообраз космонавтов, подсветка синтаксиса

плис, цифровая электроника для программистов, verilog

Previous post Next post
Up