Дешифратор

Дешифратор - это устройство преобразующее один двоичный код в другой код.

Самый простой пример дешифратора - управление семисегментным индикатором. На вход устройства подается 4х битное число, которое определяет отображаемый на индикаторе символ. К 7ми выходам устройства подключаются по отдельности все семь сегментов индикатора.

Управление семисегментным индикатором на Verilog

Вот на этой картинке я показал индикатор и пронумеровал его сегменты по своему разумению. Ниже показаны возможные отображаемые символы и их двоичные коды. Я планирую показывать на индикаторе все возможные символы "0123456789ABCDEF" для 4х битного двоичного числа.
Для управления таким индикатором как нельзя лучше подходит конструкция case-endcase языка Verilog HDL. Можно написать вот такой код на Verilog HDL для управления индикатором:

module indicator16(
 input wire [3:0]code,
 output reg [6:0]segments
);

always @*
begin
  case
(code)
   4'd0:  segments = 7'b0111111;
   4'd1:  segments = 7'b0000110;
   4'd2:  segments = 7'b1011011;
   4'd3:  segments = 7'b1001111;
   4'd4:  segments = 7'b1100110;
   4'd5:  segments = 7'b1101101;
   4'd6:  segments = 7'b1111101;
   4'd7:  segments = 7'b0000111;
   4'd8:  segments = 7'b1111111;
   4'd9:  segments = 7'b1101111;
   4'd10: segments = 7'b1110111;
   4'd11: segments = 7'b1111100;
   4'd12: segments = 7'b0111001;
   4'd13: segments = 7'b1011110;
   4'd14: segments = 7'b1111011;
   4'd15: segments = 7'b1110001;
  endcase
end

endmodule

Если откомпилировать такой модуль с помощью Altera Quartus II и посмотреть результат программой RTL Viewer из комплекта Quartus II, то вы увидите вот такую картину:
Дешифратор с точки зрения RTLViewer Altera Quartus II

Видно, что получившаяся схема как бы состоит из декодера и из семи логических элементов ИЛИ. Эти логические элементы ИЛИ показывают какой сегмент индикатора каким символам принадлежит.

Конечно, не нужно думать, что компилятор Quartus II будет пытаться разместить именно такую схему внутри ПЛИС. RTL Viewer просто показывает логику в человеко-понятном виде. На самом деле, вся логика это просто табличное преобразование одного числа в другое с помощью внутренних LUT (Look-Up Table) ПЛИС.

Попробуем просимулировать наш дешифратор. Напишем к нему тестбенч на Verilog:
`timescale 1ns / 1ns

module test16();

//моделируем тактовую частоту
reg clk;
initial clk=0;
always
  #5 clk=~clk;

//сделаем 4х битный счетчик

reg [3:0]cnt;
initial cnt=0;
always @(posedge clk)
  cnt = cnt+1;

//моделируем провода подключенные к каждому из сегментов индикатора
wire seg0,seg1,seg2,seg3,seg4,seg5,seg6;

//вставляем в тестбенч экземпляр дешифратора
indicator16 indic(
  .code(cnt),
  .segments( {seg6,seg5,seg4,seg3,seg2,seg1,seg0} )
);

initial
begin

  //объявляем генерируемый Waveform файл
  $dumpfile("out.vcd");
  $dumpvars(0,test16);
  #200 $finish;
end

endmodule

Я симулирую с помощью icarus verilog.
Просмотриваем получившихся временные диаграммы с помощью GtkWave.
Я уже писал, как это делается.

Вот наши временные диаграммы для дешифратора:

Временные диаграмма дешифратора 4x16 для управления семисегментным индикатором

Теперь предположим, что нам нужно описать только "десятичный дешифратор". Предположим нам нужно отображать на 7ми сегментном индикаторе только 10 десятичных цифр: "0123456789". В этом случае входной сигнал все равно остается 4х битным и некоторые коды на входе дешифратора являются как бы лишними. Что делать если на вход будут поданы коды с 10го по 15й?

При описании такого дешифратора все равно нужно декларировать отклик на эти "запретные" коды. Делается это той же самой конструкцией case-endcase языка Verilog, а для ненужных кодов все равно надо придумать символ на индикаторе (например минус "-" или пробел " ").

module indicator10(
 input wire [3:0]code,
 output reg [6:0]segments
);

always @*
begin
  case
(code)
   4'd0:  segments = 7'b0111111;
   4'd1:  segments = 7'b0000110;
   4'd2:  segments = 7'b1011011;
   4'd3:  segments = 7'b1001111;
   4'd4:  segments = 7'b1100110;
   4'd5:  segments = 7'b1101101;
   4'd6:  segments = 7'b1111101;
   4'd7:  segments = 7'b0000111;
   4'd8:  segments = 7'b1111111;
   4'd9:  segments = 7'b1101111;
  default:
   segments = 7'b1000000;
endcase
end
endmodule

Обратите внимание, теперь внутри конструкции case-endcase появилось еще одно ключевое слово - default. В данном случае это означает, что для всех входных code, которые не описаны отдельной строкой выход будет принимать значение 7'b1000000.

RTLViewer после компиляции покажет примерно такую же картинку, как и раньше - один декодер и несколько WideOr.
Дешифратор для семисегментного индикатора на Verilog

Функциональная симуляция с помощью icarus verilog и gtkwave дает вот такой результат:

Временные диаграммы для дешифратора семисегментного индикатора

Видно, что для кодов 10-15 (в шестнадцатеричном виде это A,B,C,D,E,F) на индикаторе будет гореть только сегмент 6, то есть отобразается символ минус "-".

Пожалуйста не забывайте декларировать все случаи для case-endcase.

Если в последнем примере не написать эти строчки "default: segments = 7'b1000000;", то получится очень нехороший результат. Компилятор считает, что для кодов с 0-го по 9-й нужно на выход выдавать определенный код. А что делать для остальных входных кодов с 10го по 15й не описано, а значит он считает, что на выходе должно быть предыдущее значение. Сделать это можно только если в схеме появятся элементы памяти в виде защелок (latch). Их поведение вообще-то не очень предсказуемо. Точнее говоря, симулятору не известно их начальное состояние. Такую схему трудно симулировать. Да и компилятор Quartus II выдаст предупреждение "Latch segments $latch has unsafe behaviur". А уж что вы увидите в RTLViewer вообще теперь выглядит ужасно:

Защелки (lftches) в дешифраторе

Правило простое - в проекте не должно быть защелек (latches). Если они появились в результате компиляции, то это признак недоработки в проекте.

Опасайтесь и избегайте защелек!

 


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