МАРСОХОД

Open Source Hardware Project

Язык описания аппаратуры Verilog HDL

Декодер

Декодер - устройство, преобразующее двоичный код в позиционный код.

Рассмотрим полный декодер. Если входных линий декодера N, то выходных линий будет 2^N. например, декодер с тремя входными линиями имеет 2^3=8 выходов. Только на одном из выходов декодера будет единица, на том, номер которого подается на шину входных сигналов. На остальных выходах декодера будет ноль.

Есть несколько способов описать декодер на языке Verilog HDL. Самый очевидный способ - с помощью конструкции языка case-endcase. Вот как можно это сделать:


module my_decoder1(
  input wire [2:0]addr,
  output reg [7:0]selector
);

always @*
begin
  case
(addr)
   3'd0: selector=8'b00000001;
   3'd1: selector=8'b00000010;
   3'd2: selector=8'b00000100;
   3'd3: selector=8'b00001000;
   3'd4: selector=8'b00010000;
   3'd5: selector=8'b00100000;
   3'd6: selector=8'b01000000;
   3'd7: selector=8'b10000000;
  endcase
end
endmodule

Если откомпилировать такой код с помощью Altera Quartus II и потом посмотреть netlist с помощью утилиты RTLViewer из комплекта Quartis II, то мы увидим, что в схеме получился... декодер!

декодер в цифровой схемотехнике

Есть еще один, менее очевидный, но более простой способ описания полного декодера. Вас может удивить его реализация:


module my_decoder2(
  input wire [2:0]addr,
  output reg [7:0]selector
);

always @*
  selector = 8'b00000001 << addr;

endmodule

Этот способ удобен, когда используется большая разрядность входных/выходных сигналов. При этом не нужно делать многострочных записей для case-endcase и вероятность ошибиться при наборе текста при этом меньше. Здесь используется сдвиговый регистр. Регистр selector с начальным значением единица сдвигается влево на столько бит, сколько значение входного сигнала addr.

В RTLViewer результат компиляции будет выглядеть вот так:

 

Реализация декодера через сдвиговый регистр

Казалось бы, причем тут декодер, когда здесь сдвиговый регистр? Я покажу, что эти устройства работают абсолютно одинаково. Для этого напишем тестбенч на языке Verilog:


`timescale 1ns / 1ns

module test();

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

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

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

//моделируем провода подключенные к выходу декодера
//выполненного с помощью case-endcase

wire sel_a0,sel_a1,sel_a2,sel_a3,sel_a4,sel_a5,sel_a6,sel_a7;

//моделируем провода подключенные к выходу декодера
//выполненного на сдвиговом регистре

wire sel_b0,sel_b1,sel_b2,sel_b3,sel_b4,sel_b5,sel_b6,sel_b7;

//вставляем в тестбенч оба экземпляра декодеров:
my_decoder1 decod1(
  .addr(cnt),
  .selector(   {sel_a0,sel_a1,sel_a2,sel_a3,sel_a4,sel_a5,sel_a6,sel_a7} )
);

my_decoder2 decod2(
  .addr(cnt),
  .selector( {sel_b0,sel_b1,sel_b2,sel_b3,sel_b4,sel_b5,sel_b6,sel_b7} )
);

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

endmodule

Симулируем "проект" из трех файлов с помощью icarus verilog:


c:\Altera\marsohod\test_decoder>iverilog -o qqq test_dec.v my_decoder1.v my_decoder2.v
c:\Altera\marsohod\test_decoder>vvp qqq
VCD info: dumpfile out.vcd opened for output.
c:\Altera\marsohod\test_decoder>gtkwave out.vcd

Вот какие временные диаграммы показывает нам GtkWave:

 

временные диаграммы сигналов двоичного декодера

Видно, что сигналы sel_b и sel_a меняются синхронно, значит и устройства абсолютно одинаковые. Скажу даже больше, скорее всего и проект будет откомпилирован для ПЛИС абсолютно одинаково и для случая с case-endcase и для случая со сдвиговым регистром.

Есть еще одна тонкая тонкость при реализации декодеров. Мы рассмотрели случай полного декодера, то есть когда входных сигналов N, а выходных 2^N. Но иногда полный декодер вроде бы и не требуется. Например, нас могут не интересовать некоторые биты выходного селектора, скажем 6й и 7й биты. Так вот, было бы совсем не правильно написать что-то вроде этого:


module my_decoder3(
  input wire [2:0]addr,
  output reg [7:0]selector
);

always @*
begin
  case
(addr)
   3'd0: selector=8'b00000001;
   3'd1: selector=8'b00000010;
   3'd2: selector=8'b00000100;
   3'd3: selector=8'b00001000;
   3'd4: selector=8'b00010000;
   3'd5: selector=8'b00100000;
   /*
   3'd6: selector=8'b01000000;
   3'd7: selector=8'b10000000;
   */

   endcase
end
endmodule

Я закоментировал две строки из case-endcase, но такая реализация ущербна. При такой реализации в проекте появятся элементы памяти в виде защелок (latches). В этом описании получается что при входных значениях 6 или 7 на выходе должно получиться не какое-то новое число, а то, что было раньше. То есть предыдущее значение где-то нужно будет запомнить, вот компилятор и добавит latches. Бойтесь и избегайте их - это зло. Лучше опишите все возможные выходные сигналы для case-endcase, но не используйте их. Не думайте, что сэкономите логические элементы таким способом, скорее получится наоборот.

Я еще напишу про защелки (latch) в других статьях.

 

Комментарии  

+1 #6 nckm 01.10.2014 13:37
Цитирую WolfTheGrey:
Не подскажите, По анологии написал модуль, чтоб многократно не повторять один и тот же код. А как его подключать в другом модуле??? чтоб избежать ошибки:
Error (10159): Verilog HDL error at timer.v(130): "encoder_binaru" is not a task or void function
module abber(input, output);
always @(input)
begin
encoder_binaru(min, LCD_massiv[8:0]);
end
endmodule
module encoder_binaru(input wire[3:0] decimal, output reg[8:0] binaru);
always @*
begin
case(decimal)
0: binaru = 9`b110111100;
1: binaru = 9`b000001100;
2: binaru = 9`b111011000;
3: binaru = 9`b101011100;
4: binaru = 9`b001101100;
5: binaru = 9`b101110100;
6: binaru = 9`b111110100;
7: binaru = 9`b000011100;
8: binaru = 9`b111111100;
9: binaru = 9`b101111100;
10: binaru = 9`b000000000;
endcase
end

