Демультиплексор выполняет функцию обратную мультиплексору - "подключает" входной сигнал к нужному выходному, номер которого задается селектором. Если входной сигнал - логическая единица, то он ведет себя точно, как декодер. Если же входной сигнал - это ноль, то не зависимо от значения селекторов, на всех выходах будет ноль.
Демультиплексор можно рассматривать как частный случай дешифратора, если считать, что входной полезный переключаемый сигнал и сигналы селекторов - это все просто "входные сигналы".
Посмотрим, как демультиплексор можно описать на языке Verilog.
Я могу предложить несколько способов описания демультиплексора. Некоторые способы будут громоздкими, другие - нет. Выбирайте сами, что вам понятней и удобней.При описании я иногда буду пользоваться специальной конструкцией языка Verilog для объединения нескольких сигналов в группу (шину сигналов): { sigN, ..., sig2 ,sig1, sig0 } - здесь используются фигурные скобки. В такой записи слева расположены сигналы старших разрядов, а справа - сигналы младших разрядов.
Вот первый вариант демультиплексора: однобитный сигнал signal должен появиться на одном из выходов out в "нужном" месте. 2х битный addr определяет позицию signal в выходном сигнале out:
module my_dmux0(
input wire signal,
input wire [1:0]addr,
output reg [3:0]out
);
always @*
begin
case( addr )
2'd0: out = { 3'b000, signal };
2'd1: out = { 2'b00, signal, 1'b0 };
2'd2: out = { 1'b0, signal, 2'b00 };
2'd3: out = { signal, 3'b000 };
endcase
end
Могу напомнить, что запись типа 3'b000 обозначает трехбитный сигнал. В данном случае все биты сброшены в ноль. Запись вида 2'd3 обозначает 2х битный сигнал, но значения битов записаны в десятичном виде. В данном случае - это три. Любое число можно записывать в десятичном, шестнадцатеричном или двоичном формате. Например число 10 - это:
4'd10 или 4'hA или 4'b1010.
Откомпилируем этот модуль в системе Altera Quartus II и посмотрим, что получилось в RTLViewer:
Видно декодер и 4 мультиплексора.
Теперь рассмотрим второй способ описания демультиплексора - входной сигнал signal сдвигаем влево в нужную позицию (на столько бит, какое значение на входах addr) и получаем выходной out:
module my_dmux1(
input wire signal,
input wire [1:0]addr,
output reg [3:0]out
);
always @*
out = signal << addr;
endmodule
Опять компилируем и смотрим в RTLViewer:
Получилось нечто совсем другое, но не удивляйтесь. Позже я покажу, что работают модули одинаково.
Третий способ - описываем демультиплексор, как декодер, при входном сигнале signal равном единице. Иначе - на выходах - ноль:
module my_dmux2(
input wire signal,
input wire [1:0]addr,
output reg [3:0]out
);
always @*
begin
if(signal)
case(addr)
2'd0: out = 4'b0001;
2'd1: out = 4'b0010;
2'd2: out = 4'b0100;
2'd3: out = 4'b1000;
endcase
else
out = 4'b0000;
end
endmodule
Вот картинка в RTLViewer:
Ну и вот последний способ: объединяем однобитный инвертированный signal и 2х битный addr и получаем 3х битный входной код для дешифратора:
module my_dmux3(
input wire signal,
input wire [1:0]addr,
output reg [3:0]out
);
always @*
begin
case( {~signal,addr} )
3'd0: out = 4'b0001;
3'd1: out = 4'b0010;
3'd2: out = 4'b0100;
3'd3: out = 4'b1000;
default:
out = 4'b0000;
endcase
end
endmodule
Последняя картинка:
Несмотря на то, что одна и та же функция демультиплексора была написана на Verilog четырьмя разными способами и даже в RTLViewer результат выглядит по разному, тем не менее, работать все эти наши модули будут абсолютно одинаково.
В этом легко убедиться с помощью тестбенча. Напишем тестовый модуль на Verilog:
`timescale 1ns / 1ns
module test();
//моделируем тактовую частоту
reg clk;
initial clk=0;
always
#5 clk=~clk;
//сделаем 2х битный счетчик
reg [1:0]cnt;
initial cnt=0;
always @(posedge clk)
cnt = cnt+1;
reg my_signal;
//моделируем провода подключенные к выходу демультиплексоров
wire out_a0,out_a1,out_a2,out_a3;
wire out_b0,out_b1,out_b2,out_b3;
wire out_c0,out_c1,out_c2,out_c3;
wire out_d0,out_d1,out_d2,out_d3;
//вставляем в тестбенч экземпляры демультиплексоров:
my_dmux0 dmx0(
.signal(my_signal),
.addr(cnt),
.out( {out_a3,out_a2,out_a1,out_a0} )
);
my_dmux1 dmx1(
.signal(my_signal),
.addr(cnt),
.out( {out_b3,out_b2,out_b1,out_b0} )
);
my_dmux2 dmx2(
.signal(my_signal),
.addr(cnt),
.out( {out_c3,out_c2,out_c1,out_c0} )
);
my_dmux3 dmx3(
.signal(my_signal),
.addr(cnt),
.out( {out_d3,out_d2,out_d1,out_d0} )
);
initial
begin
//объявляем генерируемый Waveform файл
$dumpfile("out.vcd");
$dumpvars(0,test);
my_signal = 1'b0;
#75;
my_signal = 1'b1;
#80;
my_signal = 1'b0;
#200 $finish;
end
endmodule
Тестбенч - это виртуальная среда тестирования модулей. Мы симулируем входные сигналы и наблюдаем за выходными сигналами. В среде тестбенча устанавливаем четыре разных модуля демультиплексора и подаем на их входы одинаковые сигналы. Посмотрим какие у них сигналы на выходах.
Я использую icarus verilog для симуляции проектов.
Вот такую временную диаграммы нам покажет программа GtkWave после симуляции тестбенча:
Видно, что все четыре модуля работают абсолютно одинаково.
Я скажу даже более того, несмотря на различия в описании на языке Verilog, внутри ПЛИС скорее всего получится один и тот же образ. В конечном счете, функция демультиплексора (как и декодера, дешифратора) - это табличное преобразование, которое кодируется в Look-Up Table (LUT) внутри ПЛИС.
Подробнее...