endmodule

как вставлять экземпляр модуля написано вот здесь https://marsohod.org/11-blog/79-veriloglesson2
+1 #5 WolfTheGrey 01.10.2014 01:38
Не подскажите, По анологии написал модуль, чтоб многократно не повторять один и тот же код. А как его подключать в другом модуле??? чтоб избежать ошибки:
Error (10159): Verilog HDL error at timer.v(130): "encoder_binaru " is not a task or void function
module abber(input, output);
always @(input)
begin
encoder_binaru( min, LCD_massiv[8:0] );
end
endmodule
module encoder_binaru( input wire[3:0] decimal, output reg[8:0] binaru);
always @*
begin
case(decimal)
0: binaru = 9`b110111100;
1: binaru = 9`b000001100;
2: binaru = 9`b111011000;
3: binaru = 9`b101011100;
4: binaru = 9`b001101100;
5: binaru = 9`b101110100;
6: binaru = 9`b111110100;
7: binaru = 9`b000011100;
8: binaru = 9`b111111100;
9: binaru = 9`b101111100;
10: binaru = 9`b000000000;
endcase
end

endmodule
+2 #4 ohmjke 02.08.2013 02:37
Цитирую nckm:
Цитирую Лексей:
Вы говорите, что оба варианта одинаковые, а разве во втором варианте не потребуются D — триггеры?

Нет. D-триггеры получатся только если использовать чувствительный к фронту always @(posedge clk).

А я чего-то не понял. Насчёт чувствительност и к фронут - это ясно. Но написано же, что используется сдвиговый регистр, а СР - это цепочка D-триггеров, т.е. синхронная логика.
+2 #3 Лексей 01.08.2013 08:01
Цитирую nckm:
Цитирую Лексей:
Вы говорите, что оба варианта одинаковые, а разве во втором варианте не потребуются D — триггеры?

Нет. D-триггеры получатся только если использовать чувствительный к фронту always @(posedge clk).

Спасибо, разобрался, меня смутило, что "В RTLViewer результат компиляции будет выглядеть вот так: рисунок регистра сдвига"
+1 #2 nckm 01.08.2013 06:40
Цитирую Лексей:
Вы говорите, что оба варианта одинаковые, а разве во втором варианте не потребуются D — триггеры?

Нет. D-триггеры получатся только если использовать чувствительный к фронту always @(posedge clk).
+1 #1 Лексей 01.08.2013 05:54
Вы говорите, что оба варианта одинаковые, а разве во втором варианте не потребуются D — триггеры?

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


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


GitHub YouTube Twitter
Вы здесь: Начало Verilog Декодер